From e7126753f026aefe79ede311337aaf3afd53f44a Mon Sep 17 00:00:00 2001 From: Andrew Pollock Date: Thu, 24 Sep 2015 02:33:03 +0100 Subject: [PATCH] Import grpc_0.11.1.orig.tar.gz [dgit import orig grpc_0.11.1.orig.tar.gz] --- .clang-format | 5 + .editorconfig | 7 + .gitignore | 41 + .gitmodules | 17 + .travis.yml | 42 + BUILD | 1433 ++ CONTRIBUTING.md | 54 + INSTALL | 225 + LICENSE | 28 + Makefile | 20573 ++++++++++++++++ PATENTS | 22 + README.md | 109 + build.yaml | 1008 + composer.json | 18 + doc/PROTOCOL-HTTP2.md | 192 + ...ection-backoff-interop-test-description.md | 77 + doc/connection-backoff.md | 55 + doc/connectivity-semantics-and-api.md | 147 + doc/grpc-auth-support.md | 289 + doc/health-checking.md | 70 + doc/interop-test-descriptions.md | 1087 + doc/naming.md | 52 + doc/server-reflection.md | 183 + etc/roots.pem | 5114 ++++ examples/README.md | 450 + examples/cpp/README.md | 64 + examples/cpp/cpptutorial.md | 365 + examples/cpp/helloworld/Makefile | 119 + examples/cpp/helloworld/README.md | 260 + .../cpp/helloworld/greeter_async_client.cc | 125 + .../cpp/helloworld/greeter_async_server.cc | 178 + examples/cpp/helloworld/greeter_client.cc | 95 + examples/cpp/helloworld/greeter_server.cc | 83 + examples/cpp/route_guide/Makefile | 113 + examples/cpp/route_guide/helper.cc | 178 + examples/cpp/route_guide/helper.h | 50 + .../cpp/route_guide/route_guide_client.cc | 250 + examples/cpp/route_guide/route_guide_db.json | 601 + .../cpp/route_guide/route_guide_server.cc | 202 + examples/csharp/.gitignore | 5 + examples/csharp/.nuget/packages.config | 4 + .../csharp/helloworld/.nuget/packages.config | 4 + examples/csharp/helloworld/Greeter.sln | 42 + examples/csharp/helloworld/Greeter/.gitignore | 2 + .../csharp/helloworld/Greeter/Greeter.csproj | 74 + .../csharp/helloworld/Greeter/Helloworld.cs | 248 + .../helloworld/Greeter/HelloworldGrpc.cs | 89 + .../Greeter/Properties/AssemblyInfo.cs | 22 + .../csharp/helloworld/Greeter/packages.config | 10 + .../helloworld/GreeterClient/.gitignore | 2 + .../GreeterClient/GreeterClient.csproj | 77 + .../helloworld/GreeterClient/Program.cs | 24 + .../GreeterClient/Properties/AssemblyInfo.cs | 22 + .../helloworld/GreeterClient/packages.config | 10 + .../helloworld/GreeterServer/.gitignore | 2 + .../GreeterServer/GreeterServer.csproj | 77 + .../helloworld/GreeterServer/Program.cs | 37 + .../GreeterServer/Properties/AssemblyInfo.cs | 22 + .../helloworld/GreeterServer/packages.config | 10 + examples/csharp/helloworld/README.md | 72 + .../csharp/helloworld/generate_protos.bat | 10 + examples/csharp/route_guide/.gitignore | 5 + .../csharp/route_guide/.nuget/packages.config | 4 + examples/csharp/route_guide/README.md | 386 + examples/csharp/route_guide/RouteGuide.sln | 39 + .../RouteGuide/Properties/AssemblyInfo.cs | 36 + .../route_guide/RouteGuide/RouteGuide.cs | 776 + .../route_guide/RouteGuide/RouteGuide.csproj | 93 + .../route_guide/RouteGuide/RouteGuideGrpc.cs | 155 + .../route_guide/RouteGuide/RouteGuideUtil.cs | 112 + .../route_guide/RouteGuide/packages.config | 11 + .../RouteGuide/route_guide_db.json | 601 + .../route_guide/RouteGuideClient/App.config | 6 + .../route_guide/RouteGuideClient/Program.cs | 227 + .../Properties/AssemblyInfo.cs | 36 + .../RouteGuideClient/RouteGuideClient.csproj | 93 + .../RouteGuideClient/packages.config | 10 + .../route_guide/RouteGuideServer/App.config | 6 + .../route_guide/RouteGuideServer/Program.cs | 32 + .../Properties/AssemblyInfo.cs | 36 + .../RouteGuideServer/RouteGuideImpl.cs | 138 + .../RouteGuideServer/RouteGuideServer.csproj | 94 + .../RouteGuideServer/packages.config | 10 + .../csharp/route_guide/generate_protos.bat | 10 + examples/node/.gitignore | 3 + examples/node/README.md | 60 + examples/node/greeter_client.js | 53 + examples/node/greeter_server.js | 57 + examples/node/helloworld.proto | 50 + examples/node/package.json | 10 + examples/node/route_guide/README.md | 363 + examples/node/route_guide/route_guide.proto | 120 + .../node/route_guide/route_guide_client.js | 232 + examples/node/route_guide/route_guide_db.json | 601 + .../node/route_guide/route_guide_server.js | 247 + .../AuthSample.xcodeproj/project.pbxproj | 366 + .../contents.xcworkspacedata | 7 + .../auth_sample/AuthTestService.podspec | 35 + .../auth_sample/MakeRPCViewController.h | 40 + .../auth_sample/MakeRPCViewController.m | 100 + .../auth_sample/Misc/AppDelegate.h | 38 + .../auth_sample/Misc/AppDelegate.m | 61 + .../Misc/Base.lproj/Main.storyboard | 154 + .../auth_sample/Misc/GoogleService-Info.plist | 10 + .../AppIcon.appiconset/Contents.json | 68 + .../first.imageset/Contents.json | 12 + .../Images.xcassets/first.imageset/first.pdf | Bin 0 -> 2465 bytes .../second.imageset/Contents.json | 12 + .../second.imageset/second.pdf | Bin 0 -> 2423 bytes .../objective-c/auth_sample/Misc/Info.plist | 80 + examples/objective-c/auth_sample/Misc/main.m | 41 + examples/objective-c/auth_sample/Podfile | 10 + examples/objective-c/auth_sample/README.md | 189 + .../auth_sample/SelectUserViewController.h | 42 + .../auth_sample/SelectUserViewController.m | 86 + .../objective-c/helloworld/HelloWorld.podspec | 35 + .../HelloWorld.xcodeproj/project.pbxproj | 349 + .../contents.xcworkspacedata | 7 + .../helloworld/HelloWorld/AppDelegate.h | 38 + .../helloworld/HelloWorld/AppDelegate.m | 37 + .../HelloWorld/Base.lproj/Main.storyboard | 25 + .../AppIcon.appiconset/Contents.json | 68 + .../helloworld/HelloWorld/Info.plist | 47 + .../helloworld/HelloWorld/ViewController.m | 40 + examples/objective-c/helloworld/Podfile | 7 + examples/objective-c/helloworld/README.md | 56 + examples/objective-c/helloworld/main.m | 51 + .../route_guide/Misc/AppDelegate.h | 38 + .../route_guide/Misc/AppDelegate.m | 37 + .../Misc/Base.lproj/Main.storyboard | 193 + .../AppIcon.appiconset/Contents.json | 68 + .../first.imageset/Contents.json | 12 + .../Images.xcassets/first.imageset/first.pdf | Bin 0 -> 2465 bytes .../second.imageset/Contents.json | 12 + .../second.imageset/second.pdf | Bin 0 -> 2423 bytes .../objective-c/route_guide/Misc/Info.plist | 57 + examples/objective-c/route_guide/Misc/main.m | 41 + examples/objective-c/route_guide/Podfile | 7 + examples/objective-c/route_guide/README.md | 360 + .../route_guide/RouteGuide.podspec | 35 + .../project.pbxproj | 366 + .../contents.xcworkspacedata | 7 + .../objective-c/route_guide/ViewControllers.m | 228 + .../route_guide/route_guide_db.json | 121 + examples/php/.gitignore | 2 + examples/php/README.md | 64 + examples/php/composer.json | 17 + examples/php/greeter_client.php | 49 + examples/php/helloworld.php | 160 + examples/php/helloworld.proto | 50 + examples/php/route_guide/README.md | 262 + examples/php/route_guide/route_guide.php | 729 + examples/php/route_guide/route_guide.proto | 120 + .../php/route_guide/route_guide_client.php | 205 + .../php/route_guide/run_route_guide_client.sh | 36 + examples/php/run_greeter_client.sh | 35 + examples/protos/README.md | 8 + examples/protos/auth_sample.proto | 57 + examples/protos/hellostreamingworld.proto | 54 + examples/protos/helloworld.proto | 51 + examples/protos/route_guide.proto | 124 + examples/python/helloworld/.gitignore | 1 + examples/python/helloworld/README.md | 113 + examples/python/helloworld/greeter_client.py | 47 + examples/python/helloworld/greeter_server.py | 56 + examples/python/helloworld/run_client.sh | 8 + examples/python/helloworld/run_codegen.sh | 4 + examples/python/helloworld/run_server.sh | 9 + examples/python/route_guide/.gitignore | 1 + examples/python/route_guide/README.md | 299 + .../python/route_guide/route_guide_client.py | 133 + .../python/route_guide/route_guide_db.json | 601 + .../python/route_guide/route_guide_pb2.py | 481 + .../route_guide/route_guide_resources.py | 53 + .../python/route_guide/route_guide_server.py | 134 + examples/python/route_guide/run_client.sh | 8 + examples/python/route_guide/run_codegen.sh | 4 + examples/python/route_guide/run_server.sh | 8 + examples/ruby/.gitignore | 15 + examples/ruby/Gemfile | 6 + examples/ruby/README.md | 61 + examples/ruby/greeter_client.rb | 50 + examples/ruby/greeter_server.rb | 60 + examples/ruby/grpc-demo.gemspec | 23 + examples/ruby/lib/helloworld.rb | 18 + examples/ruby/lib/helloworld_services.rb | 24 + examples/ruby/lib/route_guide.rb | 37 + examples/ruby/lib/route_guide_services.rb | 27 + examples/ruby/route_guide/README.md | 285 + .../ruby/route_guide/route_guide_client.rb | 165 + .../ruby/route_guide/route_guide_server.rb | 211 + gRPC.podspec | 590 + grpc.bzl | 128 + include/grpc++/channel.h | 139 + include/grpc++/client_context.h | 336 + include/grpc++/completion_queue.h | 212 + include/grpc++/create_channel.h | 69 + .../grpc++/generic/async_generic_service.h | 78 + include/grpc++/generic/generic_stub.h | 63 + include/grpc++/grpc++.h | 64 + include/grpc++/impl/README.md | 4 + include/grpc++/impl/call.h | 583 + include/grpc++/impl/client_unary_call.h | 74 + include/grpc++/impl/grpc_library.h | 49 + include/grpc++/impl/proto_utils.h | 76 + include/grpc++/impl/rpc_method.h | 73 + include/grpc++/impl/rpc_service_method.h | 262 + include/grpc++/impl/serialization_traits.h | 68 + include/grpc++/impl/service_type.h | 123 + include/grpc++/impl/sync.h | 45 + include/grpc++/impl/sync_cxx11.h | 49 + include/grpc++/impl/sync_no_cxx11.h | 105 + include/grpc++/impl/thd.h | 45 + include/grpc++/impl/thd_cxx11.h | 45 + include/grpc++/impl/thd_no_cxx11.h | 94 + include/grpc++/security/auth_context.h | 110 + .../grpc++/security/auth_metadata_processor.h | 74 + include/grpc++/security/credentials.h | 170 + include/grpc++/security/server_credentials.h | 92 + include/grpc++/server.h | 302 + include/grpc++/server_builder.h | 148 + include/grpc++/server_context.h | 199 + include/grpc++/support/async_stream.h | 459 + include/grpc++/support/async_unary_call.h | 155 + include/grpc++/support/byte_buffer.h | 108 + include/grpc++/support/channel_arguments.h | 98 + include/grpc++/support/config.h | 116 + include/grpc++/support/config_protobuf.h | 72 + include/grpc++/support/slice.h | 88 + include/grpc++/support/status.h | 76 + include/grpc++/support/status_code_enum.h | 152 + include/grpc++/support/string_ref.h | 123 + include/grpc++/support/stub_options.h | 43 + include/grpc++/support/sync_stream.h | 415 + include/grpc++/support/time.h | 110 + include/grpc/byte_buffer.h | 117 + include/grpc/byte_buffer_reader.h | 58 + include/grpc/census.h | 488 + include/grpc/compression.h | 112 + include/grpc/grpc.h | 687 + include/grpc/grpc_security.h | 294 + include/grpc/grpc_zookeeper.h | 59 + include/grpc/status.h | 163 + include/grpc/support/alloc.h | 58 + include/grpc/support/atm.h | 92 + include/grpc/support/atm_gcc_atomic.h | 72 + include/grpc/support/atm_gcc_sync.h | 87 + include/grpc/support/atm_win32.h | 125 + include/grpc/support/cmdline.h | 97 + include/grpc/support/cpu.h | 57 + include/grpc/support/histogram.h | 76 + include/grpc/support/host_port.h | 64 + include/grpc/support/log.h | 108 + include/grpc/support/log_win32.h | 51 + include/grpc/support/port_platform.h | 334 + include/grpc/support/slice.h | 179 + include/grpc/support/slice_buffer.h | 96 + include/grpc/support/string_util.h | 61 + include/grpc/support/subprocess.h | 57 + include/grpc/support/sync.h | 315 + include/grpc/support/sync_generic.h | 55 + include/grpc/support/sync_posix.h | 47 + include/grpc/support/sync_win32.h | 49 + include/grpc/support/thd.h | 91 + include/grpc/support/time.h | 128 + include/grpc/support/tls.h | 77 + include/grpc/support/tls_gcc.h | 56 + include/grpc/support/tls_msvc.h | 56 + include/grpc/support/tls_pthread.h | 60 + include/grpc/support/useful.h | 75 + src/compiler/config.h | 93 + src/compiler/cpp_generator.cc | 1134 + src/compiler/cpp_generator.h | 81 + src/compiler/cpp_generator_helpers.h | 70 + src/compiler/cpp_plugin.cc | 122 + src/compiler/csharp_generator.cc | 539 + src/compiler/csharp_generator.h | 47 + src/compiler/csharp_generator_helpers.h | 50 + src/compiler/csharp_plugin.cc | 72 + src/compiler/generator_helpers.h | 170 + src/compiler/objective_c_generator.cc | 253 + src/compiler/objective_c_generator.h | 54 + src/compiler/objective_c_generator_helpers.h | 58 + src/compiler/objective_c_plugin.cc | 122 + src/compiler/python_generator.cc | 749 + src/compiler/python_generator.h | 69 + src/compiler/python_plugin.cc | 45 + src/compiler/ruby_generator.cc | 171 + src/compiler/ruby_generator.h | 45 + src/compiler/ruby_generator_helpers-inl.h | 65 + src/compiler/ruby_generator_map-inl.h | 73 + src/compiler/ruby_generator_string-inl.h | 134 + src/compiler/ruby_plugin.cc | 72 + src/core/README.md | 9 + src/core/census/README.md | 76 + src/core/census/aggregation.h | 66 + src/core/census/context.c | 46 + src/core/census/context.h | 49 + src/core/census/grpc_context.c | 49 + src/core/census/grpc_filter.c | 186 + src/core/census/grpc_filter.h | 44 + src/core/census/initialize.c | 57 + src/core/census/operation.c | 63 + src/core/census/rpc_metric_id.h | 51 + src/core/census/tracing.c | 45 + src/core/channel/channel_args.c | 211 + src/core/channel/channel_args.h | 90 + src/core/channel/channel_stack.c | 220 + src/core/channel/channel_stack.h | 198 + src/core/channel/client_channel.c | 773 + src/core/channel/client_channel.h | 70 + src/core/channel/compress_filter.c | 363 + src/core/channel/compress_filter.h | 65 + src/core/channel/connected_channel.c | 157 + src/core/channel/connected_channel.h | 49 + src/core/channel/context.h | 49 + src/core/channel/http_client_filter.c | 287 + src/core/channel/http_client_filter.h | 44 + src/core/channel/http_server_filter.c | 295 + src/core/channel/http_server_filter.h | 42 + src/core/channel/noop_filter.c | 131 + src/core/channel/noop_filter.h | 44 + src/core/client_config/README.md | 66 + src/core/client_config/client_config.c | 74 + src/core/client_config/client_config.h | 52 + src/core/client_config/connector.c | 49 + src/core/client_config/connector.h | 85 + .../client_config/lb_policies/pick_first.c | 332 + .../client_config/lb_policies/pick_first.h | 44 + src/core/client_config/lb_policy.c | 94 + src/core/client_config/lb_policy.h | 121 + src/core/client_config/resolver.c | 83 + src/core/client_config/resolver.h | 97 + src/core/client_config/resolver_factory.c | 56 + src/core/client_config/resolver_factory.h | 79 + src/core/client_config/resolver_registry.c | 133 + src/core/client_config/resolver_registry.h | 65 + .../client_config/resolvers/dns_resolver.c | 256 + .../client_config/resolvers/dns_resolver.h | 42 + .../resolvers/sockaddr_resolver.c | 357 + .../resolvers/sockaddr_resolver.h | 50 + .../resolvers/zookeeper_resolver.c | 506 + .../resolvers/zookeeper_resolver.h | 42 + src/core/client_config/subchannel.c | 732 + src/core/client_config/subchannel.h | 129 + src/core/client_config/subchannel_factory.c | 46 + src/core/client_config/subchannel_factory.h | 63 + .../add_channel_arg.c | 43 + .../add_channel_arg.h | 46 + .../merge_channel_args.c | 84 + .../merge_channel_args.h | 46 + src/core/client_config/uri_parser.c | 149 + src/core/client_config/uri_parser.h | 49 + src/core/compression/algorithm.c | 104 + src/core/compression/message_compress.c | 190 + src/core/compression/message_compress.h | 52 + src/core/debug/trace.c | 132 + src/core/debug/trace.h | 43 + src/core/httpcli/format_request.c | 120 + src/core/httpcli/format_request.h | 45 + src/core/httpcli/httpcli.c | 296 + src/core/httpcli/httpcli.h | 156 + src/core/httpcli/httpcli_security_connector.c | 177 + src/core/httpcli/parser.c | 214 + src/core/httpcli/parser.h | 64 + src/core/iomgr/alarm.c | 374 + src/core/iomgr/alarm.h | 89 + src/core/iomgr/alarm_heap.c | 148 + src/core/iomgr/alarm_heap.h | 57 + src/core/iomgr/alarm_internal.h | 62 + src/core/iomgr/endpoint.c | 63 + src/core/iomgr/endpoint.h | 106 + src/core/iomgr/endpoint_pair.h | 47 + src/core/iomgr/endpoint_pair_posix.c | 79 + src/core/iomgr/endpoint_pair_windows.c | 97 + src/core/iomgr/fd_posix.c | 460 + src/core/iomgr/fd_posix.h | 182 + src/core/iomgr/iocp_windows.c | 198 + src/core/iomgr/iocp_windows.h | 52 + src/core/iomgr/iomgr.c | 280 + src/core/iomgr/iomgr.h | 80 + src/core/iomgr/iomgr_internal.h | 55 + src/core/iomgr/iomgr_posix.c | 54 + src/core/iomgr/iomgr_posix.h | 42 + src/core/iomgr/iomgr_windows.c | 71 + src/core/iomgr/pollset.h | 87 + .../iomgr/pollset_multipoller_with_epoll.c | 262 + .../pollset_multipoller_with_poll_posix.c | 233 + src/core/iomgr/pollset_posix.c | 506 + src/core/iomgr/pollset_posix.h | 127 + src/core/iomgr/pollset_set.h | 59 + src/core/iomgr/pollset_set_posix.c | 131 + src/core/iomgr/pollset_set_posix.h | 55 + src/core/iomgr/pollset_set_windows.c | 50 + src/core/iomgr/pollset_set_windows.h | 39 + src/core/iomgr/pollset_windows.c | 150 + src/core/iomgr/pollset_windows.h | 61 + src/core/iomgr/resolve_address.h | 69 + src/core/iomgr/resolve_address_posix.c | 181 + src/core/iomgr/resolve_address_windows.c | 163 + src/core/iomgr/sockaddr.h | 47 + src/core/iomgr/sockaddr_posix.h | 44 + src/core/iomgr/sockaddr_utils.c | 228 + src/core/iomgr/sockaddr_utils.h | 89 + src/core/iomgr/sockaddr_win32.h | 46 + src/core/iomgr/socket_utils_common_posix.c | 208 + src/core/iomgr/socket_utils_linux.c | 51 + src/core/iomgr/socket_utils_posix.c | 70 + src/core/iomgr/socket_utils_posix.h | 113 + src/core/iomgr/socket_windows.c | 97 + src/core/iomgr/socket_windows.h | 111 + src/core/iomgr/tcp_client.h | 52 + src/core/iomgr/tcp_client_posix.c | 278 + src/core/iomgr/tcp_client_windows.c | 216 + src/core/iomgr/tcp_posix.c | 451 + src/core/iomgr/tcp_posix.h | 59 + src/core/iomgr/tcp_server.h | 78 + src/core/iomgr/tcp_server_posix.c | 509 + src/core/iomgr/tcp_server_windows.c | 481 + src/core/iomgr/tcp_windows.c | 402 + src/core/iomgr/tcp_windows.h | 57 + src/core/iomgr/time_averaged_stats.c | 77 + src/core/iomgr/time_averaged_stats.h | 88 + src/core/iomgr/udp_server.c | 440 + src/core/iomgr/udp_server.h | 85 + src/core/iomgr/wakeup_fd_eventfd.c | 81 + src/core/iomgr/wakeup_fd_nospecial.c | 51 + src/core/iomgr/wakeup_fd_pipe.c | 97 + src/core/iomgr/wakeup_fd_pipe.h | 41 + src/core/iomgr/wakeup_fd_posix.c | 74 + src/core/iomgr/wakeup_fd_posix.h | 99 + src/core/json/json.c | 64 + src/core/json/json.h | 88 + src/core/json/json_common.h | 49 + src/core/json/json_reader.c | 659 + src/core/json/json_reader.h | 160 + src/core/json/json_string.c | 379 + src/core/json/json_writer.c | 260 + src/core/json/json_writer.h | 97 + src/core/profiling/basic_timers.c | 140 + src/core/profiling/stap_probes.d | 7 + src/core/profiling/stap_timers.c | 65 + src/core/profiling/timers.h | 146 + src/core/security/auth_filters.h | 42 + src/core/security/base64.c | 231 + src/core/security/base64.h | 52 + src/core/security/client_auth_filter.c | 355 + src/core/security/credentials.c | 1187 + src/core/security/credentials.h | 325 + src/core/security/credentials_metadata.c | 101 + src/core/security/credentials_posix.c | 61 + src/core/security/credentials_win32.c | 61 + .../security/google_default_credentials.c | 225 + src/core/security/json_token.c | 405 + src/core/security/json_token.h | 118 + src/core/security/jwt_verifier.c | 835 + src/core/security/jwt_verifier.h | 135 + src/core/security/secure_endpoint.c | 393 + src/core/security/secure_endpoint.h | 49 + src/core/security/secure_transport_setup.c | 312 + src/core/security/secure_transport_setup.h | 53 + src/core/security/security_connector.c | 680 + src/core/security/security_connector.h | 223 + src/core/security/security_context.c | 314 + src/core/security/security_context.h | 115 + src/core/security/server_auth_filter.c | 271 + src/core/security/server_secure_chttp2.c | 294 + src/core/statistics/census_init.c | 48 + src/core/statistics/census_interface.h | 76 + src/core/statistics/census_log.c | 604 + src/core/statistics/census_log.h | 91 + src/core/statistics/census_rpc_stats.c | 252 + src/core/statistics/census_rpc_stats.h | 101 + src/core/statistics/census_tracing.c | 240 + src/core/statistics/census_tracing.h | 96 + src/core/statistics/hash_table.c | 303 + src/core/statistics/hash_table.h | 131 + src/core/statistics/window_stats.c | 317 + src/core/statistics/window_stats.h | 173 + src/core/support/alloc.c | 66 + src/core/support/cmdline.c | 323 + src/core/support/cpu_iphone.c | 49 + src/core/support/cpu_linux.c | 78 + src/core/support/cpu_posix.c | 77 + src/core/support/cpu_windows.c | 47 + src/core/support/env.h | 60 + src/core/support/env_linux.c | 62 + src/core/support/env_posix.c | 57 + src/core/support/env_win32.c | 65 + src/core/support/file.c | 87 + src/core/support/file.h | 63 + src/core/support/file_posix.c | 85 + src/core/support/file_win32.c | 84 + src/core/support/histogram.c | 244 + src/core/support/host_port.c | 110 + src/core/support/log.c | 65 + src/core/support/log_android.c | 87 + src/core/support/log_linux.c | 103 + src/core/support/log_posix.c | 100 + src/core/support/log_win32.c | 123 + src/core/support/murmur_hash.c | 96 + src/core/support/murmur_hash.h | 44 + src/core/support/slice.c | 335 + src/core/support/slice_buffer.c | 231 + src/core/support/stack_lockfree.c | 175 + src/core/support/stack_lockfree.h | 51 + src/core/support/string.c | 275 + src/core/support/string.h | 111 + src/core/support/string_posix.c | 86 + src/core/support/string_win32.c | 107 + src/core/support/string_win32.h | 47 + src/core/support/subprocess_posix.c | 112 + src/core/support/sync.c | 122 + src/core/support/sync_posix.c | 92 + src/core/support/sync_win32.c | 128 + src/core/support/thd.c | 64 + src/core/support/thd_internal.h | 39 + src/core/support/thd_posix.c | 91 + src/core/support/thd_win32.c | 117 + src/core/support/time.c | 320 + src/core/support/time_posix.c | 146 + src/core/support/time_precise.h | 93 + src/core/support/time_win32.c | 99 + src/core/support/tls_pthread.c | 45 + src/core/surface/byte_buffer.c | 100 + src/core/surface/byte_buffer_queue.c | 97 + src/core/surface/byte_buffer_queue.h | 62 + src/core/surface/byte_buffer_reader.c | 105 + src/core/surface/call.c | 1751 ++ src/core/surface/call.h | 184 + src/core/surface/call_details.c | 46 + src/core/surface/call_log_batch.c | 147 + src/core/surface/channel.c | 369 + src/core/surface/channel.h | 80 + src/core/surface/channel_connectivity.c | 187 + src/core/surface/channel_create.c | 198 + src/core/surface/completion_queue.c | 342 + src/core/surface/completion_queue.h | 83 + src/core/surface/event_string.c | 81 + src/core/surface/event_string.h | 42 + src/core/surface/init.c | 145 + src/core/surface/init.h | 40 + src/core/surface/init_secure.c | 42 + src/core/surface/init_unsecure.c | 36 + src/core/surface/lame_client.c | 160 + src/core/surface/metadata_array.c | 45 + src/core/surface/secure_channel_create.c | 260 + src/core/surface/server.c | 1305 + src/core/surface/server.h | 67 + src/core/surface/server_chttp2.c | 139 + src/core/surface/server_create.c | 44 + src/core/surface/surface_trace.c | 36 + src/core/surface/surface_trace.h | 49 + src/core/surface/version.c | 41 + src/core/transport/chttp2/alpn.c | 56 + src/core/transport/chttp2/alpn.h | 49 + src/core/transport/chttp2/bin_encoder.c | 227 + src/core/transport/chttp2/bin_encoder.h | 56 + src/core/transport/chttp2/frame.h | 69 + src/core/transport/chttp2/frame_data.c | 170 + src/core/transport/chttp2/frame_data.h | 82 + src/core/transport/chttp2/frame_goaway.c | 191 + src/core/transport/chttp2/frame_goaway.h | 75 + src/core/transport/chttp2/frame_ping.c | 105 + src/core/transport/chttp2/frame_ping.h | 54 + src/core/transport/chttp2/frame_rst_stream.c | 99 + src/core/transport/chttp2/frame_rst_stream.h | 53 + src/core/transport/chttp2/frame_settings.c | 246 + src/core/transport/chttp2/frame_settings.h | 100 + .../transport/chttp2/frame_window_update.c | 114 + .../transport/chttp2/frame_window_update.h | 56 + src/core/transport/chttp2/hpack_parser.c | 1407 ++ src/core/transport/chttp2/hpack_parser.h | 113 + src/core/transport/chttp2/hpack_table.c | 224 + src/core/transport/chttp2/hpack_table.h | 97 + src/core/transport/chttp2/hpack_tables.txt | 66 + src/core/transport/chttp2/http2_errors.h | 56 + src/core/transport/chttp2/huffsyms.c | 105 + src/core/transport/chttp2/huffsyms.h | 48 + src/core/transport/chttp2/incoming_metadata.c | 182 + src/core/transport/chttp2/incoming_metadata.h | 80 + src/core/transport/chttp2/internal.h | 633 + src/core/transport/chttp2/parsing.c | 816 + src/core/transport/chttp2/status_conversion.c | 109 + src/core/transport/chttp2/status_conversion.h | 50 + src/core/transport/chttp2/stream_encoder.c | 650 + src/core/transport/chttp2/stream_encoder.h | 93 + src/core/transport/chttp2/stream_lists.c | 403 + src/core/transport/chttp2/stream_map.c | 197 + src/core/transport/chttp2/stream_map.h | 85 + src/core/transport/chttp2/timeout_encoding.c | 184 + src/core/transport/chttp2/timeout_encoding.h | 47 + src/core/transport/chttp2/varint.c | 65 + src/core/transport/chttp2/varint.h | 73 + src/core/transport/chttp2/writing.c | 246 + src/core/transport/chttp2_transport.c | 1290 + src/core/transport/chttp2_transport.h | 50 + src/core/transport/connectivity_state.c | 148 + src/core/transport/connectivity_state.h | 81 + src/core/transport/metadata.c | 716 + src/core/transport/metadata.h | 184 + src/core/transport/stream_op.c | 331 + src/core/transport/stream_op.h | 212 + src/core/transport/transport.c | 138 + src/core/transport/transport.h | 199 + src/core/transport/transport_impl.h | 72 + src/core/transport/transport_op_string.c | 166 + src/core/tsi/fake_transport_security.c | 525 + src/core/tsi/fake_transport_security.h | 61 + src/core/tsi/ssl_transport_security.c | 1433 ++ src/core/tsi/ssl_transport_security.h | 173 + src/core/tsi/test_creds/README | 62 + src/core/tsi/test_creds/badclient.key | 16 + src/core/tsi/test_creds/badclient.pem | 17 + src/core/tsi/test_creds/badserver.key | 16 + src/core/tsi/test_creds/badserver.pem | 17 + src/core/tsi/test_creds/ca-openssl.cnf | 17 + src/core/tsi/test_creds/ca.key | 16 + src/core/tsi/test_creds/ca.pem | 15 + src/core/tsi/test_creds/client.key | 16 + src/core/tsi/test_creds/client.pem | 14 + src/core/tsi/test_creds/server0.key | 16 + src/core/tsi/test_creds/server0.pem | 14 + src/core/tsi/test_creds/server1-openssl.cnf | 26 + src/core/tsi/test_creds/server1.key | 16 + src/core/tsi/test_creds/server1.pem | 16 + src/core/tsi/transport_security.c | 280 + src/core/tsi/transport_security.h | 111 + src/core/tsi/transport_security_interface.h | 344 + src/cpp/README.md | 9 + src/cpp/client/channel.cc | 142 + src/cpp/client/channel_arguments.cc | 111 + src/cpp/client/client_context.cc | 119 + src/cpp/client/create_channel.cc | 67 + src/cpp/client/create_channel_internal.cc | 46 + src/cpp/client/create_channel_internal.h | 51 + src/cpp/client/credentials.cc | 40 + src/cpp/client/generic_stub.cc | 50 + src/cpp/client/insecure_credentials.cc | 68 + src/cpp/client/secure_channel_arguments.cc | 54 + src/cpp/client/secure_credentials.cc | 147 + src/cpp/client/secure_credentials.h | 61 + src/cpp/common/auth_property_iterator.cc | 85 + src/cpp/common/call.cc | 92 + src/cpp/common/completion_queue.cc | 95 + src/cpp/common/create_auth_context.h | 42 + .../common/insecure_create_auth_context.cc | 45 + src/cpp/common/rpc_method.cc | 36 + src/cpp/common/secure_auth_context.cc | 118 + src/cpp/common/secure_auth_context.h | 75 + src/cpp/common/secure_create_auth_context.cc | 50 + src/cpp/proto/proto_utils.cc | 184 + src/cpp/server/async_generic_service.cc | 48 + src/cpp/server/create_default_thread_pool.cc | 50 + src/cpp/server/dynamic_thread_pool.cc | 134 + src/cpp/server/dynamic_thread_pool.h | 83 + src/cpp/server/fixed_size_thread_pool.cc | 85 + src/cpp/server/fixed_size_thread_pool.h | 67 + src/cpp/server/insecure_server_credentials.cc | 60 + src/cpp/server/secure_server_credentials.cc | 141 + src/cpp/server/secure_server_credentials.h | 88 + src/cpp/server/server.cc | 564 + src/cpp/server/server_builder.cc | 138 + src/cpp/server/server_context.cc | 221 + src/cpp/server/server_credentials.cc | 40 + src/cpp/server/thread_pool_interface.h | 54 + src/cpp/util/byte_buffer.cc | 82 + src/cpp/util/slice.cc | 48 + src/cpp/util/status.cc | 41 + src/cpp/util/string_ref.cc | 116 + src/cpp/util/time.cc | 95 + src/csharp/.gitignore | 12 + src/csharp/.nuget/packages.config | 4 + src/csharp/Grpc.Auth/.gitignore | 3 + src/csharp/Grpc.Auth/AuthInterceptors.cs | 88 + src/csharp/Grpc.Auth/Grpc.Auth.csproj | 103 + src/csharp/Grpc.Auth/Grpc.Auth.nuspec | 28 + .../Grpc.Auth/Properties/AssemblyInfo.cs | 11 + src/csharp/Grpc.Auth/app.config | 19 + src/csharp/Grpc.Auth/packages.config | 11 + src/csharp/Grpc.Core.Tests/.gitignore | 3 + .../Grpc.Core.Tests/ChannelOptionsTest.cs | 105 + src/csharp/Grpc.Core.Tests/ChannelTest.cs | 82 + src/csharp/Grpc.Core.Tests/ClientBaseTest.cs | 62 + .../Grpc.Core.Tests/ClientServerTest.cs | 286 + src/csharp/Grpc.Core.Tests/CompressionTest.cs | 122 + .../Grpc.Core.Tests/ContextPropagationTest.cs | 147 + .../Grpc.Core.Tests/Grpc.Core.Tests.csproj | 106 + .../Grpc.Core.Tests/GrpcEnvironmentTest.cs | 90 + .../Grpc.Core.Tests/Internal/AsyncCallTest.cs | 222 + .../Internal/ChannelArgsSafeHandleTest.cs | 75 + .../Internal/CompletionQueueEventTest.cs | 52 + .../Internal/CompletionQueueSafeHandleTest.cs | 64 + .../Internal/MetadataArraySafeHandleTest.cs | 84 + .../Grpc.Core.Tests/Internal/TimespecTest.cs | 202 + .../Grpc.Core.Tests/MarshallingErrorsTest.cs | 176 + src/csharp/Grpc.Core.Tests/MetadataTest.cs | 131 + .../Grpc.Core.Tests/MockServiceHelper.cs | 250 + .../Grpc.Core.Tests/NUnitVersionTest.cs | 77 + src/csharp/Grpc.Core.Tests/PInvokeTest.cs | 130 + .../Properties/AssemblyInfo.cs | 11 + .../Grpc.Core.Tests/ResponseHeadersTest.cs | 207 + src/csharp/Grpc.Core.Tests/ServerTest.cs | 86 + src/csharp/Grpc.Core.Tests/ShutdownTest.cs | 77 + src/csharp/Grpc.Core.Tests/TestResult.xml | 41 + src/csharp/Grpc.Core.Tests/TimeoutsTest.cs | 154 + src/csharp/Grpc.Core.Tests/packages.config | 6 + src/csharp/Grpc.Core/.gitignore | 2 + .../Grpc.Core/AsyncClientStreamingCall.cs | 135 + .../Grpc.Core/AsyncDuplexStreamingCall.cs | 125 + .../Grpc.Core/AsyncServerStreamingCall.cs | 111 + src/csharp/Grpc.Core/AsyncUnaryCall.cs | 120 + src/csharp/Grpc.Core/CallInvocationDetails.cs | 173 + src/csharp/Grpc.Core/CallOptions.cs | 181 + src/csharp/Grpc.Core/Calls.cs | 137 + src/csharp/Grpc.Core/Channel.cs | 250 + src/csharp/Grpc.Core/ChannelOptions.cs | 210 + src/csharp/Grpc.Core/ChannelState.cs | 69 + src/csharp/Grpc.Core/ClientBase.cs | 138 + src/csharp/Grpc.Core/CompressionLevel.cs | 63 + .../Grpc.Core/ContextPropagationToken.cs | 170 + src/csharp/Grpc.Core/Credentials.cs | 138 + src/csharp/Grpc.Core/Grpc.Core.csproj | 151 + src/csharp/Grpc.Core/Grpc.Core.nuspec | 29 + src/csharp/Grpc.Core/GrpcEnvironment.cs | 214 + src/csharp/Grpc.Core/IAsyncStreamReader.cs | 50 + src/csharp/Grpc.Core/IAsyncStreamWriter.cs | 62 + src/csharp/Grpc.Core/IClientStreamWriter.cs | 53 + src/csharp/Grpc.Core/IServerStreamWriter.cs | 49 + src/csharp/Grpc.Core/Internal/AsyncCall.cs | 437 + .../Grpc.Core/Internal/AsyncCallBase.cs | 361 + .../Grpc.Core/Internal/AsyncCallServer.cs | 218 + .../Grpc.Core/Internal/AsyncCompletion.cs | 94 + .../Grpc.Core/Internal/AtomicCounter.cs | 61 + .../Internal/BatchContextSafeHandle.cs | 266 + .../Grpc.Core/Internal/CStringSafeHandle.cs | 60 + .../Grpc.Core/Internal/CallSafeHandle.cs | 229 + .../Internal/ChannelArgsSafeHandle.cs | 84 + .../Grpc.Core/Internal/ChannelSafeHandle.cs | 120 + .../Grpc.Core/Internal/ClientRequestStream.cs | 84 + .../Internal/ClientResponseStream.cs | 89 + .../Internal/CompletionQueueEvent.cs | 60 + .../Internal/CompletionQueueSafeHandle.cs | 87 + .../Grpc.Core/Internal/CompletionRegistry.cs | 98 + .../Internal/CredentialsSafeHandle.cs | 78 + src/csharp/Grpc.Core/Internal/DebugStats.cs | 60 + src/csharp/Grpc.Core/Internal/Enums.cs | 108 + .../Grpc.Core/Internal/GrpcThreadPool.cs | 139 + src/csharp/Grpc.Core/Internal/INativeCall.cs | 85 + .../Internal/MetadataArraySafeHandle.cs | 117 + .../Grpc.Core/Internal/NativeLogRedirector.cs | 107 + .../Internal/SafeHandleZeroIsInvalid.cs | 66 + .../Grpc.Core/Internal/ServerCallHandler.cs | 317 + src/csharp/Grpc.Core/Internal/ServerCalls.cs | 71 + .../Internal/ServerCredentialsSafeHandle.cs | 69 + .../Grpc.Core/Internal/ServerRequestStream.cs | 83 + .../Internal/ServerResponseStream.cs | 93 + .../Grpc.Core/Internal/ServerSafeHandle.cs | 125 + src/csharp/Grpc.Core/Internal/Timespec.cs | 250 + src/csharp/Grpc.Core/KeyCertificatePair.cs | 83 + src/csharp/Grpc.Core/Logging/ConsoleLogger.cs | 150 + src/csharp/Grpc.Core/Logging/ILogger.cs | 75 + src/csharp/Grpc.Core/Marshaller.cs | 106 + src/csharp/Grpc.Core/Metadata.cs | 333 + src/csharp/Grpc.Core/Method.cs | 191 + .../Grpc.Core/Properties/AssemblyInfo.cs | 21 + src/csharp/Grpc.Core/RpcException.cs | 75 + src/csharp/Grpc.Core/Server.cs | 389 + src/csharp/Grpc.Core/ServerCallContext.cs | 201 + src/csharp/Grpc.Core/ServerCredentials.cs | 160 + src/csharp/Grpc.Core/ServerMethods.cs | 73 + src/csharp/Grpc.Core/ServerPort.cs | 120 + .../Grpc.Core/ServerServiceDefinition.cs | 172 + src/csharp/Grpc.Core/Status.cs | 95 + src/csharp/Grpc.Core/StatusCode.cs | 139 + .../Grpc.Core/Utils/AsyncStreamExtensions.cs | 100 + src/csharp/Grpc.Core/Utils/BenchmarkUtil.cs | 72 + src/csharp/Grpc.Core/Utils/Preconditions.cs | 120 + src/csharp/Grpc.Core/Version.cs | 37 + src/csharp/Grpc.Core/VersionInfo.cs | 46 + src/csharp/Grpc.Core/WriteOptions.cs | 89 + src/csharp/Grpc.Core/packages.config | 6 + .../Grpc.Examples.MathClient/.gitignore | 2 + .../Grpc.Examples.MathClient.csproj | 60 + .../Grpc.Examples.MathClient/MathClient.cs | 59 + .../Properties/AssemblyInfo.cs | 11 + .../Grpc.Examples.MathServer/.gitignore | 2 + .../Grpc.Examples.MathServer.csproj | 60 + .../Grpc.Examples.MathServer/MathServer.cs | 61 + .../Properties/AssemblyInfo.cs | 11 + src/csharp/Grpc.Examples.Tests/.gitignore | 3 + .../Grpc.Examples.Tests.csproj | 77 + .../MathClientServerTests.cs | 193 + .../Properties/AssemblyInfo.cs | 11 + .../Grpc.Examples.Tests/packages.config | 6 + src/csharp/Grpc.Examples/.gitignore | 3 + src/csharp/Grpc.Examples/Grpc.Examples.csproj | 71 + src/csharp/Grpc.Examples/Math.cs | 616 + src/csharp/Grpc.Examples/MathExamples.cs | 113 + src/csharp/Grpc.Examples/MathGrpc.cs | 154 + src/csharp/Grpc.Examples/MathServiceImpl.cs | 112 + .../Grpc.Examples/Properties/AssemblyInfo.cs | 11 + src/csharp/Grpc.Examples/Settings.StyleCop | 10 + src/csharp/Grpc.Examples/packages.config | 6 + src/csharp/Grpc.Examples/proto/math.proto | 80 + src/csharp/Grpc.HealthCheck.Tests/.gitignore | 2 + .../Grpc.HealthCheck.Tests.csproj | 87 + .../HealthClientServerTest.cs | 96 + .../HealthServiceImplTest.cs | 107 + .../Properties/AssemblyInfo.cs | 11 + .../Grpc.HealthCheck.Tests/packages.config | 5 + src/csharp/Grpc.HealthCheck/.gitignore | 2 + .../Grpc.HealthCheck/Grpc.HealthCheck.csproj | 84 + .../Grpc.HealthCheck/Grpc.HealthCheck.nuspec | 28 + src/csharp/Grpc.HealthCheck/Health.cs | 293 + src/csharp/Grpc.HealthCheck/HealthGrpc.cs | 89 + .../Grpc.HealthCheck/HealthServiceImpl.cs | 138 + .../Properties/AssemblyInfo.cs | 11 + src/csharp/Grpc.HealthCheck/Settings.StyleCop | 10 + src/csharp/Grpc.HealthCheck/packages.config | 5 + .../Grpc.HealthCheck/proto/health.proto | 52 + .../Grpc.IntegrationTesting.Client/.gitignore | 3 + .../Grpc.IntegrationTesting.Client.csproj | 64 + .../Grpc.IntegrationTesting.Client/Program.cs | 46 + .../Properties/AssemblyInfo.cs | 11 + .../Grpc.IntegrationTesting.Client/app.config | 19 + .../Grpc.IntegrationTesting.Server/.gitignore | 3 + .../Grpc.IntegrationTesting.Server.csproj | 64 + .../Grpc.IntegrationTesting.Server/Program.cs | 45 + .../Properties/AssemblyInfo.cs | 11 + .../Grpc.IntegrationTesting.Server/app.config | 19 + src/csharp/Grpc.IntegrationTesting/.gitignore | 2 + src/csharp/Grpc.IntegrationTesting/Empty.cs | 118 + .../Grpc.IntegrationTesting.csproj | 147 + .../Grpc.IntegrationTesting/InteropClient.cs | 491 + .../InteropClientServerTest.cs | 136 + .../Grpc.IntegrationTesting/InteropServer.cs | 116 + .../Grpc.IntegrationTesting/Messages.cs | 1172 + .../Properties/AssemblyInfo.cs | 11 + .../Grpc.IntegrationTesting/Settings.StyleCop | 11 + .../SslCredentialsTest.cs | 99 + src/csharp/Grpc.IntegrationTesting/Test.cs | 48 + .../TestCredentials.cs | 81 + .../Grpc.IntegrationTesting/TestGrpc.cs | 211 + .../TestServiceGrpc.cs | 204 + .../TestServiceImpl.cs | 101 + src/csharp/Grpc.IntegrationTesting/app.config | 19 + .../Grpc.IntegrationTesting/data/README | 1 + .../Grpc.IntegrationTesting/data/ca.pem | 15 + .../Grpc.IntegrationTesting/data/server1.key | 16 + .../Grpc.IntegrationTesting/data/server1.pem | 16 + .../Grpc.IntegrationTesting/packages.config | 16 + .../Grpc.IntegrationTesting/proto/empty.proto | 43 + .../proto/messages.proto | 132 + .../Grpc.IntegrationTesting/proto/test.proto | 72 + src/csharp/Grpc.Tools.nuspec | 21 + src/csharp/Grpc.nuspec | 22 + src/csharp/Grpc.sln | 118 + src/csharp/README.md | 177 + src/csharp/Settings.StyleCop | 509 + src/csharp/build_packages.bat | 34 + src/csharp/buildall.bat | 27 + src/csharp/doc/README.md | 2 + src/csharp/doc/grpc_csharp_public.shfbproj | 80 + src/csharp/ext/grpc_csharp_ext.c | 934 + src/csharp/generate_proto_csharp.sh | 48 + src/csharp/keys/Grpc.public.snk | Bin 0 -> 160 bytes src/csharp/keys/README.md | 5 + src/node/.gitignore | 2 + src/node/.jshintrc | 28 + src/node/LICENSE | 28 + src/node/README.md | 116 + src/node/bin/README.md | 16 + src/node/bin/service_packager | 2 + src/node/binding.gyp | 85 + src/node/cli/service_packager.js | 142 + src/node/cli/service_packager/index.js | 36 + .../service_packager/package.json.template | 17 + src/node/examples/math.proto | 80 + src/node/examples/math_server.js | 125 + src/node/examples/perf_test.js | 119 + src/node/examples/qps_test.js | 137 + src/node/examples/stock.proto | 62 + src/node/examples/stock_client.js | 47 + src/node/examples/stock_server.js | 87 + src/node/ext/byte_buffer.cc | 99 + src/node/ext/byte_buffer.h | 60 + src/node/ext/call.cc | 728 + src/node/ext/call.h | 135 + src/node/ext/channel.cc | 261 + src/node/ext/channel.h | 78 + src/node/ext/completion_queue_async_worker.cc | 117 + src/node/ext/completion_queue_async_worker.h | 86 + src/node/ext/credentials.cc | 246 + src/node/ext/credentials.h | 82 + src/node/ext/node_grpc.cc | 250 + src/node/ext/server.cc | 317 + src/node/ext/server.h | 82 + src/node/ext/server_credentials.cc | 217 + src/node/ext/server_credentials.h | 77 + src/node/ext/timeval.cc | 67 + src/node/ext/timeval.h | 48 + src/node/health_check/health.js | 66 + src/node/health_check/health.proto | 49 + src/node/index.js | 176 + src/node/interop/empty.proto | 43 + src/node/interop/interop_client.js | 420 + src/node/interop/interop_server.js | 203 + src/node/interop/messages.proto | 132 + src/node/interop/test.proto | 73 + src/node/jsdoc_conf.json | 22 + src/node/package.json | 61 + src/node/src/client.js | 733 + src/node/src/common.js | 140 + src/node/src/metadata.js | 181 + src/node/src/server.js | 766 + src/node/test/call_test.js | 201 + src/node/test/channel_test.js | 190 + src/node/test/common_test.js | 90 + src/node/test/constant_test.js | 131 + src/node/test/data/README | 1 + src/node/test/data/ca.pem | 15 + src/node/test/data/server1.key | 16 + src/node/test/data/server1.pem | 16 + src/node/test/echo_service.proto | 39 + src/node/test/end_to_end_test.js | 306 + src/node/test/health_test.js | 92 + src/node/test/interop_sanity_test.js | 93 + src/node/test/math_client_test.js | 139 + src/node/test/metadata_test.js | 193 + src/node/test/server_test.js | 132 + src/node/test/surface_test.js | 905 + src/node/test/test_messages.proto | 38 + src/node/test/test_service.json | 55 + src/node/test/test_service.proto | 52 + src/objective-c/.gitignore | 19 + src/objective-c/GRPCClient/GRPCCall+OAuth2.h | 49 + src/objective-c/GRPCClient/GRPCCall+OAuth2.m | 63 + src/objective-c/GRPCClient/GRPCCall+Tests.h | 54 + src/objective-c/GRPCClient/GRPCCall+Tests.m | 53 + src/objective-c/GRPCClient/GRPCCall.h | 207 + src/objective-c/GRPCClient/GRPCCall.m | 388 + src/objective-c/GRPCClient/README.md | 4 + .../GRPCClient/private/GRPCChannel.h | 46 + .../GRPCClient/private/GRPCChannel.m | 60 + .../GRPCClient/private/GRPCCompletionQueue.h | 52 + .../GRPCClient/private/GRPCCompletionQueue.m | 94 + src/objective-c/GRPCClient/private/GRPCHost.h | 58 + src/objective-c/GRPCClient/private/GRPCHost.m | 129 + .../GRPCClient/private/GRPCRequestHeaders.h | 52 + .../GRPCClient/private/GRPCRequestHeaders.m | 118 + .../GRPCClient/private/GRPCSecureChannel.h | 53 + .../GRPCClient/private/GRPCSecureChannel.m | 116 + .../GRPCClient/private/GRPCUnsecuredChannel.h | 38 + .../GRPCClient/private/GRPCUnsecuredChannel.m | 50 + .../GRPCClient/private/GRPCWrappedCall.h | 97 + .../GRPCClient/private/GRPCWrappedCall.m | 303 + .../GRPCClient/private/NSData+GRPC.h | 41 + .../GRPCClient/private/NSData+GRPC.m | 92 + .../GRPCClient/private/NSDictionary+GRPC.h | 41 + .../GRPCClient/private/NSDictionary+GRPC.m | 126 + .../GRPCClient/private/NSError+GRPC.h | 41 + .../GRPCClient/private/NSError+GRPC.m | 50 + src/objective-c/ProtoRPC/ProtoMethod.h | 48 + src/objective-c/ProtoRPC/ProtoMethod.m | 55 + src/objective-c/ProtoRPC/ProtoRPC.h | 48 + src/objective-c/ProtoRPC/ProtoRPC.m | 119 + src/objective-c/ProtoRPC/ProtoService.h | 49 + src/objective-c/ProtoRPC/ProtoService.m | 81 + src/objective-c/README.md | 174 + src/objective-c/RxLibrary/GRXBufferedPipe.h | 60 + src/objective-c/RxLibrary/GRXBufferedPipe.m | 146 + .../RxLibrary/GRXConcurrentWriteable.h | 71 + .../RxLibrary/GRXConcurrentWriteable.m | 110 + .../RxLibrary/GRXForwardingWriter.h | 49 + .../RxLibrary/GRXForwardingWriter.m | 116 + .../RxLibrary/GRXImmediateWriter.h | 80 + .../RxLibrary/GRXImmediateWriter.m | 152 + src/objective-c/RxLibrary/GRXWriteable.h | 64 + src/objective-c/RxLibrary/GRXWriteable.m | 90 + .../RxLibrary/GRXWriter+Immediate.h | 66 + .../RxLibrary/GRXWriter+Immediate.m | 64 + .../RxLibrary/GRXWriter+Transformations.h | 42 + .../RxLibrary/GRXWriter+Transformations.m | 47 + src/objective-c/RxLibrary/GRXWriter.h | 107 + src/objective-c/RxLibrary/GRXWriter.m | 38 + .../RxLibrary/NSEnumerator+GRXUtil.h | 51 + .../RxLibrary/NSEnumerator+GRXUtil.m | 54 + src/objective-c/RxLibrary/README.md | 8 + .../RxLibrary/private/GRXNSBlockEnumerator.h | 42 + .../RxLibrary/private/GRXNSBlockEnumerator.m | 61 + .../RxLibrary/private/GRXNSFastEnumerator.h | 43 + .../RxLibrary/private/GRXNSFastEnumerator.m | 87 + .../RxLibrary/private/GRXNSScalarEnumerator.h | 41 + .../RxLibrary/private/GRXNSScalarEnumerator.m | 57 + .../transformations/GRXMappingWriter.h | 40 + .../transformations/GRXMappingWriter.m | 63 + .../RemoteTestClient/RemoteTest.podspec | 28 + .../examples/RemoteTestClient/empty.proto | 44 + .../examples/RemoteTestClient/messages.proto | 133 + .../examples/RemoteTestClient/test.proto | 73 + src/objective-c/examples/Sample/Podfile | 8 + src/objective-c/examples/Sample/README.md | 1 + .../Sample/Sample.xcodeproj/project.pbxproj | 351 + .../contents.xcworkspacedata | 7 + .../examples/Sample/Sample/AppDelegate.h | 38 + .../examples/Sample/Sample/AppDelegate.m | 37 + .../Sample/Sample/Base.lproj/Main.storyboard | 57 + .../AppIcon.appiconset/Contents.json | 68 + .../examples/Sample/Sample/Info.plist | 47 + .../examples/Sample/Sample/ViewController.h | 37 + .../examples/Sample/Sample/ViewController.m | 93 + src/objective-c/examples/Sample/Sample/main.m | 41 + .../examples/SwiftSample/AppDelegate.swift | 39 + .../SwiftSample/Base.lproj/Main.storyboard | 25 + .../examples/SwiftSample/Bridging-Header.h | 45 + .../AppIcon.appiconset/Contents.json | 68 + .../examples/SwiftSample/Info.plist | 47 + src/objective-c/examples/SwiftSample/Podfile | 9 + .../SwiftSample.xcodeproj/project.pbxproj | 354 + .../contents.xcworkspacedata | 7 + .../examples/SwiftSample/ViewController.swift | 99 + .../RemoteTestClient/RemoteTest.podspec | 31 + .../RemoteTestClient/empty.proto | 44 + .../RemoteTestClient/messages.proto | 133 + .../RemoteTestClient/test.proto | 73 + .../RouteGuideClient/RouteGuide.podspec | 31 + .../RouteGuideClient/route_guide.proto | 120 + src/objective-c/tests/GRPCClientTests.m | 248 + src/objective-c/tests/Info.plist | 24 + src/objective-c/tests/InteropTests.h | 43 + src/objective-c/tests/InteropTests.m | 309 + .../tests/InteropTestsLocalCleartext.m | 59 + src/objective-c/tests/InteropTestsLocalSSL.m | 62 + src/objective-c/tests/LocalClearTextTests.m | 164 + src/objective-c/tests/Podfile | 15 + src/objective-c/tests/RxLibraryUnitTests.m | 140 + .../test-certificates.pem | 15 + src/objective-c/tests/Tests.m | 40 + .../tests/Tests.xcodeproj/project.pbxproj | 454 + .../contents.xcworkspacedata | 7 + .../xcshareddata/xcschemes/AllTests.xcscheme | 110 + src/objective-c/tests/build_tests.sh | 66 + src/objective-c/tests/run_tests.sh | 54 + src/php/.gitignore | 22 + src/php/README.md | 300 + src/php/bin/determine_extension_dir.sh | 51 + src/php/bin/generate_proto_php.sh | 40 + src/php/bin/interop_client.sh | 35 + src/php/bin/run_gen_code_test.sh | 38 + src/php/bin/run_tests.sh | 41 + src/php/composer.json | 23 + src/php/ext/grpc/CREDITS | 3 + src/php/ext/grpc/LICENSE | 32 + src/php/ext/grpc/README.md | 67 + src/php/ext/grpc/byte_buffer.c | 78 + src/php/ext/grpc/byte_buffer.h | 44 + src/php/ext/grpc/call.c | 530 + src/php/ext/grpc/call.h | 69 + src/php/ext/grpc/channel.c | 266 + src/php/ext/grpc/channel.h | 64 + src/php/ext/grpc/completion_queue.c | 50 + src/php/ext/grpc/completion_queue.h | 50 + src/php/ext/grpc/config.m4 | 75 + src/php/ext/grpc/credentials.c | 191 + src/php/ext/grpc/credentials.h | 63 + src/php/ext/grpc/package.xml | 122 + src/php/ext/grpc/php_grpc.c | 247 + src/php/ext/grpc/php_grpc.h | 98 + src/php/ext/grpc/server.c | 233 + src/php/ext/grpc/server.h | 61 + src/php/ext/grpc/server_credentials.c | 135 + src/php/ext/grpc/server_credentials.h | 63 + src/php/ext/grpc/timeval.c | 276 + src/php/ext/grpc/timeval.h | 68 + src/php/lib/Grpc/AbstractCall.php | 95 + src/php/lib/Grpc/BaseStub.php | 260 + src/php/lib/Grpc/BidiStreamingCall.php | 98 + src/php/lib/Grpc/ClientStreamingCall.php | 77 + src/php/lib/Grpc/ServerStreamingCall.php | 83 + src/php/lib/Grpc/UnaryCall.php | 71 + src/php/tests/data/README | 1 + src/php/tests/data/ca.pem | 15 + src/php/tests/data/server1.key | 16 + src/php/tests/data/server1.pem | 16 + .../AbstractGeneratedCodeTest.php | 122 + .../generated_code/GeneratedCodeTest.php | 41 + .../GeneratedCodeWithCallbackTest.php | 47 + src/php/tests/generated_code/math.proto | 80 + src/php/tests/generated_code/math_client.php | 102 + src/php/tests/interop/empty.proto | 43 + src/php/tests/interop/interop_client.php | 394 + src/php/tests/interop/message_set.php | 26 + src/php/tests/interop/messages.proto | 132 + src/php/tests/interop/test.proto | 72 + src/php/tests/unit_tests/CallTest.php | 86 + src/php/tests/unit_tests/EndToEndTest.php | 247 + .../tests/unit_tests/SecureEndToEndTest.php | 216 + src/php/tests/unit_tests/TimevalTest.php | 86 + src/python/README.md | 95 + src/python/grpcio/.gitignore | 9 + src/python/grpcio/MANIFEST.in | 3 + src/python/grpcio/README.rst | 22 + src/python/grpcio/commands.py | 102 + src/python/grpcio/grpc/__init__.py | 30 + src/python/grpcio/grpc/_adapter/.gitignore | 5 + src/python/grpcio/grpc/_adapter/__init__.py | 30 + src/python/grpcio/grpc/_adapter/_c/module.c | 67 + src/python/grpcio/grpc/_adapter/_c/types.c | 60 + src/python/grpcio/grpc/_adapter/_c/types.h | 278 + .../grpcio/grpc/_adapter/_c/types/call.c | 186 + .../grpcio/grpc/_adapter/_c/types/channel.c | 187 + .../_adapter/_c/types/client_credentials.c | 247 + .../grpc/_adapter/_c/types/completion_queue.c | 124 + .../grpcio/grpc/_adapter/_c/types/server.c | 196 + .../_adapter/_c/types/server_credentials.c | 137 + src/python/grpcio/grpc/_adapter/_c/utility.c | 524 + src/python/grpcio/grpc/_adapter/_common.py | 76 + .../grpcio/grpc/_adapter/_intermediary_low.py | 267 + src/python/grpcio/grpc/_adapter/_low.py | 132 + src/python/grpcio/grpc/_adapter/_types.py | 435 + src/python/grpcio/grpc/_adapter/fore.py | 363 + src/python/grpcio/grpc/_adapter/rear.py | 395 + src/python/grpcio/grpc/_cython/.gitignore | 7 + src/python/grpcio/grpc/_cython/README.rst | 52 + src/python/grpcio/grpc/_cython/__init__.py | 28 + .../grpcio/grpc/_cython/_cygrpc/__init__.py | 28 + .../grpcio/grpc/_cython/_cygrpc/call.pxd | 37 + .../grpcio/grpc/_cython/_cygrpc/call.pyx | 82 + .../grpcio/grpc/_cython/_cygrpc/channel.pxd | 36 + .../grpcio/grpc/_cython/_cygrpc/channel.pyx | 84 + .../grpc/_cython/_cygrpc/completion_queue.pxd | 39 + .../grpc/_cython/_cygrpc/completion_queue.pyx | 117 + .../grpc/_cython/_cygrpc/credentials.pxd | 45 + .../grpc/_cython/_cygrpc/credentials.pyx | 187 + .../grpcio/grpc/_cython/_cygrpc/grpc.pxd | 340 + .../grpcio/grpc/_cython/_cygrpc/records.pxd | 129 + .../grpcio/grpc/_cython/_cygrpc/records.pyx | 575 + .../grpcio/grpc/_cython/_cygrpc/server.pxd | 45 + .../grpcio/grpc/_cython/_cygrpc/server.pyx | 158 + src/python/grpcio/grpc/_cython/adapter_low.py | 102 + src/python/grpcio/grpc/_cython/cygrpc.pyx | 107 + src/python/grpcio/grpc/_links/__init__.py | 30 + src/python/grpcio/grpc/_links/_constants.py | 42 + src/python/grpcio/grpc/_links/invocation.py | 452 + src/python/grpcio/grpc/_links/service.py | 505 + src/python/grpcio/grpc/beta/__init__.py | 28 + .../grpcio/grpc/beta/_connectivity_channel.py | 156 + src/python/grpcio/grpc/beta/_server.py | 146 + src/python/grpcio/grpc/beta/_stub.py | 117 + .../grpcio/grpc/beta/implementations.py | 367 + src/python/grpcio/grpc/beta/interfaces.py | 214 + src/python/grpcio/grpc/beta/utilities.py | 164 + .../grpcio/grpc/early_adopter/__init__.py | 30 + .../grpc/early_adopter/implementations.py | 262 + src/python/grpcio/grpc/framework/__init__.py | 30 + .../grpcio/grpc/framework/alpha/__init__.py | 28 + .../grpc/framework/alpha/_face_utilities.py | 183 + .../grpcio/grpc/framework/alpha/_reexport.py | 203 + .../grpcio/grpc/framework/alpha/exceptions.py | 48 + .../grpcio/grpc/framework/alpha/interfaces.py | 388 + .../grpcio/grpc/framework/alpha/utilities.py | 269 + .../grpcio/grpc/framework/base/__init__.py | 30 + .../grpc/framework/base/_cancellation.py | 64 + .../grpcio/grpc/framework/base/_constants.py | 32 + .../grpcio/grpc/framework/base/_context.py | 99 + .../grpcio/grpc/framework/base/_emission.py | 125 + .../grpcio/grpc/framework/base/_ends.py | 399 + .../grpcio/grpc/framework/base/_expiration.py | 158 + .../grpcio/grpc/framework/base/_ingestion.py | 442 + .../grpcio/grpc/framework/base/_interfaces.py | 271 + .../grpcio/grpc/framework/base/_reception.py | 399 + .../grpc/framework/base/_termination.py | 204 + .../grpc/framework/base/_transmission.py | 429 + .../grpcio/grpc/framework/base/exceptions.py | 34 + .../grpc/framework/base/implementations.py | 77 + .../grpcio/grpc/framework/base/in_memory.py | 108 + .../grpcio/grpc/framework/base/interfaces.py | 363 + src/python/grpcio/grpc/framework/base/null.py | 56 + src/python/grpcio/grpc/framework/base/util.py | 94 + .../grpcio/grpc/framework/common/__init__.py | 30 + .../grpc/framework/common/cardinality.py | 42 + .../grpcio/grpc/framework/common/style.py | 40 + .../grpcio/grpc/framework/core/__init__.py | 30 + .../grpcio/grpc/framework/core/_constants.py | 60 + .../grpcio/grpc/framework/core/_context.py | 94 + .../grpcio/grpc/framework/core/_emission.py | 100 + src/python/grpcio/grpc/framework/core/_end.py | 249 + .../grpcio/grpc/framework/core/_expiration.py | 154 + .../grpcio/grpc/framework/core/_ingestion.py | 438 + .../grpcio/grpc/framework/core/_interfaces.py | 337 + .../grpcio/grpc/framework/core/_operation.py | 204 + .../grpcio/grpc/framework/core/_protocol.py | 176 + .../grpcio/grpc/framework/core/_reception.py | 159 + .../grpc/framework/core/_termination.py | 228 + .../grpc/framework/core/_transmission.py | 335 + .../grpcio/grpc/framework/core/_utilities.py | 54 + .../grpc/framework/core/implementations.py | 62 + .../grpcio/grpc/framework/crust/__init__.py | 30 + .../grpcio/grpc/framework/crust/_calls.py | 223 + .../grpcio/grpc/framework/crust/_control.py | 581 + .../grpcio/grpc/framework/crust/_service.py | 173 + .../grpc/framework/crust/implementations.py | 364 + .../grpcio/grpc/framework/face/__init__.py | 30 + .../grpcio/grpc/framework/face/_calls.py | 422 + .../grpcio/grpc/framework/face/_control.py | 198 + .../grpcio/grpc/framework/face/_service.py | 187 + .../grpc/framework/face/demonstration.py | 118 + .../grpcio/grpc/framework/face/exceptions.py | 77 + .../grpc/framework/face/implementations.py | 318 + .../grpcio/grpc/framework/face/interfaces.py | 640 + .../grpcio/grpc/framework/face/utilities.py | 177 + .../grpc/framework/foundation/__init__.py | 30 + .../framework/foundation/_timer_future.py | 228 + .../grpc/framework/foundation/abandonment.py | 38 + .../grpc/framework/foundation/activated.py | 65 + .../framework/foundation/callable_util.py | 107 + .../grpc/framework/foundation/future.py | 236 + .../grpcio/grpc/framework/foundation/later.py | 51 + .../grpc/framework/foundation/logging_pool.py | 83 + .../grpcio/grpc/framework/foundation/relay.py | 175 + .../grpc/framework/foundation/stream.py | 60 + .../grpc/framework/foundation/stream_util.py | 160 + .../grpc/framework/interfaces/__init__.py | 30 + .../framework/interfaces/base/__init__.py | 30 + .../grpc/framework/interfaces/base/base.py | 339 + .../framework/interfaces/base/utilities.py | 82 + .../framework/interfaces/face/__init__.py | 30 + .../grpc/framework/interfaces/face/face.py | 992 + .../framework/interfaces/face/utilities.py | 178 + .../framework/interfaces/links/__init__.py | 30 + .../grpc/framework/interfaces/links/links.py | 142 + .../framework/interfaces/links/utilities.py | 44 + src/python/grpcio/requirements.txt | 2 + src/python/grpcio/setup.cfg | 2 + src/python/grpcio/setup.py | 113 + src/python/grpcio_health_checking/MANIFEST.in | 2 + src/python/grpcio_health_checking/README.rst | 9 + src/python/grpcio_health_checking/commands.py | 80 + .../grpcio_health_checking/grpc/__init__.py | 30 + .../grpc/health/__init__.py | 30 + .../grpc/health/v1alpha/__init__.py | 30 + .../grpc/health/v1alpha/health.proto | 49 + .../grpc/health/v1alpha/health.py | 129 + src/python/grpcio_health_checking/setup.py | 72 + src/python/grpcio_test/.gitignore | 10 + src/python/grpcio_test/MANIFEST.in | 4 + src/python/grpcio_test/commands.py | 57 + .../grpcio_test/grpc_interop/__init__.py | 30 + .../grpc_interop/_insecure_interop_test.py | 57 + .../grpc_interop/_interop_test_case.py | 64 + .../grpc_interop/_secure_interop_test.py | 64 + src/python/grpcio_test/grpc_interop/client.py | 116 + .../grpc_interop/credentials/README | 1 + .../grpc_interop/credentials/ca.pem | 15 + .../grpc_interop/credentials/server1.key | 16 + .../grpc_interop/credentials/server1.pem | 16 + .../grpcio_test/grpc_interop/empty_pb2.py | 63 + .../grpcio_test/grpc_interop/messages_pb2.py | 447 + .../grpcio_test/grpc_interop/methods.py | 397 + .../grpcio_test/grpc_interop/resources.py | 56 + src/python/grpcio_test/grpc_interop/server.py | 74 + .../grpcio_test/grpc_interop/test_pb2.py | 178 + .../grpc_protoc_plugin/__init__.py | 30 + .../alpha_python_plugin_test.py | 541 + .../beta_python_plugin_test.py | 501 + .../grpc_protoc_plugin/python_plugin_test.py | 541 + .../grpcio_test/grpc_protoc_plugin/test.proto | 139 + src/python/grpcio_test/grpc_test/__init__.py | 30 + .../grpcio_test/grpc_test/_adapter/.gitignore | 5 + .../grpc_test/_adapter/__init__.py | 30 + ...blocking_invocation_inline_service_test.py | 46 + .../grpcio_test/grpc_test/_adapter/_c_test.py | 55 + ...vocation_synchronous_event_service_test.py | 46 + .../grpc_test/_adapter/_face_test_case.py | 106 + ...ocation_asynchronous_event_service_test.py | 46 + .../_adapter/_intermediary_low_test.py | 434 + .../grpc_test/_adapter/_links_test.py | 277 + .../_adapter/_lonely_rear_link_test.py | 100 + .../grpc_test/_adapter/_low_test.py | 312 + .../grpc_test/_adapter/_proto_scenarios.py | 261 + .../grpc_test/_adapter/_test_links.py | 80 + .../_core_over_links_base_interface_test.py | 155 + ...ver_core_over_links_face_interface_test.py | 161 + .../grpcio_test/grpc_test/_cython/.gitignore | 7 + .../grpcio_test/grpc_test/_cython/__init__.py | 28 + .../grpc_test/_cython/adapter_low_test.py | 187 + .../grpc_test/_cython/cygrpc_test.py | 262 + .../grpc_test/_cython/test_utilities.py | 46 + .../grpc_test/_junkdrawer/__init__.py | 30 + .../grpc_test/_junkdrawer/math_pb2.py | 266 + .../grpc_test/_junkdrawer/stock_pb2.py | 152 + .../grpcio_test/grpc_test/_links/__init__.py | 30 + .../_links/_lonely_invocation_link_test.py | 88 + .../grpc_test/_links/_proto_scenarios.py | 261 + .../grpc_test/_links/_transmission_test.py | 239 + .../grpcio_test/grpc_test/beta/__init__.py | 30 + .../grpc_test/beta/_beta_features_test.py | 232 + .../beta/_connectivity_channel_test.py | 191 + .../grpc_test/beta/_face_interface_test.py | 138 + .../grpc_test/beta/_not_found_test.py | 75 + .../grpc_test/beta/_utilities_test.py | 123 + .../grpc_test/beta/test_utilities.py | 56 + .../grpcio_test/grpc_test/credentials/README | 1 + .../grpcio_test/grpc_test/credentials/ca.pem | 15 + .../grpc_test/credentials/server1.key | 16 + .../grpc_test/credentials/server1.pem | 16 + .../grpc_test/early_adopter/__init__.py | 30 + .../early_adopter/implementations_test.py | 180 + .../grpc_test/framework/__init__.py | 30 + .../_crust_over_core_face_interface_test.py | 111 + .../grpc_test/framework/base/__init__.py | 30 + .../framework/base/implementations_test.py | 80 + .../framework/base/interfaces_test_case.py | 307 + .../grpc_test/framework/common/__init__.py | 30 + .../framework/common/test_constants.py | 53 + .../framework/common/test_control.py | 95 + .../framework/common/test_coverage.py | 116 + .../grpc_test/framework/core/__init__.py | 30 + .../framework/core/_base_interface_test.py | 96 + .../grpc_test/framework/face/__init__.py | 30 + .../grpc_test/framework/face/_test_case.py | 61 + ...blocking_invocation_inline_service_test.py | 46 + ...vocation_synchronous_event_service_test.py | 46 + ...ocation_asynchronous_event_service_test.py | 46 + .../framework/face/testing/__init__.py | 30 + .../framework/face/testing/base_util.py | 102 + ...ing_invocation_inline_service_test_case.py | 222 + .../framework/face/testing/callback.py | 94 + .../framework/face/testing/control.py | 87 + .../framework/face/testing/coverage.py | 123 + .../framework/face/testing/digest.py | 450 + ...ion_synchronous_event_service_test_case.py | 376 + ...on_asynchronous_event_service_test_case.py | 379 + .../framework/face/testing/interfaces.py | 117 + .../framework/face/testing/serial.py | 70 + .../framework/face/testing/service.py | 337 + .../framework/face/testing/stock_service.py | 374 + .../framework/face/testing/test_case.py | 80 + .../framework/foundation/__init__.py | 30 + .../framework/foundation/_later_test.py | 151 + .../foundation/_logging_pool_test.py | 64 + .../framework/foundation/stream_testing.py | 73 + .../framework/interfaces/__init__.py | 30 + .../framework/interfaces/base/__init__.py | 30 + .../framework/interfaces/base/_control.py | 568 + .../framework/interfaces/base/_sequence.py | 171 + .../framework/interfaces/base/_state.py | 55 + .../framework/interfaces/base/test_cases.py | 276 + .../interfaces/base/test_interfaces.py | 186 + .../interfaces/face/_3069_test_constant.py | 37 + .../framework/interfaces/face/__init__.py | 30 + .../_blocking_invocation_inline_service.py | 252 + .../framework/interfaces/face/_digest.py | 444 + ...nt_invocation_synchronous_event_service.py | 381 + ...e_invocation_asynchronous_event_service.py | 435 + .../framework/interfaces/face/_invocation.py | 213 + .../framework/interfaces/face/_receiver.py | 95 + .../framework/interfaces/face/_service.py | 332 + .../interfaces/face/_stock_service.py | 396 + .../framework/interfaces/face/test_cases.py | 67 + .../interfaces/face/test_interfaces.py | 229 + .../framework/interfaces/links/__init__.py | 30 + .../framework/interfaces/links/test_cases.py | 326 + .../interfaces/links/test_utilities.py | 167 + src/python/grpcio_test/grpc_test/resources.py | 56 + .../grpcio_test/grpc_test/test_common.py | 76 + src/python/grpcio_test/requirements.txt | 6 + src/python/grpcio_test/setup.cfg | 3 + src/python/grpcio_test/setup.py | 92 + src/ruby/.gitignore | 15 + src/ruby/.rspec | 6 + src/ruby/.rubocop.yml | 11 + src/ruby/.rubocop_todo.yml | 44 + src/ruby/CHANGELOG.md | 11 + src/ruby/Gemfile | 4 + src/ruby/README.md | 102 + src/ruby/Rakefile | 58 + src/ruby/bin/apis/google/protobuf/empty.rb | 44 + src/ruby/bin/apis/pubsub_demo.rb | 264 + src/ruby/bin/apis/tech/pubsub/proto/pubsub.rb | 174 + .../apis/tech/pubsub/proto/pubsub_services.rb | 103 + src/ruby/bin/grpc_ruby_interop_client | 33 + src/ruby/bin/grpc_ruby_interop_server | 33 + src/ruby/bin/interop/interop_client.rb | 51 + src/ruby/bin/interop/interop_server.rb | 50 + src/ruby/bin/math.proto | 80 + src/ruby/bin/math.rb | 61 + src/ruby/bin/math_client.rb | 147 + src/ruby/bin/math_server.rb | 206 + src/ruby/bin/math_services.rb | 56 + src/ruby/bin/noproto_client.rb | 108 + src/ruby/bin/noproto_server.rb | 112 + src/ruby/ext/grpc/extconf.rb | 108 + src/ruby/ext/grpc/rb_byte_buffer.c | 68 + src/ruby/ext/grpc/rb_byte_buffer.h | 47 + src/ruby/ext/grpc/rb_call.c | 843 + src/ruby/ext/grpc/rb_call.h | 60 + src/ruby/ext/grpc/rb_channel.c | 418 + src/ruby/ext/grpc/rb_channel.h | 47 + src/ruby/ext/grpc/rb_channel_args.c | 165 + src/ruby/ext/grpc/rb_channel_args.h | 53 + src/ruby/ext/grpc/rb_completion_queue.c | 179 + src/ruby/ext/grpc/rb_completion_queue.h | 55 + src/ruby/ext/grpc/rb_credentials.c | 294 + src/ruby/ext/grpc/rb_credentials.h | 47 + src/ruby/ext/grpc/rb_grpc.c | 308 + src/ruby/ext/grpc/rb_grpc.h | 85 + src/ruby/ext/grpc/rb_server.c | 395 + src/ruby/ext/grpc/rb_server.h | 47 + src/ruby/ext/grpc/rb_server_credentials.c | 280 + src/ruby/ext/grpc/rb_server_credentials.h | 47 + src/ruby/grpc.gemspec | 43 + src/ruby/lib/grpc.rb | 39 + src/ruby/lib/grpc/core/time_consts.rb | 71 + src/ruby/lib/grpc/errors.rb | 62 + src/ruby/lib/grpc/generic/active_call.rb | 490 + src/ruby/lib/grpc/generic/bidi_call.rb | 206 + src/ruby/lib/grpc/generic/client_stub.rb | 481 + src/ruby/lib/grpc/generic/rpc_desc.rb | 147 + src/ruby/lib/grpc/generic/rpc_server.rb | 497 + src/ruby/lib/grpc/generic/service.rb | 232 + src/ruby/lib/grpc/logconfig.rb | 59 + src/ruby/lib/grpc/notifier.rb | 60 + src/ruby/lib/grpc/version.rb | 33 + src/ruby/pb/README.md | 42 + src/ruby/pb/grpc/health/checker.rb | 75 + src/ruby/pb/grpc/health/v1alpha/health.proto | 50 + src/ruby/pb/grpc/health/v1alpha/health.rb | 29 + .../pb/grpc/health/v1alpha/health_services.rb | 28 + src/ruby/pb/test/client.rb | 453 + src/ruby/pb/test/proto/empty.rb | 15 + src/ruby/pb/test/proto/messages.rb | 80 + src/ruby/pb/test/proto/test.rb | 14 + src/ruby/pb/test/proto/test_services.rb | 64 + src/ruby/pb/test/server.rb | 197 + src/ruby/spec/call_spec.rb | 154 + src/ruby/spec/channel_spec.rb | 172 + src/ruby/spec/client_server_spec.rb | 446 + src/ruby/spec/completion_queue_spec.rb | 42 + src/ruby/spec/credentials_spec.rb | 71 + src/ruby/spec/generic/active_call_spec.rb | 372 + src/ruby/spec/generic/client_stub_spec.rb | 516 + src/ruby/spec/generic/rpc_desc_spec.rb | 331 + src/ruby/spec/generic/rpc_server_pool_spec.rb | 138 + src/ruby/spec/generic/rpc_server_spec.rb | 610 + src/ruby/spec/generic/service_spec.rb | 342 + src/ruby/spec/pb/health/checker_spec.rb | 232 + src/ruby/spec/server_credentials_spec.rb | 94 + src/ruby/spec/server_spec.rb | 209 + src/ruby/spec/spec_helper.rb | 69 + src/ruby/spec/testdata/README | 1 + src/ruby/spec/testdata/ca.pem | 15 + src/ruby/spec/testdata/server1.key | 16 + src/ruby/spec/testdata/server1.pem | 16 + src/ruby/spec/time_consts_spec.rb | 89 + templates/BUILD.template | 229 + templates/Makefile.template | 1879 ++ templates/README.md | 139 + templates/gRPC.podspec.template | 157 + templates/src/core/surface/version.c.template | 43 + .../doxygen/Doxyfile.c++.internal.template | 4 + templates/tools/doxygen/Doxyfile.c++.template | 4 + .../doxygen/Doxyfile.core.internal.template | 4 + .../tools/doxygen/Doxyfile.core.template | 4 + templates/tools/doxygen/Doxyfile.include | 2389 ++ .../sources_and_headers.json.template | 35 + templates/tools/run_tests/tests.json.template | 15 + templates/vsprojects/build_test_protos.sh | 42 + .../vsprojects/buildtests_c.sln.template | 7 + templates/vsprojects/cpptest.props.template | 18 + .../vsprojects/generate_debug_projects.sh | 76 + templates/vsprojects/global.props.template | 16 + .../grpc++_unsecure.vcxproj.filters.template | 4 + .../grpc++_unsecure.vcxproj.template | 4 + templates/vsprojects/grpc.sln.template | 7 + .../grpc_cpp_plugin.vcxproj.template | 4 + .../vsprojects/grpc_csharp_ext.sln.template | 7 + .../grpc_csharp_plugin.vcxproj.template | 4 + .../grpc_objective_c_plugin.vcxproj.template | 4 + .../grpc_protoc_plugins.sln.template | 7 + .../grpc_python_plugin.vcxproj.template | 4 + .../grpc_ruby_plugin.vcxproj.template | 4 + templates/vsprojects/openssl.props.template | 13 + templates/vsprojects/packages.include | 53 + templates/vsprojects/protobuf.props.template | 13 + templates/vsprojects/protoc.props.template | 13 + templates/vsprojects/sln_defs.include | 111 + .../vsprojects/vcxproj.filters_defs.include | 65 + templates/vsprojects/vcxproj.template | 18 + templates/vsprojects/vcxproj_defs.include | 326 + templates/vsprojects/winsock.props.template | 14 + templates/vsprojects/zlib-dll.props.template | 15 + templates/vsprojects/zlib.props.template | 15 + test/build/c++11.cc | 52 + test/build/openssl-alpn.c | 43 + test/build/openssl-npn.c | 45 + test/build/perftools.c | 40 + test/build/protobuf.cc | 41 + test/build/systemtap.c | 40 + test/build/zlib.c | 42 + test/build/zookeeper.c | 43 + test/core/bad_client/bad_client.c | 169 + test/core/bad_client/bad_client.h | 57 + test/core/bad_client/gen_build_yaml.py | 90 + .../core/bad_client/tests/connection_prefix.c | 73 + .../bad_client/tests/initial_settings_frame.c | 95 + test/core/channel/channel_args_test.c | 141 + test/core/channel/channel_stack_test.c | 138 + test/core/client_config/uri_parser_test.c | 71 + test/core/compression/compression_test.c | 77 + test/core/compression/message_compress_test.c | 194 + test/core/end2end/README | 7 + test/core/end2end/cq_verifier.c | 289 + test/core/end2end/cq_verifier.h | 66 + test/core/end2end/data/server1_cert.c | 115 + test/core/end2end/data/server1_key.c | 108 + test/core/end2end/data/ssl_test_data.h | 41 + test/core/end2end/data/test_root_cert.c | 101 + test/core/end2end/dualstack_socket_test.c | 316 + test/core/end2end/end2end_tests.h | 69 + test/core/end2end/fixtures/h2_compress.c | 136 + test/core/end2end/fixtures/h2_fakesec.c | 163 + test/core/end2end/fixtures/h2_full+poll.c | 118 + test/core/end2end/fixtures/h2_full.c | 117 + test/core/end2end/fixtures/h2_oauth2.c | 242 + test/core/end2end/fixtures/h2_proxy.c | 132 + .../core/end2end/fixtures/h2_sockpair+trace.c | 171 + test/core/end2end/fixtures/h2_sockpair.c | 157 + .../core/end2end/fixtures/h2_sockpair_1byte.c | 157 + test/core/end2end/fixtures/h2_ssl+poll.c | 192 + test/core/end2end/fixtures/h2_ssl.c | 189 + test/core/end2end/fixtures/h2_ssl_proxy.c | 218 + test/core/end2end/fixtures/h2_uds+poll.c | 124 + test/core/end2end/fixtures/h2_uds.c | 122 + test/core/end2end/fixtures/proxy.c | 438 + test/core/end2end/fixtures/proxy.h | 55 + test/core/end2end/gen_build_yaml.py | 220 + .../end2end/multiple_server_queues_test.c | 63 + test/core/end2end/no_server_test.c | 106 + test/core/end2end/tests/bad_hostname.c | 181 + test/core/end2end/tests/binary_metadata.c | 289 + test/core/end2end/tests/call_creds.c | 499 + test/core/end2end/tests/cancel_after_accept.c | 235 + .../end2end/tests/cancel_after_client_done.c | 239 + test/core/end2end/tests/cancel_after_invoke.c | 201 + .../core/end2end/tests/cancel_before_invoke.c | 196 + test/core/end2end/tests/cancel_in_a_vacuum.c | 129 + test/core/end2end/tests/cancel_test_helpers.h | 55 + .../end2end/tests/census_simple_request.c | 232 + .../core/end2end/tests/channel_connectivity.c | 138 + test/core/end2end/tests/compressed_payload.c | 337 + test/core/end2end/tests/default_host.c | 234 + test/core/end2end/tests/disappearing_server.c | 215 + test/core/end2end/tests/empty_batch.c | 134 + .../end2end/tests/graceful_server_shutdown.c | 212 + test/core/end2end/tests/high_initial_seqno.c | 233 + .../core/end2end/tests/invoke_large_request.c | 256 + test/core/end2end/tests/large_metadata.c | 247 + .../end2end/tests/max_concurrent_streams.c | 438 + test/core/end2end/tests/max_message_length.c | 216 + test/core/end2end/tests/metadata.c | 265 + test/core/end2end/tests/no_op.c | 106 + test/core/end2end/tests/payload.c | 270 + test/core/end2end/tests/ping_pong_streaming.c | 277 + test/core/end2end/tests/registered_call.c | 233 + test/core/end2end/tests/request_with_flags.c | 216 + .../core/end2end/tests/request_with_payload.c | 234 + .../end2end/tests/server_finishes_request.c | 212 + .../end2end/tests/shutdown_finishes_calls.c | 194 + .../end2end/tests/shutdown_finishes_tags.c | 121 + .../end2end/tests/simple_delayed_request.c | 223 + test/core/end2end/tests/simple_request.c | 247 + test/core/end2end/tests/trailing_metadata.c | 270 + test/core/fling/client.c | 234 + test/core/fling/fling_stream_test.c | 113 + test/core/fling/fling_test.c | 94 + test/core/fling/server.c | 327 + test/core/httpcli/format_request_test.c | 164 + test/core/httpcli/httpcli_test.c | 170 + test/core/httpcli/parser_test.c | 155 + test/core/httpcli/test_server.py | 31 + test/core/iomgr/alarm_heap_test.c | 278 + test/core/iomgr/alarm_list_test.c | 156 + test/core/iomgr/alarm_test.c | 225 + test/core/iomgr/endpoint_pair_test.c | 77 + test/core/iomgr/endpoint_tests.c | 290 + test/core/iomgr/endpoint_tests.h | 58 + test/core/iomgr/fd_conservation_posix_test.c | 63 + test/core/iomgr/fd_posix_test.c | 497 + test/core/iomgr/resolve_address_test.c | 136 + test/core/iomgr/sockaddr_utils_test.c | 246 + test/core/iomgr/tcp_client_posix_test.c | 250 + test/core/iomgr/tcp_posix_test.c | 483 + test/core/iomgr/tcp_server_posix_test.c | 171 + test/core/iomgr/time_averaged_stats_test.c | 208 + test/core/iomgr/udp_server_test.c | 179 + test/core/json/json_rewrite.c | 250 + test/core/json/json_rewrite_test.c | 304 + test/core/json/json_test.c | 182 + test/core/json/rewrite_test_input.json | 203 + .../json/rewrite_test_output_condensed.json | 1 + .../json/rewrite_test_output_indented.json | 272 + .../network_benchmarks/low_level_ping_pong.c | 683 + test/core/profiling/mark_timings.stp | 40 + test/core/profiling/timers_test.c | 83 + test/core/security/auth_context_test.c | 153 + test/core/security/base64_test.c | 223 + test/core/security/create_jwt.c | 110 + test/core/security/credentials_test.c | 864 + test/core/security/fetch_oauth2.c | 122 + test/core/security/json_token_test.c | 506 + test/core/security/jwt_verifier_test.c | 563 + test/core/security/oauth2_utils.c | 96 + test/core/security/oauth2_utils.h | 51 + .../print_google_default_creds_token.c | 110 + test/core/security/secure_endpoint_test.c | 175 + test/core/security/security_connector_test.c | 257 + test/core/security/verify_jwt.c | 122 + test/core/statistics/census_log_tests.c | 591 + test/core/statistics/census_log_tests.h | 51 + test/core/statistics/census_stub_test.c | 77 + test/core/statistics/hash_table_test.c | 301 + .../multiple_writers_circular_buffer_test.c | 46 + test/core/statistics/multiple_writers_test.c | 46 + test/core/statistics/performance_test.c | 46 + test/core/statistics/quick_test.c | 54 + test/core/statistics/rpc_stats_test.c | 197 + test/core/statistics/small_log_test.c | 46 + test/core/statistics/trace_test.c | 255 + test/core/statistics/window_stats_test.c | 317 + test/core/support/cmdline_test.c | 317 + test/core/support/env_test.c | 64 + test/core/support/file_test.c | 173 + test/core/support/histogram_test.c | 178 + test/core/support/host_port_test.c | 73 + test/core/support/log_test.c | 59 + test/core/support/murmur_hash_test.c | 88 + test/core/support/slice_buffer_test.c | 75 + test/core/support/slice_test.c | 228 + test/core/support/stack_lockfree_test.c | 154 + test/core/support/string_test.c | 300 + test/core/support/sync_test.c | 456 + test/core/support/thd_test.c | 101 + test/core/support/time_test.c | 267 + test/core/support/tls_test.c | 83 + test/core/support/useful_test.c | 73 + test/core/surface/byte_buffer_reader_test.c | 197 + test/core/surface/completion_queue_test.c | 341 + test/core/surface/lame_client_test.c | 103 + test/core/surface/multi_init_test.c | 63 + test/core/transport/chttp2/alpn_test.c | 53 + test/core/transport/chttp2/bin_encoder_test.c | 182 + .../core/transport/chttp2/hpack_parser_test.c | 223 + test/core/transport/chttp2/hpack_table_test.c | 275 + .../transport/chttp2/status_conversion_test.c | 138 + .../transport/chttp2/stream_encoder_test.c | 359 + test/core/transport/chttp2/stream_map_test.c | 228 + .../transport/chttp2/timeout_encoding_test.c | 153 + test/core/transport/metadata_test.c | 283 + test/core/transport/stream_op_test.c | 116 + test/core/tsi/transport_security_test.c | 303 + test/core/util/grpc_profiler.c | 60 + test/core/util/grpc_profiler.h | 48 + test/core/util/parse_hexstring.c | 70 + test/core/util/parse_hexstring.h | 41 + test/core/util/port.h | 52 + test/core/util/port_posix.c | 260 + test/core/util/port_windows.c | 263 + test/core/util/reconnect_server.c | 162 + test/core/util/reconnect_server.h | 69 + test/core/util/slice_splitter.c | 138 + test/core/util/slice_splitter.h | 68 + test/core/util/test_config.c | 99 + test/core/util/test_config.h | 77 + test/cpp/client/channel_arguments_test.cc | 124 + test/cpp/client/credentials_test.cc | 60 + .../cpp/common/auth_property_iterator_test.cc | 106 + test/cpp/common/secure_auth_context_test.cc | 110 + test/cpp/end2end/async_end2end_test.cc | 783 + test/cpp/end2end/client_crash_test.cc | 159 + test/cpp/end2end/client_crash_test_server.cc | 93 + test/cpp/end2end/end2end_test.cc | 1105 + test/cpp/end2end/generic_end2end_test.cc | 282 + test/cpp/end2end/mock_test.cc | 284 + test/cpp/end2end/server_crash_test.cc | 174 + test/cpp/end2end/server_crash_test_client.cc | 90 + test/cpp/end2end/shutdown_test.cc | 155 + test/cpp/end2end/streaming_throughput_test.cc | 189 + test/cpp/end2end/thread_stress_test.cc | 238 + test/cpp/end2end/zookeeper_test.cc | 219 + test/cpp/interop/client.cc | 163 + test/cpp/interop/client_helper.cc | 127 + test/cpp/interop/client_helper.h | 74 + test/cpp/interop/interop_client.cc | 580 + test/cpp/interop/interop_client.h | 88 + test/cpp/interop/interop_test.cc | 140 + test/cpp/interop/reconnect_interop_client.cc | 102 + test/cpp/interop/reconnect_interop_server.cc | 188 + test/cpp/interop/rnd.dat | Bin 0 -> 524288 bytes test/cpp/interop/server.cc | 274 + test/cpp/interop/server_helper.cc | 85 + test/cpp/interop/server_helper.h | 65 + .../cpp/qps/async_streaming_ping_pong_test.cc | 83 + test/cpp/qps/async_unary_ping_pong_test.cc | 82 + test/cpp/qps/client.h | 286 + test/cpp/qps/client_async.cc | 475 + test/cpp/qps/client_sync.cc | 165 + test/cpp/qps/driver.cc | 269 + test/cpp/qps/driver.h | 74 + test/cpp/qps/histogram.h | 85 + test/cpp/qps/interarrival.h | 178 + test/cpp/qps/perf_db.proto | 71 + test/cpp/qps/perf_db_client.cc | 140 + test/cpp/qps/perf_db_client.h | 113 + test/cpp/qps/qps-sweep.sh | 54 + test/cpp/qps/qps_driver.cc | 175 + test/cpp/qps/qps_interarrival_test.cc | 76 + test/cpp/qps/qps_openloop_test.cc | 87 + test/cpp/qps/qps_test.cc | 84 + test/cpp/qps/qps_test_with_poll.cc | 90 + test/cpp/qps/qps_worker.cc | 238 + test/cpp/qps/qps_worker.h | 60 + test/cpp/qps/qpstest.proto | 218 + test/cpp/qps/report.cc | 175 + test/cpp/qps/report.h | 140 + test/cpp/qps/server.h | 84 + test/cpp/qps/server_async.cc | 342 + test/cpp/qps/server_sync.cc | 116 + test/cpp/qps/single_run_localhost.sh | 56 + test/cpp/qps/stats.h | 61 + test/cpp/qps/sync_streaming_ping_pong_test.cc | 82 + test/cpp/qps/sync_unary_ping_pong_test.cc | 83 + test/cpp/qps/timer.cc | 71 + test/cpp/qps/timer.h | 57 + test/cpp/qps/worker.cc | 76 + test/cpp/util/benchmark_config.cc | 89 + test/cpp/util/benchmark_config.h | 57 + test/cpp/util/byte_buffer_test.cc | 114 + test/cpp/util/cli_call.cc | 111 + test/cpp/util/cli_call.h | 62 + test/cpp/util/cli_call_test.cc | 142 + test/cpp/util/create_test_channel.cc | 96 + test/cpp/util/create_test_channel.h | 58 + test/cpp/util/echo.proto | 47 + test/cpp/util/echo_duplicate.proto | 42 + test/cpp/util/grpc_cli.cc | 190 + test/cpp/util/messages.proto | 60 + test/cpp/util/slice_test.cc | 77 + test/cpp/util/status_test.cc | 77 + test/cpp/util/string_ref_helper.cc | 44 + test/cpp/util/string_ref_helper.h | 47 + test/cpp/util/string_ref_test.cc | 215 + test/cpp/util/subprocess.cc | 59 + test/cpp/util/subprocess.h | 61 + test/cpp/util/test_config.cc | 52 + test/cpp/util/test_config.h | 45 + test/cpp/util/time_test.cc | 84 + test/proto/empty.proto | 43 + test/proto/messages.proto | 167 + test/proto/test.proto | 87 + tools/README.md | 17 + tools/buildgen/build-cleaner.py | 103 + tools/buildgen/bunch.py | 67 + tools/buildgen/generate_build_additions.sh | 38 + tools/buildgen/generate_projects-old.sh | 76 + tools/buildgen/generate_projects.py | 85 + tools/buildgen/generate_projects.sh | 50 + tools/buildgen/mako_renderer.py | 178 + tools/buildgen/plugins/expand_bin_attrs.py | 52 + tools/buildgen/plugins/expand_filegroups.py | 74 + tools/buildgen/plugins/generate_vsprojects.py | 95 + tools/buildgen/plugins/list_protos.py | 70 + tools/codegen/core/gen_hpack_tables.c | 353 + .../core/gen_legal_metadata_characters.c | 76 + tools/distrib/check_copyright.py | 126 + tools/distrib/guard_headers.sh | 90 + tools/distrib/python/.gitignore | 1 + tools/distrib/python/docgen.py | 113 + tools/distrib/python/submit.py | 88 + tools/dockerfile/grpc_base/Dockerfile | 68 + tools/dockerfile/grpc_base/README.md | 11 + tools/dockerfile/grpc_build_deb/Dockerfile | 51 + tools/dockerfile/grpc_build_deb/version.txt | 1 + tools/dockerfile/grpc_clang/Dockerfile | 61 + tools/dockerfile/grpc_csharp_mono/Dockerfile | 55 + tools/dockerfile/grpc_csharp_mono/build.sh | 10 + .../grpc_csharp_mono_base/Dockerfile | 56 + tools/dockerfile/grpc_cxx/Dockerfile | 57 + tools/dockerfile/grpc_cxx/build.sh | 42 + tools/dockerfile/grpc_dist_proto/Dockerfile | 76 + tools/dockerfile/grpc_dist_proto/version.txt | 1 + tools/dockerfile/grpc_go/Dockerfile | 46 + tools/dockerfile/grpc_go/README.md | 4 + tools/dockerfile/grpc_go/build.sh | 34 + tools/dockerfile/grpc_java/Dockerfile | 41 + tools/dockerfile/grpc_java/README.md | 9 + tools/dockerfile/grpc_java/build.sh | 37 + tools/dockerfile/grpc_java_android/Dockerfile | 62 + tools/dockerfile/grpc_java_android/README.md | 42 + tools/dockerfile/grpc_java_base/Dockerfile | 54 + tools/dockerfile/grpc_java_base/README.md | 9 + tools/dockerfile/grpc_node/Dockerfile | 53 + tools/dockerfile/grpc_node/build.sh | 36 + tools/dockerfile/grpc_node_base/Dockerfile | 53 + tools/dockerfile/grpc_php/Dockerfile | 60 + tools/dockerfile/grpc_php/README.md | 10 + tools/dockerfile/grpc_php/build.sh | 18 + tools/dockerfile/grpc_php_base/Dockerfile | 122 + tools/dockerfile/grpc_php_base/README.md | 9 + tools/dockerfile/grpc_python/Dockerfile | 57 + tools/dockerfile/grpc_python/README.md | 11 + tools/dockerfile/grpc_python_base/Dockerfile | 49 + tools/dockerfile/grpc_python_base/README.md | 7 + tools/dockerfile/grpc_ruby/Dockerfile | 56 + tools/dockerfile/grpc_ruby/README.md | 10 + tools/dockerfile/grpc_ruby/build.sh | 36 + tools/dockerfile/grpc_ruby_base/Dockerfile | 92 + tools/dockerfile/grpc_ruby_base/README.md | 9 + tools/dockerfile/grpc_ruby_deb/Dockerfile | 60 + tools/dockerfile/grpc_ruby_deb/README.md | 5 + tools/dockerfile/grpc_scan_build/Dockerfile | 47 + tools/doxygen/Doxyfile.c++ | 2405 ++ tools/doxygen/Doxyfile.c++.internal | 2445 ++ tools/doxygen/Doxyfile.core | 2398 ++ tools/doxygen/Doxyfile.core.internal | 2701 ++ tools/doxygen/run_doxygen.sh | 41 + tools/gce_setup/README.md | 48 + tools/gce_setup/build_images.sh | 46 + tools/gce_setup/builder.sh | 58 + tools/gce_setup/cloud_prod_runner.sh | 89 + tools/gce_setup/cloud_prod_test.sh | 62 + tools/gce_setup/compute_extras.sh | 284 + tools/gce_setup/grpc_docker.sh | 1726 ++ tools/gce_setup/interop_test.sh | 77 + tools/gce_setup/interop_test_runner.sh | 75 + tools/gce_setup/new_grpc_docker_builder.sh | 182 + .../new_grpc_docker_builder_on_startup.sh | 170 + tools/gce_setup/post.html | 11 + tools/gce_setup/pre.html | 15 + tools/gce_setup/private_build_and_test.sh | 89 + tools/gce_setup/shared_startup_funcs.sh | 562 + tools/jenkins/docker_run_jenkins.sh | 45 + tools/jenkins/grpc_jenkins_slave/Dockerfile | 161 + .../grpc_jenkins_slave_32bits/Dockerfile | 161 + tools/jenkins/grpc_linuxbrew/Dockerfile | 62 + tools/jenkins/run_distribution.sh | 144 + tools/jenkins/run_jenkins.sh | 130 + tools/profile_analyzer/profile_analyzer.py | 188 + tools/run_tests/antagonist.py | 34 + tools/run_tests/build_csharp.sh | 50 + tools/run_tests/build_node.sh | 44 + tools/run_tests/build_php.sh | 48 + tools/run_tests/build_python.sh | 81 + tools/run_tests/build_ruby.sh | 42 + tools/run_tests/check_sources_and_headers.py | 55 + tools/run_tests/jobset.py | 360 + tools/run_tests/port_server.py | 117 + tools/run_tests/prepare_travis.sh | 67 + tools/run_tests/run_csharp.bat | 19 + tools/run_tests/run_csharp.sh | 54 + tools/run_tests/run_interops.py | 37 + tools/run_tests/run_interops_build.sh | 75 + tools/run_tests/run_interops_test.sh | 54 + tools/run_tests/run_lcov.sh | 45 + tools/run_tests/run_node.sh | 42 + tools/run_tests/run_python.sh | 53 + tools/run_tests/run_ruby.sh | 36 + tools/run_tests/run_sanity.sh | 60 + tools/run_tests/run_tests.py | 692 + tools/run_tests/sources_and_headers.json | 14526 +++++++++++ tools/run_tests/tests.json | 13102 ++++++++++ tools/run_tests/watch_dirs.py | 75 + tools/tsan_suppressions.txt | 8 + vsprojects/.gitignore | 11 + vsprojects/README.md | 91 + vsprojects/build.bat | 10 + vsprojects/build_plugins.bat | 23 + vsprojects/buildtests_c.sln | 17605 +++++++++++++ vsprojects/cpptest.props | 1 + vsprojects/dummy.c | 0 vsprojects/global.props | 14 + .../grpc++_unsecure/grpc++_unsecure.vcxproj | 338 + .../grpc++_unsecure.vcxproj.filters | 271 + vsprojects/grpc.sln | 399 + .../grpc_cpp_plugin/grpc_cpp_plugin.vcxproj | 143 + vsprojects/grpc_csharp_ext.sln | 81 + .../grpc_csharp_plugin.vcxproj | 143 + .../grpc_objective_c_plugin.vcxproj | 143 + vsprojects/grpc_protoc_plugins.sln | 112 + .../grpc_python_plugin.vcxproj | 143 + .../grpc_ruby_plugin/grpc_ruby_plugin.vcxproj | 143 + vsprojects/nuget_package/.gitignore | 3 + vsprojects/nuget_package/README.md | 22 + vsprojects/nuget_package/buildall.bat | 45 + .../grpc.native.csharp_ext.nuspec | 30 + .../grpc.native.csharp_ext.props | 12 + .../grpc.native.csharp_ext.targets | 14 + vsprojects/openssl.props | 1 + vsprojects/protobuf.props | 1 + vsprojects/protoc.props | 1 + .../gen_hpack_tables/gen_hpack_tables.vcxproj | 145 + .../gen_hpack_tables.vcxproj.filters | 21 + .../gen_legal_metadata_characters.vcxproj | 137 + ..._legal_metadata_characters.vcxproj.filters | 21 + vsprojects/vcxproj/gpr/gpr.vcxproj | 248 + vsprojects/vcxproj/gpr/gpr.vcxproj.filters | 247 + .../gpr_test_util/gpr_test_util.vcxproj | 145 + .../gpr_test_util.vcxproj.filters | 26 + vsprojects/vcxproj/grpc++/grpc++.vcxproj | 351 + .../vcxproj/grpc++/grpc++.vcxproj.filters | 295 + .../grpc++_test_config.vcxproj | 140 + .../grpc++_test_config.vcxproj.filters | 26 + .../grpc++_test_util/grpc++_test_util.vcxproj | 181 + .../grpc++_test_util.vcxproj.filters | 53 + .../grpc++_unsecure/grpc++_unsecure.vcxproj | 338 + .../grpc++_unsecure.vcxproj.filters | 271 + vsprojects/vcxproj/grpc/grpc.vcxproj | 660 + vsprojects/vcxproj/grpc/grpc.vcxproj.filters | 874 + vsprojects/vcxproj/grpc/packages.config | 10 + .../grpc_cpp_plugin/grpc_cpp_plugin.vcxproj | 143 + .../grpc_cpp_plugin.vcxproj.filters | 18 + .../grpc_create_jwt/grpc_create_jwt.vcxproj | 151 + .../grpc_create_jwt.vcxproj.filters | 21 + .../grpc_csharp_ext/grpc_csharp_ext.vcxproj | 166 + .../grpc_csharp_ext.vcxproj.filters | 21 + .../grpc_csharp_plugin.vcxproj | 143 + .../grpc_csharp_plugin.vcxproj.filters | 18 + .../grpc_fetch_oauth2.vcxproj | 151 + .../grpc_fetch_oauth2.vcxproj.filters | 21 + .../grpc_objective_c_plugin.vcxproj | 143 + .../grpc_objective_c_plugin.vcxproj.filters | 18 + .../grpc_plugin_support.vcxproj | 162 + .../grpc_plugin_support.vcxproj.filters | 86 + ...c_print_google_default_creds_token.vcxproj | 151 + ...google_default_creds_token.vcxproj.filters | 21 + .../grpc_python_plugin.vcxproj | 143 + .../grpc_python_plugin.vcxproj.filters | 18 + .../grpc_ruby_plugin/grpc_ruby_plugin.vcxproj | 143 + .../grpc_ruby_plugin.vcxproj.filters | 18 + .../grpc_test_util/grpc_test_util.vcxproj | 181 + .../grpc_test_util.vcxproj.filters | 98 + .../grpc_test_util_unsecure.vcxproj | 174 + .../grpc_test_util_unsecure.vcxproj.filters | 83 + .../grpc_unsecure/grpc_unsecure.vcxproj | 587 + .../grpc_unsecure.vcxproj.filters | 766 + .../grpc_verify_jwt/grpc_verify_jwt.vcxproj | 151 + .../grpc_verify_jwt.vcxproj.filters | 21 + .../interop_client_helper.vcxproj | 165 + .../interop_client_helper.vcxproj.filters | 32 + .../interop_client_main.vcxproj | 192 + .../interop_client_main.vcxproj.filters | 41 + .../interop_server_helper.vcxproj | 154 + .../interop_server_helper.vcxproj.filters | 26 + .../interop_server_main.vcxproj | 187 + .../interop_server_main.vcxproj.filters | 33 + vsprojects/vcxproj/qps/qps.vcxproj | 195 + vsprojects/vcxproj/qps/qps.vcxproj.filters | 92 + .../reconnect_server/reconnect_server.vcxproj | 154 + .../reconnect_server.vcxproj.filters | 26 + .../alarm_heap_test/alarm_heap_test.vcxproj | 172 + .../alarm_heap_test.vcxproj.filters | 21 + .../alarm_list_test/alarm_list_test.vcxproj | 172 + .../alarm_list_test.vcxproj.filters | 21 + .../test/alarm_test/alarm_test.vcxproj | 172 + .../alarm_test/alarm_test.vcxproj.filters | 21 + .../vcxproj/test/alpn_test/alpn_test.vcxproj | 172 + .../test/alpn_test/alpn_test.vcxproj.filters | 24 + .../async_end2end_test.vcxproj | 180 + .../async_end2end_test.vcxproj.filters | 21 + .../auth_property_iterator_test.vcxproj | 180 + ...uth_property_iterator_test.vcxproj.filters | 21 + .../bad_client_test/bad_client_test.vcxproj | 154 + .../bad_client_test.vcxproj.filters | 26 + .../bin_encoder_test/bin_encoder_test.vcxproj | 172 + .../bin_encoder_test.vcxproj.filters | 24 + .../channel_arguments_test.vcxproj | 171 + .../channel_arguments_test.vcxproj.filters | 21 + .../chttp2_status_conversion_test.vcxproj | 172 + ...tp2_status_conversion_test.vcxproj.filters | 24 + .../chttp2_stream_encoder_test.vcxproj | 172 + ...chttp2_stream_encoder_test.vcxproj.filters | 24 + .../chttp2_stream_map_test.vcxproj | 172 + .../chttp2_stream_map_test.vcxproj.filters | 24 + .../test/cli_call_test/cli_call_test.vcxproj | 180 + .../cli_call_test.vcxproj.filters | 21 + .../client_crash_test_server.vcxproj | 180 + .../client_crash_test_server.vcxproj.filters | 21 + .../compression_test/compression_test.vcxproj | 172 + .../compression_test.vcxproj.filters | 21 + .../connection_prefix_bad_client_test.vcxproj | 175 + ...ion_prefix_bad_client_test.vcxproj.filters | 24 + .../credentials_test/credentials_test.vcxproj | 171 + .../credentials_test.vcxproj.filters | 21 + .../cxx_byte_buffer_test.vcxproj | 177 + .../cxx_byte_buffer_test.vcxproj.filters | 21 + .../cxx_slice_test/cxx_slice_test.vcxproj | 177 + .../cxx_slice_test.vcxproj.filters | 21 + .../cxx_string_ref_test.vcxproj | 165 + .../cxx_string_ref_test.vcxproj.filters | 21 + .../test/cxx_time_test/cxx_time_test.vcxproj | 177 + .../cxx_time_test.vcxproj.filters | 21 + .../test/end2end_certs/end2end_certs.vcxproj | 141 + .../end2end_certs.vcxproj.filters | 30 + .../end2end_fixture_h2_compress.vcxproj | 154 + ...nd2end_fixture_h2_compress.vcxproj.filters | 29 + .../end2end_fixture_h2_fakesec.vcxproj | 157 + ...end2end_fixture_h2_fakesec.vcxproj.filters | 29 + .../end2end_fixture_h2_full.vcxproj | 154 + .../end2end_fixture_h2_full.vcxproj.filters | 29 + .../end2end_fixture_h2_oauth2.vcxproj | 157 + .../end2end_fixture_h2_oauth2.vcxproj.filters | 29 + .../end2end_fixture_h2_proxy.vcxproj | 154 + .../end2end_fixture_h2_proxy.vcxproj.filters | 29 + .../end2end_fixture_h2_sockpair+trace.vcxproj | 154 + ..._fixture_h2_sockpair+trace.vcxproj.filters | 29 + .../end2end_fixture_h2_sockpair.vcxproj | 154 + ...nd2end_fixture_h2_sockpair.vcxproj.filters | 29 + .../end2end_fixture_h2_sockpair_1byte.vcxproj | 154 + ..._fixture_h2_sockpair_1byte.vcxproj.filters | 29 + .../end2end_fixture_h2_ssl.vcxproj | 157 + .../end2end_fixture_h2_ssl.vcxproj.filters | 29 + .../end2end_fixture_h2_ssl_proxy.vcxproj | 157 + ...d2end_fixture_h2_ssl_proxy.vcxproj.filters | 29 + .../test/end2end_test/end2end_test.vcxproj | 180 + .../end2end_test/end2end_test.vcxproj.filters | 21 + .../end2end_test_bad_hostname.vcxproj | 155 + .../end2end_test_bad_hostname.vcxproj.filters | 32 + .../end2end_test_binary_metadata.vcxproj | 155 + ...d2end_test_binary_metadata.vcxproj.filters | 32 + .../end2end_test_call_creds.vcxproj | 158 + .../end2end_test_call_creds.vcxproj.filters | 32 + .../end2end_test_cancel_after_accept.vcxproj | 155 + ...d_test_cancel_after_accept.vcxproj.filters | 32 + ...2end_test_cancel_after_client_done.vcxproj | 155 + ...t_cancel_after_client_done.vcxproj.filters | 32 + .../end2end_test_cancel_after_invoke.vcxproj | 155 + ...d_test_cancel_after_invoke.vcxproj.filters | 32 + .../end2end_test_cancel_before_invoke.vcxproj | 155 + ..._test_cancel_before_invoke.vcxproj.filters | 32 + .../end2end_test_cancel_in_a_vacuum.vcxproj | 155 + ...nd_test_cancel_in_a_vacuum.vcxproj.filters | 32 + ...end2end_test_census_simple_request.vcxproj | 155 + ...test_census_simple_request.vcxproj.filters | 32 + .../end2end_test_channel_connectivity.vcxproj | 155 + ..._test_channel_connectivity.vcxproj.filters | 32 + .../end2end_test_compressed_payload.vcxproj | 155 + ...nd_test_compressed_payload.vcxproj.filters | 32 + .../end2end_test_default_host.vcxproj | 155 + .../end2end_test_default_host.vcxproj.filters | 32 + .../end2end_test_disappearing_server.vcxproj | 155 + ...d_test_disappearing_server.vcxproj.filters | 32 + .../end2end_test_empty_batch.vcxproj | 155 + .../end2end_test_empty_batch.vcxproj.filters | 32 + ...2end_test_graceful_server_shutdown.vcxproj | 155 + ...t_graceful_server_shutdown.vcxproj.filters | 32 + .../end2end_test_high_initial_seqno.vcxproj | 155 + ...nd_test_high_initial_seqno.vcxproj.filters | 32 + .../end2end_test_invoke_large_request.vcxproj | 155 + ..._test_invoke_large_request.vcxproj.filters | 32 + .../end2end_test_large_metadata.vcxproj | 155 + ...nd2end_test_large_metadata.vcxproj.filters | 32 + ...nd2end_test_max_concurrent_streams.vcxproj | 155 + ...est_max_concurrent_streams.vcxproj.filters | 32 + .../end2end_test_max_message_length.vcxproj | 155 + ...nd_test_max_message_length.vcxproj.filters | 32 + .../end2end_test_metadata.vcxproj | 155 + .../end2end_test_metadata.vcxproj.filters | 32 + .../end2end_test_no_op.vcxproj | 155 + .../end2end_test_no_op.vcxproj.filters | 32 + .../end2end_test_payload.vcxproj | 155 + .../end2end_test_payload.vcxproj.filters | 32 + .../end2end_test_ping_pong_streaming.vcxproj | 155 + ...d_test_ping_pong_streaming.vcxproj.filters | 32 + .../end2end_test_registered_call.vcxproj | 155 + ...d2end_test_registered_call.vcxproj.filters | 32 + .../end2end_test_request_with_flags.vcxproj | 155 + ...nd_test_request_with_flags.vcxproj.filters | 32 + .../end2end_test_request_with_payload.vcxproj | 155 + ..._test_request_with_payload.vcxproj.filters | 32 + ...d2end_test_server_finishes_request.vcxproj | 155 + ...st_server_finishes_request.vcxproj.filters | 32 + ...d2end_test_shutdown_finishes_calls.vcxproj | 155 + ...st_shutdown_finishes_calls.vcxproj.filters | 32 + ...nd2end_test_shutdown_finishes_tags.vcxproj | 155 + ...est_shutdown_finishes_tags.vcxproj.filters | 32 + ...nd2end_test_simple_delayed_request.vcxproj | 155 + ...est_simple_delayed_request.vcxproj.filters | 32 + .../end2end_test_simple_request.vcxproj | 155 + ...nd2end_test_simple_request.vcxproj.filters | 32 + .../end2end_test_trailing_metadata.vcxproj | 155 + ...end_test_trailing_metadata.vcxproj.filters | 32 + .../endpoint_pair_test.vcxproj | 172 + .../endpoint_pair_test.vcxproj.filters | 21 + .../test/fling_client/fling_client.vcxproj | 172 + .../fling_client/fling_client.vcxproj.filters | 21 + .../test/fling_server/fling_server.vcxproj | 172 + .../fling_server/fling_server.vcxproj.filters | 21 + .../generic_end2end_test.vcxproj | 180 + .../generic_end2end_test.vcxproj.filters | 21 + .../gpr_cmdline_test/gpr_cmdline_test.vcxproj | 166 + .../gpr_cmdline_test.vcxproj.filters | 21 + .../test/gpr_env_test/gpr_env_test.vcxproj | 166 + .../gpr_env_test/gpr_env_test.vcxproj.filters | 21 + .../test/gpr_file_test/gpr_file_test.vcxproj | 166 + .../gpr_file_test.vcxproj.filters | 21 + .../gpr_histogram_test.vcxproj | 166 + .../gpr_histogram_test.vcxproj.filters | 21 + .../gpr_host_port_test.vcxproj | 166 + .../gpr_host_port_test.vcxproj.filters | 21 + .../test/gpr_log_test/gpr_log_test.vcxproj | 166 + .../gpr_log_test/gpr_log_test.vcxproj.filters | 21 + .../gpr_slice_buffer_test.vcxproj | 166 + .../gpr_slice_buffer_test.vcxproj.filters | 21 + .../gpr_slice_test/gpr_slice_test.vcxproj | 166 + .../gpr_slice_test.vcxproj.filters | 21 + .../gpr_stack_lockfree_test.vcxproj | 166 + .../gpr_stack_lockfree_test.vcxproj.filters | 21 + .../gpr_string_test/gpr_string_test.vcxproj | 166 + .../gpr_string_test.vcxproj.filters | 21 + .../test/gpr_sync_test/gpr_sync_test.vcxproj | 166 + .../gpr_sync_test.vcxproj.filters | 21 + .../test/gpr_thd_test/gpr_thd_test.vcxproj | 166 + .../gpr_thd_test/gpr_thd_test.vcxproj.filters | 21 + .../test/gpr_time_test/gpr_time_test.vcxproj | 166 + .../gpr_time_test.vcxproj.filters | 21 + .../test/gpr_tls_test/gpr_tls_test.vcxproj | 166 + .../gpr_tls_test/gpr_tls_test.vcxproj.filters | 21 + .../gpr_useful_test/gpr_useful_test.vcxproj | 166 + .../gpr_useful_test.vcxproj.filters | 21 + .../grpc_auth_context_test.vcxproj | 172 + .../grpc_auth_context_test.vcxproj.filters | 21 + .../grpc_base64_test/grpc_base64_test.vcxproj | 172 + .../grpc_base64_test.vcxproj.filters | 21 + .../grpc_byte_buffer_reader_test.vcxproj | 172 + ...pc_byte_buffer_reader_test.vcxproj.filters | 21 + .../grpc_channel_args_test.vcxproj | 172 + .../grpc_channel_args_test.vcxproj.filters | 21 + .../grpc_channel_stack_test.vcxproj | 172 + .../grpc_channel_stack_test.vcxproj.filters | 21 + .../vcxproj/test/grpc_cli/grpc_cli.vcxproj | 183 + .../test/grpc_cli/grpc_cli.vcxproj.filters | 21 + .../grpc_completion_queue_test.vcxproj | 172 + ...grpc_completion_queue_test.vcxproj.filters | 21 + .../grpc_credentials_test.vcxproj | 172 + .../grpc_credentials_test.vcxproj.filters | 21 + .../grpc_jwt_verifier_test.vcxproj | 172 + .../grpc_jwt_verifier_test.vcxproj.filters | 21 + .../grpc_security_connector_test.vcxproj | 172 + ...pc_security_connector_test.vcxproj.filters | 21 + .../grpc_stream_op_test.vcxproj | 172 + .../grpc_stream_op_test.vcxproj.filters | 21 + ...2_compress_bad_hostname_nosec_test.vcxproj | 178 + ...ss_bad_hostname_nosec_test.vcxproj.filters | 7 + .../h2_compress_bad_hostname_test.vcxproj | 181 + ...compress_bad_hostname_test.vcxproj.filters | 7 + ...ompress_binary_metadata_nosec_test.vcxproj | 178 + ...binary_metadata_nosec_test.vcxproj.filters | 7 + .../h2_compress_binary_metadata_test.vcxproj | 181 + ...press_binary_metadata_test.vcxproj.filters | 7 + .../h2_compress_call_creds_test.vcxproj | 181 + ...2_compress_call_creds_test.vcxproj.filters | 7 + ...ess_cancel_after_accept_nosec_test.vcxproj | 178 + ...el_after_accept_nosec_test.vcxproj.filters | 7 + ..._compress_cancel_after_accept_test.vcxproj | 181 + ...s_cancel_after_accept_test.vcxproj.filters | 7 + ...ancel_after_client_done_nosec_test.vcxproj | 178 + ...ter_client_done_nosec_test.vcxproj.filters | 7 + ...ress_cancel_after_client_done_test.vcxproj | 181 + ...cel_after_client_done_test.vcxproj.filters | 7 + ...ess_cancel_after_invoke_nosec_test.vcxproj | 178 + ...el_after_invoke_nosec_test.vcxproj.filters | 7 + ..._compress_cancel_after_invoke_test.vcxproj | 181 + ...s_cancel_after_invoke_test.vcxproj.filters | 7 + ...ss_cancel_before_invoke_nosec_test.vcxproj | 178 + ...l_before_invoke_nosec_test.vcxproj.filters | 7 + ...compress_cancel_before_invoke_test.vcxproj | 181 + ..._cancel_before_invoke_test.vcxproj.filters | 7 + ...ress_cancel_in_a_vacuum_nosec_test.vcxproj | 178 + ...cel_in_a_vacuum_nosec_test.vcxproj.filters | 7 + ...2_compress_cancel_in_a_vacuum_test.vcxproj | 181 + ...ss_cancel_in_a_vacuum_test.vcxproj.filters | 7 + ...s_census_simple_request_nosec_test.vcxproj | 178 + ..._simple_request_nosec_test.vcxproj.filters | 7 + ...ompress_census_simple_request_test.vcxproj | 181 + ...census_simple_request_test.vcxproj.filters | 7 + ...ss_channel_connectivity_nosec_test.vcxproj | 178 + ...el_connectivity_nosec_test.vcxproj.filters | 7 + ...compress_channel_connectivity_test.vcxproj | 181 + ..._channel_connectivity_test.vcxproj.filters | 7 + ...ress_compressed_payload_nosec_test.vcxproj | 178 + ...pressed_payload_nosec_test.vcxproj.filters | 7 + ...2_compress_compressed_payload_test.vcxproj | 181 + ...ss_compressed_payload_test.vcxproj.filters | 7 + ...2_compress_default_host_nosec_test.vcxproj | 178 + ...ss_default_host_nosec_test.vcxproj.filters | 7 + .../h2_compress_default_host_test.vcxproj | 181 + ...compress_default_host_test.vcxproj.filters | 7 + ...ess_disappearing_server_nosec_test.vcxproj | 178 + ...ppearing_server_nosec_test.vcxproj.filters | 7 + ..._compress_disappearing_server_test.vcxproj | 181 + ...s_disappearing_server_test.vcxproj.filters | 7 + ...h2_compress_empty_batch_nosec_test.vcxproj | 178 + ...ess_empty_batch_nosec_test.vcxproj.filters | 7 + .../h2_compress_empty_batch_test.vcxproj | 181 + ..._compress_empty_batch_test.vcxproj.filters | 7 + ...raceful_server_shutdown_nosec_test.vcxproj | 178 + ...server_shutdown_nosec_test.vcxproj.filters | 7 + ...ress_graceful_server_shutdown_test.vcxproj | 181 + ...ceful_server_shutdown_test.vcxproj.filters | 7 + ...ress_high_initial_seqno_nosec_test.vcxproj | 178 + ...h_initial_seqno_nosec_test.vcxproj.filters | 7 + ...2_compress_high_initial_seqno_test.vcxproj | 181 + ...ss_high_initial_seqno_test.vcxproj.filters | 7 + ...ss_invoke_large_request_nosec_test.vcxproj | 178 + ...e_large_request_nosec_test.vcxproj.filters | 7 + ...compress_invoke_large_request_test.vcxproj | 181 + ..._invoke_large_request_test.vcxproj.filters | 7 + ...compress_large_metadata_nosec_test.vcxproj | 178 + ..._large_metadata_nosec_test.vcxproj.filters | 7 + .../h2_compress_large_metadata_test.vcxproj | 181 + ...mpress_large_metadata_test.vcxproj.filters | 7 + ..._max_concurrent_streams_nosec_test.vcxproj | 178 + ...current_streams_nosec_test.vcxproj.filters | 7 + ...mpress_max_concurrent_streams_test.vcxproj | 181 + ...ax_concurrent_streams_test.vcxproj.filters | 7 + ...ress_max_message_length_nosec_test.vcxproj | 178 + ..._message_length_nosec_test.vcxproj.filters | 7 + ...2_compress_max_message_length_test.vcxproj | 181 + ...ss_max_message_length_test.vcxproj.filters | 7 + .../h2_compress_metadata_nosec_test.vcxproj | 178 + ...mpress_metadata_nosec_test.vcxproj.filters | 7 + .../h2_compress_metadata_test.vcxproj | 181 + .../h2_compress_metadata_test.vcxproj.filters | 7 + .../h2_compress_no_op_nosec_test.vcxproj | 178 + ..._compress_no_op_nosec_test.vcxproj.filters | 7 + .../h2_compress_no_op_test.vcxproj | 181 + .../h2_compress_no_op_test.vcxproj.filters | 7 + .../h2_compress_payload_nosec_test.vcxproj | 178 + ...ompress_payload_nosec_test.vcxproj.filters | 7 + .../h2_compress_payload_test.vcxproj | 181 + .../h2_compress_payload_test.vcxproj.filters | 7 + ...ess_ping_pong_streaming_nosec_test.vcxproj | 178 + ..._pong_streaming_nosec_test.vcxproj.filters | 7 + ..._compress_ping_pong_streaming_test.vcxproj | 181 + ...s_ping_pong_streaming_test.vcxproj.filters | 7 + ...ompress_registered_call_nosec_test.vcxproj | 178 + ...registered_call_nosec_test.vcxproj.filters | 7 + .../h2_compress_registered_call_test.vcxproj | 181 + ...press_registered_call_test.vcxproj.filters | 7 + ...ress_request_with_flags_nosec_test.vcxproj | 178 + ...uest_with_flags_nosec_test.vcxproj.filters | 7 + ...2_compress_request_with_flags_test.vcxproj | 181 + ...ss_request_with_flags_test.vcxproj.filters | 7 + ...ss_request_with_payload_nosec_test.vcxproj | 178 + ...st_with_payload_nosec_test.vcxproj.filters | 7 + ...compress_request_with_payload_test.vcxproj | 181 + ..._request_with_payload_test.vcxproj.filters | 7 + ...server_finishes_request_nosec_test.vcxproj | 178 + ...inishes_request_nosec_test.vcxproj.filters | 7 + ...press_server_finishes_request_test.vcxproj | 181 + ...rver_finishes_request_test.vcxproj.filters | 7 + ...shutdown_finishes_calls_nosec_test.vcxproj | 178 + ..._finishes_calls_nosec_test.vcxproj.filters | 7 + ...press_shutdown_finishes_calls_test.vcxproj | 181 + ...utdown_finishes_calls_test.vcxproj.filters | 7 + ..._shutdown_finishes_tags_nosec_test.vcxproj | 178 + ...n_finishes_tags_nosec_test.vcxproj.filters | 7 + ...mpress_shutdown_finishes_tags_test.vcxproj | 181 + ...hutdown_finishes_tags_test.vcxproj.filters | 7 + ..._simple_delayed_request_nosec_test.vcxproj | 178 + ...delayed_request_nosec_test.vcxproj.filters | 7 + ...mpress_simple_delayed_request_test.vcxproj | 181 + ...imple_delayed_request_test.vcxproj.filters | 7 + ...compress_simple_request_nosec_test.vcxproj | 178 + ..._simple_request_nosec_test.vcxproj.filters | 7 + .../h2_compress_simple_request_test.vcxproj | 181 + ...mpress_simple_request_test.vcxproj.filters | 7 + ...press_trailing_metadata_nosec_test.vcxproj | 178 + ...ailing_metadata_nosec_test.vcxproj.filters | 7 + ...h2_compress_trailing_metadata_test.vcxproj | 181 + ...ess_trailing_metadata_test.vcxproj.filters | 7 + .../h2_fakesec_bad_hostname_test.vcxproj | 181 + ..._fakesec_bad_hostname_test.vcxproj.filters | 7 + .../h2_fakesec_binary_metadata_test.vcxproj | 181 + ...kesec_binary_metadata_test.vcxproj.filters | 7 + .../h2_fakesec_call_creds_test.vcxproj | 181 + ...h2_fakesec_call_creds_test.vcxproj.filters | 7 + ...2_fakesec_cancel_after_accept_test.vcxproj | 181 + ...c_cancel_after_accept_test.vcxproj.filters | 7 + ...esec_cancel_after_client_done_test.vcxproj | 181 + ...cel_after_client_done_test.vcxproj.filters | 7 + ...2_fakesec_cancel_after_invoke_test.vcxproj | 181 + ...c_cancel_after_invoke_test.vcxproj.filters | 7 + ..._fakesec_cancel_before_invoke_test.vcxproj | 181 + ..._cancel_before_invoke_test.vcxproj.filters | 7 + ...h2_fakesec_cancel_in_a_vacuum_test.vcxproj | 181 + ...ec_cancel_in_a_vacuum_test.vcxproj.filters | 7 + ...fakesec_census_simple_request_test.vcxproj | 181 + ...census_simple_request_test.vcxproj.filters | 7 + ..._fakesec_channel_connectivity_test.vcxproj | 181 + ..._channel_connectivity_test.vcxproj.filters | 7 + ...h2_fakesec_compressed_payload_test.vcxproj | 181 + ...ec_compressed_payload_test.vcxproj.filters | 7 + .../h2_fakesec_default_host_test.vcxproj | 181 + ..._fakesec_default_host_test.vcxproj.filters | 7 + ...2_fakesec_disappearing_server_test.vcxproj | 181 + ...c_disappearing_server_test.vcxproj.filters | 7 + .../h2_fakesec_empty_batch_test.vcxproj | 181 + ...2_fakesec_empty_batch_test.vcxproj.filters | 7 + ...esec_graceful_server_shutdown_test.vcxproj | 181 + ...ceful_server_shutdown_test.vcxproj.filters | 7 + ...h2_fakesec_high_initial_seqno_test.vcxproj | 181 + ...ec_high_initial_seqno_test.vcxproj.filters | 7 + ..._fakesec_invoke_large_request_test.vcxproj | 181 + ..._invoke_large_request_test.vcxproj.filters | 7 + .../h2_fakesec_large_metadata_test.vcxproj | 181 + ...akesec_large_metadata_test.vcxproj.filters | 7 + ...akesec_max_concurrent_streams_test.vcxproj | 181 + ...ax_concurrent_streams_test.vcxproj.filters | 7 + ...h2_fakesec_max_message_length_test.vcxproj | 181 + ...ec_max_message_length_test.vcxproj.filters | 7 + .../h2_fakesec_metadata_test.vcxproj | 181 + .../h2_fakesec_metadata_test.vcxproj.filters | 7 + .../h2_fakesec_no_op_test.vcxproj | 181 + .../h2_fakesec_no_op_test.vcxproj.filters | 7 + .../h2_fakesec_payload_test.vcxproj | 181 + .../h2_fakesec_payload_test.vcxproj.filters | 7 + ...2_fakesec_ping_pong_streaming_test.vcxproj | 181 + ...c_ping_pong_streaming_test.vcxproj.filters | 7 + .../h2_fakesec_registered_call_test.vcxproj | 181 + ...kesec_registered_call_test.vcxproj.filters | 7 + ...h2_fakesec_request_with_flags_test.vcxproj | 181 + ...ec_request_with_flags_test.vcxproj.filters | 7 + ..._fakesec_request_with_payload_test.vcxproj | 181 + ..._request_with_payload_test.vcxproj.filters | 7 + ...kesec_server_finishes_request_test.vcxproj | 181 + ...rver_finishes_request_test.vcxproj.filters | 7 + ...kesec_shutdown_finishes_calls_test.vcxproj | 181 + ...utdown_finishes_calls_test.vcxproj.filters | 7 + ...akesec_shutdown_finishes_tags_test.vcxproj | 181 + ...hutdown_finishes_tags_test.vcxproj.filters | 7 + ...akesec_simple_delayed_request_test.vcxproj | 181 + ...imple_delayed_request_test.vcxproj.filters | 7 + .../h2_fakesec_simple_request_test.vcxproj | 181 + ...akesec_simple_request_test.vcxproj.filters | 7 + .../h2_fakesec_trailing_metadata_test.vcxproj | 181 + ...sec_trailing_metadata_test.vcxproj.filters | 7 + .../h2_full_bad_hostname_nosec_test.vcxproj | 178 + ...ll_bad_hostname_nosec_test.vcxproj.filters | 7 + .../h2_full_bad_hostname_test.vcxproj | 181 + .../h2_full_bad_hostname_test.vcxproj.filters | 7 + ...h2_full_binary_metadata_nosec_test.vcxproj | 178 + ...binary_metadata_nosec_test.vcxproj.filters | 7 + .../h2_full_binary_metadata_test.vcxproj | 181 + ..._full_binary_metadata_test.vcxproj.filters | 7 + .../h2_full_call_creds_test.vcxproj | 181 + .../h2_full_call_creds_test.vcxproj.filters | 7 + ...ull_cancel_after_accept_nosec_test.vcxproj | 178 + ...el_after_accept_nosec_test.vcxproj.filters | 7 + .../h2_full_cancel_after_accept_test.vcxproj | 181 + ...l_cancel_after_accept_test.vcxproj.filters | 7 + ...ancel_after_client_done_nosec_test.vcxproj | 178 + ...ter_client_done_nosec_test.vcxproj.filters | 7 + ...full_cancel_after_client_done_test.vcxproj | 181 + ...cel_after_client_done_test.vcxproj.filters | 7 + ...ull_cancel_after_invoke_nosec_test.vcxproj | 178 + ...el_after_invoke_nosec_test.vcxproj.filters | 7 + .../h2_full_cancel_after_invoke_test.vcxproj | 181 + ...l_cancel_after_invoke_test.vcxproj.filters | 7 + ...ll_cancel_before_invoke_nosec_test.vcxproj | 178 + ...l_before_invoke_nosec_test.vcxproj.filters | 7 + .../h2_full_cancel_before_invoke_test.vcxproj | 181 + ..._cancel_before_invoke_test.vcxproj.filters | 7 + ...full_cancel_in_a_vacuum_nosec_test.vcxproj | 178 + ...cel_in_a_vacuum_nosec_test.vcxproj.filters | 7 + .../h2_full_cancel_in_a_vacuum_test.vcxproj | 181 + ...ll_cancel_in_a_vacuum_test.vcxproj.filters | 7 + ...l_census_simple_request_nosec_test.vcxproj | 178 + ..._simple_request_nosec_test.vcxproj.filters | 7 + ...h2_full_census_simple_request_test.vcxproj | 181 + ...census_simple_request_test.vcxproj.filters | 7 + ...ll_channel_connectivity_nosec_test.vcxproj | 178 + ...el_connectivity_nosec_test.vcxproj.filters | 7 + .../h2_full_channel_connectivity_test.vcxproj | 181 + ..._channel_connectivity_test.vcxproj.filters | 7 + ...full_compressed_payload_nosec_test.vcxproj | 178 + ...pressed_payload_nosec_test.vcxproj.filters | 7 + .../h2_full_compressed_payload_test.vcxproj | 181 + ...ll_compressed_payload_test.vcxproj.filters | 7 + .../h2_full_default_host_nosec_test.vcxproj | 178 + ...ll_default_host_nosec_test.vcxproj.filters | 7 + .../h2_full_default_host_test.vcxproj | 181 + .../h2_full_default_host_test.vcxproj.filters | 7 + ...ull_disappearing_server_nosec_test.vcxproj | 178 + ...ppearing_server_nosec_test.vcxproj.filters | 7 + .../h2_full_disappearing_server_test.vcxproj | 181 + ...l_disappearing_server_test.vcxproj.filters | 7 + .../h2_full_empty_batch_nosec_test.vcxproj | 178 + ...ull_empty_batch_nosec_test.vcxproj.filters | 7 + .../h2_full_empty_batch_test.vcxproj | 181 + .../h2_full_empty_batch_test.vcxproj.filters | 7 + ...raceful_server_shutdown_nosec_test.vcxproj | 178 + ...server_shutdown_nosec_test.vcxproj.filters | 7 + ...full_graceful_server_shutdown_test.vcxproj | 181 + ...ceful_server_shutdown_test.vcxproj.filters | 7 + ...full_high_initial_seqno_nosec_test.vcxproj | 178 + ...h_initial_seqno_nosec_test.vcxproj.filters | 7 + .../h2_full_high_initial_seqno_test.vcxproj | 181 + ...ll_high_initial_seqno_test.vcxproj.filters | 7 + ...ll_invoke_large_request_nosec_test.vcxproj | 178 + ...e_large_request_nosec_test.vcxproj.filters | 7 + .../h2_full_invoke_large_request_test.vcxproj | 181 + ..._invoke_large_request_test.vcxproj.filters | 7 + .../h2_full_large_metadata_nosec_test.vcxproj | 178 + ..._large_metadata_nosec_test.vcxproj.filters | 7 + .../h2_full_large_metadata_test.vcxproj | 181 + ...2_full_large_metadata_test.vcxproj.filters | 7 + ..._max_concurrent_streams_nosec_test.vcxproj | 178 + ...current_streams_nosec_test.vcxproj.filters | 7 + ...2_full_max_concurrent_streams_test.vcxproj | 181 + ...ax_concurrent_streams_test.vcxproj.filters | 7 + ...full_max_message_length_nosec_test.vcxproj | 178 + ..._message_length_nosec_test.vcxproj.filters | 7 + .../h2_full_max_message_length_test.vcxproj | 181 + ...ll_max_message_length_test.vcxproj.filters | 7 + .../h2_full_metadata_nosec_test.vcxproj | 178 + ...2_full_metadata_nosec_test.vcxproj.filters | 7 + .../h2_full_metadata_test.vcxproj | 181 + .../h2_full_metadata_test.vcxproj.filters | 7 + .../h2_full_no_op_nosec_test.vcxproj | 178 + .../h2_full_no_op_nosec_test.vcxproj.filters | 7 + .../h2_full_no_op_test.vcxproj | 181 + .../h2_full_no_op_test.vcxproj.filters | 7 + .../h2_full_payload_nosec_test.vcxproj | 178 + ...h2_full_payload_nosec_test.vcxproj.filters | 7 + .../h2_full_payload_test.vcxproj | 181 + .../h2_full_payload_test.vcxproj.filters | 7 + ...ull_ping_pong_streaming_nosec_test.vcxproj | 178 + ..._pong_streaming_nosec_test.vcxproj.filters | 7 + .../h2_full_ping_pong_streaming_test.vcxproj | 181 + ...l_ping_pong_streaming_test.vcxproj.filters | 7 + ...h2_full_registered_call_nosec_test.vcxproj | 178 + ...registered_call_nosec_test.vcxproj.filters | 7 + .../h2_full_registered_call_test.vcxproj | 181 + ..._full_registered_call_test.vcxproj.filters | 7 + ...full_request_with_flags_nosec_test.vcxproj | 178 + ...uest_with_flags_nosec_test.vcxproj.filters | 7 + .../h2_full_request_with_flags_test.vcxproj | 181 + ...ll_request_with_flags_test.vcxproj.filters | 7 + ...ll_request_with_payload_nosec_test.vcxproj | 178 + ...st_with_payload_nosec_test.vcxproj.filters | 7 + .../h2_full_request_with_payload_test.vcxproj | 181 + ..._request_with_payload_test.vcxproj.filters | 7 + ...server_finishes_request_nosec_test.vcxproj | 178 + ...inishes_request_nosec_test.vcxproj.filters | 7 + ..._full_server_finishes_request_test.vcxproj | 181 + ...rver_finishes_request_test.vcxproj.filters | 7 + ...shutdown_finishes_calls_nosec_test.vcxproj | 178 + ..._finishes_calls_nosec_test.vcxproj.filters | 7 + ..._full_shutdown_finishes_calls_test.vcxproj | 181 + ...utdown_finishes_calls_test.vcxproj.filters | 7 + ..._shutdown_finishes_tags_nosec_test.vcxproj | 178 + ...n_finishes_tags_nosec_test.vcxproj.filters | 7 + ...2_full_shutdown_finishes_tags_test.vcxproj | 181 + ...hutdown_finishes_tags_test.vcxproj.filters | 7 + ..._simple_delayed_request_nosec_test.vcxproj | 178 + ...delayed_request_nosec_test.vcxproj.filters | 7 + ...2_full_simple_delayed_request_test.vcxproj | 181 + ...imple_delayed_request_test.vcxproj.filters | 7 + .../h2_full_simple_request_nosec_test.vcxproj | 178 + ..._simple_request_nosec_test.vcxproj.filters | 7 + .../h2_full_simple_request_test.vcxproj | 181 + ...2_full_simple_request_test.vcxproj.filters | 7 + ..._full_trailing_metadata_nosec_test.vcxproj | 178 + ...ailing_metadata_nosec_test.vcxproj.filters | 7 + .../h2_full_trailing_metadata_test.vcxproj | 181 + ...ull_trailing_metadata_test.vcxproj.filters | 7 + .../h2_oauth2_bad_hostname_test.vcxproj | 181 + ...2_oauth2_bad_hostname_test.vcxproj.filters | 7 + .../h2_oauth2_binary_metadata_test.vcxproj | 181 + ...auth2_binary_metadata_test.vcxproj.filters | 7 + .../h2_oauth2_call_creds_test.vcxproj | 181 + .../h2_oauth2_call_creds_test.vcxproj.filters | 7 + ...h2_oauth2_cancel_after_accept_test.vcxproj | 181 + ...2_cancel_after_accept_test.vcxproj.filters | 7 + ...uth2_cancel_after_client_done_test.vcxproj | 181 + ...cel_after_client_done_test.vcxproj.filters | 7 + ...h2_oauth2_cancel_after_invoke_test.vcxproj | 181 + ...2_cancel_after_invoke_test.vcxproj.filters | 7 + ...2_oauth2_cancel_before_invoke_test.vcxproj | 181 + ..._cancel_before_invoke_test.vcxproj.filters | 7 + .../h2_oauth2_cancel_in_a_vacuum_test.vcxproj | 181 + ...h2_cancel_in_a_vacuum_test.vcxproj.filters | 7 + ..._oauth2_census_simple_request_test.vcxproj | 181 + ...census_simple_request_test.vcxproj.filters | 7 + ...2_oauth2_channel_connectivity_test.vcxproj | 181 + ..._channel_connectivity_test.vcxproj.filters | 7 + .../h2_oauth2_compressed_payload_test.vcxproj | 181 + ...h2_compressed_payload_test.vcxproj.filters | 7 + .../h2_oauth2_default_host_test.vcxproj | 181 + ...2_oauth2_default_host_test.vcxproj.filters | 7 + ...h2_oauth2_disappearing_server_test.vcxproj | 181 + ...2_disappearing_server_test.vcxproj.filters | 7 + .../h2_oauth2_empty_batch_test.vcxproj | 181 + ...h2_oauth2_empty_batch_test.vcxproj.filters | 7 + ...uth2_graceful_server_shutdown_test.vcxproj | 181 + ...ceful_server_shutdown_test.vcxproj.filters | 7 + .../h2_oauth2_high_initial_seqno_test.vcxproj | 181 + ...h2_high_initial_seqno_test.vcxproj.filters | 7 + ...2_oauth2_invoke_large_request_test.vcxproj | 181 + ..._invoke_large_request_test.vcxproj.filters | 7 + .../h2_oauth2_large_metadata_test.vcxproj | 181 + ...oauth2_large_metadata_test.vcxproj.filters | 7 + ...oauth2_max_concurrent_streams_test.vcxproj | 181 + ...ax_concurrent_streams_test.vcxproj.filters | 7 + .../h2_oauth2_max_message_length_test.vcxproj | 181 + ...h2_max_message_length_test.vcxproj.filters | 7 + .../h2_oauth2_metadata_test.vcxproj | 181 + .../h2_oauth2_metadata_test.vcxproj.filters | 7 + .../h2_oauth2_no_op_test.vcxproj | 181 + .../h2_oauth2_no_op_test.vcxproj.filters | 7 + .../h2_oauth2_payload_test.vcxproj | 181 + .../h2_oauth2_payload_test.vcxproj.filters | 7 + ...h2_oauth2_ping_pong_streaming_test.vcxproj | 181 + ...2_ping_pong_streaming_test.vcxproj.filters | 7 + .../h2_oauth2_registered_call_test.vcxproj | 181 + ...auth2_registered_call_test.vcxproj.filters | 7 + .../h2_oauth2_request_with_flags_test.vcxproj | 181 + ...h2_request_with_flags_test.vcxproj.filters | 7 + ...2_oauth2_request_with_payload_test.vcxproj | 181 + ..._request_with_payload_test.vcxproj.filters | 7 + ...auth2_server_finishes_request_test.vcxproj | 181 + ...rver_finishes_request_test.vcxproj.filters | 7 + ...auth2_shutdown_finishes_calls_test.vcxproj | 181 + ...utdown_finishes_calls_test.vcxproj.filters | 7 + ...oauth2_shutdown_finishes_tags_test.vcxproj | 181 + ...hutdown_finishes_tags_test.vcxproj.filters | 7 + ...oauth2_simple_delayed_request_test.vcxproj | 181 + ...imple_delayed_request_test.vcxproj.filters | 7 + .../h2_oauth2_simple_request_test.vcxproj | 181 + ...oauth2_simple_request_test.vcxproj.filters | 7 + .../h2_oauth2_trailing_metadata_test.vcxproj | 181 + ...th2_trailing_metadata_test.vcxproj.filters | 7 + .../h2_proxy_bad_hostname_nosec_test.vcxproj | 178 + ...xy_bad_hostname_nosec_test.vcxproj.filters | 7 + .../h2_proxy_bad_hostname_test.vcxproj | 181 + ...h2_proxy_bad_hostname_test.vcxproj.filters | 7 + ...2_proxy_binary_metadata_nosec_test.vcxproj | 178 + ...binary_metadata_nosec_test.vcxproj.filters | 7 + .../h2_proxy_binary_metadata_test.vcxproj | 181 + ...proxy_binary_metadata_test.vcxproj.filters | 7 + .../h2_proxy_call_creds_test.vcxproj | 181 + .../h2_proxy_call_creds_test.vcxproj.filters | 7 + ...oxy_cancel_after_accept_nosec_test.vcxproj | 178 + ...el_after_accept_nosec_test.vcxproj.filters | 7 + .../h2_proxy_cancel_after_accept_test.vcxproj | 181 + ...y_cancel_after_accept_test.vcxproj.filters | 7 + ...ancel_after_client_done_nosec_test.vcxproj | 178 + ...ter_client_done_nosec_test.vcxproj.filters | 7 + ...roxy_cancel_after_client_done_test.vcxproj | 181 + ...cel_after_client_done_test.vcxproj.filters | 7 + ...oxy_cancel_after_invoke_nosec_test.vcxproj | 178 + ...el_after_invoke_nosec_test.vcxproj.filters | 7 + .../h2_proxy_cancel_after_invoke_test.vcxproj | 181 + ...y_cancel_after_invoke_test.vcxproj.filters | 7 + ...xy_cancel_before_invoke_nosec_test.vcxproj | 178 + ...l_before_invoke_nosec_test.vcxproj.filters | 7 + ...h2_proxy_cancel_before_invoke_test.vcxproj | 181 + ..._cancel_before_invoke_test.vcxproj.filters | 7 + ...roxy_cancel_in_a_vacuum_nosec_test.vcxproj | 178 + ...cel_in_a_vacuum_nosec_test.vcxproj.filters | 7 + .../h2_proxy_cancel_in_a_vacuum_test.vcxproj | 181 + ...xy_cancel_in_a_vacuum_test.vcxproj.filters | 7 + ...y_census_simple_request_nosec_test.vcxproj | 178 + ..._simple_request_nosec_test.vcxproj.filters | 7 + ...2_proxy_census_simple_request_test.vcxproj | 181 + ...census_simple_request_test.vcxproj.filters | 7 + .../h2_proxy_default_host_nosec_test.vcxproj | 178 + ...xy_default_host_nosec_test.vcxproj.filters | 7 + .../h2_proxy_default_host_test.vcxproj | 181 + ...h2_proxy_default_host_test.vcxproj.filters | 7 + ...oxy_disappearing_server_nosec_test.vcxproj | 178 + ...ppearing_server_nosec_test.vcxproj.filters | 7 + .../h2_proxy_disappearing_server_test.vcxproj | 181 + ...y_disappearing_server_test.vcxproj.filters | 7 + .../h2_proxy_empty_batch_nosec_test.vcxproj | 178 + ...oxy_empty_batch_nosec_test.vcxproj.filters | 7 + .../h2_proxy_empty_batch_test.vcxproj | 181 + .../h2_proxy_empty_batch_test.vcxproj.filters | 7 + ...raceful_server_shutdown_nosec_test.vcxproj | 178 + ...server_shutdown_nosec_test.vcxproj.filters | 7 + ...roxy_graceful_server_shutdown_test.vcxproj | 181 + ...ceful_server_shutdown_test.vcxproj.filters | 7 + ...roxy_high_initial_seqno_nosec_test.vcxproj | 178 + ...h_initial_seqno_nosec_test.vcxproj.filters | 7 + .../h2_proxy_high_initial_seqno_test.vcxproj | 181 + ...xy_high_initial_seqno_test.vcxproj.filters | 7 + ...xy_invoke_large_request_nosec_test.vcxproj | 178 + ...e_large_request_nosec_test.vcxproj.filters | 7 + ...h2_proxy_invoke_large_request_test.vcxproj | 181 + ..._invoke_large_request_test.vcxproj.filters | 7 + ...h2_proxy_large_metadata_nosec_test.vcxproj | 178 + ..._large_metadata_nosec_test.vcxproj.filters | 7 + .../h2_proxy_large_metadata_test.vcxproj | 181 + ..._proxy_large_metadata_test.vcxproj.filters | 7 + ...roxy_max_message_length_nosec_test.vcxproj | 178 + ..._message_length_nosec_test.vcxproj.filters | 7 + .../h2_proxy_max_message_length_test.vcxproj | 181 + ...xy_max_message_length_test.vcxproj.filters | 7 + .../h2_proxy_metadata_nosec_test.vcxproj | 178 + ..._proxy_metadata_nosec_test.vcxproj.filters | 7 + .../h2_proxy_metadata_test.vcxproj | 181 + .../h2_proxy_metadata_test.vcxproj.filters | 7 + .../h2_proxy_no_op_nosec_test.vcxproj | 178 + .../h2_proxy_no_op_nosec_test.vcxproj.filters | 7 + .../h2_proxy_no_op_test.vcxproj | 181 + .../h2_proxy_no_op_test.vcxproj.filters | 7 + .../h2_proxy_payload_nosec_test.vcxproj | 178 + ...2_proxy_payload_nosec_test.vcxproj.filters | 7 + .../h2_proxy_payload_test.vcxproj | 181 + .../h2_proxy_payload_test.vcxproj.filters | 7 + ...oxy_ping_pong_streaming_nosec_test.vcxproj | 178 + ..._pong_streaming_nosec_test.vcxproj.filters | 7 + .../h2_proxy_ping_pong_streaming_test.vcxproj | 181 + ...y_ping_pong_streaming_test.vcxproj.filters | 7 + ...2_proxy_registered_call_nosec_test.vcxproj | 178 + ...registered_call_nosec_test.vcxproj.filters | 7 + .../h2_proxy_registered_call_test.vcxproj | 181 + ...proxy_registered_call_test.vcxproj.filters | 7 + ...xy_request_with_payload_nosec_test.vcxproj | 178 + ...st_with_payload_nosec_test.vcxproj.filters | 7 + ...h2_proxy_request_with_payload_test.vcxproj | 181 + ..._request_with_payload_test.vcxproj.filters | 7 + ...server_finishes_request_nosec_test.vcxproj | 178 + ...inishes_request_nosec_test.vcxproj.filters | 7 + ...proxy_server_finishes_request_test.vcxproj | 181 + ...rver_finishes_request_test.vcxproj.filters | 7 + ...shutdown_finishes_calls_nosec_test.vcxproj | 178 + ..._finishes_calls_nosec_test.vcxproj.filters | 7 + ...proxy_shutdown_finishes_calls_test.vcxproj | 181 + ...utdown_finishes_calls_test.vcxproj.filters | 7 + ..._shutdown_finishes_tags_nosec_test.vcxproj | 178 + ...n_finishes_tags_nosec_test.vcxproj.filters | 7 + ..._proxy_shutdown_finishes_tags_test.vcxproj | 181 + ...hutdown_finishes_tags_test.vcxproj.filters | 7 + ..._simple_delayed_request_nosec_test.vcxproj | 178 + ...delayed_request_nosec_test.vcxproj.filters | 7 + ..._proxy_simple_delayed_request_test.vcxproj | 181 + ...imple_delayed_request_test.vcxproj.filters | 7 + ...h2_proxy_simple_request_nosec_test.vcxproj | 178 + ..._simple_request_nosec_test.vcxproj.filters | 7 + .../h2_proxy_simple_request_test.vcxproj | 181 + ..._proxy_simple_request_test.vcxproj.filters | 7 + ...proxy_trailing_metadata_nosec_test.vcxproj | 178 + ...ailing_metadata_nosec_test.vcxproj.filters | 7 + .../h2_proxy_trailing_metadata_test.vcxproj | 181 + ...oxy_trailing_metadata_test.vcxproj.filters | 7 + ...pair+trace_bad_hostname_nosec_test.vcxproj | 178 + ...ce_bad_hostname_nosec_test.vcxproj.filters | 7 + ...2_sockpair+trace_bad_hostname_test.vcxproj | 181 + ...ir+trace_bad_hostname_test.vcxproj.filters | 7 + ...r+trace_binary_metadata_nosec_test.vcxproj | 178 + ...binary_metadata_nosec_test.vcxproj.filters | 7 + ...ockpair+trace_binary_metadata_test.vcxproj | 181 + ...trace_binary_metadata_test.vcxproj.filters | 7 + .../h2_sockpair+trace_call_creds_test.vcxproj | 181 + ...pair+trace_call_creds_test.vcxproj.filters | 7 + ...ace_cancel_after_accept_nosec_test.vcxproj | 178 + ...el_after_accept_nosec_test.vcxproj.filters | 7 + ...air+trace_cancel_after_accept_test.vcxproj | 181 + ...e_cancel_after_accept_test.vcxproj.filters | 7 + ...ancel_after_client_done_nosec_test.vcxproj | 178 + ...ter_client_done_nosec_test.vcxproj.filters | 7 + ...race_cancel_after_client_done_test.vcxproj | 181 + ...cel_after_client_done_test.vcxproj.filters | 7 + ...ace_cancel_after_invoke_nosec_test.vcxproj | 178 + ...el_after_invoke_nosec_test.vcxproj.filters | 7 + ...air+trace_cancel_after_invoke_test.vcxproj | 181 + ...e_cancel_after_invoke_test.vcxproj.filters | 7 + ...ce_cancel_before_invoke_nosec_test.vcxproj | 178 + ...l_before_invoke_nosec_test.vcxproj.filters | 7 + ...ir+trace_cancel_before_invoke_test.vcxproj | 181 + ..._cancel_before_invoke_test.vcxproj.filters | 7 + ...race_cancel_in_a_vacuum_nosec_test.vcxproj | 178 + ...cel_in_a_vacuum_nosec_test.vcxproj.filters | 7 + ...pair+trace_cancel_in_a_vacuum_test.vcxproj | 181 + ...ce_cancel_in_a_vacuum_test.vcxproj.filters | 7 + ...e_census_simple_request_nosec_test.vcxproj | 178 + ..._simple_request_nosec_test.vcxproj.filters | 7 + ...r+trace_census_simple_request_test.vcxproj | 181 + ...census_simple_request_test.vcxproj.filters | 7 + ...race_compressed_payload_nosec_test.vcxproj | 178 + ...pressed_payload_nosec_test.vcxproj.filters | 7 + ...pair+trace_compressed_payload_test.vcxproj | 181 + ...ce_compressed_payload_test.vcxproj.filters | 7 + ...kpair+trace_empty_batch_nosec_test.vcxproj | 178 + ...ace_empty_batch_nosec_test.vcxproj.filters | 7 + ...h2_sockpair+trace_empty_batch_test.vcxproj | 181 + ...air+trace_empty_batch_test.vcxproj.filters | 7 + ...raceful_server_shutdown_nosec_test.vcxproj | 178 + ...server_shutdown_nosec_test.vcxproj.filters | 7 + ...race_graceful_server_shutdown_test.vcxproj | 181 + ...ceful_server_shutdown_test.vcxproj.filters | 7 + ...race_high_initial_seqno_nosec_test.vcxproj | 178 + ...h_initial_seqno_nosec_test.vcxproj.filters | 7 + ...pair+trace_high_initial_seqno_test.vcxproj | 181 + ...ce_high_initial_seqno_test.vcxproj.filters | 7 + ...ce_invoke_large_request_nosec_test.vcxproj | 178 + ...e_large_request_nosec_test.vcxproj.filters | 7 + ...ir+trace_invoke_large_request_test.vcxproj | 181 + ..._invoke_large_request_test.vcxproj.filters | 7 + ...ir+trace_large_metadata_nosec_test.vcxproj | 178 + ..._large_metadata_nosec_test.vcxproj.filters | 7 + ...sockpair+trace_large_metadata_test.vcxproj | 181 + ...+trace_large_metadata_test.vcxproj.filters | 7 + ..._max_concurrent_streams_nosec_test.vcxproj | 178 + ...current_streams_nosec_test.vcxproj.filters | 7 + ...+trace_max_concurrent_streams_test.vcxproj | 181 + ...ax_concurrent_streams_test.vcxproj.filters | 7 + ...race_max_message_length_nosec_test.vcxproj | 178 + ..._message_length_nosec_test.vcxproj.filters | 7 + ...pair+trace_max_message_length_test.vcxproj | 181 + ...ce_max_message_length_test.vcxproj.filters | 7 + ...sockpair+trace_metadata_nosec_test.vcxproj | 178 + ...+trace_metadata_nosec_test.vcxproj.filters | 7 + .../h2_sockpair+trace_metadata_test.vcxproj | 181 + ...ckpair+trace_metadata_test.vcxproj.filters | 7 + ...h2_sockpair+trace_no_op_nosec_test.vcxproj | 178 + ...air+trace_no_op_nosec_test.vcxproj.filters | 7 + .../h2_sockpair+trace_no_op_test.vcxproj | 181 + ..._sockpair+trace_no_op_test.vcxproj.filters | 7 + ..._sockpair+trace_payload_nosec_test.vcxproj | 178 + ...r+trace_payload_nosec_test.vcxproj.filters | 7 + .../h2_sockpair+trace_payload_test.vcxproj | 181 + ...ockpair+trace_payload_test.vcxproj.filters | 7 + ...ace_ping_pong_streaming_nosec_test.vcxproj | 178 + ..._pong_streaming_nosec_test.vcxproj.filters | 7 + ...air+trace_ping_pong_streaming_test.vcxproj | 181 + ...e_ping_pong_streaming_test.vcxproj.filters | 7 + ...r+trace_registered_call_nosec_test.vcxproj | 178 + ...registered_call_nosec_test.vcxproj.filters | 7 + ...ockpair+trace_registered_call_test.vcxproj | 181 + ...trace_registered_call_test.vcxproj.filters | 7 + ...race_request_with_flags_nosec_test.vcxproj | 178 + ...uest_with_flags_nosec_test.vcxproj.filters | 7 + ...pair+trace_request_with_flags_test.vcxproj | 181 + ...ce_request_with_flags_test.vcxproj.filters | 7 + ...ce_request_with_payload_nosec_test.vcxproj | 178 + ...st_with_payload_nosec_test.vcxproj.filters | 7 + ...ir+trace_request_with_payload_test.vcxproj | 181 + ..._request_with_payload_test.vcxproj.filters | 7 + ...server_finishes_request_nosec_test.vcxproj | 178 + ...inishes_request_nosec_test.vcxproj.filters | 7 + ...trace_server_finishes_request_test.vcxproj | 181 + ...rver_finishes_request_test.vcxproj.filters | 7 + ...shutdown_finishes_calls_nosec_test.vcxproj | 178 + ..._finishes_calls_nosec_test.vcxproj.filters | 7 + ...trace_shutdown_finishes_calls_test.vcxproj | 181 + ...utdown_finishes_calls_test.vcxproj.filters | 7 + ..._shutdown_finishes_tags_nosec_test.vcxproj | 178 + ...n_finishes_tags_nosec_test.vcxproj.filters | 7 + ...+trace_shutdown_finishes_tags_test.vcxproj | 181 + ...hutdown_finishes_tags_test.vcxproj.filters | 7 + ...ir+trace_simple_request_nosec_test.vcxproj | 178 + ..._simple_request_nosec_test.vcxproj.filters | 7 + ...sockpair+trace_simple_request_test.vcxproj | 181 + ...+trace_simple_request_test.vcxproj.filters | 7 + ...trace_trailing_metadata_nosec_test.vcxproj | 178 + ...ailing_metadata_nosec_test.vcxproj.filters | 7 + ...kpair+trace_trailing_metadata_test.vcxproj | 181 + ...ace_trailing_metadata_test.vcxproj.filters | 7 + ...pair_1byte_bad_hostname_nosec_test.vcxproj | 178 + ...te_bad_hostname_nosec_test.vcxproj.filters | 7 + ...2_sockpair_1byte_bad_hostname_test.vcxproj | 181 + ...ir_1byte_bad_hostname_test.vcxproj.filters | 7 + ...r_1byte_binary_metadata_nosec_test.vcxproj | 178 + ...binary_metadata_nosec_test.vcxproj.filters | 7 + ...ockpair_1byte_binary_metadata_test.vcxproj | 181 + ...1byte_binary_metadata_test.vcxproj.filters | 7 + .../h2_sockpair_1byte_call_creds_test.vcxproj | 181 + ...pair_1byte_call_creds_test.vcxproj.filters | 7 + ...yte_cancel_after_accept_nosec_test.vcxproj | 178 + ...el_after_accept_nosec_test.vcxproj.filters | 7 + ...air_1byte_cancel_after_accept_test.vcxproj | 181 + ...e_cancel_after_accept_test.vcxproj.filters | 7 + ...ancel_after_client_done_nosec_test.vcxproj | 178 + ...ter_client_done_nosec_test.vcxproj.filters | 7 + ...byte_cancel_after_client_done_test.vcxproj | 181 + ...cel_after_client_done_test.vcxproj.filters | 7 + ...yte_cancel_after_invoke_nosec_test.vcxproj | 178 + ...el_after_invoke_nosec_test.vcxproj.filters | 7 + ...air_1byte_cancel_after_invoke_test.vcxproj | 181 + ...e_cancel_after_invoke_test.vcxproj.filters | 7 + ...te_cancel_before_invoke_nosec_test.vcxproj | 178 + ...l_before_invoke_nosec_test.vcxproj.filters | 7 + ...ir_1byte_cancel_before_invoke_test.vcxproj | 181 + ..._cancel_before_invoke_test.vcxproj.filters | 7 + ...byte_cancel_in_a_vacuum_nosec_test.vcxproj | 178 + ...cel_in_a_vacuum_nosec_test.vcxproj.filters | 7 + ...pair_1byte_cancel_in_a_vacuum_test.vcxproj | 181 + ...te_cancel_in_a_vacuum_test.vcxproj.filters | 7 + ...e_census_simple_request_nosec_test.vcxproj | 178 + ..._simple_request_nosec_test.vcxproj.filters | 7 + ...r_1byte_census_simple_request_test.vcxproj | 181 + ...census_simple_request_test.vcxproj.filters | 7 + ...byte_compressed_payload_nosec_test.vcxproj | 178 + ...pressed_payload_nosec_test.vcxproj.filters | 7 + ...pair_1byte_compressed_payload_test.vcxproj | 181 + ...te_compressed_payload_test.vcxproj.filters | 7 + ...kpair_1byte_empty_batch_nosec_test.vcxproj | 178 + ...yte_empty_batch_nosec_test.vcxproj.filters | 7 + ...h2_sockpair_1byte_empty_batch_test.vcxproj | 181 + ...air_1byte_empty_batch_test.vcxproj.filters | 7 + ...raceful_server_shutdown_nosec_test.vcxproj | 178 + ...server_shutdown_nosec_test.vcxproj.filters | 7 + ...byte_graceful_server_shutdown_test.vcxproj | 181 + ...ceful_server_shutdown_test.vcxproj.filters | 7 + ...byte_high_initial_seqno_nosec_test.vcxproj | 178 + ...h_initial_seqno_nosec_test.vcxproj.filters | 7 + ...pair_1byte_high_initial_seqno_test.vcxproj | 181 + ...te_high_initial_seqno_test.vcxproj.filters | 7 + ...te_invoke_large_request_nosec_test.vcxproj | 178 + ...e_large_request_nosec_test.vcxproj.filters | 7 + ...ir_1byte_invoke_large_request_test.vcxproj | 181 + ..._invoke_large_request_test.vcxproj.filters | 7 + ...ir_1byte_large_metadata_nosec_test.vcxproj | 178 + ..._large_metadata_nosec_test.vcxproj.filters | 7 + ...sockpair_1byte_large_metadata_test.vcxproj | 181 + ..._1byte_large_metadata_test.vcxproj.filters | 7 + ..._max_concurrent_streams_nosec_test.vcxproj | 178 + ...current_streams_nosec_test.vcxproj.filters | 7 + ..._1byte_max_concurrent_streams_test.vcxproj | 181 + ...ax_concurrent_streams_test.vcxproj.filters | 7 + ...byte_max_message_length_nosec_test.vcxproj | 178 + ..._message_length_nosec_test.vcxproj.filters | 7 + ...pair_1byte_max_message_length_test.vcxproj | 181 + ...te_max_message_length_test.vcxproj.filters | 7 + ...sockpair_1byte_metadata_nosec_test.vcxproj | 178 + ..._1byte_metadata_nosec_test.vcxproj.filters | 7 + .../h2_sockpair_1byte_metadata_test.vcxproj | 181 + ...ckpair_1byte_metadata_test.vcxproj.filters | 7 + ...h2_sockpair_1byte_no_op_nosec_test.vcxproj | 178 + ...air_1byte_no_op_nosec_test.vcxproj.filters | 7 + .../h2_sockpair_1byte_no_op_test.vcxproj | 181 + ..._sockpair_1byte_no_op_test.vcxproj.filters | 7 + ..._sockpair_1byte_payload_nosec_test.vcxproj | 178 + ...r_1byte_payload_nosec_test.vcxproj.filters | 7 + .../h2_sockpair_1byte_payload_test.vcxproj | 181 + ...ockpair_1byte_payload_test.vcxproj.filters | 7 + ...yte_ping_pong_streaming_nosec_test.vcxproj | 178 + ..._pong_streaming_nosec_test.vcxproj.filters | 7 + ...air_1byte_ping_pong_streaming_test.vcxproj | 181 + ...e_ping_pong_streaming_test.vcxproj.filters | 7 + ...r_1byte_registered_call_nosec_test.vcxproj | 178 + ...registered_call_nosec_test.vcxproj.filters | 7 + ...ockpair_1byte_registered_call_test.vcxproj | 181 + ...1byte_registered_call_test.vcxproj.filters | 7 + ...byte_request_with_flags_nosec_test.vcxproj | 178 + ...uest_with_flags_nosec_test.vcxproj.filters | 7 + ...pair_1byte_request_with_flags_test.vcxproj | 181 + ...te_request_with_flags_test.vcxproj.filters | 7 + ...te_request_with_payload_nosec_test.vcxproj | 178 + ...st_with_payload_nosec_test.vcxproj.filters | 7 + ...ir_1byte_request_with_payload_test.vcxproj | 181 + ..._request_with_payload_test.vcxproj.filters | 7 + ...server_finishes_request_nosec_test.vcxproj | 178 + ...inishes_request_nosec_test.vcxproj.filters | 7 + ...1byte_server_finishes_request_test.vcxproj | 181 + ...rver_finishes_request_test.vcxproj.filters | 7 + ...shutdown_finishes_calls_nosec_test.vcxproj | 178 + ..._finishes_calls_nosec_test.vcxproj.filters | 7 + ...1byte_shutdown_finishes_calls_test.vcxproj | 181 + ...utdown_finishes_calls_test.vcxproj.filters | 7 + ..._shutdown_finishes_tags_nosec_test.vcxproj | 178 + ...n_finishes_tags_nosec_test.vcxproj.filters | 7 + ..._1byte_shutdown_finishes_tags_test.vcxproj | 181 + ...hutdown_finishes_tags_test.vcxproj.filters | 7 + ...ir_1byte_simple_request_nosec_test.vcxproj | 178 + ..._simple_request_nosec_test.vcxproj.filters | 7 + ...sockpair_1byte_simple_request_test.vcxproj | 181 + ..._1byte_simple_request_test.vcxproj.filters | 7 + ...1byte_trailing_metadata_nosec_test.vcxproj | 178 + ...ailing_metadata_nosec_test.vcxproj.filters | 7 + ...kpair_1byte_trailing_metadata_test.vcxproj | 181 + ...yte_trailing_metadata_test.vcxproj.filters | 7 + ...2_sockpair_bad_hostname_nosec_test.vcxproj | 178 + ...ir_bad_hostname_nosec_test.vcxproj.filters | 7 + .../h2_sockpair_bad_hostname_test.vcxproj | 181 + ...sockpair_bad_hostname_test.vcxproj.filters | 7 + ...ockpair_binary_metadata_nosec_test.vcxproj | 178 + ...binary_metadata_nosec_test.vcxproj.filters | 7 + .../h2_sockpair_binary_metadata_test.vcxproj | 181 + ...kpair_binary_metadata_test.vcxproj.filters | 7 + .../h2_sockpair_call_creds_test.vcxproj | 181 + ...2_sockpair_call_creds_test.vcxproj.filters | 7 + ...air_cancel_after_accept_nosec_test.vcxproj | 178 + ...el_after_accept_nosec_test.vcxproj.filters | 7 + ..._sockpair_cancel_after_accept_test.vcxproj | 181 + ...r_cancel_after_accept_test.vcxproj.filters | 7 + ...ancel_after_client_done_nosec_test.vcxproj | 178 + ...ter_client_done_nosec_test.vcxproj.filters | 7 + ...pair_cancel_after_client_done_test.vcxproj | 181 + ...cel_after_client_done_test.vcxproj.filters | 7 + ...air_cancel_after_invoke_nosec_test.vcxproj | 178 + ...el_after_invoke_nosec_test.vcxproj.filters | 7 + ..._sockpair_cancel_after_invoke_test.vcxproj | 181 + ...r_cancel_after_invoke_test.vcxproj.filters | 7 + ...ir_cancel_before_invoke_nosec_test.vcxproj | 178 + ...l_before_invoke_nosec_test.vcxproj.filters | 7 + ...sockpair_cancel_before_invoke_test.vcxproj | 181 + ..._cancel_before_invoke_test.vcxproj.filters | 7 + ...pair_cancel_in_a_vacuum_nosec_test.vcxproj | 178 + ...cel_in_a_vacuum_nosec_test.vcxproj.filters | 7 + ...2_sockpair_cancel_in_a_vacuum_test.vcxproj | 181 + ...ir_cancel_in_a_vacuum_test.vcxproj.filters | 7 + ...r_census_simple_request_nosec_test.vcxproj | 178 + ..._simple_request_nosec_test.vcxproj.filters | 7 + ...ockpair_census_simple_request_test.vcxproj | 181 + ...census_simple_request_test.vcxproj.filters | 7 + ...pair_compressed_payload_nosec_test.vcxproj | 178 + ...pressed_payload_nosec_test.vcxproj.filters | 7 + ...2_sockpair_compressed_payload_test.vcxproj | 181 + ...ir_compressed_payload_test.vcxproj.filters | 7 + ...h2_sockpair_empty_batch_nosec_test.vcxproj | 178 + ...air_empty_batch_nosec_test.vcxproj.filters | 7 + .../h2_sockpair_empty_batch_test.vcxproj | 181 + ..._sockpair_empty_batch_test.vcxproj.filters | 7 + ...raceful_server_shutdown_nosec_test.vcxproj | 178 + ...server_shutdown_nosec_test.vcxproj.filters | 7 + ...pair_graceful_server_shutdown_test.vcxproj | 181 + ...ceful_server_shutdown_test.vcxproj.filters | 7 + ...pair_high_initial_seqno_nosec_test.vcxproj | 178 + ...h_initial_seqno_nosec_test.vcxproj.filters | 7 + ...2_sockpair_high_initial_seqno_test.vcxproj | 181 + ...ir_high_initial_seqno_test.vcxproj.filters | 7 + ...ir_invoke_large_request_nosec_test.vcxproj | 178 + ...e_large_request_nosec_test.vcxproj.filters | 7 + ...sockpair_invoke_large_request_test.vcxproj | 181 + ..._invoke_large_request_test.vcxproj.filters | 7 + ...sockpair_large_metadata_nosec_test.vcxproj | 178 + ..._large_metadata_nosec_test.vcxproj.filters | 7 + .../h2_sockpair_large_metadata_test.vcxproj | 181 + ...ckpair_large_metadata_test.vcxproj.filters | 7 + ..._max_concurrent_streams_nosec_test.vcxproj | 178 + ...current_streams_nosec_test.vcxproj.filters | 7 + ...ckpair_max_concurrent_streams_test.vcxproj | 181 + ...ax_concurrent_streams_test.vcxproj.filters | 7 + ...pair_max_message_length_nosec_test.vcxproj | 178 + ..._message_length_nosec_test.vcxproj.filters | 7 + ...2_sockpair_max_message_length_test.vcxproj | 181 + ...ir_max_message_length_test.vcxproj.filters | 7 + .../h2_sockpair_metadata_nosec_test.vcxproj | 178 + ...ckpair_metadata_nosec_test.vcxproj.filters | 7 + .../h2_sockpair_metadata_test.vcxproj | 181 + .../h2_sockpair_metadata_test.vcxproj.filters | 7 + .../h2_sockpair_no_op_nosec_test.vcxproj | 178 + ..._sockpair_no_op_nosec_test.vcxproj.filters | 7 + .../h2_sockpair_no_op_test.vcxproj | 181 + .../h2_sockpair_no_op_test.vcxproj.filters | 7 + .../h2_sockpair_payload_nosec_test.vcxproj | 178 + ...ockpair_payload_nosec_test.vcxproj.filters | 7 + .../h2_sockpair_payload_test.vcxproj | 181 + .../h2_sockpair_payload_test.vcxproj.filters | 7 + ...air_ping_pong_streaming_nosec_test.vcxproj | 178 + ..._pong_streaming_nosec_test.vcxproj.filters | 7 + ..._sockpair_ping_pong_streaming_test.vcxproj | 181 + ...r_ping_pong_streaming_test.vcxproj.filters | 7 + ...ockpair_registered_call_nosec_test.vcxproj | 178 + ...registered_call_nosec_test.vcxproj.filters | 7 + .../h2_sockpair_registered_call_test.vcxproj | 181 + ...kpair_registered_call_test.vcxproj.filters | 7 + ...pair_request_with_flags_nosec_test.vcxproj | 178 + ...uest_with_flags_nosec_test.vcxproj.filters | 7 + ...2_sockpair_request_with_flags_test.vcxproj | 181 + ...ir_request_with_flags_test.vcxproj.filters | 7 + ...ir_request_with_payload_nosec_test.vcxproj | 178 + ...st_with_payload_nosec_test.vcxproj.filters | 7 + ...sockpair_request_with_payload_test.vcxproj | 181 + ..._request_with_payload_test.vcxproj.filters | 7 + ...server_finishes_request_nosec_test.vcxproj | 178 + ...inishes_request_nosec_test.vcxproj.filters | 7 + ...kpair_server_finishes_request_test.vcxproj | 181 + ...rver_finishes_request_test.vcxproj.filters | 7 + ...shutdown_finishes_calls_nosec_test.vcxproj | 178 + ..._finishes_calls_nosec_test.vcxproj.filters | 7 + ...kpair_shutdown_finishes_calls_test.vcxproj | 181 + ...utdown_finishes_calls_test.vcxproj.filters | 7 + ..._shutdown_finishes_tags_nosec_test.vcxproj | 178 + ...n_finishes_tags_nosec_test.vcxproj.filters | 7 + ...ckpair_shutdown_finishes_tags_test.vcxproj | 181 + ...hutdown_finishes_tags_test.vcxproj.filters | 7 + ...sockpair_simple_request_nosec_test.vcxproj | 178 + ..._simple_request_nosec_test.vcxproj.filters | 7 + .../h2_sockpair_simple_request_test.vcxproj | 181 + ...ckpair_simple_request_test.vcxproj.filters | 7 + ...kpair_trailing_metadata_nosec_test.vcxproj | 178 + ...ailing_metadata_nosec_test.vcxproj.filters | 7 + ...h2_sockpair_trailing_metadata_test.vcxproj | 181 + ...air_trailing_metadata_test.vcxproj.filters | 7 + .../h2_ssl_bad_hostname_test.vcxproj | 181 + .../h2_ssl_bad_hostname_test.vcxproj.filters | 7 + .../h2_ssl_binary_metadata_test.vcxproj | 181 + ...2_ssl_binary_metadata_test.vcxproj.filters | 7 + .../h2_ssl_call_creds_test.vcxproj | 181 + .../h2_ssl_call_creds_test.vcxproj.filters | 7 + .../h2_ssl_cancel_after_accept_test.vcxproj | 181 + ...l_cancel_after_accept_test.vcxproj.filters | 7 + ..._ssl_cancel_after_client_done_test.vcxproj | 181 + ...cel_after_client_done_test.vcxproj.filters | 7 + .../h2_ssl_cancel_after_invoke_test.vcxproj | 181 + ...l_cancel_after_invoke_test.vcxproj.filters | 7 + .../h2_ssl_cancel_before_invoke_test.vcxproj | 181 + ..._cancel_before_invoke_test.vcxproj.filters | 7 + .../h2_ssl_cancel_in_a_vacuum_test.vcxproj | 181 + ...sl_cancel_in_a_vacuum_test.vcxproj.filters | 7 + .../h2_ssl_census_simple_request_test.vcxproj | 181 + ...census_simple_request_test.vcxproj.filters | 7 + .../h2_ssl_channel_connectivity_test.vcxproj | 181 + ..._channel_connectivity_test.vcxproj.filters | 7 + .../h2_ssl_compressed_payload_test.vcxproj | 181 + ...sl_compressed_payload_test.vcxproj.filters | 7 + .../h2_ssl_default_host_test.vcxproj | 181 + .../h2_ssl_default_host_test.vcxproj.filters | 7 + .../h2_ssl_disappearing_server_test.vcxproj | 181 + ...l_disappearing_server_test.vcxproj.filters | 7 + .../h2_ssl_empty_batch_test.vcxproj | 181 + .../h2_ssl_empty_batch_test.vcxproj.filters | 7 + ..._ssl_graceful_server_shutdown_test.vcxproj | 181 + ...ceful_server_shutdown_test.vcxproj.filters | 7 + .../h2_ssl_high_initial_seqno_test.vcxproj | 181 + ...sl_high_initial_seqno_test.vcxproj.filters | 7 + .../h2_ssl_invoke_large_request_test.vcxproj | 181 + ..._invoke_large_request_test.vcxproj.filters | 7 + .../h2_ssl_large_metadata_test.vcxproj | 181 + ...h2_ssl_large_metadata_test.vcxproj.filters | 7 + ...h2_ssl_max_concurrent_streams_test.vcxproj | 181 + ...ax_concurrent_streams_test.vcxproj.filters | 7 + .../h2_ssl_max_message_length_test.vcxproj | 181 + ...sl_max_message_length_test.vcxproj.filters | 7 + .../h2_ssl_metadata_test.vcxproj | 181 + .../h2_ssl_metadata_test.vcxproj.filters | 7 + .../h2_ssl_no_op_test.vcxproj | 181 + .../h2_ssl_no_op_test.vcxproj.filters | 7 + .../h2_ssl_payload_test.vcxproj | 181 + .../h2_ssl_payload_test.vcxproj.filters | 7 + .../h2_ssl_ping_pong_streaming_test.vcxproj | 181 + ...l_ping_pong_streaming_test.vcxproj.filters | 7 + .../h2_ssl_proxy_bad_hostname_test.vcxproj | 181 + ...sl_proxy_bad_hostname_test.vcxproj.filters | 7 + .../h2_ssl_proxy_binary_metadata_test.vcxproj | 181 + ...proxy_binary_metadata_test.vcxproj.filters | 7 + .../h2_ssl_proxy_call_creds_test.vcxproj | 181 + ..._ssl_proxy_call_creds_test.vcxproj.filters | 7 + ...ssl_proxy_cancel_after_accept_test.vcxproj | 181 + ...y_cancel_after_accept_test.vcxproj.filters | 7 + ...roxy_cancel_after_client_done_test.vcxproj | 181 + ...cel_after_client_done_test.vcxproj.filters | 7 + ...ssl_proxy_cancel_after_invoke_test.vcxproj | 181 + ...y_cancel_after_invoke_test.vcxproj.filters | 7 + ...sl_proxy_cancel_before_invoke_test.vcxproj | 181 + ..._cancel_before_invoke_test.vcxproj.filters | 7 + ..._ssl_proxy_cancel_in_a_vacuum_test.vcxproj | 181 + ...xy_cancel_in_a_vacuum_test.vcxproj.filters | 7 + ...l_proxy_census_simple_request_test.vcxproj | 181 + ...census_simple_request_test.vcxproj.filters | 7 + .../h2_ssl_proxy_default_host_test.vcxproj | 181 + ...sl_proxy_default_host_test.vcxproj.filters | 7 + ...ssl_proxy_disappearing_server_test.vcxproj | 181 + ...y_disappearing_server_test.vcxproj.filters | 7 + .../h2_ssl_proxy_empty_batch_test.vcxproj | 181 + ...ssl_proxy_empty_batch_test.vcxproj.filters | 7 + ...roxy_graceful_server_shutdown_test.vcxproj | 181 + ...ceful_server_shutdown_test.vcxproj.filters | 7 + ..._ssl_proxy_high_initial_seqno_test.vcxproj | 181 + ...xy_high_initial_seqno_test.vcxproj.filters | 7 + ...sl_proxy_invoke_large_request_test.vcxproj | 181 + ..._invoke_large_request_test.vcxproj.filters | 7 + .../h2_ssl_proxy_large_metadata_test.vcxproj | 181 + ..._proxy_large_metadata_test.vcxproj.filters | 7 + ..._ssl_proxy_max_message_length_test.vcxproj | 181 + ...xy_max_message_length_test.vcxproj.filters | 7 + .../h2_ssl_proxy_metadata_test.vcxproj | 181 + ...h2_ssl_proxy_metadata_test.vcxproj.filters | 7 + .../h2_ssl_proxy_no_op_test.vcxproj | 181 + .../h2_ssl_proxy_no_op_test.vcxproj.filters | 7 + .../h2_ssl_proxy_payload_test.vcxproj | 181 + .../h2_ssl_proxy_payload_test.vcxproj.filters | 7 + ...ssl_proxy_ping_pong_streaming_test.vcxproj | 181 + ...y_ping_pong_streaming_test.vcxproj.filters | 7 + .../h2_ssl_proxy_registered_call_test.vcxproj | 181 + ...proxy_registered_call_test.vcxproj.filters | 7 + ...sl_proxy_request_with_payload_test.vcxproj | 181 + ..._request_with_payload_test.vcxproj.filters | 7 + ...proxy_server_finishes_request_test.vcxproj | 181 + ...rver_finishes_request_test.vcxproj.filters | 7 + ...proxy_shutdown_finishes_calls_test.vcxproj | 181 + ...utdown_finishes_calls_test.vcxproj.filters | 7 + ..._proxy_shutdown_finishes_tags_test.vcxproj | 181 + ...hutdown_finishes_tags_test.vcxproj.filters | 7 + ..._proxy_simple_delayed_request_test.vcxproj | 181 + ...imple_delayed_request_test.vcxproj.filters | 7 + .../h2_ssl_proxy_simple_request_test.vcxproj | 181 + ..._proxy_simple_request_test.vcxproj.filters | 7 + ...2_ssl_proxy_trailing_metadata_test.vcxproj | 181 + ...oxy_trailing_metadata_test.vcxproj.filters | 7 + .../h2_ssl_registered_call_test.vcxproj | 181 + ...2_ssl_registered_call_test.vcxproj.filters | 7 + .../h2_ssl_request_with_flags_test.vcxproj | 181 + ...sl_request_with_flags_test.vcxproj.filters | 7 + .../h2_ssl_request_with_payload_test.vcxproj | 181 + ..._request_with_payload_test.vcxproj.filters | 7 + ...2_ssl_server_finishes_request_test.vcxproj | 181 + ...rver_finishes_request_test.vcxproj.filters | 7 + ...2_ssl_shutdown_finishes_calls_test.vcxproj | 181 + ...utdown_finishes_calls_test.vcxproj.filters | 7 + ...h2_ssl_shutdown_finishes_tags_test.vcxproj | 181 + ...hutdown_finishes_tags_test.vcxproj.filters | 7 + ...h2_ssl_simple_delayed_request_test.vcxproj | 181 + ...imple_delayed_request_test.vcxproj.filters | 7 + .../h2_ssl_simple_request_test.vcxproj | 181 + ...h2_ssl_simple_request_test.vcxproj.filters | 7 + .../h2_ssl_trailing_metadata_test.vcxproj | 181 + ...ssl_trailing_metadata_test.vcxproj.filters | 7 + .../hpack_parser_test.vcxproj | 172 + .../hpack_parser_test.vcxproj.filters | 24 + .../hpack_table_test/hpack_table_test.vcxproj | 172 + .../hpack_table_test.vcxproj.filters | 24 + .../httpcli_format_request_test.vcxproj | 172 + ...ttpcli_format_request_test.vcxproj.filters | 21 + .../httpcli_parser_test.vcxproj | 172 + .../httpcli_parser_test.vcxproj.filters | 21 + ...ial_settings_frame_bad_client_test.vcxproj | 175 + ...ings_frame_bad_client_test.vcxproj.filters | 24 + .../test/json_rewrite/json_rewrite.vcxproj | 166 + .../json_rewrite/json_rewrite.vcxproj.filters | 21 + .../json_rewrite_test.vcxproj | 172 + .../json_rewrite_test.vcxproj.filters | 21 + .../vcxproj/test/json_test/json_test.vcxproj | 172 + .../test/json_test/json_test.vcxproj.filters | 21 + .../lame_client_test/lame_client_test.vcxproj | 172 + .../lame_client_test.vcxproj.filters | 21 + .../message_compress_test.vcxproj | 172 + .../message_compress_test.vcxproj.filters | 21 + .../vcxproj/test/mock_test/mock_test.vcxproj | 180 + .../test/mock_test/mock_test.vcxproj.filters | 21 + .../multi_init_test/multi_init_test.vcxproj | 172 + .../multi_init_test.vcxproj.filters | 21 + .../multiple_server_queues_test.vcxproj | 172 + ...ultiple_server_queues_test.vcxproj.filters | 21 + .../murmur_hash_test/murmur_hash_test.vcxproj | 166 + .../murmur_hash_test.vcxproj.filters | 21 + .../no_server_test/no_server_test.vcxproj | 172 + .../no_server_test.vcxproj.filters | 21 + .../reconnect_interop_client.vcxproj | 207 + .../reconnect_interop_client.vcxproj.filters | 33 + .../reconnect_interop_server.vcxproj | 210 + .../reconnect_interop_server.vcxproj.filters | 33 + .../resolve_address_test.vcxproj | 172 + .../resolve_address_test.vcxproj.filters | 21 + .../secure_auth_context_test.vcxproj | 180 + .../secure_auth_context_test.vcxproj.filters | 21 + .../secure_endpoint_test.vcxproj | 172 + .../secure_endpoint_test.vcxproj.filters | 21 + .../server_crash_test_client.vcxproj | 180 + .../server_crash_test_client.vcxproj.filters | 21 + .../test/shutdown_test/shutdown_test.vcxproj | 180 + .../shutdown_test.vcxproj.filters | 21 + .../sockaddr_utils_test.vcxproj | 172 + .../sockaddr_utils_test.vcxproj.filters | 21 + .../test/status_test/status_test.vcxproj | 177 + .../status_test/status_test.vcxproj.filters | 21 + .../thread_stress_test.vcxproj | 180 + .../thread_stress_test.vcxproj.filters | 21 + .../time_averaged_stats_test.vcxproj | 172 + .../time_averaged_stats_test.vcxproj.filters | 21 + .../timeout_encoding_test.vcxproj | 172 + .../timeout_encoding_test.vcxproj.filters | 24 + .../test/timers_test/timers_test.vcxproj | 172 + .../timers_test/timers_test.vcxproj.filters | 21 + .../transport_metadata_test.vcxproj | 172 + .../transport_metadata_test.vcxproj.filters | 21 + .../uri_parser_test/uri_parser_test.vcxproj | 172 + .../uri_parser_test.vcxproj.filters | 21 + vsprojects/winsock.props | 12 + vsprojects/zlib-dll.props | 13 + vsprojects/zlib.props | 13 + 3187 files changed, 466001 insertions(+) create mode 100644 .clang-format create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 .travis.yml create mode 100644 BUILD create mode 100644 CONTRIBUTING.md create mode 100644 INSTALL create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 PATENTS create mode 100644 README.md create mode 100644 build.yaml create mode 100644 composer.json create mode 100644 doc/PROTOCOL-HTTP2.md create mode 100644 doc/connection-backoff-interop-test-description.md create mode 100644 doc/connection-backoff.md create mode 100644 doc/connectivity-semantics-and-api.md create mode 100644 doc/grpc-auth-support.md create mode 100644 doc/health-checking.md create mode 100644 doc/interop-test-descriptions.md create mode 100644 doc/naming.md create mode 100644 doc/server-reflection.md create mode 100644 etc/roots.pem create mode 100644 examples/README.md create mode 100644 examples/cpp/README.md create mode 100644 examples/cpp/cpptutorial.md create mode 100644 examples/cpp/helloworld/Makefile create mode 100644 examples/cpp/helloworld/README.md create mode 100644 examples/cpp/helloworld/greeter_async_client.cc create mode 100644 examples/cpp/helloworld/greeter_async_server.cc create mode 100644 examples/cpp/helloworld/greeter_client.cc create mode 100644 examples/cpp/helloworld/greeter_server.cc create mode 100644 examples/cpp/route_guide/Makefile create mode 100644 examples/cpp/route_guide/helper.cc create mode 100644 examples/cpp/route_guide/helper.h create mode 100644 examples/cpp/route_guide/route_guide_client.cc create mode 100644 examples/cpp/route_guide/route_guide_db.json create mode 100644 examples/cpp/route_guide/route_guide_server.cc create mode 100644 examples/csharp/.gitignore create mode 100644 examples/csharp/.nuget/packages.config create mode 100644 examples/csharp/helloworld/.nuget/packages.config create mode 100644 examples/csharp/helloworld/Greeter.sln create mode 100644 examples/csharp/helloworld/Greeter/.gitignore create mode 100644 examples/csharp/helloworld/Greeter/Greeter.csproj create mode 100644 examples/csharp/helloworld/Greeter/Helloworld.cs create mode 100644 examples/csharp/helloworld/Greeter/HelloworldGrpc.cs create mode 100644 examples/csharp/helloworld/Greeter/Properties/AssemblyInfo.cs create mode 100644 examples/csharp/helloworld/Greeter/packages.config create mode 100644 examples/csharp/helloworld/GreeterClient/.gitignore create mode 100644 examples/csharp/helloworld/GreeterClient/GreeterClient.csproj create mode 100644 examples/csharp/helloworld/GreeterClient/Program.cs create mode 100644 examples/csharp/helloworld/GreeterClient/Properties/AssemblyInfo.cs create mode 100644 examples/csharp/helloworld/GreeterClient/packages.config create mode 100644 examples/csharp/helloworld/GreeterServer/.gitignore create mode 100644 examples/csharp/helloworld/GreeterServer/GreeterServer.csproj create mode 100644 examples/csharp/helloworld/GreeterServer/Program.cs create mode 100644 examples/csharp/helloworld/GreeterServer/Properties/AssemblyInfo.cs create mode 100644 examples/csharp/helloworld/GreeterServer/packages.config create mode 100644 examples/csharp/helloworld/README.md create mode 100644 examples/csharp/helloworld/generate_protos.bat create mode 100644 examples/csharp/route_guide/.gitignore create mode 100644 examples/csharp/route_guide/.nuget/packages.config create mode 100644 examples/csharp/route_guide/README.md create mode 100644 examples/csharp/route_guide/RouteGuide.sln create mode 100644 examples/csharp/route_guide/RouteGuide/Properties/AssemblyInfo.cs create mode 100644 examples/csharp/route_guide/RouteGuide/RouteGuide.cs create mode 100644 examples/csharp/route_guide/RouteGuide/RouteGuide.csproj create mode 100644 examples/csharp/route_guide/RouteGuide/RouteGuideGrpc.cs create mode 100644 examples/csharp/route_guide/RouteGuide/RouteGuideUtil.cs create mode 100644 examples/csharp/route_guide/RouteGuide/packages.config create mode 100644 examples/csharp/route_guide/RouteGuide/route_guide_db.json create mode 100644 examples/csharp/route_guide/RouteGuideClient/App.config create mode 100644 examples/csharp/route_guide/RouteGuideClient/Program.cs create mode 100644 examples/csharp/route_guide/RouteGuideClient/Properties/AssemblyInfo.cs create mode 100644 examples/csharp/route_guide/RouteGuideClient/RouteGuideClient.csproj create mode 100644 examples/csharp/route_guide/RouteGuideClient/packages.config create mode 100644 examples/csharp/route_guide/RouteGuideServer/App.config create mode 100644 examples/csharp/route_guide/RouteGuideServer/Program.cs create mode 100644 examples/csharp/route_guide/RouteGuideServer/Properties/AssemblyInfo.cs create mode 100644 examples/csharp/route_guide/RouteGuideServer/RouteGuideImpl.cs create mode 100644 examples/csharp/route_guide/RouteGuideServer/RouteGuideServer.csproj create mode 100644 examples/csharp/route_guide/RouteGuideServer/packages.config create mode 100644 examples/csharp/route_guide/generate_protos.bat create mode 100644 examples/node/.gitignore create mode 100644 examples/node/README.md create mode 100644 examples/node/greeter_client.js create mode 100644 examples/node/greeter_server.js create mode 100644 examples/node/helloworld.proto create mode 100644 examples/node/package.json create mode 100644 examples/node/route_guide/README.md create mode 100644 examples/node/route_guide/route_guide.proto create mode 100644 examples/node/route_guide/route_guide_client.js create mode 100644 examples/node/route_guide/route_guide_db.json create mode 100644 examples/node/route_guide/route_guide_server.js create mode 100644 examples/objective-c/auth_sample/AuthSample.xcodeproj/project.pbxproj create mode 100644 examples/objective-c/auth_sample/AuthSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 examples/objective-c/auth_sample/AuthTestService.podspec create mode 100644 examples/objective-c/auth_sample/MakeRPCViewController.h create mode 100644 examples/objective-c/auth_sample/MakeRPCViewController.m create mode 100644 examples/objective-c/auth_sample/Misc/AppDelegate.h create mode 100644 examples/objective-c/auth_sample/Misc/AppDelegate.m create mode 100644 examples/objective-c/auth_sample/Misc/Base.lproj/Main.storyboard create mode 100644 examples/objective-c/auth_sample/Misc/GoogleService-Info.plist create mode 100644 examples/objective-c/auth_sample/Misc/Images.xcassets/AppIcon.appiconset/Contents.json create mode 100644 examples/objective-c/auth_sample/Misc/Images.xcassets/first.imageset/Contents.json create mode 100644 examples/objective-c/auth_sample/Misc/Images.xcassets/first.imageset/first.pdf create mode 100644 examples/objective-c/auth_sample/Misc/Images.xcassets/second.imageset/Contents.json create mode 100644 examples/objective-c/auth_sample/Misc/Images.xcassets/second.imageset/second.pdf create mode 100644 examples/objective-c/auth_sample/Misc/Info.plist create mode 100644 examples/objective-c/auth_sample/Misc/main.m create mode 100644 examples/objective-c/auth_sample/Podfile create mode 100644 examples/objective-c/auth_sample/README.md create mode 100644 examples/objective-c/auth_sample/SelectUserViewController.h create mode 100644 examples/objective-c/auth_sample/SelectUserViewController.m create mode 100644 examples/objective-c/helloworld/HelloWorld.podspec create mode 100644 examples/objective-c/helloworld/HelloWorld.xcodeproj/project.pbxproj create mode 100644 examples/objective-c/helloworld/HelloWorld.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 examples/objective-c/helloworld/HelloWorld/AppDelegate.h create mode 100644 examples/objective-c/helloworld/HelloWorld/AppDelegate.m create mode 100644 examples/objective-c/helloworld/HelloWorld/Base.lproj/Main.storyboard create mode 100644 examples/objective-c/helloworld/HelloWorld/Images.xcassets/AppIcon.appiconset/Contents.json create mode 100644 examples/objective-c/helloworld/HelloWorld/Info.plist create mode 100644 examples/objective-c/helloworld/HelloWorld/ViewController.m create mode 100644 examples/objective-c/helloworld/Podfile create mode 100644 examples/objective-c/helloworld/README.md create mode 100644 examples/objective-c/helloworld/main.m create mode 100644 examples/objective-c/route_guide/Misc/AppDelegate.h create mode 100644 examples/objective-c/route_guide/Misc/AppDelegate.m create mode 100644 examples/objective-c/route_guide/Misc/Base.lproj/Main.storyboard create mode 100644 examples/objective-c/route_guide/Misc/Images.xcassets/AppIcon.appiconset/Contents.json create mode 100644 examples/objective-c/route_guide/Misc/Images.xcassets/first.imageset/Contents.json create mode 100644 examples/objective-c/route_guide/Misc/Images.xcassets/first.imageset/first.pdf create mode 100644 examples/objective-c/route_guide/Misc/Images.xcassets/second.imageset/Contents.json create mode 100644 examples/objective-c/route_guide/Misc/Images.xcassets/second.imageset/second.pdf create mode 100644 examples/objective-c/route_guide/Misc/Info.plist create mode 100644 examples/objective-c/route_guide/Misc/main.m create mode 100644 examples/objective-c/route_guide/Podfile create mode 100644 examples/objective-c/route_guide/README.md create mode 100644 examples/objective-c/route_guide/RouteGuide.podspec create mode 100644 examples/objective-c/route_guide/RouteGuideClient.xcodeproj/project.pbxproj create mode 100644 examples/objective-c/route_guide/RouteGuideClient.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 examples/objective-c/route_guide/ViewControllers.m create mode 100644 examples/objective-c/route_guide/route_guide_db.json create mode 100644 examples/php/.gitignore create mode 100644 examples/php/README.md create mode 100644 examples/php/composer.json create mode 100644 examples/php/greeter_client.php create mode 100644 examples/php/helloworld.php create mode 100644 examples/php/helloworld.proto create mode 100644 examples/php/route_guide/README.md create mode 100644 examples/php/route_guide/route_guide.php create mode 100644 examples/php/route_guide/route_guide.proto create mode 100644 examples/php/route_guide/route_guide_client.php create mode 100755 examples/php/route_guide/run_route_guide_client.sh create mode 100755 examples/php/run_greeter_client.sh create mode 100644 examples/protos/README.md create mode 100644 examples/protos/auth_sample.proto create mode 100644 examples/protos/hellostreamingworld.proto create mode 100644 examples/protos/helloworld.proto create mode 100644 examples/protos/route_guide.proto create mode 100644 examples/python/helloworld/.gitignore create mode 100644 examples/python/helloworld/README.md create mode 100644 examples/python/helloworld/greeter_client.py create mode 100644 examples/python/helloworld/greeter_server.py create mode 100755 examples/python/helloworld/run_client.sh create mode 100755 examples/python/helloworld/run_codegen.sh create mode 100755 examples/python/helloworld/run_server.sh create mode 100644 examples/python/route_guide/.gitignore create mode 100644 examples/python/route_guide/README.md create mode 100644 examples/python/route_guide/route_guide_client.py create mode 100644 examples/python/route_guide/route_guide_db.json create mode 100644 examples/python/route_guide/route_guide_pb2.py create mode 100755 examples/python/route_guide/route_guide_resources.py create mode 100644 examples/python/route_guide/route_guide_server.py create mode 100755 examples/python/route_guide/run_client.sh create mode 100755 examples/python/route_guide/run_codegen.sh create mode 100755 examples/python/route_guide/run_server.sh create mode 100644 examples/ruby/.gitignore create mode 100644 examples/ruby/Gemfile create mode 100644 examples/ruby/README.md create mode 100755 examples/ruby/greeter_client.rb create mode 100755 examples/ruby/greeter_server.rb create mode 100644 examples/ruby/grpc-demo.gemspec create mode 100644 examples/ruby/lib/helloworld.rb create mode 100644 examples/ruby/lib/helloworld_services.rb create mode 100644 examples/ruby/lib/route_guide.rb create mode 100644 examples/ruby/lib/route_guide_services.rb create mode 100644 examples/ruby/route_guide/README.md create mode 100755 examples/ruby/route_guide/route_guide_client.rb create mode 100755 examples/ruby/route_guide/route_guide_server.rb create mode 100644 gRPC.podspec create mode 100644 grpc.bzl create mode 100644 include/grpc++/channel.h create mode 100644 include/grpc++/client_context.h create mode 100644 include/grpc++/completion_queue.h create mode 100644 include/grpc++/create_channel.h create mode 100644 include/grpc++/generic/async_generic_service.h create mode 100644 include/grpc++/generic/generic_stub.h create mode 100644 include/grpc++/grpc++.h create mode 100644 include/grpc++/impl/README.md create mode 100644 include/grpc++/impl/call.h create mode 100644 include/grpc++/impl/client_unary_call.h create mode 100644 include/grpc++/impl/grpc_library.h create mode 100644 include/grpc++/impl/proto_utils.h create mode 100644 include/grpc++/impl/rpc_method.h create mode 100644 include/grpc++/impl/rpc_service_method.h create mode 100644 include/grpc++/impl/serialization_traits.h create mode 100644 include/grpc++/impl/service_type.h create mode 100644 include/grpc++/impl/sync.h create mode 100644 include/grpc++/impl/sync_cxx11.h create mode 100644 include/grpc++/impl/sync_no_cxx11.h create mode 100644 include/grpc++/impl/thd.h create mode 100644 include/grpc++/impl/thd_cxx11.h create mode 100644 include/grpc++/impl/thd_no_cxx11.h create mode 100644 include/grpc++/security/auth_context.h create mode 100644 include/grpc++/security/auth_metadata_processor.h create mode 100644 include/grpc++/security/credentials.h create mode 100644 include/grpc++/security/server_credentials.h create mode 100644 include/grpc++/server.h create mode 100644 include/grpc++/server_builder.h create mode 100644 include/grpc++/server_context.h create mode 100644 include/grpc++/support/async_stream.h create mode 100644 include/grpc++/support/async_unary_call.h create mode 100644 include/grpc++/support/byte_buffer.h create mode 100644 include/grpc++/support/channel_arguments.h create mode 100644 include/grpc++/support/config.h create mode 100644 include/grpc++/support/config_protobuf.h create mode 100644 include/grpc++/support/slice.h create mode 100644 include/grpc++/support/status.h create mode 100644 include/grpc++/support/status_code_enum.h create mode 100644 include/grpc++/support/string_ref.h create mode 100644 include/grpc++/support/stub_options.h create mode 100644 include/grpc++/support/sync_stream.h create mode 100644 include/grpc++/support/time.h create mode 100644 include/grpc/byte_buffer.h create mode 100644 include/grpc/byte_buffer_reader.h create mode 100644 include/grpc/census.h create mode 100644 include/grpc/compression.h create mode 100644 include/grpc/grpc.h create mode 100644 include/grpc/grpc_security.h create mode 100644 include/grpc/grpc_zookeeper.h create mode 100644 include/grpc/status.h create mode 100644 include/grpc/support/alloc.h create mode 100644 include/grpc/support/atm.h create mode 100644 include/grpc/support/atm_gcc_atomic.h create mode 100644 include/grpc/support/atm_gcc_sync.h create mode 100644 include/grpc/support/atm_win32.h create mode 100644 include/grpc/support/cmdline.h create mode 100644 include/grpc/support/cpu.h create mode 100644 include/grpc/support/histogram.h create mode 100644 include/grpc/support/host_port.h create mode 100644 include/grpc/support/log.h create mode 100644 include/grpc/support/log_win32.h create mode 100644 include/grpc/support/port_platform.h create mode 100644 include/grpc/support/slice.h create mode 100644 include/grpc/support/slice_buffer.h create mode 100644 include/grpc/support/string_util.h create mode 100644 include/grpc/support/subprocess.h create mode 100644 include/grpc/support/sync.h create mode 100644 include/grpc/support/sync_generic.h create mode 100644 include/grpc/support/sync_posix.h create mode 100644 include/grpc/support/sync_win32.h create mode 100644 include/grpc/support/thd.h create mode 100644 include/grpc/support/time.h create mode 100644 include/grpc/support/tls.h create mode 100644 include/grpc/support/tls_gcc.h create mode 100644 include/grpc/support/tls_msvc.h create mode 100644 include/grpc/support/tls_pthread.h create mode 100644 include/grpc/support/useful.h create mode 100644 src/compiler/config.h create mode 100644 src/compiler/cpp_generator.cc create mode 100644 src/compiler/cpp_generator.h create mode 100644 src/compiler/cpp_generator_helpers.h create mode 100644 src/compiler/cpp_plugin.cc create mode 100644 src/compiler/csharp_generator.cc create mode 100644 src/compiler/csharp_generator.h create mode 100644 src/compiler/csharp_generator_helpers.h create mode 100644 src/compiler/csharp_plugin.cc create mode 100644 src/compiler/generator_helpers.h create mode 100644 src/compiler/objective_c_generator.cc create mode 100644 src/compiler/objective_c_generator.h create mode 100644 src/compiler/objective_c_generator_helpers.h create mode 100644 src/compiler/objective_c_plugin.cc create mode 100644 src/compiler/python_generator.cc create mode 100644 src/compiler/python_generator.h create mode 100644 src/compiler/python_plugin.cc create mode 100644 src/compiler/ruby_generator.cc create mode 100644 src/compiler/ruby_generator.h create mode 100644 src/compiler/ruby_generator_helpers-inl.h create mode 100644 src/compiler/ruby_generator_map-inl.h create mode 100644 src/compiler/ruby_generator_string-inl.h create mode 100644 src/compiler/ruby_plugin.cc create mode 100644 src/core/README.md create mode 100644 src/core/census/README.md create mode 100644 src/core/census/aggregation.h create mode 100644 src/core/census/context.c create mode 100644 src/core/census/context.h create mode 100644 src/core/census/grpc_context.c create mode 100644 src/core/census/grpc_filter.c create mode 100644 src/core/census/grpc_filter.h create mode 100644 src/core/census/initialize.c create mode 100644 src/core/census/operation.c create mode 100644 src/core/census/rpc_metric_id.h create mode 100644 src/core/census/tracing.c create mode 100644 src/core/channel/channel_args.c create mode 100644 src/core/channel/channel_args.h create mode 100644 src/core/channel/channel_stack.c create mode 100644 src/core/channel/channel_stack.h create mode 100644 src/core/channel/client_channel.c create mode 100644 src/core/channel/client_channel.h create mode 100644 src/core/channel/compress_filter.c create mode 100644 src/core/channel/compress_filter.h create mode 100644 src/core/channel/connected_channel.c create mode 100644 src/core/channel/connected_channel.h create mode 100644 src/core/channel/context.h create mode 100644 src/core/channel/http_client_filter.c create mode 100644 src/core/channel/http_client_filter.h create mode 100644 src/core/channel/http_server_filter.c create mode 100644 src/core/channel/http_server_filter.h create mode 100644 src/core/channel/noop_filter.c create mode 100644 src/core/channel/noop_filter.h create mode 100644 src/core/client_config/README.md create mode 100644 src/core/client_config/client_config.c create mode 100644 src/core/client_config/client_config.h create mode 100644 src/core/client_config/connector.c create mode 100644 src/core/client_config/connector.h create mode 100644 src/core/client_config/lb_policies/pick_first.c create mode 100644 src/core/client_config/lb_policies/pick_first.h create mode 100644 src/core/client_config/lb_policy.c create mode 100644 src/core/client_config/lb_policy.h create mode 100644 src/core/client_config/resolver.c create mode 100644 src/core/client_config/resolver.h create mode 100644 src/core/client_config/resolver_factory.c create mode 100644 src/core/client_config/resolver_factory.h create mode 100644 src/core/client_config/resolver_registry.c create mode 100644 src/core/client_config/resolver_registry.h create mode 100644 src/core/client_config/resolvers/dns_resolver.c create mode 100644 src/core/client_config/resolvers/dns_resolver.h create mode 100644 src/core/client_config/resolvers/sockaddr_resolver.c create mode 100644 src/core/client_config/resolvers/sockaddr_resolver.h create mode 100644 src/core/client_config/resolvers/zookeeper_resolver.c create mode 100644 src/core/client_config/resolvers/zookeeper_resolver.h create mode 100644 src/core/client_config/subchannel.c create mode 100644 src/core/client_config/subchannel.h create mode 100644 src/core/client_config/subchannel_factory.c create mode 100644 src/core/client_config/subchannel_factory.h create mode 100644 src/core/client_config/subchannel_factory_decorators/add_channel_arg.c create mode 100644 src/core/client_config/subchannel_factory_decorators/add_channel_arg.h create mode 100644 src/core/client_config/subchannel_factory_decorators/merge_channel_args.c create mode 100644 src/core/client_config/subchannel_factory_decorators/merge_channel_args.h create mode 100644 src/core/client_config/uri_parser.c create mode 100644 src/core/client_config/uri_parser.h create mode 100644 src/core/compression/algorithm.c create mode 100644 src/core/compression/message_compress.c create mode 100644 src/core/compression/message_compress.h create mode 100644 src/core/debug/trace.c create mode 100644 src/core/debug/trace.h create mode 100644 src/core/httpcli/format_request.c create mode 100644 src/core/httpcli/format_request.h create mode 100644 src/core/httpcli/httpcli.c create mode 100644 src/core/httpcli/httpcli.h create mode 100644 src/core/httpcli/httpcli_security_connector.c create mode 100644 src/core/httpcli/parser.c create mode 100644 src/core/httpcli/parser.h create mode 100644 src/core/iomgr/alarm.c create mode 100644 src/core/iomgr/alarm.h create mode 100644 src/core/iomgr/alarm_heap.c create mode 100644 src/core/iomgr/alarm_heap.h create mode 100644 src/core/iomgr/alarm_internal.h create mode 100644 src/core/iomgr/endpoint.c create mode 100644 src/core/iomgr/endpoint.h create mode 100644 src/core/iomgr/endpoint_pair.h create mode 100644 src/core/iomgr/endpoint_pair_posix.c create mode 100644 src/core/iomgr/endpoint_pair_windows.c create mode 100644 src/core/iomgr/fd_posix.c create mode 100644 src/core/iomgr/fd_posix.h create mode 100644 src/core/iomgr/iocp_windows.c create mode 100644 src/core/iomgr/iocp_windows.h create mode 100644 src/core/iomgr/iomgr.c create mode 100644 src/core/iomgr/iomgr.h create mode 100644 src/core/iomgr/iomgr_internal.h create mode 100644 src/core/iomgr/iomgr_posix.c create mode 100644 src/core/iomgr/iomgr_posix.h create mode 100644 src/core/iomgr/iomgr_windows.c create mode 100644 src/core/iomgr/pollset.h create mode 100644 src/core/iomgr/pollset_multipoller_with_epoll.c create mode 100644 src/core/iomgr/pollset_multipoller_with_poll_posix.c create mode 100644 src/core/iomgr/pollset_posix.c create mode 100644 src/core/iomgr/pollset_posix.h create mode 100644 src/core/iomgr/pollset_set.h create mode 100644 src/core/iomgr/pollset_set_posix.c create mode 100644 src/core/iomgr/pollset_set_posix.h create mode 100644 src/core/iomgr/pollset_set_windows.c create mode 100644 src/core/iomgr/pollset_set_windows.h create mode 100644 src/core/iomgr/pollset_windows.c create mode 100644 src/core/iomgr/pollset_windows.h create mode 100644 src/core/iomgr/resolve_address.h create mode 100644 src/core/iomgr/resolve_address_posix.c create mode 100644 src/core/iomgr/resolve_address_windows.c create mode 100644 src/core/iomgr/sockaddr.h create mode 100644 src/core/iomgr/sockaddr_posix.h create mode 100644 src/core/iomgr/sockaddr_utils.c create mode 100644 src/core/iomgr/sockaddr_utils.h create mode 100644 src/core/iomgr/sockaddr_win32.h create mode 100644 src/core/iomgr/socket_utils_common_posix.c create mode 100644 src/core/iomgr/socket_utils_linux.c create mode 100644 src/core/iomgr/socket_utils_posix.c create mode 100644 src/core/iomgr/socket_utils_posix.h create mode 100644 src/core/iomgr/socket_windows.c create mode 100644 src/core/iomgr/socket_windows.h create mode 100644 src/core/iomgr/tcp_client.h create mode 100644 src/core/iomgr/tcp_client_posix.c create mode 100644 src/core/iomgr/tcp_client_windows.c create mode 100644 src/core/iomgr/tcp_posix.c create mode 100644 src/core/iomgr/tcp_posix.h create mode 100644 src/core/iomgr/tcp_server.h create mode 100644 src/core/iomgr/tcp_server_posix.c create mode 100644 src/core/iomgr/tcp_server_windows.c create mode 100644 src/core/iomgr/tcp_windows.c create mode 100644 src/core/iomgr/tcp_windows.h create mode 100644 src/core/iomgr/time_averaged_stats.c create mode 100644 src/core/iomgr/time_averaged_stats.h create mode 100644 src/core/iomgr/udp_server.c create mode 100644 src/core/iomgr/udp_server.h create mode 100644 src/core/iomgr/wakeup_fd_eventfd.c create mode 100644 src/core/iomgr/wakeup_fd_nospecial.c create mode 100644 src/core/iomgr/wakeup_fd_pipe.c create mode 100644 src/core/iomgr/wakeup_fd_pipe.h create mode 100644 src/core/iomgr/wakeup_fd_posix.c create mode 100644 src/core/iomgr/wakeup_fd_posix.h create mode 100644 src/core/json/json.c create mode 100644 src/core/json/json.h create mode 100644 src/core/json/json_common.h create mode 100644 src/core/json/json_reader.c create mode 100644 src/core/json/json_reader.h create mode 100644 src/core/json/json_string.c create mode 100644 src/core/json/json_writer.c create mode 100644 src/core/json/json_writer.h create mode 100644 src/core/profiling/basic_timers.c create mode 100644 src/core/profiling/stap_probes.d create mode 100644 src/core/profiling/stap_timers.c create mode 100644 src/core/profiling/timers.h create mode 100644 src/core/security/auth_filters.h create mode 100644 src/core/security/base64.c create mode 100644 src/core/security/base64.h create mode 100644 src/core/security/client_auth_filter.c create mode 100644 src/core/security/credentials.c create mode 100644 src/core/security/credentials.h create mode 100644 src/core/security/credentials_metadata.c create mode 100644 src/core/security/credentials_posix.c create mode 100644 src/core/security/credentials_win32.c create mode 100644 src/core/security/google_default_credentials.c create mode 100644 src/core/security/json_token.c create mode 100644 src/core/security/json_token.h create mode 100644 src/core/security/jwt_verifier.c create mode 100644 src/core/security/jwt_verifier.h create mode 100644 src/core/security/secure_endpoint.c create mode 100644 src/core/security/secure_endpoint.h create mode 100644 src/core/security/secure_transport_setup.c create mode 100644 src/core/security/secure_transport_setup.h create mode 100644 src/core/security/security_connector.c create mode 100644 src/core/security/security_connector.h create mode 100644 src/core/security/security_context.c create mode 100644 src/core/security/security_context.h create mode 100644 src/core/security/server_auth_filter.c create mode 100644 src/core/security/server_secure_chttp2.c create mode 100644 src/core/statistics/census_init.c create mode 100644 src/core/statistics/census_interface.h create mode 100644 src/core/statistics/census_log.c create mode 100644 src/core/statistics/census_log.h create mode 100644 src/core/statistics/census_rpc_stats.c create mode 100644 src/core/statistics/census_rpc_stats.h create mode 100644 src/core/statistics/census_tracing.c create mode 100644 src/core/statistics/census_tracing.h create mode 100644 src/core/statistics/hash_table.c create mode 100644 src/core/statistics/hash_table.h create mode 100644 src/core/statistics/window_stats.c create mode 100644 src/core/statistics/window_stats.h create mode 100644 src/core/support/alloc.c create mode 100644 src/core/support/cmdline.c create mode 100644 src/core/support/cpu_iphone.c create mode 100644 src/core/support/cpu_linux.c create mode 100644 src/core/support/cpu_posix.c create mode 100644 src/core/support/cpu_windows.c create mode 100644 src/core/support/env.h create mode 100644 src/core/support/env_linux.c create mode 100644 src/core/support/env_posix.c create mode 100644 src/core/support/env_win32.c create mode 100644 src/core/support/file.c create mode 100644 src/core/support/file.h create mode 100644 src/core/support/file_posix.c create mode 100644 src/core/support/file_win32.c create mode 100644 src/core/support/histogram.c create mode 100644 src/core/support/host_port.c create mode 100644 src/core/support/log.c create mode 100644 src/core/support/log_android.c create mode 100644 src/core/support/log_linux.c create mode 100644 src/core/support/log_posix.c create mode 100644 src/core/support/log_win32.c create mode 100644 src/core/support/murmur_hash.c create mode 100644 src/core/support/murmur_hash.h create mode 100644 src/core/support/slice.c create mode 100644 src/core/support/slice_buffer.c create mode 100644 src/core/support/stack_lockfree.c create mode 100644 src/core/support/stack_lockfree.h create mode 100644 src/core/support/string.c create mode 100644 src/core/support/string.h create mode 100644 src/core/support/string_posix.c create mode 100644 src/core/support/string_win32.c create mode 100644 src/core/support/string_win32.h create mode 100644 src/core/support/subprocess_posix.c create mode 100644 src/core/support/sync.c create mode 100644 src/core/support/sync_posix.c create mode 100644 src/core/support/sync_win32.c create mode 100644 src/core/support/thd.c create mode 100644 src/core/support/thd_internal.h create mode 100644 src/core/support/thd_posix.c create mode 100644 src/core/support/thd_win32.c create mode 100644 src/core/support/time.c create mode 100644 src/core/support/time_posix.c create mode 100644 src/core/support/time_precise.h create mode 100644 src/core/support/time_win32.c create mode 100644 src/core/support/tls_pthread.c create mode 100644 src/core/surface/byte_buffer.c create mode 100644 src/core/surface/byte_buffer_queue.c create mode 100644 src/core/surface/byte_buffer_queue.h create mode 100644 src/core/surface/byte_buffer_reader.c create mode 100644 src/core/surface/call.c create mode 100644 src/core/surface/call.h create mode 100644 src/core/surface/call_details.c create mode 100644 src/core/surface/call_log_batch.c create mode 100644 src/core/surface/channel.c create mode 100644 src/core/surface/channel.h create mode 100644 src/core/surface/channel_connectivity.c create mode 100644 src/core/surface/channel_create.c create mode 100644 src/core/surface/completion_queue.c create mode 100644 src/core/surface/completion_queue.h create mode 100644 src/core/surface/event_string.c create mode 100644 src/core/surface/event_string.h create mode 100644 src/core/surface/init.c create mode 100644 src/core/surface/init.h create mode 100644 src/core/surface/init_secure.c create mode 100644 src/core/surface/init_unsecure.c create mode 100644 src/core/surface/lame_client.c create mode 100644 src/core/surface/metadata_array.c create mode 100644 src/core/surface/secure_channel_create.c create mode 100644 src/core/surface/server.c create mode 100644 src/core/surface/server.h create mode 100644 src/core/surface/server_chttp2.c create mode 100644 src/core/surface/server_create.c create mode 100644 src/core/surface/surface_trace.c create mode 100644 src/core/surface/surface_trace.h create mode 100644 src/core/surface/version.c create mode 100644 src/core/transport/chttp2/alpn.c create mode 100644 src/core/transport/chttp2/alpn.h create mode 100644 src/core/transport/chttp2/bin_encoder.c create mode 100644 src/core/transport/chttp2/bin_encoder.h create mode 100644 src/core/transport/chttp2/frame.h create mode 100644 src/core/transport/chttp2/frame_data.c create mode 100644 src/core/transport/chttp2/frame_data.h create mode 100644 src/core/transport/chttp2/frame_goaway.c create mode 100644 src/core/transport/chttp2/frame_goaway.h create mode 100644 src/core/transport/chttp2/frame_ping.c create mode 100644 src/core/transport/chttp2/frame_ping.h create mode 100644 src/core/transport/chttp2/frame_rst_stream.c create mode 100644 src/core/transport/chttp2/frame_rst_stream.h create mode 100644 src/core/transport/chttp2/frame_settings.c create mode 100644 src/core/transport/chttp2/frame_settings.h create mode 100644 src/core/transport/chttp2/frame_window_update.c create mode 100644 src/core/transport/chttp2/frame_window_update.h create mode 100644 src/core/transport/chttp2/hpack_parser.c create mode 100644 src/core/transport/chttp2/hpack_parser.h create mode 100644 src/core/transport/chttp2/hpack_table.c create mode 100644 src/core/transport/chttp2/hpack_table.h create mode 100644 src/core/transport/chttp2/hpack_tables.txt create mode 100644 src/core/transport/chttp2/http2_errors.h create mode 100644 src/core/transport/chttp2/huffsyms.c create mode 100644 src/core/transport/chttp2/huffsyms.h create mode 100644 src/core/transport/chttp2/incoming_metadata.c create mode 100644 src/core/transport/chttp2/incoming_metadata.h create mode 100644 src/core/transport/chttp2/internal.h create mode 100644 src/core/transport/chttp2/parsing.c create mode 100644 src/core/transport/chttp2/status_conversion.c create mode 100644 src/core/transport/chttp2/status_conversion.h create mode 100644 src/core/transport/chttp2/stream_encoder.c create mode 100644 src/core/transport/chttp2/stream_encoder.h create mode 100644 src/core/transport/chttp2/stream_lists.c create mode 100644 src/core/transport/chttp2/stream_map.c create mode 100644 src/core/transport/chttp2/stream_map.h create mode 100644 src/core/transport/chttp2/timeout_encoding.c create mode 100644 src/core/transport/chttp2/timeout_encoding.h create mode 100644 src/core/transport/chttp2/varint.c create mode 100644 src/core/transport/chttp2/varint.h create mode 100644 src/core/transport/chttp2/writing.c create mode 100644 src/core/transport/chttp2_transport.c create mode 100644 src/core/transport/chttp2_transport.h create mode 100644 src/core/transport/connectivity_state.c create mode 100644 src/core/transport/connectivity_state.h create mode 100644 src/core/transport/metadata.c create mode 100644 src/core/transport/metadata.h create mode 100644 src/core/transport/stream_op.c create mode 100644 src/core/transport/stream_op.h create mode 100644 src/core/transport/transport.c create mode 100644 src/core/transport/transport.h create mode 100644 src/core/transport/transport_impl.h create mode 100644 src/core/transport/transport_op_string.c create mode 100644 src/core/tsi/fake_transport_security.c create mode 100644 src/core/tsi/fake_transport_security.h create mode 100644 src/core/tsi/ssl_transport_security.c create mode 100644 src/core/tsi/ssl_transport_security.h create mode 100644 src/core/tsi/test_creds/README create mode 100644 src/core/tsi/test_creds/badclient.key create mode 100644 src/core/tsi/test_creds/badclient.pem create mode 100644 src/core/tsi/test_creds/badserver.key create mode 100644 src/core/tsi/test_creds/badserver.pem create mode 100644 src/core/tsi/test_creds/ca-openssl.cnf create mode 100644 src/core/tsi/test_creds/ca.key create mode 100644 src/core/tsi/test_creds/ca.pem create mode 100644 src/core/tsi/test_creds/client.key create mode 100644 src/core/tsi/test_creds/client.pem create mode 100644 src/core/tsi/test_creds/server0.key create mode 100644 src/core/tsi/test_creds/server0.pem create mode 100644 src/core/tsi/test_creds/server1-openssl.cnf create mode 100644 src/core/tsi/test_creds/server1.key create mode 100644 src/core/tsi/test_creds/server1.pem create mode 100644 src/core/tsi/transport_security.c create mode 100644 src/core/tsi/transport_security.h create mode 100644 src/core/tsi/transport_security_interface.h create mode 100644 src/cpp/README.md create mode 100644 src/cpp/client/channel.cc create mode 100644 src/cpp/client/channel_arguments.cc create mode 100644 src/cpp/client/client_context.cc create mode 100644 src/cpp/client/create_channel.cc create mode 100644 src/cpp/client/create_channel_internal.cc create mode 100644 src/cpp/client/create_channel_internal.h create mode 100644 src/cpp/client/credentials.cc create mode 100644 src/cpp/client/generic_stub.cc create mode 100644 src/cpp/client/insecure_credentials.cc create mode 100644 src/cpp/client/secure_channel_arguments.cc create mode 100644 src/cpp/client/secure_credentials.cc create mode 100644 src/cpp/client/secure_credentials.h create mode 100644 src/cpp/common/auth_property_iterator.cc create mode 100644 src/cpp/common/call.cc create mode 100644 src/cpp/common/completion_queue.cc create mode 100644 src/cpp/common/create_auth_context.h create mode 100644 src/cpp/common/insecure_create_auth_context.cc create mode 100644 src/cpp/common/rpc_method.cc create mode 100644 src/cpp/common/secure_auth_context.cc create mode 100644 src/cpp/common/secure_auth_context.h create mode 100644 src/cpp/common/secure_create_auth_context.cc create mode 100644 src/cpp/proto/proto_utils.cc create mode 100644 src/cpp/server/async_generic_service.cc create mode 100644 src/cpp/server/create_default_thread_pool.cc create mode 100644 src/cpp/server/dynamic_thread_pool.cc create mode 100644 src/cpp/server/dynamic_thread_pool.h create mode 100644 src/cpp/server/fixed_size_thread_pool.cc create mode 100644 src/cpp/server/fixed_size_thread_pool.h create mode 100644 src/cpp/server/insecure_server_credentials.cc create mode 100644 src/cpp/server/secure_server_credentials.cc create mode 100644 src/cpp/server/secure_server_credentials.h create mode 100644 src/cpp/server/server.cc create mode 100644 src/cpp/server/server_builder.cc create mode 100644 src/cpp/server/server_context.cc create mode 100644 src/cpp/server/server_credentials.cc create mode 100644 src/cpp/server/thread_pool_interface.h create mode 100644 src/cpp/util/byte_buffer.cc create mode 100644 src/cpp/util/slice.cc create mode 100644 src/cpp/util/status.cc create mode 100644 src/cpp/util/string_ref.cc create mode 100644 src/cpp/util/time.cc create mode 100644 src/csharp/.gitignore create mode 100644 src/csharp/.nuget/packages.config create mode 100644 src/csharp/Grpc.Auth/.gitignore create mode 100644 src/csharp/Grpc.Auth/AuthInterceptors.cs create mode 100644 src/csharp/Grpc.Auth/Grpc.Auth.csproj create mode 100644 src/csharp/Grpc.Auth/Grpc.Auth.nuspec create mode 100644 src/csharp/Grpc.Auth/Properties/AssemblyInfo.cs create mode 100644 src/csharp/Grpc.Auth/app.config create mode 100644 src/csharp/Grpc.Auth/packages.config create mode 100644 src/csharp/Grpc.Core.Tests/.gitignore create mode 100644 src/csharp/Grpc.Core.Tests/ChannelOptionsTest.cs create mode 100644 src/csharp/Grpc.Core.Tests/ChannelTest.cs create mode 100644 src/csharp/Grpc.Core.Tests/ClientBaseTest.cs create mode 100644 src/csharp/Grpc.Core.Tests/ClientServerTest.cs create mode 100644 src/csharp/Grpc.Core.Tests/CompressionTest.cs create mode 100644 src/csharp/Grpc.Core.Tests/ContextPropagationTest.cs create mode 100644 src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj create mode 100644 src/csharp/Grpc.Core.Tests/GrpcEnvironmentTest.cs create mode 100644 src/csharp/Grpc.Core.Tests/Internal/AsyncCallTest.cs create mode 100644 src/csharp/Grpc.Core.Tests/Internal/ChannelArgsSafeHandleTest.cs create mode 100644 src/csharp/Grpc.Core.Tests/Internal/CompletionQueueEventTest.cs create mode 100644 src/csharp/Grpc.Core.Tests/Internal/CompletionQueueSafeHandleTest.cs create mode 100644 src/csharp/Grpc.Core.Tests/Internal/MetadataArraySafeHandleTest.cs create mode 100644 src/csharp/Grpc.Core.Tests/Internal/TimespecTest.cs create mode 100644 src/csharp/Grpc.Core.Tests/MarshallingErrorsTest.cs create mode 100644 src/csharp/Grpc.Core.Tests/MetadataTest.cs create mode 100644 src/csharp/Grpc.Core.Tests/MockServiceHelper.cs create mode 100644 src/csharp/Grpc.Core.Tests/NUnitVersionTest.cs create mode 100644 src/csharp/Grpc.Core.Tests/PInvokeTest.cs create mode 100644 src/csharp/Grpc.Core.Tests/Properties/AssemblyInfo.cs create mode 100644 src/csharp/Grpc.Core.Tests/ResponseHeadersTest.cs create mode 100644 src/csharp/Grpc.Core.Tests/ServerTest.cs create mode 100644 src/csharp/Grpc.Core.Tests/ShutdownTest.cs create mode 100644 src/csharp/Grpc.Core.Tests/TestResult.xml create mode 100644 src/csharp/Grpc.Core.Tests/TimeoutsTest.cs create mode 100644 src/csharp/Grpc.Core.Tests/packages.config create mode 100644 src/csharp/Grpc.Core/.gitignore create mode 100644 src/csharp/Grpc.Core/AsyncClientStreamingCall.cs create mode 100644 src/csharp/Grpc.Core/AsyncDuplexStreamingCall.cs create mode 100644 src/csharp/Grpc.Core/AsyncServerStreamingCall.cs create mode 100644 src/csharp/Grpc.Core/AsyncUnaryCall.cs create mode 100644 src/csharp/Grpc.Core/CallInvocationDetails.cs create mode 100644 src/csharp/Grpc.Core/CallOptions.cs create mode 100644 src/csharp/Grpc.Core/Calls.cs create mode 100644 src/csharp/Grpc.Core/Channel.cs create mode 100644 src/csharp/Grpc.Core/ChannelOptions.cs create mode 100644 src/csharp/Grpc.Core/ChannelState.cs create mode 100644 src/csharp/Grpc.Core/ClientBase.cs create mode 100644 src/csharp/Grpc.Core/CompressionLevel.cs create mode 100644 src/csharp/Grpc.Core/ContextPropagationToken.cs create mode 100644 src/csharp/Grpc.Core/Credentials.cs create mode 100644 src/csharp/Grpc.Core/Grpc.Core.csproj create mode 100644 src/csharp/Grpc.Core/Grpc.Core.nuspec create mode 100644 src/csharp/Grpc.Core/GrpcEnvironment.cs create mode 100644 src/csharp/Grpc.Core/IAsyncStreamReader.cs create mode 100644 src/csharp/Grpc.Core/IAsyncStreamWriter.cs create mode 100644 src/csharp/Grpc.Core/IClientStreamWriter.cs create mode 100644 src/csharp/Grpc.Core/IServerStreamWriter.cs create mode 100644 src/csharp/Grpc.Core/Internal/AsyncCall.cs create mode 100644 src/csharp/Grpc.Core/Internal/AsyncCallBase.cs create mode 100644 src/csharp/Grpc.Core/Internal/AsyncCallServer.cs create mode 100644 src/csharp/Grpc.Core/Internal/AsyncCompletion.cs create mode 100644 src/csharp/Grpc.Core/Internal/AtomicCounter.cs create mode 100644 src/csharp/Grpc.Core/Internal/BatchContextSafeHandle.cs create mode 100644 src/csharp/Grpc.Core/Internal/CStringSafeHandle.cs create mode 100644 src/csharp/Grpc.Core/Internal/CallSafeHandle.cs create mode 100644 src/csharp/Grpc.Core/Internal/ChannelArgsSafeHandle.cs create mode 100644 src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs create mode 100644 src/csharp/Grpc.Core/Internal/ClientRequestStream.cs create mode 100644 src/csharp/Grpc.Core/Internal/ClientResponseStream.cs create mode 100644 src/csharp/Grpc.Core/Internal/CompletionQueueEvent.cs create mode 100644 src/csharp/Grpc.Core/Internal/CompletionQueueSafeHandle.cs create mode 100644 src/csharp/Grpc.Core/Internal/CompletionRegistry.cs create mode 100644 src/csharp/Grpc.Core/Internal/CredentialsSafeHandle.cs create mode 100644 src/csharp/Grpc.Core/Internal/DebugStats.cs create mode 100644 src/csharp/Grpc.Core/Internal/Enums.cs create mode 100644 src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs create mode 100644 src/csharp/Grpc.Core/Internal/INativeCall.cs create mode 100644 src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs create mode 100644 src/csharp/Grpc.Core/Internal/NativeLogRedirector.cs create mode 100644 src/csharp/Grpc.Core/Internal/SafeHandleZeroIsInvalid.cs create mode 100644 src/csharp/Grpc.Core/Internal/ServerCallHandler.cs create mode 100644 src/csharp/Grpc.Core/Internal/ServerCalls.cs create mode 100644 src/csharp/Grpc.Core/Internal/ServerCredentialsSafeHandle.cs create mode 100644 src/csharp/Grpc.Core/Internal/ServerRequestStream.cs create mode 100644 src/csharp/Grpc.Core/Internal/ServerResponseStream.cs create mode 100644 src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs create mode 100644 src/csharp/Grpc.Core/Internal/Timespec.cs create mode 100644 src/csharp/Grpc.Core/KeyCertificatePair.cs create mode 100644 src/csharp/Grpc.Core/Logging/ConsoleLogger.cs create mode 100644 src/csharp/Grpc.Core/Logging/ILogger.cs create mode 100644 src/csharp/Grpc.Core/Marshaller.cs create mode 100644 src/csharp/Grpc.Core/Metadata.cs create mode 100644 src/csharp/Grpc.Core/Method.cs create mode 100644 src/csharp/Grpc.Core/Properties/AssemblyInfo.cs create mode 100644 src/csharp/Grpc.Core/RpcException.cs create mode 100644 src/csharp/Grpc.Core/Server.cs create mode 100644 src/csharp/Grpc.Core/ServerCallContext.cs create mode 100644 src/csharp/Grpc.Core/ServerCredentials.cs create mode 100644 src/csharp/Grpc.Core/ServerMethods.cs create mode 100644 src/csharp/Grpc.Core/ServerPort.cs create mode 100644 src/csharp/Grpc.Core/ServerServiceDefinition.cs create mode 100644 src/csharp/Grpc.Core/Status.cs create mode 100644 src/csharp/Grpc.Core/StatusCode.cs create mode 100644 src/csharp/Grpc.Core/Utils/AsyncStreamExtensions.cs create mode 100644 src/csharp/Grpc.Core/Utils/BenchmarkUtil.cs create mode 100644 src/csharp/Grpc.Core/Utils/Preconditions.cs create mode 100644 src/csharp/Grpc.Core/Version.cs create mode 100644 src/csharp/Grpc.Core/VersionInfo.cs create mode 100644 src/csharp/Grpc.Core/WriteOptions.cs create mode 100644 src/csharp/Grpc.Core/packages.config create mode 100644 src/csharp/Grpc.Examples.MathClient/.gitignore create mode 100644 src/csharp/Grpc.Examples.MathClient/Grpc.Examples.MathClient.csproj create mode 100644 src/csharp/Grpc.Examples.MathClient/MathClient.cs create mode 100644 src/csharp/Grpc.Examples.MathClient/Properties/AssemblyInfo.cs create mode 100644 src/csharp/Grpc.Examples.MathServer/.gitignore create mode 100644 src/csharp/Grpc.Examples.MathServer/Grpc.Examples.MathServer.csproj create mode 100644 src/csharp/Grpc.Examples.MathServer/MathServer.cs create mode 100644 src/csharp/Grpc.Examples.MathServer/Properties/AssemblyInfo.cs create mode 100644 src/csharp/Grpc.Examples.Tests/.gitignore create mode 100644 src/csharp/Grpc.Examples.Tests/Grpc.Examples.Tests.csproj create mode 100644 src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs create mode 100644 src/csharp/Grpc.Examples.Tests/Properties/AssemblyInfo.cs create mode 100644 src/csharp/Grpc.Examples.Tests/packages.config create mode 100644 src/csharp/Grpc.Examples/.gitignore create mode 100644 src/csharp/Grpc.Examples/Grpc.Examples.csproj create mode 100644 src/csharp/Grpc.Examples/Math.cs create mode 100644 src/csharp/Grpc.Examples/MathExamples.cs create mode 100644 src/csharp/Grpc.Examples/MathGrpc.cs create mode 100644 src/csharp/Grpc.Examples/MathServiceImpl.cs create mode 100644 src/csharp/Grpc.Examples/Properties/AssemblyInfo.cs create mode 100644 src/csharp/Grpc.Examples/Settings.StyleCop create mode 100644 src/csharp/Grpc.Examples/packages.config create mode 100644 src/csharp/Grpc.Examples/proto/math.proto create mode 100644 src/csharp/Grpc.HealthCheck.Tests/.gitignore create mode 100644 src/csharp/Grpc.HealthCheck.Tests/Grpc.HealthCheck.Tests.csproj create mode 100644 src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs create mode 100644 src/csharp/Grpc.HealthCheck.Tests/HealthServiceImplTest.cs create mode 100644 src/csharp/Grpc.HealthCheck.Tests/Properties/AssemblyInfo.cs create mode 100644 src/csharp/Grpc.HealthCheck.Tests/packages.config create mode 100644 src/csharp/Grpc.HealthCheck/.gitignore create mode 100644 src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.csproj create mode 100644 src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.nuspec create mode 100644 src/csharp/Grpc.HealthCheck/Health.cs create mode 100644 src/csharp/Grpc.HealthCheck/HealthGrpc.cs create mode 100644 src/csharp/Grpc.HealthCheck/HealthServiceImpl.cs create mode 100644 src/csharp/Grpc.HealthCheck/Properties/AssemblyInfo.cs create mode 100644 src/csharp/Grpc.HealthCheck/Settings.StyleCop create mode 100644 src/csharp/Grpc.HealthCheck/packages.config create mode 100644 src/csharp/Grpc.HealthCheck/proto/health.proto create mode 100644 src/csharp/Grpc.IntegrationTesting.Client/.gitignore create mode 100644 src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj create mode 100644 src/csharp/Grpc.IntegrationTesting.Client/Program.cs create mode 100644 src/csharp/Grpc.IntegrationTesting.Client/Properties/AssemblyInfo.cs create mode 100644 src/csharp/Grpc.IntegrationTesting.Client/app.config create mode 100644 src/csharp/Grpc.IntegrationTesting.Server/.gitignore create mode 100644 src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj create mode 100644 src/csharp/Grpc.IntegrationTesting.Server/Program.cs create mode 100644 src/csharp/Grpc.IntegrationTesting.Server/Properties/AssemblyInfo.cs create mode 100644 src/csharp/Grpc.IntegrationTesting.Server/app.config create mode 100644 src/csharp/Grpc.IntegrationTesting/.gitignore create mode 100644 src/csharp/Grpc.IntegrationTesting/Empty.cs create mode 100644 src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj create mode 100644 src/csharp/Grpc.IntegrationTesting/InteropClient.cs create mode 100644 src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs create mode 100644 src/csharp/Grpc.IntegrationTesting/InteropServer.cs create mode 100644 src/csharp/Grpc.IntegrationTesting/Messages.cs create mode 100644 src/csharp/Grpc.IntegrationTesting/Properties/AssemblyInfo.cs create mode 100644 src/csharp/Grpc.IntegrationTesting/Settings.StyleCop create mode 100644 src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs create mode 100644 src/csharp/Grpc.IntegrationTesting/Test.cs create mode 100644 src/csharp/Grpc.IntegrationTesting/TestCredentials.cs create mode 100644 src/csharp/Grpc.IntegrationTesting/TestGrpc.cs create mode 100644 src/csharp/Grpc.IntegrationTesting/TestServiceGrpc.cs create mode 100644 src/csharp/Grpc.IntegrationTesting/TestServiceImpl.cs create mode 100644 src/csharp/Grpc.IntegrationTesting/app.config create mode 100644 src/csharp/Grpc.IntegrationTesting/data/README create mode 100644 src/csharp/Grpc.IntegrationTesting/data/ca.pem create mode 100644 src/csharp/Grpc.IntegrationTesting/data/server1.key create mode 100644 src/csharp/Grpc.IntegrationTesting/data/server1.pem create mode 100644 src/csharp/Grpc.IntegrationTesting/packages.config create mode 100644 src/csharp/Grpc.IntegrationTesting/proto/empty.proto create mode 100644 src/csharp/Grpc.IntegrationTesting/proto/messages.proto create mode 100644 src/csharp/Grpc.IntegrationTesting/proto/test.proto create mode 100644 src/csharp/Grpc.Tools.nuspec create mode 100644 src/csharp/Grpc.nuspec create mode 100644 src/csharp/Grpc.sln create mode 100644 src/csharp/README.md create mode 100644 src/csharp/Settings.StyleCop create mode 100644 src/csharp/build_packages.bat create mode 100644 src/csharp/buildall.bat create mode 100644 src/csharp/doc/README.md create mode 100644 src/csharp/doc/grpc_csharp_public.shfbproj create mode 100644 src/csharp/ext/grpc_csharp_ext.c create mode 100755 src/csharp/generate_proto_csharp.sh create mode 100644 src/csharp/keys/Grpc.public.snk create mode 100644 src/csharp/keys/README.md create mode 100644 src/node/.gitignore create mode 100644 src/node/.jshintrc create mode 100644 src/node/LICENSE create mode 100644 src/node/README.md create mode 100644 src/node/bin/README.md create mode 100755 src/node/bin/service_packager create mode 100644 src/node/binding.gyp create mode 100644 src/node/cli/service_packager.js create mode 100644 src/node/cli/service_packager/index.js create mode 100644 src/node/cli/service_packager/package.json.template create mode 100644 src/node/examples/math.proto create mode 100644 src/node/examples/math_server.js create mode 100644 src/node/examples/perf_test.js create mode 100644 src/node/examples/qps_test.js create mode 100644 src/node/examples/stock.proto create mode 100644 src/node/examples/stock_client.js create mode 100644 src/node/examples/stock_server.js create mode 100644 src/node/ext/byte_buffer.cc create mode 100644 src/node/ext/byte_buffer.h create mode 100644 src/node/ext/call.cc create mode 100644 src/node/ext/call.h create mode 100644 src/node/ext/channel.cc create mode 100644 src/node/ext/channel.h create mode 100644 src/node/ext/completion_queue_async_worker.cc create mode 100644 src/node/ext/completion_queue_async_worker.h create mode 100644 src/node/ext/credentials.cc create mode 100644 src/node/ext/credentials.h create mode 100644 src/node/ext/node_grpc.cc create mode 100644 src/node/ext/server.cc create mode 100644 src/node/ext/server.h create mode 100644 src/node/ext/server_credentials.cc create mode 100644 src/node/ext/server_credentials.h create mode 100644 src/node/ext/timeval.cc create mode 100644 src/node/ext/timeval.h create mode 100644 src/node/health_check/health.js create mode 100644 src/node/health_check/health.proto create mode 100644 src/node/index.js create mode 100644 src/node/interop/empty.proto create mode 100644 src/node/interop/interop_client.js create mode 100644 src/node/interop/interop_server.js create mode 100644 src/node/interop/messages.proto create mode 100644 src/node/interop/test.proto create mode 100644 src/node/jsdoc_conf.json create mode 100644 src/node/package.json create mode 100644 src/node/src/client.js create mode 100644 src/node/src/common.js create mode 100644 src/node/src/metadata.js create mode 100644 src/node/src/server.js create mode 100644 src/node/test/call_test.js create mode 100644 src/node/test/channel_test.js create mode 100644 src/node/test/common_test.js create mode 100644 src/node/test/constant_test.js create mode 100644 src/node/test/data/README create mode 100644 src/node/test/data/ca.pem create mode 100644 src/node/test/data/server1.key create mode 100644 src/node/test/data/server1.pem create mode 100644 src/node/test/echo_service.proto create mode 100644 src/node/test/end_to_end_test.js create mode 100644 src/node/test/health_test.js create mode 100644 src/node/test/interop_sanity_test.js create mode 100644 src/node/test/math_client_test.js create mode 100644 src/node/test/metadata_test.js create mode 100644 src/node/test/server_test.js create mode 100644 src/node/test/surface_test.js create mode 100644 src/node/test/test_messages.proto create mode 100644 src/node/test/test_service.json create mode 100644 src/node/test/test_service.proto create mode 100644 src/objective-c/.gitignore create mode 100644 src/objective-c/GRPCClient/GRPCCall+OAuth2.h create mode 100644 src/objective-c/GRPCClient/GRPCCall+OAuth2.m create mode 100644 src/objective-c/GRPCClient/GRPCCall+Tests.h create mode 100644 src/objective-c/GRPCClient/GRPCCall+Tests.m create mode 100644 src/objective-c/GRPCClient/GRPCCall.h create mode 100644 src/objective-c/GRPCClient/GRPCCall.m create mode 100644 src/objective-c/GRPCClient/README.md create mode 100644 src/objective-c/GRPCClient/private/GRPCChannel.h create mode 100644 src/objective-c/GRPCClient/private/GRPCChannel.m create mode 100644 src/objective-c/GRPCClient/private/GRPCCompletionQueue.h create mode 100644 src/objective-c/GRPCClient/private/GRPCCompletionQueue.m create mode 100644 src/objective-c/GRPCClient/private/GRPCHost.h create mode 100644 src/objective-c/GRPCClient/private/GRPCHost.m create mode 100644 src/objective-c/GRPCClient/private/GRPCRequestHeaders.h create mode 100644 src/objective-c/GRPCClient/private/GRPCRequestHeaders.m create mode 100644 src/objective-c/GRPCClient/private/GRPCSecureChannel.h create mode 100644 src/objective-c/GRPCClient/private/GRPCSecureChannel.m create mode 100644 src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.h create mode 100644 src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.m create mode 100644 src/objective-c/GRPCClient/private/GRPCWrappedCall.h create mode 100644 src/objective-c/GRPCClient/private/GRPCWrappedCall.m create mode 100644 src/objective-c/GRPCClient/private/NSData+GRPC.h create mode 100644 src/objective-c/GRPCClient/private/NSData+GRPC.m create mode 100644 src/objective-c/GRPCClient/private/NSDictionary+GRPC.h create mode 100644 src/objective-c/GRPCClient/private/NSDictionary+GRPC.m create mode 100644 src/objective-c/GRPCClient/private/NSError+GRPC.h create mode 100644 src/objective-c/GRPCClient/private/NSError+GRPC.m create mode 100644 src/objective-c/ProtoRPC/ProtoMethod.h create mode 100644 src/objective-c/ProtoRPC/ProtoMethod.m create mode 100644 src/objective-c/ProtoRPC/ProtoRPC.h create mode 100644 src/objective-c/ProtoRPC/ProtoRPC.m create mode 100644 src/objective-c/ProtoRPC/ProtoService.h create mode 100644 src/objective-c/ProtoRPC/ProtoService.m create mode 100644 src/objective-c/README.md create mode 100644 src/objective-c/RxLibrary/GRXBufferedPipe.h create mode 100644 src/objective-c/RxLibrary/GRXBufferedPipe.m create mode 100644 src/objective-c/RxLibrary/GRXConcurrentWriteable.h create mode 100644 src/objective-c/RxLibrary/GRXConcurrentWriteable.m create mode 100644 src/objective-c/RxLibrary/GRXForwardingWriter.h create mode 100644 src/objective-c/RxLibrary/GRXForwardingWriter.m create mode 100644 src/objective-c/RxLibrary/GRXImmediateWriter.h create mode 100644 src/objective-c/RxLibrary/GRXImmediateWriter.m create mode 100644 src/objective-c/RxLibrary/GRXWriteable.h create mode 100644 src/objective-c/RxLibrary/GRXWriteable.m create mode 100644 src/objective-c/RxLibrary/GRXWriter+Immediate.h create mode 100644 src/objective-c/RxLibrary/GRXWriter+Immediate.m create mode 100644 src/objective-c/RxLibrary/GRXWriter+Transformations.h create mode 100644 src/objective-c/RxLibrary/GRXWriter+Transformations.m create mode 100644 src/objective-c/RxLibrary/GRXWriter.h create mode 100644 src/objective-c/RxLibrary/GRXWriter.m create mode 100644 src/objective-c/RxLibrary/NSEnumerator+GRXUtil.h create mode 100644 src/objective-c/RxLibrary/NSEnumerator+GRXUtil.m create mode 100644 src/objective-c/RxLibrary/README.md create mode 100644 src/objective-c/RxLibrary/private/GRXNSBlockEnumerator.h create mode 100644 src/objective-c/RxLibrary/private/GRXNSBlockEnumerator.m create mode 100644 src/objective-c/RxLibrary/private/GRXNSFastEnumerator.h create mode 100644 src/objective-c/RxLibrary/private/GRXNSFastEnumerator.m create mode 100644 src/objective-c/RxLibrary/private/GRXNSScalarEnumerator.h create mode 100644 src/objective-c/RxLibrary/private/GRXNSScalarEnumerator.m create mode 100644 src/objective-c/RxLibrary/transformations/GRXMappingWriter.h create mode 100644 src/objective-c/RxLibrary/transformations/GRXMappingWriter.m create mode 100644 src/objective-c/examples/RemoteTestClient/RemoteTest.podspec create mode 100644 src/objective-c/examples/RemoteTestClient/empty.proto create mode 100644 src/objective-c/examples/RemoteTestClient/messages.proto create mode 100644 src/objective-c/examples/RemoteTestClient/test.proto create mode 100644 src/objective-c/examples/Sample/Podfile create mode 100644 src/objective-c/examples/Sample/README.md create mode 100644 src/objective-c/examples/Sample/Sample.xcodeproj/project.pbxproj create mode 100644 src/objective-c/examples/Sample/Sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 src/objective-c/examples/Sample/Sample/AppDelegate.h create mode 100644 src/objective-c/examples/Sample/Sample/AppDelegate.m create mode 100644 src/objective-c/examples/Sample/Sample/Base.lproj/Main.storyboard create mode 100644 src/objective-c/examples/Sample/Sample/Images.xcassets/AppIcon.appiconset/Contents.json create mode 100644 src/objective-c/examples/Sample/Sample/Info.plist create mode 100644 src/objective-c/examples/Sample/Sample/ViewController.h create mode 100644 src/objective-c/examples/Sample/Sample/ViewController.m create mode 100644 src/objective-c/examples/Sample/Sample/main.m create mode 100644 src/objective-c/examples/SwiftSample/AppDelegate.swift create mode 100644 src/objective-c/examples/SwiftSample/Base.lproj/Main.storyboard create mode 100644 src/objective-c/examples/SwiftSample/Bridging-Header.h create mode 100644 src/objective-c/examples/SwiftSample/Images.xcassets/AppIcon.appiconset/Contents.json create mode 100644 src/objective-c/examples/SwiftSample/Info.plist create mode 100644 src/objective-c/examples/SwiftSample/Podfile create mode 100644 src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/project.pbxproj create mode 100644 src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 src/objective-c/examples/SwiftSample/ViewController.swift create mode 100644 src/objective-c/generated_libraries/RemoteTestClient/RemoteTest.podspec create mode 100644 src/objective-c/generated_libraries/RemoteTestClient/empty.proto create mode 100644 src/objective-c/generated_libraries/RemoteTestClient/messages.proto create mode 100644 src/objective-c/generated_libraries/RemoteTestClient/test.proto create mode 100644 src/objective-c/generated_libraries/RouteGuideClient/RouteGuide.podspec create mode 100644 src/objective-c/generated_libraries/RouteGuideClient/route_guide.proto create mode 100644 src/objective-c/tests/GRPCClientTests.m create mode 100644 src/objective-c/tests/Info.plist create mode 100644 src/objective-c/tests/InteropTests.h create mode 100644 src/objective-c/tests/InteropTests.m create mode 100644 src/objective-c/tests/InteropTestsLocalCleartext.m create mode 100644 src/objective-c/tests/InteropTestsLocalSSL.m create mode 100644 src/objective-c/tests/LocalClearTextTests.m create mode 100644 src/objective-c/tests/Podfile create mode 100644 src/objective-c/tests/RxLibraryUnitTests.m create mode 100644 src/objective-c/tests/TestCertificates.bundle/test-certificates.pem create mode 100644 src/objective-c/tests/Tests.m create mode 100644 src/objective-c/tests/Tests.xcodeproj/project.pbxproj create mode 100644 src/objective-c/tests/Tests.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/AllTests.xcscheme create mode 100755 src/objective-c/tests/build_tests.sh create mode 100755 src/objective-c/tests/run_tests.sh create mode 100644 src/php/.gitignore create mode 100644 src/php/README.md create mode 100755 src/php/bin/determine_extension_dir.sh create mode 100755 src/php/bin/generate_proto_php.sh create mode 100755 src/php/bin/interop_client.sh create mode 100755 src/php/bin/run_gen_code_test.sh create mode 100755 src/php/bin/run_tests.sh create mode 100644 src/php/composer.json create mode 100644 src/php/ext/grpc/CREDITS create mode 100644 src/php/ext/grpc/LICENSE create mode 100644 src/php/ext/grpc/README.md create mode 100644 src/php/ext/grpc/byte_buffer.c create mode 100644 src/php/ext/grpc/byte_buffer.h create mode 100644 src/php/ext/grpc/call.c create mode 100644 src/php/ext/grpc/call.h create mode 100644 src/php/ext/grpc/channel.c create mode 100755 src/php/ext/grpc/channel.h create mode 100644 src/php/ext/grpc/completion_queue.c create mode 100644 src/php/ext/grpc/completion_queue.h create mode 100755 src/php/ext/grpc/config.m4 create mode 100644 src/php/ext/grpc/credentials.c create mode 100755 src/php/ext/grpc/credentials.h create mode 100644 src/php/ext/grpc/package.xml create mode 100644 src/php/ext/grpc/php_grpc.c create mode 100644 src/php/ext/grpc/php_grpc.h create mode 100644 src/php/ext/grpc/server.c create mode 100755 src/php/ext/grpc/server.h create mode 100644 src/php/ext/grpc/server_credentials.c create mode 100755 src/php/ext/grpc/server_credentials.h create mode 100644 src/php/ext/grpc/timeval.c create mode 100755 src/php/ext/grpc/timeval.h create mode 100644 src/php/lib/Grpc/AbstractCall.php create mode 100755 src/php/lib/Grpc/BaseStub.php create mode 100644 src/php/lib/Grpc/BidiStreamingCall.php create mode 100644 src/php/lib/Grpc/ClientStreamingCall.php create mode 100644 src/php/lib/Grpc/ServerStreamingCall.php create mode 100644 src/php/lib/Grpc/UnaryCall.php create mode 100644 src/php/tests/data/README create mode 100755 src/php/tests/data/ca.pem create mode 100755 src/php/tests/data/server1.key create mode 100755 src/php/tests/data/server1.pem create mode 100644 src/php/tests/generated_code/AbstractGeneratedCodeTest.php create mode 100755 src/php/tests/generated_code/GeneratedCodeTest.php create mode 100644 src/php/tests/generated_code/GeneratedCodeWithCallbackTest.php create mode 100644 src/php/tests/generated_code/math.proto create mode 100644 src/php/tests/generated_code/math_client.php create mode 100644 src/php/tests/interop/empty.proto create mode 100755 src/php/tests/interop/interop_client.php create mode 100755 src/php/tests/interop/message_set.php create mode 100644 src/php/tests/interop/messages.proto create mode 100644 src/php/tests/interop/test.proto create mode 100755 src/php/tests/unit_tests/CallTest.php create mode 100755 src/php/tests/unit_tests/EndToEndTest.php create mode 100755 src/php/tests/unit_tests/SecureEndToEndTest.php create mode 100755 src/php/tests/unit_tests/TimevalTest.php create mode 100644 src/python/README.md create mode 100644 src/python/grpcio/.gitignore create mode 100644 src/python/grpcio/MANIFEST.in create mode 100644 src/python/grpcio/README.rst create mode 100644 src/python/grpcio/commands.py create mode 100644 src/python/grpcio/grpc/__init__.py create mode 100644 src/python/grpcio/grpc/_adapter/.gitignore create mode 100644 src/python/grpcio/grpc/_adapter/__init__.py create mode 100644 src/python/grpcio/grpc/_adapter/_c/module.c create mode 100644 src/python/grpcio/grpc/_adapter/_c/types.c create mode 100644 src/python/grpcio/grpc/_adapter/_c/types.h create mode 100644 src/python/grpcio/grpc/_adapter/_c/types/call.c create mode 100644 src/python/grpcio/grpc/_adapter/_c/types/channel.c create mode 100644 src/python/grpcio/grpc/_adapter/_c/types/client_credentials.c create mode 100644 src/python/grpcio/grpc/_adapter/_c/types/completion_queue.c create mode 100644 src/python/grpcio/grpc/_adapter/_c/types/server.c create mode 100644 src/python/grpcio/grpc/_adapter/_c/types/server_credentials.c create mode 100644 src/python/grpcio/grpc/_adapter/_c/utility.c create mode 100644 src/python/grpcio/grpc/_adapter/_common.py create mode 100644 src/python/grpcio/grpc/_adapter/_intermediary_low.py create mode 100644 src/python/grpcio/grpc/_adapter/_low.py create mode 100644 src/python/grpcio/grpc/_adapter/_types.py create mode 100644 src/python/grpcio/grpc/_adapter/fore.py create mode 100644 src/python/grpcio/grpc/_adapter/rear.py create mode 100644 src/python/grpcio/grpc/_cython/.gitignore create mode 100644 src/python/grpcio/grpc/_cython/README.rst create mode 100644 src/python/grpcio/grpc/_cython/__init__.py create mode 100644 src/python/grpcio/grpc/_cython/_cygrpc/__init__.py create mode 100644 src/python/grpcio/grpc/_cython/_cygrpc/call.pxd create mode 100644 src/python/grpcio/grpc/_cython/_cygrpc/call.pyx create mode 100644 src/python/grpcio/grpc/_cython/_cygrpc/channel.pxd create mode 100644 src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx create mode 100644 src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pxd create mode 100644 src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx create mode 100644 src/python/grpcio/grpc/_cython/_cygrpc/credentials.pxd create mode 100644 src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx create mode 100644 src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxd create mode 100644 src/python/grpcio/grpc/_cython/_cygrpc/records.pxd create mode 100644 src/python/grpcio/grpc/_cython/_cygrpc/records.pyx create mode 100644 src/python/grpcio/grpc/_cython/_cygrpc/server.pxd create mode 100644 src/python/grpcio/grpc/_cython/_cygrpc/server.pyx create mode 100644 src/python/grpcio/grpc/_cython/adapter_low.py create mode 100644 src/python/grpcio/grpc/_cython/cygrpc.pyx create mode 100644 src/python/grpcio/grpc/_links/__init__.py create mode 100644 src/python/grpcio/grpc/_links/_constants.py create mode 100644 src/python/grpcio/grpc/_links/invocation.py create mode 100644 src/python/grpcio/grpc/_links/service.py create mode 100644 src/python/grpcio/grpc/beta/__init__.py create mode 100644 src/python/grpcio/grpc/beta/_connectivity_channel.py create mode 100644 src/python/grpcio/grpc/beta/_server.py create mode 100644 src/python/grpcio/grpc/beta/_stub.py create mode 100644 src/python/grpcio/grpc/beta/implementations.py create mode 100644 src/python/grpcio/grpc/beta/interfaces.py create mode 100644 src/python/grpcio/grpc/beta/utilities.py create mode 100644 src/python/grpcio/grpc/early_adopter/__init__.py create mode 100644 src/python/grpcio/grpc/early_adopter/implementations.py create mode 100644 src/python/grpcio/grpc/framework/__init__.py create mode 100644 src/python/grpcio/grpc/framework/alpha/__init__.py create mode 100644 src/python/grpcio/grpc/framework/alpha/_face_utilities.py create mode 100644 src/python/grpcio/grpc/framework/alpha/_reexport.py create mode 100644 src/python/grpcio/grpc/framework/alpha/exceptions.py create mode 100644 src/python/grpcio/grpc/framework/alpha/interfaces.py create mode 100644 src/python/grpcio/grpc/framework/alpha/utilities.py create mode 100644 src/python/grpcio/grpc/framework/base/__init__.py create mode 100644 src/python/grpcio/grpc/framework/base/_cancellation.py create mode 100644 src/python/grpcio/grpc/framework/base/_constants.py create mode 100644 src/python/grpcio/grpc/framework/base/_context.py create mode 100644 src/python/grpcio/grpc/framework/base/_emission.py create mode 100644 src/python/grpcio/grpc/framework/base/_ends.py create mode 100644 src/python/grpcio/grpc/framework/base/_expiration.py create mode 100644 src/python/grpcio/grpc/framework/base/_ingestion.py create mode 100644 src/python/grpcio/grpc/framework/base/_interfaces.py create mode 100644 src/python/grpcio/grpc/framework/base/_reception.py create mode 100644 src/python/grpcio/grpc/framework/base/_termination.py create mode 100644 src/python/grpcio/grpc/framework/base/_transmission.py create mode 100644 src/python/grpcio/grpc/framework/base/exceptions.py create mode 100644 src/python/grpcio/grpc/framework/base/implementations.py create mode 100644 src/python/grpcio/grpc/framework/base/in_memory.py create mode 100644 src/python/grpcio/grpc/framework/base/interfaces.py create mode 100644 src/python/grpcio/grpc/framework/base/null.py create mode 100644 src/python/grpcio/grpc/framework/base/util.py create mode 100644 src/python/grpcio/grpc/framework/common/__init__.py create mode 100644 src/python/grpcio/grpc/framework/common/cardinality.py create mode 100644 src/python/grpcio/grpc/framework/common/style.py create mode 100644 src/python/grpcio/grpc/framework/core/__init__.py create mode 100644 src/python/grpcio/grpc/framework/core/_constants.py create mode 100644 src/python/grpcio/grpc/framework/core/_context.py create mode 100644 src/python/grpcio/grpc/framework/core/_emission.py create mode 100644 src/python/grpcio/grpc/framework/core/_end.py create mode 100644 src/python/grpcio/grpc/framework/core/_expiration.py create mode 100644 src/python/grpcio/grpc/framework/core/_ingestion.py create mode 100644 src/python/grpcio/grpc/framework/core/_interfaces.py create mode 100644 src/python/grpcio/grpc/framework/core/_operation.py create mode 100644 src/python/grpcio/grpc/framework/core/_protocol.py create mode 100644 src/python/grpcio/grpc/framework/core/_reception.py create mode 100644 src/python/grpcio/grpc/framework/core/_termination.py create mode 100644 src/python/grpcio/grpc/framework/core/_transmission.py create mode 100644 src/python/grpcio/grpc/framework/core/_utilities.py create mode 100644 src/python/grpcio/grpc/framework/core/implementations.py create mode 100644 src/python/grpcio/grpc/framework/crust/__init__.py create mode 100644 src/python/grpcio/grpc/framework/crust/_calls.py create mode 100644 src/python/grpcio/grpc/framework/crust/_control.py create mode 100644 src/python/grpcio/grpc/framework/crust/_service.py create mode 100644 src/python/grpcio/grpc/framework/crust/implementations.py create mode 100644 src/python/grpcio/grpc/framework/face/__init__.py create mode 100644 src/python/grpcio/grpc/framework/face/_calls.py create mode 100644 src/python/grpcio/grpc/framework/face/_control.py create mode 100644 src/python/grpcio/grpc/framework/face/_service.py create mode 100644 src/python/grpcio/grpc/framework/face/demonstration.py create mode 100644 src/python/grpcio/grpc/framework/face/exceptions.py create mode 100644 src/python/grpcio/grpc/framework/face/implementations.py create mode 100644 src/python/grpcio/grpc/framework/face/interfaces.py create mode 100644 src/python/grpcio/grpc/framework/face/utilities.py create mode 100644 src/python/grpcio/grpc/framework/foundation/__init__.py create mode 100644 src/python/grpcio/grpc/framework/foundation/_timer_future.py create mode 100644 src/python/grpcio/grpc/framework/foundation/abandonment.py create mode 100644 src/python/grpcio/grpc/framework/foundation/activated.py create mode 100644 src/python/grpcio/grpc/framework/foundation/callable_util.py create mode 100644 src/python/grpcio/grpc/framework/foundation/future.py create mode 100644 src/python/grpcio/grpc/framework/foundation/later.py create mode 100644 src/python/grpcio/grpc/framework/foundation/logging_pool.py create mode 100644 src/python/grpcio/grpc/framework/foundation/relay.py create mode 100644 src/python/grpcio/grpc/framework/foundation/stream.py create mode 100644 src/python/grpcio/grpc/framework/foundation/stream_util.py create mode 100644 src/python/grpcio/grpc/framework/interfaces/__init__.py create mode 100644 src/python/grpcio/grpc/framework/interfaces/base/__init__.py create mode 100644 src/python/grpcio/grpc/framework/interfaces/base/base.py create mode 100644 src/python/grpcio/grpc/framework/interfaces/base/utilities.py create mode 100644 src/python/grpcio/grpc/framework/interfaces/face/__init__.py create mode 100644 src/python/grpcio/grpc/framework/interfaces/face/face.py create mode 100644 src/python/grpcio/grpc/framework/interfaces/face/utilities.py create mode 100644 src/python/grpcio/grpc/framework/interfaces/links/__init__.py create mode 100644 src/python/grpcio/grpc/framework/interfaces/links/links.py create mode 100644 src/python/grpcio/grpc/framework/interfaces/links/utilities.py create mode 100644 src/python/grpcio/requirements.txt create mode 100644 src/python/grpcio/setup.cfg create mode 100644 src/python/grpcio/setup.py create mode 100644 src/python/grpcio_health_checking/MANIFEST.in create mode 100644 src/python/grpcio_health_checking/README.rst create mode 100644 src/python/grpcio_health_checking/commands.py create mode 100644 src/python/grpcio_health_checking/grpc/__init__.py create mode 100644 src/python/grpcio_health_checking/grpc/health/__init__.py create mode 100644 src/python/grpcio_health_checking/grpc/health/v1alpha/__init__.py create mode 100644 src/python/grpcio_health_checking/grpc/health/v1alpha/health.proto create mode 100644 src/python/grpcio_health_checking/grpc/health/v1alpha/health.py create mode 100644 src/python/grpcio_health_checking/setup.py create mode 100644 src/python/grpcio_test/.gitignore create mode 100644 src/python/grpcio_test/MANIFEST.in create mode 100644 src/python/grpcio_test/commands.py create mode 100644 src/python/grpcio_test/grpc_interop/__init__.py create mode 100644 src/python/grpcio_test/grpc_interop/_insecure_interop_test.py create mode 100644 src/python/grpcio_test/grpc_interop/_interop_test_case.py create mode 100644 src/python/grpcio_test/grpc_interop/_secure_interop_test.py create mode 100644 src/python/grpcio_test/grpc_interop/client.py create mode 100644 src/python/grpcio_test/grpc_interop/credentials/README create mode 100755 src/python/grpcio_test/grpc_interop/credentials/ca.pem create mode 100755 src/python/grpcio_test/grpc_interop/credentials/server1.key create mode 100755 src/python/grpcio_test/grpc_interop/credentials/server1.pem create mode 100644 src/python/grpcio_test/grpc_interop/empty_pb2.py create mode 100644 src/python/grpcio_test/grpc_interop/messages_pb2.py create mode 100644 src/python/grpcio_test/grpc_interop/methods.py create mode 100644 src/python/grpcio_test/grpc_interop/resources.py create mode 100644 src/python/grpcio_test/grpc_interop/server.py create mode 100644 src/python/grpcio_test/grpc_interop/test_pb2.py create mode 100644 src/python/grpcio_test/grpc_protoc_plugin/__init__.py create mode 100644 src/python/grpcio_test/grpc_protoc_plugin/alpha_python_plugin_test.py create mode 100644 src/python/grpcio_test/grpc_protoc_plugin/beta_python_plugin_test.py create mode 100644 src/python/grpcio_test/grpc_protoc_plugin/python_plugin_test.py create mode 100644 src/python/grpcio_test/grpc_protoc_plugin/test.proto create mode 100644 src/python/grpcio_test/grpc_test/__init__.py create mode 100644 src/python/grpcio_test/grpc_test/_adapter/.gitignore create mode 100644 src/python/grpcio_test/grpc_test/_adapter/__init__.py create mode 100644 src/python/grpcio_test/grpc_test/_adapter/_blocking_invocation_inline_service_test.py create mode 100644 src/python/grpcio_test/grpc_test/_adapter/_c_test.py create mode 100644 src/python/grpcio_test/grpc_test/_adapter/_event_invocation_synchronous_event_service_test.py create mode 100644 src/python/grpcio_test/grpc_test/_adapter/_face_test_case.py create mode 100644 src/python/grpcio_test/grpc_test/_adapter/_future_invocation_asynchronous_event_service_test.py create mode 100644 src/python/grpcio_test/grpc_test/_adapter/_intermediary_low_test.py create mode 100644 src/python/grpcio_test/grpc_test/_adapter/_links_test.py create mode 100644 src/python/grpcio_test/grpc_test/_adapter/_lonely_rear_link_test.py create mode 100644 src/python/grpcio_test/grpc_test/_adapter/_low_test.py create mode 100644 src/python/grpcio_test/grpc_test/_adapter/_proto_scenarios.py create mode 100644 src/python/grpcio_test/grpc_test/_adapter/_test_links.py create mode 100644 src/python/grpcio_test/grpc_test/_core_over_links_base_interface_test.py create mode 100644 src/python/grpcio_test/grpc_test/_crust_over_core_over_links_face_interface_test.py create mode 100644 src/python/grpcio_test/grpc_test/_cython/.gitignore create mode 100644 src/python/grpcio_test/grpc_test/_cython/__init__.py create mode 100644 src/python/grpcio_test/grpc_test/_cython/adapter_low_test.py create mode 100644 src/python/grpcio_test/grpc_test/_cython/cygrpc_test.py create mode 100644 src/python/grpcio_test/grpc_test/_cython/test_utilities.py create mode 100644 src/python/grpcio_test/grpc_test/_junkdrawer/__init__.py create mode 100644 src/python/grpcio_test/grpc_test/_junkdrawer/math_pb2.py create mode 100644 src/python/grpcio_test/grpc_test/_junkdrawer/stock_pb2.py create mode 100644 src/python/grpcio_test/grpc_test/_links/__init__.py create mode 100644 src/python/grpcio_test/grpc_test/_links/_lonely_invocation_link_test.py create mode 100644 src/python/grpcio_test/grpc_test/_links/_proto_scenarios.py create mode 100644 src/python/grpcio_test/grpc_test/_links/_transmission_test.py create mode 100644 src/python/grpcio_test/grpc_test/beta/__init__.py create mode 100644 src/python/grpcio_test/grpc_test/beta/_beta_features_test.py create mode 100644 src/python/grpcio_test/grpc_test/beta/_connectivity_channel_test.py create mode 100644 src/python/grpcio_test/grpc_test/beta/_face_interface_test.py create mode 100644 src/python/grpcio_test/grpc_test/beta/_not_found_test.py create mode 100644 src/python/grpcio_test/grpc_test/beta/_utilities_test.py create mode 100644 src/python/grpcio_test/grpc_test/beta/test_utilities.py create mode 100644 src/python/grpcio_test/grpc_test/credentials/README create mode 100755 src/python/grpcio_test/grpc_test/credentials/ca.pem create mode 100755 src/python/grpcio_test/grpc_test/credentials/server1.key create mode 100755 src/python/grpcio_test/grpc_test/credentials/server1.pem create mode 100644 src/python/grpcio_test/grpc_test/early_adopter/__init__.py create mode 100644 src/python/grpcio_test/grpc_test/early_adopter/implementations_test.py create mode 100644 src/python/grpcio_test/grpc_test/framework/__init__.py create mode 100644 src/python/grpcio_test/grpc_test/framework/_crust_over_core_face_interface_test.py create mode 100644 src/python/grpcio_test/grpc_test/framework/base/__init__.py create mode 100644 src/python/grpcio_test/grpc_test/framework/base/implementations_test.py create mode 100644 src/python/grpcio_test/grpc_test/framework/base/interfaces_test_case.py create mode 100644 src/python/grpcio_test/grpc_test/framework/common/__init__.py create mode 100644 src/python/grpcio_test/grpc_test/framework/common/test_constants.py create mode 100644 src/python/grpcio_test/grpc_test/framework/common/test_control.py create mode 100644 src/python/grpcio_test/grpc_test/framework/common/test_coverage.py create mode 100644 src/python/grpcio_test/grpc_test/framework/core/__init__.py create mode 100644 src/python/grpcio_test/grpc_test/framework/core/_base_interface_test.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/__init__.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/_test_case.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/blocking_invocation_inline_service_test.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/event_invocation_synchronous_event_service_test.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/future_invocation_asynchronous_event_service_test.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/testing/__init__.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/testing/base_util.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/testing/blocking_invocation_inline_service_test_case.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/testing/callback.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/testing/control.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/testing/coverage.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/testing/digest.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/testing/event_invocation_synchronous_event_service_test_case.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/testing/future_invocation_asynchronous_event_service_test_case.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/testing/interfaces.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/testing/serial.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/testing/service.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/testing/stock_service.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/testing/test_case.py create mode 100644 src/python/grpcio_test/grpc_test/framework/foundation/__init__.py create mode 100644 src/python/grpcio_test/grpc_test/framework/foundation/_later_test.py create mode 100644 src/python/grpcio_test/grpc_test/framework/foundation/_logging_pool_test.py create mode 100644 src/python/grpcio_test/grpc_test/framework/foundation/stream_testing.py create mode 100644 src/python/grpcio_test/grpc_test/framework/interfaces/__init__.py create mode 100644 src/python/grpcio_test/grpc_test/framework/interfaces/base/__init__.py create mode 100644 src/python/grpcio_test/grpc_test/framework/interfaces/base/_control.py create mode 100644 src/python/grpcio_test/grpc_test/framework/interfaces/base/_sequence.py create mode 100644 src/python/grpcio_test/grpc_test/framework/interfaces/base/_state.py create mode 100644 src/python/grpcio_test/grpc_test/framework/interfaces/base/test_cases.py create mode 100644 src/python/grpcio_test/grpc_test/framework/interfaces/base/test_interfaces.py create mode 100644 src/python/grpcio_test/grpc_test/framework/interfaces/face/_3069_test_constant.py create mode 100644 src/python/grpcio_test/grpc_test/framework/interfaces/face/__init__.py create mode 100644 src/python/grpcio_test/grpc_test/framework/interfaces/face/_blocking_invocation_inline_service.py create mode 100644 src/python/grpcio_test/grpc_test/framework/interfaces/face/_digest.py create mode 100644 src/python/grpcio_test/grpc_test/framework/interfaces/face/_event_invocation_synchronous_event_service.py create mode 100644 src/python/grpcio_test/grpc_test/framework/interfaces/face/_future_invocation_asynchronous_event_service.py create mode 100644 src/python/grpcio_test/grpc_test/framework/interfaces/face/_invocation.py create mode 100644 src/python/grpcio_test/grpc_test/framework/interfaces/face/_receiver.py create mode 100644 src/python/grpcio_test/grpc_test/framework/interfaces/face/_service.py create mode 100644 src/python/grpcio_test/grpc_test/framework/interfaces/face/_stock_service.py create mode 100644 src/python/grpcio_test/grpc_test/framework/interfaces/face/test_cases.py create mode 100644 src/python/grpcio_test/grpc_test/framework/interfaces/face/test_interfaces.py create mode 100644 src/python/grpcio_test/grpc_test/framework/interfaces/links/__init__.py create mode 100644 src/python/grpcio_test/grpc_test/framework/interfaces/links/test_cases.py create mode 100644 src/python/grpcio_test/grpc_test/framework/interfaces/links/test_utilities.py create mode 100644 src/python/grpcio_test/grpc_test/resources.py create mode 100644 src/python/grpcio_test/grpc_test/test_common.py create mode 100644 src/python/grpcio_test/requirements.txt create mode 100644 src/python/grpcio_test/setup.cfg create mode 100644 src/python/grpcio_test/setup.py create mode 100755 src/ruby/.gitignore create mode 100755 src/ruby/.rspec create mode 100644 src/ruby/.rubocop.yml create mode 100644 src/ruby/.rubocop_todo.yml create mode 100644 src/ruby/CHANGELOG.md create mode 100755 src/ruby/Gemfile create mode 100644 src/ruby/README.md create mode 100755 src/ruby/Rakefile create mode 100644 src/ruby/bin/apis/google/protobuf/empty.rb create mode 100755 src/ruby/bin/apis/pubsub_demo.rb create mode 100644 src/ruby/bin/apis/tech/pubsub/proto/pubsub.rb create mode 100644 src/ruby/bin/apis/tech/pubsub/proto/pubsub_services.rb create mode 100755 src/ruby/bin/grpc_ruby_interop_client create mode 100755 src/ruby/bin/grpc_ruby_interop_server create mode 100755 src/ruby/bin/interop/interop_client.rb create mode 100755 src/ruby/bin/interop/interop_server.rb create mode 100755 src/ruby/bin/math.proto create mode 100755 src/ruby/bin/math.rb create mode 100755 src/ruby/bin/math_client.rb create mode 100755 src/ruby/bin/math_server.rb create mode 100755 src/ruby/bin/math_services.rb create mode 100755 src/ruby/bin/noproto_client.rb create mode 100755 src/ruby/bin/noproto_server.rb create mode 100644 src/ruby/ext/grpc/extconf.rb create mode 100644 src/ruby/ext/grpc/rb_byte_buffer.c create mode 100644 src/ruby/ext/grpc/rb_byte_buffer.h create mode 100644 src/ruby/ext/grpc/rb_call.c create mode 100644 src/ruby/ext/grpc/rb_call.h create mode 100644 src/ruby/ext/grpc/rb_channel.c create mode 100644 src/ruby/ext/grpc/rb_channel.h create mode 100644 src/ruby/ext/grpc/rb_channel_args.c create mode 100644 src/ruby/ext/grpc/rb_channel_args.h create mode 100644 src/ruby/ext/grpc/rb_completion_queue.c create mode 100644 src/ruby/ext/grpc/rb_completion_queue.h create mode 100644 src/ruby/ext/grpc/rb_credentials.c create mode 100644 src/ruby/ext/grpc/rb_credentials.h create mode 100644 src/ruby/ext/grpc/rb_grpc.c create mode 100644 src/ruby/ext/grpc/rb_grpc.h create mode 100644 src/ruby/ext/grpc/rb_server.c create mode 100644 src/ruby/ext/grpc/rb_server.h create mode 100644 src/ruby/ext/grpc/rb_server_credentials.c create mode 100644 src/ruby/ext/grpc/rb_server_credentials.h create mode 100755 src/ruby/grpc.gemspec create mode 100644 src/ruby/lib/grpc.rb create mode 100644 src/ruby/lib/grpc/core/time_consts.rb create mode 100644 src/ruby/lib/grpc/errors.rb create mode 100644 src/ruby/lib/grpc/generic/active_call.rb create mode 100644 src/ruby/lib/grpc/generic/bidi_call.rb create mode 100644 src/ruby/lib/grpc/generic/client_stub.rb create mode 100644 src/ruby/lib/grpc/generic/rpc_desc.rb create mode 100644 src/ruby/lib/grpc/generic/rpc_server.rb create mode 100644 src/ruby/lib/grpc/generic/service.rb create mode 100644 src/ruby/lib/grpc/logconfig.rb create mode 100644 src/ruby/lib/grpc/notifier.rb create mode 100644 src/ruby/lib/grpc/version.rb create mode 100644 src/ruby/pb/README.md create mode 100644 src/ruby/pb/grpc/health/checker.rb create mode 100644 src/ruby/pb/grpc/health/v1alpha/health.proto create mode 100644 src/ruby/pb/grpc/health/v1alpha/health.rb create mode 100644 src/ruby/pb/grpc/health/v1alpha/health_services.rb create mode 100755 src/ruby/pb/test/client.rb create mode 100644 src/ruby/pb/test/proto/empty.rb create mode 100644 src/ruby/pb/test/proto/messages.rb create mode 100644 src/ruby/pb/test/proto/test.rb create mode 100644 src/ruby/pb/test/proto/test_services.rb create mode 100755 src/ruby/pb/test/server.rb create mode 100644 src/ruby/spec/call_spec.rb create mode 100644 src/ruby/spec/channel_spec.rb create mode 100644 src/ruby/spec/client_server_spec.rb create mode 100644 src/ruby/spec/completion_queue_spec.rb create mode 100644 src/ruby/spec/credentials_spec.rb create mode 100644 src/ruby/spec/generic/active_call_spec.rb create mode 100644 src/ruby/spec/generic/client_stub_spec.rb create mode 100644 src/ruby/spec/generic/rpc_desc_spec.rb create mode 100644 src/ruby/spec/generic/rpc_server_pool_spec.rb create mode 100644 src/ruby/spec/generic/rpc_server_spec.rb create mode 100644 src/ruby/spec/generic/service_spec.rb create mode 100644 src/ruby/spec/pb/health/checker_spec.rb create mode 100644 src/ruby/spec/server_credentials_spec.rb create mode 100644 src/ruby/spec/server_spec.rb create mode 100644 src/ruby/spec/spec_helper.rb create mode 100644 src/ruby/spec/testdata/README create mode 100755 src/ruby/spec/testdata/ca.pem create mode 100755 src/ruby/spec/testdata/server1.key create mode 100755 src/ruby/spec/testdata/server1.pem create mode 100644 src/ruby/spec/time_consts_spec.rb create mode 100644 templates/BUILD.template create mode 100644 templates/Makefile.template create mode 100644 templates/README.md create mode 100644 templates/gRPC.podspec.template create mode 100644 templates/src/core/surface/version.c.template create mode 100644 templates/tools/doxygen/Doxyfile.c++.internal.template create mode 100644 templates/tools/doxygen/Doxyfile.c++.template create mode 100644 templates/tools/doxygen/Doxyfile.core.internal.template create mode 100644 templates/tools/doxygen/Doxyfile.core.template create mode 100644 templates/tools/doxygen/Doxyfile.include create mode 100644 templates/tools/run_tests/sources_and_headers.json.template create mode 100644 templates/tools/run_tests/tests.json.template create mode 100644 templates/vsprojects/build_test_protos.sh create mode 100644 templates/vsprojects/buildtests_c.sln.template create mode 100644 templates/vsprojects/cpptest.props.template create mode 100755 templates/vsprojects/generate_debug_projects.sh create mode 100644 templates/vsprojects/global.props.template create mode 100644 templates/vsprojects/grpc++_unsecure/grpc++_unsecure.vcxproj.filters.template create mode 100644 templates/vsprojects/grpc++_unsecure/grpc++_unsecure.vcxproj.template create mode 100644 templates/vsprojects/grpc.sln.template create mode 100644 templates/vsprojects/grpc_cpp_plugin/grpc_cpp_plugin.vcxproj.template create mode 100644 templates/vsprojects/grpc_csharp_ext.sln.template create mode 100644 templates/vsprojects/grpc_csharp_plugin/grpc_csharp_plugin.vcxproj.template create mode 100644 templates/vsprojects/grpc_objective_c_plugin/grpc_objective_c_plugin.vcxproj.template create mode 100644 templates/vsprojects/grpc_protoc_plugins.sln.template create mode 100644 templates/vsprojects/grpc_python_plugin/grpc_python_plugin.vcxproj.template create mode 100644 templates/vsprojects/grpc_ruby_plugin/grpc_ruby_plugin.vcxproj.template create mode 100644 templates/vsprojects/openssl.props.template create mode 100644 templates/vsprojects/packages.include create mode 100644 templates/vsprojects/protobuf.props.template create mode 100644 templates/vsprojects/protoc.props.template create mode 100644 templates/vsprojects/sln_defs.include create mode 100644 templates/vsprojects/vcxproj.filters_defs.include create mode 100644 templates/vsprojects/vcxproj.template create mode 100644 templates/vsprojects/vcxproj_defs.include create mode 100644 templates/vsprojects/winsock.props.template create mode 100644 templates/vsprojects/zlib-dll.props.template create mode 100644 templates/vsprojects/zlib.props.template create mode 100644 test/build/c++11.cc create mode 100644 test/build/openssl-alpn.c create mode 100644 test/build/openssl-npn.c create mode 100644 test/build/perftools.c create mode 100644 test/build/protobuf.cc create mode 100644 test/build/systemtap.c create mode 100644 test/build/zlib.c create mode 100644 test/build/zookeeper.c create mode 100644 test/core/bad_client/bad_client.c create mode 100644 test/core/bad_client/bad_client.h create mode 100755 test/core/bad_client/gen_build_yaml.py create mode 100644 test/core/bad_client/tests/connection_prefix.c create mode 100644 test/core/bad_client/tests/initial_settings_frame.c create mode 100644 test/core/channel/channel_args_test.c create mode 100644 test/core/channel/channel_stack_test.c create mode 100644 test/core/client_config/uri_parser_test.c create mode 100644 test/core/compression/compression_test.c create mode 100644 test/core/compression/message_compress_test.c create mode 100644 test/core/end2end/README create mode 100644 test/core/end2end/cq_verifier.c create mode 100644 test/core/end2end/cq_verifier.h create mode 100644 test/core/end2end/data/server1_cert.c create mode 100644 test/core/end2end/data/server1_key.c create mode 100644 test/core/end2end/data/ssl_test_data.h create mode 100644 test/core/end2end/data/test_root_cert.c create mode 100644 test/core/end2end/dualstack_socket_test.c create mode 100644 test/core/end2end/end2end_tests.h create mode 100644 test/core/end2end/fixtures/h2_compress.c create mode 100644 test/core/end2end/fixtures/h2_fakesec.c create mode 100644 test/core/end2end/fixtures/h2_full+poll.c create mode 100644 test/core/end2end/fixtures/h2_full.c create mode 100644 test/core/end2end/fixtures/h2_oauth2.c create mode 100644 test/core/end2end/fixtures/h2_proxy.c create mode 100644 test/core/end2end/fixtures/h2_sockpair+trace.c create mode 100644 test/core/end2end/fixtures/h2_sockpair.c create mode 100644 test/core/end2end/fixtures/h2_sockpair_1byte.c create mode 100644 test/core/end2end/fixtures/h2_ssl+poll.c create mode 100644 test/core/end2end/fixtures/h2_ssl.c create mode 100644 test/core/end2end/fixtures/h2_ssl_proxy.c create mode 100644 test/core/end2end/fixtures/h2_uds+poll.c create mode 100644 test/core/end2end/fixtures/h2_uds.c create mode 100644 test/core/end2end/fixtures/proxy.c create mode 100644 test/core/end2end/fixtures/proxy.h create mode 100755 test/core/end2end/gen_build_yaml.py create mode 100644 test/core/end2end/multiple_server_queues_test.c create mode 100644 test/core/end2end/no_server_test.c create mode 100644 test/core/end2end/tests/bad_hostname.c create mode 100644 test/core/end2end/tests/binary_metadata.c create mode 100644 test/core/end2end/tests/call_creds.c create mode 100644 test/core/end2end/tests/cancel_after_accept.c create mode 100644 test/core/end2end/tests/cancel_after_client_done.c create mode 100644 test/core/end2end/tests/cancel_after_invoke.c create mode 100644 test/core/end2end/tests/cancel_before_invoke.c create mode 100644 test/core/end2end/tests/cancel_in_a_vacuum.c create mode 100644 test/core/end2end/tests/cancel_test_helpers.h create mode 100644 test/core/end2end/tests/census_simple_request.c create mode 100644 test/core/end2end/tests/channel_connectivity.c create mode 100644 test/core/end2end/tests/compressed_payload.c create mode 100644 test/core/end2end/tests/default_host.c create mode 100644 test/core/end2end/tests/disappearing_server.c create mode 100644 test/core/end2end/tests/empty_batch.c create mode 100644 test/core/end2end/tests/graceful_server_shutdown.c create mode 100644 test/core/end2end/tests/high_initial_seqno.c create mode 100644 test/core/end2end/tests/invoke_large_request.c create mode 100644 test/core/end2end/tests/large_metadata.c create mode 100644 test/core/end2end/tests/max_concurrent_streams.c create mode 100644 test/core/end2end/tests/max_message_length.c create mode 100644 test/core/end2end/tests/metadata.c create mode 100644 test/core/end2end/tests/no_op.c create mode 100644 test/core/end2end/tests/payload.c create mode 100644 test/core/end2end/tests/ping_pong_streaming.c create mode 100644 test/core/end2end/tests/registered_call.c create mode 100644 test/core/end2end/tests/request_with_flags.c create mode 100644 test/core/end2end/tests/request_with_payload.c create mode 100644 test/core/end2end/tests/server_finishes_request.c create mode 100644 test/core/end2end/tests/shutdown_finishes_calls.c create mode 100644 test/core/end2end/tests/shutdown_finishes_tags.c create mode 100644 test/core/end2end/tests/simple_delayed_request.c create mode 100644 test/core/end2end/tests/simple_request.c create mode 100644 test/core/end2end/tests/trailing_metadata.c create mode 100644 test/core/fling/client.c create mode 100644 test/core/fling/fling_stream_test.c create mode 100644 test/core/fling/fling_test.c create mode 100644 test/core/fling/server.c create mode 100644 test/core/httpcli/format_request_test.c create mode 100644 test/core/httpcli/httpcli_test.c create mode 100644 test/core/httpcli/parser_test.c create mode 100755 test/core/httpcli/test_server.py create mode 100644 test/core/iomgr/alarm_heap_test.c create mode 100644 test/core/iomgr/alarm_list_test.c create mode 100644 test/core/iomgr/alarm_test.c create mode 100644 test/core/iomgr/endpoint_pair_test.c create mode 100644 test/core/iomgr/endpoint_tests.c create mode 100644 test/core/iomgr/endpoint_tests.h create mode 100644 test/core/iomgr/fd_conservation_posix_test.c create mode 100644 test/core/iomgr/fd_posix_test.c create mode 100644 test/core/iomgr/resolve_address_test.c create mode 100644 test/core/iomgr/sockaddr_utils_test.c create mode 100644 test/core/iomgr/tcp_client_posix_test.c create mode 100644 test/core/iomgr/tcp_posix_test.c create mode 100644 test/core/iomgr/tcp_server_posix_test.c create mode 100644 test/core/iomgr/time_averaged_stats_test.c create mode 100644 test/core/iomgr/udp_server_test.c create mode 100644 test/core/json/json_rewrite.c create mode 100644 test/core/json/json_rewrite_test.c create mode 100644 test/core/json/json_test.c create mode 100644 test/core/json/rewrite_test_input.json create mode 100644 test/core/json/rewrite_test_output_condensed.json create mode 100644 test/core/json/rewrite_test_output_indented.json create mode 100644 test/core/network_benchmarks/low_level_ping_pong.c create mode 100644 test/core/profiling/mark_timings.stp create mode 100644 test/core/profiling/timers_test.c create mode 100644 test/core/security/auth_context_test.c create mode 100644 test/core/security/base64_test.c create mode 100644 test/core/security/create_jwt.c create mode 100644 test/core/security/credentials_test.c create mode 100644 test/core/security/fetch_oauth2.c create mode 100644 test/core/security/json_token_test.c create mode 100644 test/core/security/jwt_verifier_test.c create mode 100644 test/core/security/oauth2_utils.c create mode 100644 test/core/security/oauth2_utils.h create mode 100644 test/core/security/print_google_default_creds_token.c create mode 100644 test/core/security/secure_endpoint_test.c create mode 100644 test/core/security/security_connector_test.c create mode 100644 test/core/security/verify_jwt.c create mode 100644 test/core/statistics/census_log_tests.c create mode 100644 test/core/statistics/census_log_tests.h create mode 100644 test/core/statistics/census_stub_test.c create mode 100644 test/core/statistics/hash_table_test.c create mode 100644 test/core/statistics/multiple_writers_circular_buffer_test.c create mode 100644 test/core/statistics/multiple_writers_test.c create mode 100644 test/core/statistics/performance_test.c create mode 100644 test/core/statistics/quick_test.c create mode 100644 test/core/statistics/rpc_stats_test.c create mode 100644 test/core/statistics/small_log_test.c create mode 100644 test/core/statistics/trace_test.c create mode 100644 test/core/statistics/window_stats_test.c create mode 100644 test/core/support/cmdline_test.c create mode 100644 test/core/support/env_test.c create mode 100644 test/core/support/file_test.c create mode 100644 test/core/support/histogram_test.c create mode 100644 test/core/support/host_port_test.c create mode 100644 test/core/support/log_test.c create mode 100644 test/core/support/murmur_hash_test.c create mode 100644 test/core/support/slice_buffer_test.c create mode 100644 test/core/support/slice_test.c create mode 100644 test/core/support/stack_lockfree_test.c create mode 100644 test/core/support/string_test.c create mode 100644 test/core/support/sync_test.c create mode 100644 test/core/support/thd_test.c create mode 100644 test/core/support/time_test.c create mode 100644 test/core/support/tls_test.c create mode 100644 test/core/support/useful_test.c create mode 100644 test/core/surface/byte_buffer_reader_test.c create mode 100644 test/core/surface/completion_queue_test.c create mode 100644 test/core/surface/lame_client_test.c create mode 100644 test/core/surface/multi_init_test.c create mode 100644 test/core/transport/chttp2/alpn_test.c create mode 100644 test/core/transport/chttp2/bin_encoder_test.c create mode 100644 test/core/transport/chttp2/hpack_parser_test.c create mode 100644 test/core/transport/chttp2/hpack_table_test.c create mode 100644 test/core/transport/chttp2/status_conversion_test.c create mode 100644 test/core/transport/chttp2/stream_encoder_test.c create mode 100644 test/core/transport/chttp2/stream_map_test.c create mode 100644 test/core/transport/chttp2/timeout_encoding_test.c create mode 100644 test/core/transport/metadata_test.c create mode 100644 test/core/transport/stream_op_test.c create mode 100644 test/core/tsi/transport_security_test.c create mode 100644 test/core/util/grpc_profiler.c create mode 100644 test/core/util/grpc_profiler.h create mode 100644 test/core/util/parse_hexstring.c create mode 100644 test/core/util/parse_hexstring.h create mode 100644 test/core/util/port.h create mode 100644 test/core/util/port_posix.c create mode 100644 test/core/util/port_windows.c create mode 100644 test/core/util/reconnect_server.c create mode 100644 test/core/util/reconnect_server.h create mode 100644 test/core/util/slice_splitter.c create mode 100644 test/core/util/slice_splitter.h create mode 100644 test/core/util/test_config.c create mode 100644 test/core/util/test_config.h create mode 100644 test/cpp/client/channel_arguments_test.cc create mode 100644 test/cpp/client/credentials_test.cc create mode 100644 test/cpp/common/auth_property_iterator_test.cc create mode 100644 test/cpp/common/secure_auth_context_test.cc create mode 100644 test/cpp/end2end/async_end2end_test.cc create mode 100644 test/cpp/end2end/client_crash_test.cc create mode 100644 test/cpp/end2end/client_crash_test_server.cc create mode 100644 test/cpp/end2end/end2end_test.cc create mode 100644 test/cpp/end2end/generic_end2end_test.cc create mode 100644 test/cpp/end2end/mock_test.cc create mode 100644 test/cpp/end2end/server_crash_test.cc create mode 100644 test/cpp/end2end/server_crash_test_client.cc create mode 100644 test/cpp/end2end/shutdown_test.cc create mode 100644 test/cpp/end2end/streaming_throughput_test.cc create mode 100644 test/cpp/end2end/thread_stress_test.cc create mode 100644 test/cpp/end2end/zookeeper_test.cc create mode 100644 test/cpp/interop/client.cc create mode 100644 test/cpp/interop/client_helper.cc create mode 100644 test/cpp/interop/client_helper.h create mode 100644 test/cpp/interop/interop_client.cc create mode 100644 test/cpp/interop/interop_client.h create mode 100644 test/cpp/interop/interop_test.cc create mode 100644 test/cpp/interop/reconnect_interop_client.cc create mode 100644 test/cpp/interop/reconnect_interop_server.cc create mode 100644 test/cpp/interop/rnd.dat create mode 100644 test/cpp/interop/server.cc create mode 100644 test/cpp/interop/server_helper.cc create mode 100644 test/cpp/interop/server_helper.h create mode 100644 test/cpp/qps/async_streaming_ping_pong_test.cc create mode 100644 test/cpp/qps/async_unary_ping_pong_test.cc create mode 100644 test/cpp/qps/client.h create mode 100644 test/cpp/qps/client_async.cc create mode 100644 test/cpp/qps/client_sync.cc create mode 100644 test/cpp/qps/driver.cc create mode 100644 test/cpp/qps/driver.h create mode 100644 test/cpp/qps/histogram.h create mode 100644 test/cpp/qps/interarrival.h create mode 100644 test/cpp/qps/perf_db.proto create mode 100644 test/cpp/qps/perf_db_client.cc create mode 100644 test/cpp/qps/perf_db_client.h create mode 100755 test/cpp/qps/qps-sweep.sh create mode 100644 test/cpp/qps/qps_driver.cc create mode 100644 test/cpp/qps/qps_interarrival_test.cc create mode 100644 test/cpp/qps/qps_openloop_test.cc create mode 100644 test/cpp/qps/qps_test.cc create mode 100644 test/cpp/qps/qps_test_with_poll.cc create mode 100644 test/cpp/qps/qps_worker.cc create mode 100644 test/cpp/qps/qps_worker.h create mode 100644 test/cpp/qps/qpstest.proto create mode 100644 test/cpp/qps/report.cc create mode 100644 test/cpp/qps/report.h create mode 100644 test/cpp/qps/server.h create mode 100644 test/cpp/qps/server_async.cc create mode 100644 test/cpp/qps/server_sync.cc create mode 100755 test/cpp/qps/single_run_localhost.sh create mode 100644 test/cpp/qps/stats.h create mode 100644 test/cpp/qps/sync_streaming_ping_pong_test.cc create mode 100644 test/cpp/qps/sync_unary_ping_pong_test.cc create mode 100644 test/cpp/qps/timer.cc create mode 100644 test/cpp/qps/timer.h create mode 100644 test/cpp/qps/worker.cc create mode 100644 test/cpp/util/benchmark_config.cc create mode 100644 test/cpp/util/benchmark_config.h create mode 100644 test/cpp/util/byte_buffer_test.cc create mode 100644 test/cpp/util/cli_call.cc create mode 100644 test/cpp/util/cli_call.h create mode 100644 test/cpp/util/cli_call_test.cc create mode 100644 test/cpp/util/create_test_channel.cc create mode 100644 test/cpp/util/create_test_channel.h create mode 100644 test/cpp/util/echo.proto create mode 100644 test/cpp/util/echo_duplicate.proto create mode 100644 test/cpp/util/grpc_cli.cc create mode 100644 test/cpp/util/messages.proto create mode 100644 test/cpp/util/slice_test.cc create mode 100644 test/cpp/util/status_test.cc create mode 100644 test/cpp/util/string_ref_helper.cc create mode 100644 test/cpp/util/string_ref_helper.h create mode 100644 test/cpp/util/string_ref_test.cc create mode 100644 test/cpp/util/subprocess.cc create mode 100644 test/cpp/util/subprocess.h create mode 100644 test/cpp/util/test_config.cc create mode 100644 test/cpp/util/test_config.h create mode 100644 test/cpp/util/time_test.cc create mode 100644 test/proto/empty.proto create mode 100644 test/proto/messages.proto create mode 100644 test/proto/test.proto create mode 100644 tools/README.md create mode 100755 tools/buildgen/build-cleaner.py create mode 100755 tools/buildgen/bunch.py create mode 100644 tools/buildgen/generate_build_additions.sh create mode 100644 tools/buildgen/generate_projects-old.sh create mode 100755 tools/buildgen/generate_projects.py create mode 100755 tools/buildgen/generate_projects.sh create mode 100755 tools/buildgen/mako_renderer.py create mode 100755 tools/buildgen/plugins/expand_bin_attrs.py create mode 100755 tools/buildgen/plugins/expand_filegroups.py create mode 100755 tools/buildgen/plugins/generate_vsprojects.py create mode 100755 tools/buildgen/plugins/list_protos.py create mode 100644 tools/codegen/core/gen_hpack_tables.c create mode 100644 tools/codegen/core/gen_legal_metadata_characters.c create mode 100755 tools/distrib/check_copyright.py create mode 100755 tools/distrib/guard_headers.sh create mode 100644 tools/distrib/python/.gitignore create mode 100755 tools/distrib/python/docgen.py create mode 100755 tools/distrib/python/submit.py create mode 100644 tools/dockerfile/grpc_base/Dockerfile create mode 100644 tools/dockerfile/grpc_base/README.md create mode 100644 tools/dockerfile/grpc_build_deb/Dockerfile create mode 100644 tools/dockerfile/grpc_build_deb/version.txt create mode 100644 tools/dockerfile/grpc_clang/Dockerfile create mode 100644 tools/dockerfile/grpc_csharp_mono/Dockerfile create mode 100755 tools/dockerfile/grpc_csharp_mono/build.sh create mode 100644 tools/dockerfile/grpc_csharp_mono_base/Dockerfile create mode 100644 tools/dockerfile/grpc_cxx/Dockerfile create mode 100755 tools/dockerfile/grpc_cxx/build.sh create mode 100644 tools/dockerfile/grpc_dist_proto/Dockerfile create mode 100644 tools/dockerfile/grpc_dist_proto/version.txt create mode 100644 tools/dockerfile/grpc_go/Dockerfile create mode 100644 tools/dockerfile/grpc_go/README.md create mode 100755 tools/dockerfile/grpc_go/build.sh create mode 100644 tools/dockerfile/grpc_java/Dockerfile create mode 100644 tools/dockerfile/grpc_java/README.md create mode 100755 tools/dockerfile/grpc_java/build.sh create mode 100644 tools/dockerfile/grpc_java_android/Dockerfile create mode 100644 tools/dockerfile/grpc_java_android/README.md create mode 100644 tools/dockerfile/grpc_java_base/Dockerfile create mode 100644 tools/dockerfile/grpc_java_base/README.md create mode 100644 tools/dockerfile/grpc_node/Dockerfile create mode 100755 tools/dockerfile/grpc_node/build.sh create mode 100644 tools/dockerfile/grpc_node_base/Dockerfile create mode 100644 tools/dockerfile/grpc_php/Dockerfile create mode 100644 tools/dockerfile/grpc_php/README.md create mode 100755 tools/dockerfile/grpc_php/build.sh create mode 100644 tools/dockerfile/grpc_php_base/Dockerfile create mode 100644 tools/dockerfile/grpc_php_base/README.md create mode 100644 tools/dockerfile/grpc_python/Dockerfile create mode 100644 tools/dockerfile/grpc_python/README.md create mode 100644 tools/dockerfile/grpc_python_base/Dockerfile create mode 100644 tools/dockerfile/grpc_python_base/README.md create mode 100644 tools/dockerfile/grpc_ruby/Dockerfile create mode 100644 tools/dockerfile/grpc_ruby/README.md create mode 100755 tools/dockerfile/grpc_ruby/build.sh create mode 100644 tools/dockerfile/grpc_ruby_base/Dockerfile create mode 100644 tools/dockerfile/grpc_ruby_base/README.md create mode 100644 tools/dockerfile/grpc_ruby_deb/Dockerfile create mode 100644 tools/dockerfile/grpc_ruby_deb/README.md create mode 100644 tools/dockerfile/grpc_scan_build/Dockerfile create mode 100644 tools/doxygen/Doxyfile.c++ create mode 100644 tools/doxygen/Doxyfile.c++.internal create mode 100644 tools/doxygen/Doxyfile.core create mode 100644 tools/doxygen/Doxyfile.core.internal create mode 100755 tools/doxygen/run_doxygen.sh create mode 100644 tools/gce_setup/README.md create mode 100755 tools/gce_setup/build_images.sh create mode 100755 tools/gce_setup/builder.sh create mode 100755 tools/gce_setup/cloud_prod_runner.sh create mode 100755 tools/gce_setup/cloud_prod_test.sh create mode 100755 tools/gce_setup/compute_extras.sh create mode 100755 tools/gce_setup/grpc_docker.sh create mode 100755 tools/gce_setup/interop_test.sh create mode 100755 tools/gce_setup/interop_test_runner.sh create mode 100755 tools/gce_setup/new_grpc_docker_builder.sh create mode 100755 tools/gce_setup/new_grpc_docker_builder_on_startup.sh create mode 100644 tools/gce_setup/post.html create mode 100644 tools/gce_setup/pre.html create mode 100755 tools/gce_setup/private_build_and_test.sh create mode 100755 tools/gce_setup/shared_startup_funcs.sh create mode 100755 tools/jenkins/docker_run_jenkins.sh create mode 100644 tools/jenkins/grpc_jenkins_slave/Dockerfile create mode 100644 tools/jenkins/grpc_jenkins_slave_32bits/Dockerfile create mode 100644 tools/jenkins/grpc_linuxbrew/Dockerfile create mode 100755 tools/jenkins/run_distribution.sh create mode 100755 tools/jenkins/run_jenkins.sh create mode 100755 tools/profile_analyzer/profile_analyzer.py create mode 100755 tools/run_tests/antagonist.py create mode 100755 tools/run_tests/build_csharp.sh create mode 100755 tools/run_tests/build_node.sh create mode 100755 tools/run_tests/build_php.sh create mode 100755 tools/run_tests/build_python.sh create mode 100755 tools/run_tests/build_ruby.sh create mode 100755 tools/run_tests/check_sources_and_headers.py create mode 100755 tools/run_tests/jobset.py create mode 100755 tools/run_tests/port_server.py create mode 100755 tools/run_tests/prepare_travis.sh create mode 100644 tools/run_tests/run_csharp.bat create mode 100755 tools/run_tests/run_csharp.sh create mode 100755 tools/run_tests/run_interops.py create mode 100755 tools/run_tests/run_interops_build.sh create mode 100755 tools/run_tests/run_interops_test.sh create mode 100755 tools/run_tests/run_lcov.sh create mode 100755 tools/run_tests/run_node.sh create mode 100755 tools/run_tests/run_python.sh create mode 100755 tools/run_tests/run_ruby.sh create mode 100755 tools/run_tests/run_sanity.sh create mode 100755 tools/run_tests/run_tests.py create mode 100644 tools/run_tests/sources_and_headers.json create mode 100644 tools/run_tests/tests.json create mode 100755 tools/run_tests/watch_dirs.py create mode 100644 tools/tsan_suppressions.txt create mode 100644 vsprojects/.gitignore create mode 100644 vsprojects/README.md create mode 100644 vsprojects/build.bat create mode 100644 vsprojects/build_plugins.bat create mode 100644 vsprojects/buildtests_c.sln create mode 100644 vsprojects/cpptest.props create mode 100644 vsprojects/dummy.c create mode 100644 vsprojects/global.props create mode 100644 vsprojects/grpc++_unsecure/grpc++_unsecure.vcxproj create mode 100644 vsprojects/grpc++_unsecure/grpc++_unsecure.vcxproj.filters create mode 100644 vsprojects/grpc.sln create mode 100644 vsprojects/grpc_cpp_plugin/grpc_cpp_plugin.vcxproj create mode 100644 vsprojects/grpc_csharp_ext.sln create mode 100644 vsprojects/grpc_csharp_plugin/grpc_csharp_plugin.vcxproj create mode 100644 vsprojects/grpc_objective_c_plugin/grpc_objective_c_plugin.vcxproj create mode 100644 vsprojects/grpc_protoc_plugins.sln create mode 100644 vsprojects/grpc_python_plugin/grpc_python_plugin.vcxproj create mode 100644 vsprojects/grpc_ruby_plugin/grpc_ruby_plugin.vcxproj create mode 100644 vsprojects/nuget_package/.gitignore create mode 100644 vsprojects/nuget_package/README.md create mode 100644 vsprojects/nuget_package/buildall.bat create mode 100644 vsprojects/nuget_package/grpc.native.csharp_ext.nuspec create mode 100644 vsprojects/nuget_package/grpc.native.csharp_ext.props create mode 100644 vsprojects/nuget_package/grpc.native.csharp_ext.targets create mode 100644 vsprojects/openssl.props create mode 100644 vsprojects/protobuf.props create mode 100644 vsprojects/protoc.props create mode 100644 vsprojects/vcxproj/gen_hpack_tables/gen_hpack_tables.vcxproj create mode 100644 vsprojects/vcxproj/gen_hpack_tables/gen_hpack_tables.vcxproj.filters create mode 100644 vsprojects/vcxproj/gen_legal_metadata_characters/gen_legal_metadata_characters.vcxproj create mode 100644 vsprojects/vcxproj/gen_legal_metadata_characters/gen_legal_metadata_characters.vcxproj.filters create mode 100644 vsprojects/vcxproj/gpr/gpr.vcxproj create mode 100644 vsprojects/vcxproj/gpr/gpr.vcxproj.filters create mode 100644 vsprojects/vcxproj/gpr_test_util/gpr_test_util.vcxproj create mode 100644 vsprojects/vcxproj/gpr_test_util/gpr_test_util.vcxproj.filters create mode 100644 vsprojects/vcxproj/grpc++/grpc++.vcxproj create mode 100644 vsprojects/vcxproj/grpc++/grpc++.vcxproj.filters create mode 100644 vsprojects/vcxproj/grpc++_test_config/grpc++_test_config.vcxproj create mode 100644 vsprojects/vcxproj/grpc++_test_config/grpc++_test_config.vcxproj.filters create mode 100644 vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj create mode 100644 vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj.filters create mode 100644 vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj create mode 100644 vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj.filters create mode 100644 vsprojects/vcxproj/grpc/grpc.vcxproj create mode 100644 vsprojects/vcxproj/grpc/grpc.vcxproj.filters create mode 100644 vsprojects/vcxproj/grpc/packages.config create mode 100644 vsprojects/vcxproj/grpc_cpp_plugin/grpc_cpp_plugin.vcxproj create mode 100644 vsprojects/vcxproj/grpc_cpp_plugin/grpc_cpp_plugin.vcxproj.filters create mode 100644 vsprojects/vcxproj/grpc_create_jwt/grpc_create_jwt.vcxproj create mode 100644 vsprojects/vcxproj/grpc_create_jwt/grpc_create_jwt.vcxproj.filters create mode 100644 vsprojects/vcxproj/grpc_csharp_ext/grpc_csharp_ext.vcxproj create mode 100644 vsprojects/vcxproj/grpc_csharp_ext/grpc_csharp_ext.vcxproj.filters create mode 100644 vsprojects/vcxproj/grpc_csharp_plugin/grpc_csharp_plugin.vcxproj create mode 100644 vsprojects/vcxproj/grpc_csharp_plugin/grpc_csharp_plugin.vcxproj.filters create mode 100644 vsprojects/vcxproj/grpc_fetch_oauth2/grpc_fetch_oauth2.vcxproj create mode 100644 vsprojects/vcxproj/grpc_fetch_oauth2/grpc_fetch_oauth2.vcxproj.filters create mode 100644 vsprojects/vcxproj/grpc_objective_c_plugin/grpc_objective_c_plugin.vcxproj create mode 100644 vsprojects/vcxproj/grpc_objective_c_plugin/grpc_objective_c_plugin.vcxproj.filters create mode 100644 vsprojects/vcxproj/grpc_plugin_support/grpc_plugin_support.vcxproj create mode 100644 vsprojects/vcxproj/grpc_plugin_support/grpc_plugin_support.vcxproj.filters create mode 100644 vsprojects/vcxproj/grpc_print_google_default_creds_token/grpc_print_google_default_creds_token.vcxproj create mode 100644 vsprojects/vcxproj/grpc_print_google_default_creds_token/grpc_print_google_default_creds_token.vcxproj.filters create mode 100644 vsprojects/vcxproj/grpc_python_plugin/grpc_python_plugin.vcxproj create mode 100644 vsprojects/vcxproj/grpc_python_plugin/grpc_python_plugin.vcxproj.filters create mode 100644 vsprojects/vcxproj/grpc_ruby_plugin/grpc_ruby_plugin.vcxproj create mode 100644 vsprojects/vcxproj/grpc_ruby_plugin/grpc_ruby_plugin.vcxproj.filters create mode 100644 vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj create mode 100644 vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj.filters create mode 100644 vsprojects/vcxproj/grpc_test_util_unsecure/grpc_test_util_unsecure.vcxproj create mode 100644 vsprojects/vcxproj/grpc_test_util_unsecure/grpc_test_util_unsecure.vcxproj.filters create mode 100644 vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj create mode 100644 vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters create mode 100644 vsprojects/vcxproj/grpc_verify_jwt/grpc_verify_jwt.vcxproj create mode 100644 vsprojects/vcxproj/grpc_verify_jwt/grpc_verify_jwt.vcxproj.filters create mode 100644 vsprojects/vcxproj/interop_client_helper/interop_client_helper.vcxproj create mode 100644 vsprojects/vcxproj/interop_client_helper/interop_client_helper.vcxproj.filters create mode 100644 vsprojects/vcxproj/interop_client_main/interop_client_main.vcxproj create mode 100644 vsprojects/vcxproj/interop_client_main/interop_client_main.vcxproj.filters create mode 100644 vsprojects/vcxproj/interop_server_helper/interop_server_helper.vcxproj create mode 100644 vsprojects/vcxproj/interop_server_helper/interop_server_helper.vcxproj.filters create mode 100644 vsprojects/vcxproj/interop_server_main/interop_server_main.vcxproj create mode 100644 vsprojects/vcxproj/interop_server_main/interop_server_main.vcxproj.filters create mode 100644 vsprojects/vcxproj/qps/qps.vcxproj create mode 100644 vsprojects/vcxproj/qps/qps.vcxproj.filters create mode 100644 vsprojects/vcxproj/reconnect_server/reconnect_server.vcxproj create mode 100644 vsprojects/vcxproj/reconnect_server/reconnect_server.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/alarm_heap_test/alarm_heap_test.vcxproj create mode 100644 vsprojects/vcxproj/test/alarm_heap_test/alarm_heap_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/alarm_list_test/alarm_list_test.vcxproj create mode 100644 vsprojects/vcxproj/test/alarm_list_test/alarm_list_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/alarm_test/alarm_test.vcxproj create mode 100644 vsprojects/vcxproj/test/alarm_test/alarm_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/alpn_test/alpn_test.vcxproj create mode 100644 vsprojects/vcxproj/test/alpn_test/alpn_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/async_end2end_test/async_end2end_test.vcxproj create mode 100644 vsprojects/vcxproj/test/async_end2end_test/async_end2end_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/auth_property_iterator_test/auth_property_iterator_test.vcxproj create mode 100644 vsprojects/vcxproj/test/auth_property_iterator_test/auth_property_iterator_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/bad_client_test/bad_client_test.vcxproj create mode 100644 vsprojects/vcxproj/test/bad_client_test/bad_client_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/bin_encoder_test/bin_encoder_test.vcxproj create mode 100644 vsprojects/vcxproj/test/bin_encoder_test/bin_encoder_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/channel_arguments_test/channel_arguments_test.vcxproj create mode 100644 vsprojects/vcxproj/test/channel_arguments_test/channel_arguments_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/chttp2_status_conversion_test/chttp2_status_conversion_test.vcxproj create mode 100644 vsprojects/vcxproj/test/chttp2_status_conversion_test/chttp2_status_conversion_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/chttp2_stream_encoder_test/chttp2_stream_encoder_test.vcxproj create mode 100644 vsprojects/vcxproj/test/chttp2_stream_encoder_test/chttp2_stream_encoder_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/chttp2_stream_map_test/chttp2_stream_map_test.vcxproj create mode 100644 vsprojects/vcxproj/test/chttp2_stream_map_test/chttp2_stream_map_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/cli_call_test/cli_call_test.vcxproj create mode 100644 vsprojects/vcxproj/test/cli_call_test/cli_call_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/client_crash_test_server/client_crash_test_server.vcxproj create mode 100644 vsprojects/vcxproj/test/client_crash_test_server/client_crash_test_server.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/compression_test/compression_test.vcxproj create mode 100644 vsprojects/vcxproj/test/compression_test/compression_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/connection_prefix_bad_client_test/connection_prefix_bad_client_test.vcxproj create mode 100644 vsprojects/vcxproj/test/connection_prefix_bad_client_test/connection_prefix_bad_client_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/credentials_test/credentials_test.vcxproj create mode 100644 vsprojects/vcxproj/test/credentials_test/credentials_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/cxx_byte_buffer_test/cxx_byte_buffer_test.vcxproj create mode 100644 vsprojects/vcxproj/test/cxx_byte_buffer_test/cxx_byte_buffer_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/cxx_slice_test/cxx_slice_test.vcxproj create mode 100644 vsprojects/vcxproj/test/cxx_slice_test/cxx_slice_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/cxx_string_ref_test/cxx_string_ref_test.vcxproj create mode 100644 vsprojects/vcxproj/test/cxx_string_ref_test/cxx_string_ref_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/cxx_time_test/cxx_time_test.vcxproj create mode 100644 vsprojects/vcxproj/test/cxx_time_test/cxx_time_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_certs/end2end_certs.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_certs/end2end_certs.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_fixture_h2_compress/end2end_fixture_h2_compress.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_fixture_h2_compress/end2end_fixture_h2_compress.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_fixture_h2_fakesec/end2end_fixture_h2_fakesec.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_fixture_h2_fakesec/end2end_fixture_h2_fakesec.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_fixture_h2_full/end2end_fixture_h2_full.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_fixture_h2_full/end2end_fixture_h2_full.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_fixture_h2_oauth2/end2end_fixture_h2_oauth2.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_fixture_h2_oauth2/end2end_fixture_h2_oauth2.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_fixture_h2_proxy/end2end_fixture_h2_proxy.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_fixture_h2_proxy/end2end_fixture_h2_proxy.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_fixture_h2_sockpair+trace/end2end_fixture_h2_sockpair+trace.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_fixture_h2_sockpair+trace/end2end_fixture_h2_sockpair+trace.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_fixture_h2_sockpair/end2end_fixture_h2_sockpair.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_fixture_h2_sockpair/end2end_fixture_h2_sockpair.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_fixture_h2_sockpair_1byte/end2end_fixture_h2_sockpair_1byte.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_fixture_h2_sockpair_1byte/end2end_fixture_h2_sockpair_1byte.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_fixture_h2_ssl/end2end_fixture_h2_ssl.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_fixture_h2_ssl/end2end_fixture_h2_ssl.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_fixture_h2_ssl_proxy/end2end_fixture_h2_ssl_proxy.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_fixture_h2_ssl_proxy/end2end_fixture_h2_ssl_proxy.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_test/end2end_test.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_test/end2end_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_test_bad_hostname/end2end_test_bad_hostname.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_test_bad_hostname/end2end_test_bad_hostname.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_test_binary_metadata/end2end_test_binary_metadata.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_test_binary_metadata/end2end_test_binary_metadata.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_test_call_creds/end2end_test_call_creds.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_test_call_creds/end2end_test_call_creds.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_test_cancel_after_accept/end2end_test_cancel_after_accept.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_test_cancel_after_accept/end2end_test_cancel_after_accept.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_test_cancel_after_client_done/end2end_test_cancel_after_client_done.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_test_cancel_after_client_done/end2end_test_cancel_after_client_done.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_test_cancel_after_invoke/end2end_test_cancel_after_invoke.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_test_cancel_after_invoke/end2end_test_cancel_after_invoke.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_test_cancel_before_invoke/end2end_test_cancel_before_invoke.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_test_cancel_before_invoke/end2end_test_cancel_before_invoke.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_test_cancel_in_a_vacuum/end2end_test_cancel_in_a_vacuum.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_test_cancel_in_a_vacuum/end2end_test_cancel_in_a_vacuum.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_test_census_simple_request/end2end_test_census_simple_request.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_test_census_simple_request/end2end_test_census_simple_request.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_test_channel_connectivity/end2end_test_channel_connectivity.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_test_channel_connectivity/end2end_test_channel_connectivity.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_test_compressed_payload/end2end_test_compressed_payload.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_test_compressed_payload/end2end_test_compressed_payload.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_test_default_host/end2end_test_default_host.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_test_default_host/end2end_test_default_host.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_test_disappearing_server/end2end_test_disappearing_server.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_test_disappearing_server/end2end_test_disappearing_server.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_test_empty_batch/end2end_test_empty_batch.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_test_empty_batch/end2end_test_empty_batch.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_test_graceful_server_shutdown/end2end_test_graceful_server_shutdown.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_test_graceful_server_shutdown/end2end_test_graceful_server_shutdown.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_test_high_initial_seqno/end2end_test_high_initial_seqno.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_test_high_initial_seqno/end2end_test_high_initial_seqno.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_test_invoke_large_request/end2end_test_invoke_large_request.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_test_invoke_large_request/end2end_test_invoke_large_request.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_test_large_metadata/end2end_test_large_metadata.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_test_large_metadata/end2end_test_large_metadata.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_test_max_concurrent_streams/end2end_test_max_concurrent_streams.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_test_max_concurrent_streams/end2end_test_max_concurrent_streams.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_test_max_message_length/end2end_test_max_message_length.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_test_max_message_length/end2end_test_max_message_length.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_test_metadata/end2end_test_metadata.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_test_metadata/end2end_test_metadata.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_test_no_op/end2end_test_no_op.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_test_no_op/end2end_test_no_op.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_test_payload/end2end_test_payload.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_test_payload/end2end_test_payload.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_test_ping_pong_streaming/end2end_test_ping_pong_streaming.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_test_ping_pong_streaming/end2end_test_ping_pong_streaming.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_test_registered_call/end2end_test_registered_call.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_test_registered_call/end2end_test_registered_call.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_test_request_with_flags/end2end_test_request_with_flags.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_test_request_with_flags/end2end_test_request_with_flags.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_test_request_with_payload/end2end_test_request_with_payload.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_test_request_with_payload/end2end_test_request_with_payload.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_test_server_finishes_request/end2end_test_server_finishes_request.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_test_server_finishes_request/end2end_test_server_finishes_request.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_test_shutdown_finishes_calls/end2end_test_shutdown_finishes_calls.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_test_shutdown_finishes_calls/end2end_test_shutdown_finishes_calls.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_test_shutdown_finishes_tags/end2end_test_shutdown_finishes_tags.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_test_shutdown_finishes_tags/end2end_test_shutdown_finishes_tags.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_test_simple_delayed_request/end2end_test_simple_delayed_request.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_test_simple_delayed_request/end2end_test_simple_delayed_request.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_test_simple_request/end2end_test_simple_request.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_test_simple_request/end2end_test_simple_request.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/end2end_test_trailing_metadata/end2end_test_trailing_metadata.vcxproj create mode 100644 vsprojects/vcxproj/test/end2end_test_trailing_metadata/end2end_test_trailing_metadata.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/endpoint_pair_test/endpoint_pair_test.vcxproj create mode 100644 vsprojects/vcxproj/test/endpoint_pair_test/endpoint_pair_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/fling_client/fling_client.vcxproj create mode 100644 vsprojects/vcxproj/test/fling_client/fling_client.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/fling_server/fling_server.vcxproj create mode 100644 vsprojects/vcxproj/test/fling_server/fling_server.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/generic_end2end_test/generic_end2end_test.vcxproj create mode 100644 vsprojects/vcxproj/test/generic_end2end_test/generic_end2end_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/gpr_cmdline_test/gpr_cmdline_test.vcxproj create mode 100644 vsprojects/vcxproj/test/gpr_cmdline_test/gpr_cmdline_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/gpr_env_test/gpr_env_test.vcxproj create mode 100644 vsprojects/vcxproj/test/gpr_env_test/gpr_env_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/gpr_file_test/gpr_file_test.vcxproj create mode 100644 vsprojects/vcxproj/test/gpr_file_test/gpr_file_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/gpr_histogram_test/gpr_histogram_test.vcxproj create mode 100644 vsprojects/vcxproj/test/gpr_histogram_test/gpr_histogram_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/gpr_host_port_test/gpr_host_port_test.vcxproj create mode 100644 vsprojects/vcxproj/test/gpr_host_port_test/gpr_host_port_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/gpr_log_test/gpr_log_test.vcxproj create mode 100644 vsprojects/vcxproj/test/gpr_log_test/gpr_log_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/gpr_slice_buffer_test/gpr_slice_buffer_test.vcxproj create mode 100644 vsprojects/vcxproj/test/gpr_slice_buffer_test/gpr_slice_buffer_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/gpr_slice_test/gpr_slice_test.vcxproj create mode 100644 vsprojects/vcxproj/test/gpr_slice_test/gpr_slice_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/gpr_stack_lockfree_test/gpr_stack_lockfree_test.vcxproj create mode 100644 vsprojects/vcxproj/test/gpr_stack_lockfree_test/gpr_stack_lockfree_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/gpr_string_test/gpr_string_test.vcxproj create mode 100644 vsprojects/vcxproj/test/gpr_string_test/gpr_string_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/gpr_sync_test/gpr_sync_test.vcxproj create mode 100644 vsprojects/vcxproj/test/gpr_sync_test/gpr_sync_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/gpr_thd_test/gpr_thd_test.vcxproj create mode 100644 vsprojects/vcxproj/test/gpr_thd_test/gpr_thd_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/gpr_time_test/gpr_time_test.vcxproj create mode 100644 vsprojects/vcxproj/test/gpr_time_test/gpr_time_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/gpr_tls_test/gpr_tls_test.vcxproj create mode 100644 vsprojects/vcxproj/test/gpr_tls_test/gpr_tls_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/gpr_useful_test/gpr_useful_test.vcxproj create mode 100644 vsprojects/vcxproj/test/gpr_useful_test/gpr_useful_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/grpc_auth_context_test/grpc_auth_context_test.vcxproj create mode 100644 vsprojects/vcxproj/test/grpc_auth_context_test/grpc_auth_context_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/grpc_base64_test/grpc_base64_test.vcxproj create mode 100644 vsprojects/vcxproj/test/grpc_base64_test/grpc_base64_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/grpc_byte_buffer_reader_test/grpc_byte_buffer_reader_test.vcxproj create mode 100644 vsprojects/vcxproj/test/grpc_byte_buffer_reader_test/grpc_byte_buffer_reader_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/grpc_channel_args_test/grpc_channel_args_test.vcxproj create mode 100644 vsprojects/vcxproj/test/grpc_channel_args_test/grpc_channel_args_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/grpc_channel_stack_test/grpc_channel_stack_test.vcxproj create mode 100644 vsprojects/vcxproj/test/grpc_channel_stack_test/grpc_channel_stack_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/grpc_cli/grpc_cli.vcxproj create mode 100644 vsprojects/vcxproj/test/grpc_cli/grpc_cli.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/grpc_completion_queue_test/grpc_completion_queue_test.vcxproj create mode 100644 vsprojects/vcxproj/test/grpc_completion_queue_test/grpc_completion_queue_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/grpc_credentials_test/grpc_credentials_test.vcxproj create mode 100644 vsprojects/vcxproj/test/grpc_credentials_test/grpc_credentials_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/grpc_jwt_verifier_test/grpc_jwt_verifier_test.vcxproj create mode 100644 vsprojects/vcxproj/test/grpc_jwt_verifier_test/grpc_jwt_verifier_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/grpc_security_connector_test/grpc_security_connector_test.vcxproj create mode 100644 vsprojects/vcxproj/test/grpc_security_connector_test/grpc_security_connector_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/grpc_stream_op_test/grpc_stream_op_test.vcxproj create mode 100644 vsprojects/vcxproj/test/grpc_stream_op_test/grpc_stream_op_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_bad_hostname_nosec_test/h2_compress_bad_hostname_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_bad_hostname_nosec_test/h2_compress_bad_hostname_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_bad_hostname_test/h2_compress_bad_hostname_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_bad_hostname_test/h2_compress_bad_hostname_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_binary_metadata_nosec_test/h2_compress_binary_metadata_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_binary_metadata_nosec_test/h2_compress_binary_metadata_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_binary_metadata_test/h2_compress_binary_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_binary_metadata_test/h2_compress_binary_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_call_creds_test/h2_compress_call_creds_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_call_creds_test/h2_compress_call_creds_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_cancel_after_accept_nosec_test/h2_compress_cancel_after_accept_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_cancel_after_accept_nosec_test/h2_compress_cancel_after_accept_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_cancel_after_accept_test/h2_compress_cancel_after_accept_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_cancel_after_accept_test/h2_compress_cancel_after_accept_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_cancel_after_client_done_nosec_test/h2_compress_cancel_after_client_done_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_cancel_after_client_done_nosec_test/h2_compress_cancel_after_client_done_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_cancel_after_client_done_test/h2_compress_cancel_after_client_done_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_cancel_after_client_done_test/h2_compress_cancel_after_client_done_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_cancel_after_invoke_nosec_test/h2_compress_cancel_after_invoke_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_cancel_after_invoke_nosec_test/h2_compress_cancel_after_invoke_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_cancel_after_invoke_test/h2_compress_cancel_after_invoke_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_cancel_after_invoke_test/h2_compress_cancel_after_invoke_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_cancel_before_invoke_nosec_test/h2_compress_cancel_before_invoke_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_cancel_before_invoke_nosec_test/h2_compress_cancel_before_invoke_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_cancel_before_invoke_test/h2_compress_cancel_before_invoke_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_cancel_before_invoke_test/h2_compress_cancel_before_invoke_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_cancel_in_a_vacuum_nosec_test/h2_compress_cancel_in_a_vacuum_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_cancel_in_a_vacuum_nosec_test/h2_compress_cancel_in_a_vacuum_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_cancel_in_a_vacuum_test/h2_compress_cancel_in_a_vacuum_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_cancel_in_a_vacuum_test/h2_compress_cancel_in_a_vacuum_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_census_simple_request_nosec_test/h2_compress_census_simple_request_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_census_simple_request_nosec_test/h2_compress_census_simple_request_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_census_simple_request_test/h2_compress_census_simple_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_census_simple_request_test/h2_compress_census_simple_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_channel_connectivity_nosec_test/h2_compress_channel_connectivity_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_channel_connectivity_nosec_test/h2_compress_channel_connectivity_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_channel_connectivity_test/h2_compress_channel_connectivity_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_channel_connectivity_test/h2_compress_channel_connectivity_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_compressed_payload_nosec_test/h2_compress_compressed_payload_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_compressed_payload_nosec_test/h2_compress_compressed_payload_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_compressed_payload_test/h2_compress_compressed_payload_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_compressed_payload_test/h2_compress_compressed_payload_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_default_host_nosec_test/h2_compress_default_host_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_default_host_nosec_test/h2_compress_default_host_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_default_host_test/h2_compress_default_host_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_default_host_test/h2_compress_default_host_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_disappearing_server_nosec_test/h2_compress_disappearing_server_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_disappearing_server_nosec_test/h2_compress_disappearing_server_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_disappearing_server_test/h2_compress_disappearing_server_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_disappearing_server_test/h2_compress_disappearing_server_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_empty_batch_nosec_test/h2_compress_empty_batch_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_empty_batch_nosec_test/h2_compress_empty_batch_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_empty_batch_test/h2_compress_empty_batch_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_empty_batch_test/h2_compress_empty_batch_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_graceful_server_shutdown_nosec_test/h2_compress_graceful_server_shutdown_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_graceful_server_shutdown_nosec_test/h2_compress_graceful_server_shutdown_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_graceful_server_shutdown_test/h2_compress_graceful_server_shutdown_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_graceful_server_shutdown_test/h2_compress_graceful_server_shutdown_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_high_initial_seqno_nosec_test/h2_compress_high_initial_seqno_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_high_initial_seqno_nosec_test/h2_compress_high_initial_seqno_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_high_initial_seqno_test/h2_compress_high_initial_seqno_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_high_initial_seqno_test/h2_compress_high_initial_seqno_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_invoke_large_request_nosec_test/h2_compress_invoke_large_request_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_invoke_large_request_nosec_test/h2_compress_invoke_large_request_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_invoke_large_request_test/h2_compress_invoke_large_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_invoke_large_request_test/h2_compress_invoke_large_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_large_metadata_nosec_test/h2_compress_large_metadata_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_large_metadata_nosec_test/h2_compress_large_metadata_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_large_metadata_test/h2_compress_large_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_large_metadata_test/h2_compress_large_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_max_concurrent_streams_nosec_test/h2_compress_max_concurrent_streams_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_max_concurrent_streams_nosec_test/h2_compress_max_concurrent_streams_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_max_concurrent_streams_test/h2_compress_max_concurrent_streams_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_max_concurrent_streams_test/h2_compress_max_concurrent_streams_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_max_message_length_nosec_test/h2_compress_max_message_length_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_max_message_length_nosec_test/h2_compress_max_message_length_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_max_message_length_test/h2_compress_max_message_length_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_max_message_length_test/h2_compress_max_message_length_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_metadata_nosec_test/h2_compress_metadata_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_metadata_nosec_test/h2_compress_metadata_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_metadata_test/h2_compress_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_metadata_test/h2_compress_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_no_op_nosec_test/h2_compress_no_op_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_no_op_nosec_test/h2_compress_no_op_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_no_op_test/h2_compress_no_op_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_no_op_test/h2_compress_no_op_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_payload_nosec_test/h2_compress_payload_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_payload_nosec_test/h2_compress_payload_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_payload_test/h2_compress_payload_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_payload_test/h2_compress_payload_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_ping_pong_streaming_nosec_test/h2_compress_ping_pong_streaming_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_ping_pong_streaming_nosec_test/h2_compress_ping_pong_streaming_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_ping_pong_streaming_test/h2_compress_ping_pong_streaming_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_ping_pong_streaming_test/h2_compress_ping_pong_streaming_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_registered_call_nosec_test/h2_compress_registered_call_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_registered_call_nosec_test/h2_compress_registered_call_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_registered_call_test/h2_compress_registered_call_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_registered_call_test/h2_compress_registered_call_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_request_with_flags_nosec_test/h2_compress_request_with_flags_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_request_with_flags_nosec_test/h2_compress_request_with_flags_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_request_with_flags_test/h2_compress_request_with_flags_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_request_with_flags_test/h2_compress_request_with_flags_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_request_with_payload_nosec_test/h2_compress_request_with_payload_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_request_with_payload_nosec_test/h2_compress_request_with_payload_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_request_with_payload_test/h2_compress_request_with_payload_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_request_with_payload_test/h2_compress_request_with_payload_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_server_finishes_request_nosec_test/h2_compress_server_finishes_request_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_server_finishes_request_nosec_test/h2_compress_server_finishes_request_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_server_finishes_request_test/h2_compress_server_finishes_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_server_finishes_request_test/h2_compress_server_finishes_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_shutdown_finishes_calls_nosec_test/h2_compress_shutdown_finishes_calls_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_shutdown_finishes_calls_nosec_test/h2_compress_shutdown_finishes_calls_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_shutdown_finishes_calls_test/h2_compress_shutdown_finishes_calls_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_shutdown_finishes_calls_test/h2_compress_shutdown_finishes_calls_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_shutdown_finishes_tags_nosec_test/h2_compress_shutdown_finishes_tags_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_shutdown_finishes_tags_nosec_test/h2_compress_shutdown_finishes_tags_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_shutdown_finishes_tags_test/h2_compress_shutdown_finishes_tags_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_shutdown_finishes_tags_test/h2_compress_shutdown_finishes_tags_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_simple_delayed_request_nosec_test/h2_compress_simple_delayed_request_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_simple_delayed_request_nosec_test/h2_compress_simple_delayed_request_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_simple_delayed_request_test/h2_compress_simple_delayed_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_simple_delayed_request_test/h2_compress_simple_delayed_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_simple_request_nosec_test/h2_compress_simple_request_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_simple_request_nosec_test/h2_compress_simple_request_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_simple_request_test/h2_compress_simple_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_simple_request_test/h2_compress_simple_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_trailing_metadata_nosec_test/h2_compress_trailing_metadata_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_trailing_metadata_nosec_test/h2_compress_trailing_metadata_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_compress_trailing_metadata_test/h2_compress_trailing_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_compress_trailing_metadata_test/h2_compress_trailing_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_fakesec_bad_hostname_test/h2_fakesec_bad_hostname_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_fakesec_bad_hostname_test/h2_fakesec_bad_hostname_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_fakesec_binary_metadata_test/h2_fakesec_binary_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_fakesec_binary_metadata_test/h2_fakesec_binary_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_fakesec_call_creds_test/h2_fakesec_call_creds_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_fakesec_call_creds_test/h2_fakesec_call_creds_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_fakesec_cancel_after_accept_test/h2_fakesec_cancel_after_accept_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_fakesec_cancel_after_accept_test/h2_fakesec_cancel_after_accept_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_fakesec_cancel_after_client_done_test/h2_fakesec_cancel_after_client_done_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_fakesec_cancel_after_client_done_test/h2_fakesec_cancel_after_client_done_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_fakesec_cancel_after_invoke_test/h2_fakesec_cancel_after_invoke_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_fakesec_cancel_after_invoke_test/h2_fakesec_cancel_after_invoke_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_fakesec_cancel_before_invoke_test/h2_fakesec_cancel_before_invoke_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_fakesec_cancel_before_invoke_test/h2_fakesec_cancel_before_invoke_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_fakesec_cancel_in_a_vacuum_test/h2_fakesec_cancel_in_a_vacuum_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_fakesec_cancel_in_a_vacuum_test/h2_fakesec_cancel_in_a_vacuum_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_fakesec_census_simple_request_test/h2_fakesec_census_simple_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_fakesec_census_simple_request_test/h2_fakesec_census_simple_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_fakesec_channel_connectivity_test/h2_fakesec_channel_connectivity_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_fakesec_channel_connectivity_test/h2_fakesec_channel_connectivity_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_fakesec_compressed_payload_test/h2_fakesec_compressed_payload_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_fakesec_compressed_payload_test/h2_fakesec_compressed_payload_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_fakesec_default_host_test/h2_fakesec_default_host_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_fakesec_default_host_test/h2_fakesec_default_host_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_fakesec_disappearing_server_test/h2_fakesec_disappearing_server_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_fakesec_disappearing_server_test/h2_fakesec_disappearing_server_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_fakesec_empty_batch_test/h2_fakesec_empty_batch_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_fakesec_empty_batch_test/h2_fakesec_empty_batch_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_fakesec_graceful_server_shutdown_test/h2_fakesec_graceful_server_shutdown_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_fakesec_graceful_server_shutdown_test/h2_fakesec_graceful_server_shutdown_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_fakesec_high_initial_seqno_test/h2_fakesec_high_initial_seqno_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_fakesec_high_initial_seqno_test/h2_fakesec_high_initial_seqno_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_fakesec_invoke_large_request_test/h2_fakesec_invoke_large_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_fakesec_invoke_large_request_test/h2_fakesec_invoke_large_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_fakesec_large_metadata_test/h2_fakesec_large_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_fakesec_large_metadata_test/h2_fakesec_large_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_fakesec_max_concurrent_streams_test/h2_fakesec_max_concurrent_streams_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_fakesec_max_concurrent_streams_test/h2_fakesec_max_concurrent_streams_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_fakesec_max_message_length_test/h2_fakesec_max_message_length_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_fakesec_max_message_length_test/h2_fakesec_max_message_length_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_fakesec_metadata_test/h2_fakesec_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_fakesec_metadata_test/h2_fakesec_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_fakesec_no_op_test/h2_fakesec_no_op_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_fakesec_no_op_test/h2_fakesec_no_op_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_fakesec_payload_test/h2_fakesec_payload_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_fakesec_payload_test/h2_fakesec_payload_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_fakesec_ping_pong_streaming_test/h2_fakesec_ping_pong_streaming_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_fakesec_ping_pong_streaming_test/h2_fakesec_ping_pong_streaming_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_fakesec_registered_call_test/h2_fakesec_registered_call_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_fakesec_registered_call_test/h2_fakesec_registered_call_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_fakesec_request_with_flags_test/h2_fakesec_request_with_flags_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_fakesec_request_with_flags_test/h2_fakesec_request_with_flags_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_fakesec_request_with_payload_test/h2_fakesec_request_with_payload_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_fakesec_request_with_payload_test/h2_fakesec_request_with_payload_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_fakesec_server_finishes_request_test/h2_fakesec_server_finishes_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_fakesec_server_finishes_request_test/h2_fakesec_server_finishes_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_fakesec_shutdown_finishes_calls_test/h2_fakesec_shutdown_finishes_calls_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_fakesec_shutdown_finishes_calls_test/h2_fakesec_shutdown_finishes_calls_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_fakesec_shutdown_finishes_tags_test/h2_fakesec_shutdown_finishes_tags_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_fakesec_shutdown_finishes_tags_test/h2_fakesec_shutdown_finishes_tags_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_fakesec_simple_delayed_request_test/h2_fakesec_simple_delayed_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_fakesec_simple_delayed_request_test/h2_fakesec_simple_delayed_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_fakesec_simple_request_test/h2_fakesec_simple_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_fakesec_simple_request_test/h2_fakesec_simple_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_fakesec_trailing_metadata_test/h2_fakesec_trailing_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_fakesec_trailing_metadata_test/h2_fakesec_trailing_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_bad_hostname_nosec_test/h2_full_bad_hostname_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_bad_hostname_nosec_test/h2_full_bad_hostname_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_bad_hostname_test/h2_full_bad_hostname_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_bad_hostname_test/h2_full_bad_hostname_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_binary_metadata_nosec_test/h2_full_binary_metadata_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_binary_metadata_nosec_test/h2_full_binary_metadata_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_binary_metadata_test/h2_full_binary_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_binary_metadata_test/h2_full_binary_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_call_creds_test/h2_full_call_creds_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_call_creds_test/h2_full_call_creds_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_cancel_after_accept_nosec_test/h2_full_cancel_after_accept_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_cancel_after_accept_nosec_test/h2_full_cancel_after_accept_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_cancel_after_accept_test/h2_full_cancel_after_accept_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_cancel_after_accept_test/h2_full_cancel_after_accept_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_cancel_after_client_done_nosec_test/h2_full_cancel_after_client_done_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_cancel_after_client_done_nosec_test/h2_full_cancel_after_client_done_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_cancel_after_client_done_test/h2_full_cancel_after_client_done_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_cancel_after_client_done_test/h2_full_cancel_after_client_done_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_cancel_after_invoke_nosec_test/h2_full_cancel_after_invoke_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_cancel_after_invoke_nosec_test/h2_full_cancel_after_invoke_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_cancel_after_invoke_test/h2_full_cancel_after_invoke_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_cancel_after_invoke_test/h2_full_cancel_after_invoke_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_cancel_before_invoke_nosec_test/h2_full_cancel_before_invoke_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_cancel_before_invoke_nosec_test/h2_full_cancel_before_invoke_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_cancel_before_invoke_test/h2_full_cancel_before_invoke_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_cancel_before_invoke_test/h2_full_cancel_before_invoke_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_cancel_in_a_vacuum_nosec_test/h2_full_cancel_in_a_vacuum_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_cancel_in_a_vacuum_nosec_test/h2_full_cancel_in_a_vacuum_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_cancel_in_a_vacuum_test/h2_full_cancel_in_a_vacuum_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_cancel_in_a_vacuum_test/h2_full_cancel_in_a_vacuum_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_census_simple_request_nosec_test/h2_full_census_simple_request_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_census_simple_request_nosec_test/h2_full_census_simple_request_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_census_simple_request_test/h2_full_census_simple_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_census_simple_request_test/h2_full_census_simple_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_channel_connectivity_nosec_test/h2_full_channel_connectivity_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_channel_connectivity_nosec_test/h2_full_channel_connectivity_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_channel_connectivity_test/h2_full_channel_connectivity_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_channel_connectivity_test/h2_full_channel_connectivity_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_compressed_payload_nosec_test/h2_full_compressed_payload_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_compressed_payload_nosec_test/h2_full_compressed_payload_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_compressed_payload_test/h2_full_compressed_payload_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_compressed_payload_test/h2_full_compressed_payload_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_default_host_nosec_test/h2_full_default_host_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_default_host_nosec_test/h2_full_default_host_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_default_host_test/h2_full_default_host_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_default_host_test/h2_full_default_host_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_disappearing_server_nosec_test/h2_full_disappearing_server_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_disappearing_server_nosec_test/h2_full_disappearing_server_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_disappearing_server_test/h2_full_disappearing_server_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_disappearing_server_test/h2_full_disappearing_server_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_empty_batch_nosec_test/h2_full_empty_batch_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_empty_batch_nosec_test/h2_full_empty_batch_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_empty_batch_test/h2_full_empty_batch_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_empty_batch_test/h2_full_empty_batch_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_graceful_server_shutdown_nosec_test/h2_full_graceful_server_shutdown_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_graceful_server_shutdown_nosec_test/h2_full_graceful_server_shutdown_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_graceful_server_shutdown_test/h2_full_graceful_server_shutdown_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_graceful_server_shutdown_test/h2_full_graceful_server_shutdown_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_high_initial_seqno_nosec_test/h2_full_high_initial_seqno_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_high_initial_seqno_nosec_test/h2_full_high_initial_seqno_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_high_initial_seqno_test/h2_full_high_initial_seqno_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_high_initial_seqno_test/h2_full_high_initial_seqno_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_invoke_large_request_nosec_test/h2_full_invoke_large_request_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_invoke_large_request_nosec_test/h2_full_invoke_large_request_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_invoke_large_request_test/h2_full_invoke_large_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_invoke_large_request_test/h2_full_invoke_large_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_large_metadata_nosec_test/h2_full_large_metadata_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_large_metadata_nosec_test/h2_full_large_metadata_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_large_metadata_test/h2_full_large_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_large_metadata_test/h2_full_large_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_max_concurrent_streams_nosec_test/h2_full_max_concurrent_streams_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_max_concurrent_streams_nosec_test/h2_full_max_concurrent_streams_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_max_concurrent_streams_test/h2_full_max_concurrent_streams_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_max_concurrent_streams_test/h2_full_max_concurrent_streams_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_max_message_length_nosec_test/h2_full_max_message_length_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_max_message_length_nosec_test/h2_full_max_message_length_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_max_message_length_test/h2_full_max_message_length_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_max_message_length_test/h2_full_max_message_length_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_metadata_nosec_test/h2_full_metadata_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_metadata_nosec_test/h2_full_metadata_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_metadata_test/h2_full_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_metadata_test/h2_full_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_no_op_nosec_test/h2_full_no_op_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_no_op_nosec_test/h2_full_no_op_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_no_op_test/h2_full_no_op_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_no_op_test/h2_full_no_op_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_payload_nosec_test/h2_full_payload_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_payload_nosec_test/h2_full_payload_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_payload_test/h2_full_payload_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_payload_test/h2_full_payload_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_ping_pong_streaming_nosec_test/h2_full_ping_pong_streaming_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_ping_pong_streaming_nosec_test/h2_full_ping_pong_streaming_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_ping_pong_streaming_test/h2_full_ping_pong_streaming_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_ping_pong_streaming_test/h2_full_ping_pong_streaming_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_registered_call_nosec_test/h2_full_registered_call_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_registered_call_nosec_test/h2_full_registered_call_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_registered_call_test/h2_full_registered_call_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_registered_call_test/h2_full_registered_call_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_request_with_flags_nosec_test/h2_full_request_with_flags_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_request_with_flags_nosec_test/h2_full_request_with_flags_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_request_with_flags_test/h2_full_request_with_flags_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_request_with_flags_test/h2_full_request_with_flags_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_request_with_payload_nosec_test/h2_full_request_with_payload_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_request_with_payload_nosec_test/h2_full_request_with_payload_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_request_with_payload_test/h2_full_request_with_payload_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_request_with_payload_test/h2_full_request_with_payload_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_server_finishes_request_nosec_test/h2_full_server_finishes_request_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_server_finishes_request_nosec_test/h2_full_server_finishes_request_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_server_finishes_request_test/h2_full_server_finishes_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_server_finishes_request_test/h2_full_server_finishes_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_shutdown_finishes_calls_nosec_test/h2_full_shutdown_finishes_calls_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_shutdown_finishes_calls_nosec_test/h2_full_shutdown_finishes_calls_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_shutdown_finishes_calls_test/h2_full_shutdown_finishes_calls_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_shutdown_finishes_calls_test/h2_full_shutdown_finishes_calls_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_shutdown_finishes_tags_nosec_test/h2_full_shutdown_finishes_tags_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_shutdown_finishes_tags_nosec_test/h2_full_shutdown_finishes_tags_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_shutdown_finishes_tags_test/h2_full_shutdown_finishes_tags_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_shutdown_finishes_tags_test/h2_full_shutdown_finishes_tags_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_simple_delayed_request_nosec_test/h2_full_simple_delayed_request_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_simple_delayed_request_nosec_test/h2_full_simple_delayed_request_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_simple_delayed_request_test/h2_full_simple_delayed_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_simple_delayed_request_test/h2_full_simple_delayed_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_simple_request_nosec_test/h2_full_simple_request_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_simple_request_nosec_test/h2_full_simple_request_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_simple_request_test/h2_full_simple_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_simple_request_test/h2_full_simple_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_trailing_metadata_nosec_test/h2_full_trailing_metadata_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_trailing_metadata_nosec_test/h2_full_trailing_metadata_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_full_trailing_metadata_test/h2_full_trailing_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_full_trailing_metadata_test/h2_full_trailing_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_oauth2_bad_hostname_test/h2_oauth2_bad_hostname_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_oauth2_bad_hostname_test/h2_oauth2_bad_hostname_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_oauth2_binary_metadata_test/h2_oauth2_binary_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_oauth2_binary_metadata_test/h2_oauth2_binary_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_oauth2_call_creds_test/h2_oauth2_call_creds_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_oauth2_call_creds_test/h2_oauth2_call_creds_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_oauth2_cancel_after_accept_test/h2_oauth2_cancel_after_accept_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_oauth2_cancel_after_accept_test/h2_oauth2_cancel_after_accept_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_oauth2_cancel_after_client_done_test/h2_oauth2_cancel_after_client_done_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_oauth2_cancel_after_client_done_test/h2_oauth2_cancel_after_client_done_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_oauth2_cancel_after_invoke_test/h2_oauth2_cancel_after_invoke_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_oauth2_cancel_after_invoke_test/h2_oauth2_cancel_after_invoke_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_oauth2_cancel_before_invoke_test/h2_oauth2_cancel_before_invoke_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_oauth2_cancel_before_invoke_test/h2_oauth2_cancel_before_invoke_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_oauth2_cancel_in_a_vacuum_test/h2_oauth2_cancel_in_a_vacuum_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_oauth2_cancel_in_a_vacuum_test/h2_oauth2_cancel_in_a_vacuum_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_oauth2_census_simple_request_test/h2_oauth2_census_simple_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_oauth2_census_simple_request_test/h2_oauth2_census_simple_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_oauth2_channel_connectivity_test/h2_oauth2_channel_connectivity_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_oauth2_channel_connectivity_test/h2_oauth2_channel_connectivity_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_oauth2_compressed_payload_test/h2_oauth2_compressed_payload_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_oauth2_compressed_payload_test/h2_oauth2_compressed_payload_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_oauth2_default_host_test/h2_oauth2_default_host_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_oauth2_default_host_test/h2_oauth2_default_host_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_oauth2_disappearing_server_test/h2_oauth2_disappearing_server_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_oauth2_disappearing_server_test/h2_oauth2_disappearing_server_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_oauth2_empty_batch_test/h2_oauth2_empty_batch_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_oauth2_empty_batch_test/h2_oauth2_empty_batch_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_oauth2_graceful_server_shutdown_test/h2_oauth2_graceful_server_shutdown_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_oauth2_graceful_server_shutdown_test/h2_oauth2_graceful_server_shutdown_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_oauth2_high_initial_seqno_test/h2_oauth2_high_initial_seqno_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_oauth2_high_initial_seqno_test/h2_oauth2_high_initial_seqno_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_oauth2_invoke_large_request_test/h2_oauth2_invoke_large_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_oauth2_invoke_large_request_test/h2_oauth2_invoke_large_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_oauth2_large_metadata_test/h2_oauth2_large_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_oauth2_large_metadata_test/h2_oauth2_large_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_oauth2_max_concurrent_streams_test/h2_oauth2_max_concurrent_streams_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_oauth2_max_concurrent_streams_test/h2_oauth2_max_concurrent_streams_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_oauth2_max_message_length_test/h2_oauth2_max_message_length_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_oauth2_max_message_length_test/h2_oauth2_max_message_length_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_oauth2_metadata_test/h2_oauth2_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_oauth2_metadata_test/h2_oauth2_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_oauth2_no_op_test/h2_oauth2_no_op_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_oauth2_no_op_test/h2_oauth2_no_op_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_oauth2_payload_test/h2_oauth2_payload_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_oauth2_payload_test/h2_oauth2_payload_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_oauth2_ping_pong_streaming_test/h2_oauth2_ping_pong_streaming_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_oauth2_ping_pong_streaming_test/h2_oauth2_ping_pong_streaming_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_oauth2_registered_call_test/h2_oauth2_registered_call_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_oauth2_registered_call_test/h2_oauth2_registered_call_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_oauth2_request_with_flags_test/h2_oauth2_request_with_flags_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_oauth2_request_with_flags_test/h2_oauth2_request_with_flags_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_oauth2_request_with_payload_test/h2_oauth2_request_with_payload_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_oauth2_request_with_payload_test/h2_oauth2_request_with_payload_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_oauth2_server_finishes_request_test/h2_oauth2_server_finishes_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_oauth2_server_finishes_request_test/h2_oauth2_server_finishes_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_oauth2_shutdown_finishes_calls_test/h2_oauth2_shutdown_finishes_calls_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_oauth2_shutdown_finishes_calls_test/h2_oauth2_shutdown_finishes_calls_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_oauth2_shutdown_finishes_tags_test/h2_oauth2_shutdown_finishes_tags_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_oauth2_shutdown_finishes_tags_test/h2_oauth2_shutdown_finishes_tags_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_oauth2_simple_delayed_request_test/h2_oauth2_simple_delayed_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_oauth2_simple_delayed_request_test/h2_oauth2_simple_delayed_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_oauth2_simple_request_test/h2_oauth2_simple_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_oauth2_simple_request_test/h2_oauth2_simple_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_oauth2_trailing_metadata_test/h2_oauth2_trailing_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_oauth2_trailing_metadata_test/h2_oauth2_trailing_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_bad_hostname_nosec_test/h2_proxy_bad_hostname_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_bad_hostname_nosec_test/h2_proxy_bad_hostname_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_bad_hostname_test/h2_proxy_bad_hostname_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_bad_hostname_test/h2_proxy_bad_hostname_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_binary_metadata_nosec_test/h2_proxy_binary_metadata_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_binary_metadata_nosec_test/h2_proxy_binary_metadata_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_binary_metadata_test/h2_proxy_binary_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_binary_metadata_test/h2_proxy_binary_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_call_creds_test/h2_proxy_call_creds_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_call_creds_test/h2_proxy_call_creds_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_cancel_after_accept_nosec_test/h2_proxy_cancel_after_accept_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_cancel_after_accept_nosec_test/h2_proxy_cancel_after_accept_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_cancel_after_accept_test/h2_proxy_cancel_after_accept_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_cancel_after_accept_test/h2_proxy_cancel_after_accept_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_cancel_after_client_done_nosec_test/h2_proxy_cancel_after_client_done_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_cancel_after_client_done_nosec_test/h2_proxy_cancel_after_client_done_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_cancel_after_client_done_test/h2_proxy_cancel_after_client_done_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_cancel_after_client_done_test/h2_proxy_cancel_after_client_done_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_cancel_after_invoke_nosec_test/h2_proxy_cancel_after_invoke_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_cancel_after_invoke_nosec_test/h2_proxy_cancel_after_invoke_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_cancel_after_invoke_test/h2_proxy_cancel_after_invoke_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_cancel_after_invoke_test/h2_proxy_cancel_after_invoke_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_cancel_before_invoke_nosec_test/h2_proxy_cancel_before_invoke_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_cancel_before_invoke_nosec_test/h2_proxy_cancel_before_invoke_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_cancel_before_invoke_test/h2_proxy_cancel_before_invoke_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_cancel_before_invoke_test/h2_proxy_cancel_before_invoke_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_cancel_in_a_vacuum_nosec_test/h2_proxy_cancel_in_a_vacuum_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_cancel_in_a_vacuum_nosec_test/h2_proxy_cancel_in_a_vacuum_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_cancel_in_a_vacuum_test/h2_proxy_cancel_in_a_vacuum_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_cancel_in_a_vacuum_test/h2_proxy_cancel_in_a_vacuum_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_census_simple_request_nosec_test/h2_proxy_census_simple_request_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_census_simple_request_nosec_test/h2_proxy_census_simple_request_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_census_simple_request_test/h2_proxy_census_simple_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_census_simple_request_test/h2_proxy_census_simple_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_default_host_nosec_test/h2_proxy_default_host_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_default_host_nosec_test/h2_proxy_default_host_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_default_host_test/h2_proxy_default_host_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_default_host_test/h2_proxy_default_host_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_disappearing_server_nosec_test/h2_proxy_disappearing_server_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_disappearing_server_nosec_test/h2_proxy_disappearing_server_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_disappearing_server_test/h2_proxy_disappearing_server_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_disappearing_server_test/h2_proxy_disappearing_server_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_empty_batch_nosec_test/h2_proxy_empty_batch_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_empty_batch_nosec_test/h2_proxy_empty_batch_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_empty_batch_test/h2_proxy_empty_batch_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_empty_batch_test/h2_proxy_empty_batch_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_graceful_server_shutdown_nosec_test/h2_proxy_graceful_server_shutdown_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_graceful_server_shutdown_nosec_test/h2_proxy_graceful_server_shutdown_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_graceful_server_shutdown_test/h2_proxy_graceful_server_shutdown_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_graceful_server_shutdown_test/h2_proxy_graceful_server_shutdown_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_high_initial_seqno_nosec_test/h2_proxy_high_initial_seqno_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_high_initial_seqno_nosec_test/h2_proxy_high_initial_seqno_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_high_initial_seqno_test/h2_proxy_high_initial_seqno_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_high_initial_seqno_test/h2_proxy_high_initial_seqno_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_invoke_large_request_nosec_test/h2_proxy_invoke_large_request_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_invoke_large_request_nosec_test/h2_proxy_invoke_large_request_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_invoke_large_request_test/h2_proxy_invoke_large_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_invoke_large_request_test/h2_proxy_invoke_large_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_large_metadata_nosec_test/h2_proxy_large_metadata_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_large_metadata_nosec_test/h2_proxy_large_metadata_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_large_metadata_test/h2_proxy_large_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_large_metadata_test/h2_proxy_large_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_max_message_length_nosec_test/h2_proxy_max_message_length_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_max_message_length_nosec_test/h2_proxy_max_message_length_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_max_message_length_test/h2_proxy_max_message_length_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_max_message_length_test/h2_proxy_max_message_length_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_metadata_nosec_test/h2_proxy_metadata_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_metadata_nosec_test/h2_proxy_metadata_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_metadata_test/h2_proxy_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_metadata_test/h2_proxy_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_no_op_nosec_test/h2_proxy_no_op_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_no_op_nosec_test/h2_proxy_no_op_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_no_op_test/h2_proxy_no_op_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_no_op_test/h2_proxy_no_op_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_payload_nosec_test/h2_proxy_payload_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_payload_nosec_test/h2_proxy_payload_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_payload_test/h2_proxy_payload_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_payload_test/h2_proxy_payload_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_ping_pong_streaming_nosec_test/h2_proxy_ping_pong_streaming_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_ping_pong_streaming_nosec_test/h2_proxy_ping_pong_streaming_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_ping_pong_streaming_test/h2_proxy_ping_pong_streaming_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_ping_pong_streaming_test/h2_proxy_ping_pong_streaming_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_registered_call_nosec_test/h2_proxy_registered_call_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_registered_call_nosec_test/h2_proxy_registered_call_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_registered_call_test/h2_proxy_registered_call_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_registered_call_test/h2_proxy_registered_call_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_request_with_payload_nosec_test/h2_proxy_request_with_payload_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_request_with_payload_nosec_test/h2_proxy_request_with_payload_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_request_with_payload_test/h2_proxy_request_with_payload_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_request_with_payload_test/h2_proxy_request_with_payload_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_server_finishes_request_nosec_test/h2_proxy_server_finishes_request_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_server_finishes_request_nosec_test/h2_proxy_server_finishes_request_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_server_finishes_request_test/h2_proxy_server_finishes_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_server_finishes_request_test/h2_proxy_server_finishes_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_shutdown_finishes_calls_nosec_test/h2_proxy_shutdown_finishes_calls_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_shutdown_finishes_calls_nosec_test/h2_proxy_shutdown_finishes_calls_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_shutdown_finishes_calls_test/h2_proxy_shutdown_finishes_calls_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_shutdown_finishes_calls_test/h2_proxy_shutdown_finishes_calls_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_shutdown_finishes_tags_nosec_test/h2_proxy_shutdown_finishes_tags_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_shutdown_finishes_tags_nosec_test/h2_proxy_shutdown_finishes_tags_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_shutdown_finishes_tags_test/h2_proxy_shutdown_finishes_tags_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_shutdown_finishes_tags_test/h2_proxy_shutdown_finishes_tags_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_simple_delayed_request_nosec_test/h2_proxy_simple_delayed_request_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_simple_delayed_request_nosec_test/h2_proxy_simple_delayed_request_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_simple_delayed_request_test/h2_proxy_simple_delayed_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_simple_delayed_request_test/h2_proxy_simple_delayed_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_simple_request_nosec_test/h2_proxy_simple_request_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_simple_request_nosec_test/h2_proxy_simple_request_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_simple_request_test/h2_proxy_simple_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_simple_request_test/h2_proxy_simple_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_trailing_metadata_nosec_test/h2_proxy_trailing_metadata_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_trailing_metadata_nosec_test/h2_proxy_trailing_metadata_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_proxy_trailing_metadata_test/h2_proxy_trailing_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_proxy_trailing_metadata_test/h2_proxy_trailing_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_bad_hostname_nosec_test/h2_sockpair+trace_bad_hostname_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_bad_hostname_nosec_test/h2_sockpair+trace_bad_hostname_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_bad_hostname_test/h2_sockpair+trace_bad_hostname_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_bad_hostname_test/h2_sockpair+trace_bad_hostname_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_binary_metadata_nosec_test/h2_sockpair+trace_binary_metadata_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_binary_metadata_nosec_test/h2_sockpair+trace_binary_metadata_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_binary_metadata_test/h2_sockpair+trace_binary_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_binary_metadata_test/h2_sockpair+trace_binary_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_call_creds_test/h2_sockpair+trace_call_creds_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_call_creds_test/h2_sockpair+trace_call_creds_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_cancel_after_accept_nosec_test/h2_sockpair+trace_cancel_after_accept_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_cancel_after_accept_nosec_test/h2_sockpair+trace_cancel_after_accept_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_cancel_after_accept_test/h2_sockpair+trace_cancel_after_accept_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_cancel_after_accept_test/h2_sockpair+trace_cancel_after_accept_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_cancel_after_client_done_nosec_test/h2_sockpair+trace_cancel_after_client_done_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_cancel_after_client_done_nosec_test/h2_sockpair+trace_cancel_after_client_done_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_cancel_after_client_done_test/h2_sockpair+trace_cancel_after_client_done_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_cancel_after_client_done_test/h2_sockpair+trace_cancel_after_client_done_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_cancel_after_invoke_nosec_test/h2_sockpair+trace_cancel_after_invoke_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_cancel_after_invoke_nosec_test/h2_sockpair+trace_cancel_after_invoke_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_cancel_after_invoke_test/h2_sockpair+trace_cancel_after_invoke_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_cancel_after_invoke_test/h2_sockpair+trace_cancel_after_invoke_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_cancel_before_invoke_nosec_test/h2_sockpair+trace_cancel_before_invoke_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_cancel_before_invoke_nosec_test/h2_sockpair+trace_cancel_before_invoke_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_cancel_before_invoke_test/h2_sockpair+trace_cancel_before_invoke_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_cancel_before_invoke_test/h2_sockpair+trace_cancel_before_invoke_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_cancel_in_a_vacuum_nosec_test/h2_sockpair+trace_cancel_in_a_vacuum_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_cancel_in_a_vacuum_nosec_test/h2_sockpair+trace_cancel_in_a_vacuum_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_cancel_in_a_vacuum_test/h2_sockpair+trace_cancel_in_a_vacuum_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_cancel_in_a_vacuum_test/h2_sockpair+trace_cancel_in_a_vacuum_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_census_simple_request_nosec_test/h2_sockpair+trace_census_simple_request_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_census_simple_request_nosec_test/h2_sockpair+trace_census_simple_request_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_census_simple_request_test/h2_sockpair+trace_census_simple_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_census_simple_request_test/h2_sockpair+trace_census_simple_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_compressed_payload_nosec_test/h2_sockpair+trace_compressed_payload_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_compressed_payload_nosec_test/h2_sockpair+trace_compressed_payload_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_compressed_payload_test/h2_sockpair+trace_compressed_payload_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_compressed_payload_test/h2_sockpair+trace_compressed_payload_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_empty_batch_nosec_test/h2_sockpair+trace_empty_batch_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_empty_batch_nosec_test/h2_sockpair+trace_empty_batch_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_empty_batch_test/h2_sockpair+trace_empty_batch_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_empty_batch_test/h2_sockpair+trace_empty_batch_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_graceful_server_shutdown_nosec_test/h2_sockpair+trace_graceful_server_shutdown_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_graceful_server_shutdown_nosec_test/h2_sockpair+trace_graceful_server_shutdown_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_graceful_server_shutdown_test/h2_sockpair+trace_graceful_server_shutdown_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_graceful_server_shutdown_test/h2_sockpair+trace_graceful_server_shutdown_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_high_initial_seqno_nosec_test/h2_sockpair+trace_high_initial_seqno_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_high_initial_seqno_nosec_test/h2_sockpair+trace_high_initial_seqno_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_high_initial_seqno_test/h2_sockpair+trace_high_initial_seqno_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_high_initial_seqno_test/h2_sockpair+trace_high_initial_seqno_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_invoke_large_request_nosec_test/h2_sockpair+trace_invoke_large_request_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_invoke_large_request_nosec_test/h2_sockpair+trace_invoke_large_request_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_invoke_large_request_test/h2_sockpair+trace_invoke_large_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_invoke_large_request_test/h2_sockpair+trace_invoke_large_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_large_metadata_nosec_test/h2_sockpair+trace_large_metadata_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_large_metadata_nosec_test/h2_sockpair+trace_large_metadata_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_large_metadata_test/h2_sockpair+trace_large_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_large_metadata_test/h2_sockpair+trace_large_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_max_concurrent_streams_nosec_test/h2_sockpair+trace_max_concurrent_streams_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_max_concurrent_streams_nosec_test/h2_sockpair+trace_max_concurrent_streams_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_max_concurrent_streams_test/h2_sockpair+trace_max_concurrent_streams_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_max_concurrent_streams_test/h2_sockpair+trace_max_concurrent_streams_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_max_message_length_nosec_test/h2_sockpair+trace_max_message_length_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_max_message_length_nosec_test/h2_sockpair+trace_max_message_length_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_max_message_length_test/h2_sockpair+trace_max_message_length_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_max_message_length_test/h2_sockpair+trace_max_message_length_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_metadata_nosec_test/h2_sockpair+trace_metadata_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_metadata_nosec_test/h2_sockpair+trace_metadata_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_metadata_test/h2_sockpair+trace_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_metadata_test/h2_sockpair+trace_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_no_op_nosec_test/h2_sockpair+trace_no_op_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_no_op_nosec_test/h2_sockpair+trace_no_op_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_no_op_test/h2_sockpair+trace_no_op_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_no_op_test/h2_sockpair+trace_no_op_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_payload_nosec_test/h2_sockpair+trace_payload_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_payload_nosec_test/h2_sockpair+trace_payload_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_payload_test/h2_sockpair+trace_payload_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_payload_test/h2_sockpair+trace_payload_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_ping_pong_streaming_nosec_test/h2_sockpair+trace_ping_pong_streaming_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_ping_pong_streaming_nosec_test/h2_sockpair+trace_ping_pong_streaming_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_ping_pong_streaming_test/h2_sockpair+trace_ping_pong_streaming_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_ping_pong_streaming_test/h2_sockpair+trace_ping_pong_streaming_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_registered_call_nosec_test/h2_sockpair+trace_registered_call_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_registered_call_nosec_test/h2_sockpair+trace_registered_call_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_registered_call_test/h2_sockpair+trace_registered_call_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_registered_call_test/h2_sockpair+trace_registered_call_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_request_with_flags_nosec_test/h2_sockpair+trace_request_with_flags_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_request_with_flags_nosec_test/h2_sockpair+trace_request_with_flags_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_request_with_flags_test/h2_sockpair+trace_request_with_flags_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_request_with_flags_test/h2_sockpair+trace_request_with_flags_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_request_with_payload_nosec_test/h2_sockpair+trace_request_with_payload_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_request_with_payload_nosec_test/h2_sockpair+trace_request_with_payload_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_request_with_payload_test/h2_sockpair+trace_request_with_payload_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_request_with_payload_test/h2_sockpair+trace_request_with_payload_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_server_finishes_request_nosec_test/h2_sockpair+trace_server_finishes_request_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_server_finishes_request_nosec_test/h2_sockpair+trace_server_finishes_request_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_server_finishes_request_test/h2_sockpair+trace_server_finishes_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_server_finishes_request_test/h2_sockpair+trace_server_finishes_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_shutdown_finishes_calls_nosec_test/h2_sockpair+trace_shutdown_finishes_calls_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_shutdown_finishes_calls_nosec_test/h2_sockpair+trace_shutdown_finishes_calls_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_shutdown_finishes_calls_test/h2_sockpair+trace_shutdown_finishes_calls_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_shutdown_finishes_calls_test/h2_sockpair+trace_shutdown_finishes_calls_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_shutdown_finishes_tags_nosec_test/h2_sockpair+trace_shutdown_finishes_tags_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_shutdown_finishes_tags_nosec_test/h2_sockpair+trace_shutdown_finishes_tags_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_shutdown_finishes_tags_test/h2_sockpair+trace_shutdown_finishes_tags_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_shutdown_finishes_tags_test/h2_sockpair+trace_shutdown_finishes_tags_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_simple_request_nosec_test/h2_sockpair+trace_simple_request_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_simple_request_nosec_test/h2_sockpair+trace_simple_request_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_simple_request_test/h2_sockpair+trace_simple_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_simple_request_test/h2_sockpair+trace_simple_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_trailing_metadata_nosec_test/h2_sockpair+trace_trailing_metadata_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_trailing_metadata_nosec_test/h2_sockpair+trace_trailing_metadata_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_trailing_metadata_test/h2_sockpair+trace_trailing_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair+trace_trailing_metadata_test/h2_sockpair+trace_trailing_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_bad_hostname_nosec_test/h2_sockpair_1byte_bad_hostname_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_bad_hostname_nosec_test/h2_sockpair_1byte_bad_hostname_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_bad_hostname_test/h2_sockpair_1byte_bad_hostname_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_bad_hostname_test/h2_sockpair_1byte_bad_hostname_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_binary_metadata_nosec_test/h2_sockpair_1byte_binary_metadata_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_binary_metadata_nosec_test/h2_sockpair_1byte_binary_metadata_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_binary_metadata_test/h2_sockpair_1byte_binary_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_binary_metadata_test/h2_sockpair_1byte_binary_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_call_creds_test/h2_sockpair_1byte_call_creds_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_call_creds_test/h2_sockpair_1byte_call_creds_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_cancel_after_accept_nosec_test/h2_sockpair_1byte_cancel_after_accept_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_cancel_after_accept_nosec_test/h2_sockpair_1byte_cancel_after_accept_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_cancel_after_accept_test/h2_sockpair_1byte_cancel_after_accept_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_cancel_after_accept_test/h2_sockpair_1byte_cancel_after_accept_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_cancel_after_client_done_nosec_test/h2_sockpair_1byte_cancel_after_client_done_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_cancel_after_client_done_nosec_test/h2_sockpair_1byte_cancel_after_client_done_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_cancel_after_client_done_test/h2_sockpair_1byte_cancel_after_client_done_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_cancel_after_client_done_test/h2_sockpair_1byte_cancel_after_client_done_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_cancel_after_invoke_nosec_test/h2_sockpair_1byte_cancel_after_invoke_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_cancel_after_invoke_nosec_test/h2_sockpair_1byte_cancel_after_invoke_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_cancel_after_invoke_test/h2_sockpair_1byte_cancel_after_invoke_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_cancel_after_invoke_test/h2_sockpair_1byte_cancel_after_invoke_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_cancel_before_invoke_nosec_test/h2_sockpair_1byte_cancel_before_invoke_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_cancel_before_invoke_nosec_test/h2_sockpair_1byte_cancel_before_invoke_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_cancel_before_invoke_test/h2_sockpair_1byte_cancel_before_invoke_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_cancel_before_invoke_test/h2_sockpair_1byte_cancel_before_invoke_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_cancel_in_a_vacuum_nosec_test/h2_sockpair_1byte_cancel_in_a_vacuum_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_cancel_in_a_vacuum_nosec_test/h2_sockpair_1byte_cancel_in_a_vacuum_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_cancel_in_a_vacuum_test/h2_sockpair_1byte_cancel_in_a_vacuum_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_cancel_in_a_vacuum_test/h2_sockpair_1byte_cancel_in_a_vacuum_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_census_simple_request_nosec_test/h2_sockpair_1byte_census_simple_request_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_census_simple_request_nosec_test/h2_sockpair_1byte_census_simple_request_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_census_simple_request_test/h2_sockpair_1byte_census_simple_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_census_simple_request_test/h2_sockpair_1byte_census_simple_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_compressed_payload_nosec_test/h2_sockpair_1byte_compressed_payload_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_compressed_payload_nosec_test/h2_sockpair_1byte_compressed_payload_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_compressed_payload_test/h2_sockpair_1byte_compressed_payload_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_compressed_payload_test/h2_sockpair_1byte_compressed_payload_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_empty_batch_nosec_test/h2_sockpair_1byte_empty_batch_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_empty_batch_nosec_test/h2_sockpair_1byte_empty_batch_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_empty_batch_test/h2_sockpair_1byte_empty_batch_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_empty_batch_test/h2_sockpair_1byte_empty_batch_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_graceful_server_shutdown_nosec_test/h2_sockpair_1byte_graceful_server_shutdown_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_graceful_server_shutdown_nosec_test/h2_sockpair_1byte_graceful_server_shutdown_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_graceful_server_shutdown_test/h2_sockpair_1byte_graceful_server_shutdown_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_graceful_server_shutdown_test/h2_sockpair_1byte_graceful_server_shutdown_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_high_initial_seqno_nosec_test/h2_sockpair_1byte_high_initial_seqno_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_high_initial_seqno_nosec_test/h2_sockpair_1byte_high_initial_seqno_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_high_initial_seqno_test/h2_sockpair_1byte_high_initial_seqno_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_high_initial_seqno_test/h2_sockpair_1byte_high_initial_seqno_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_invoke_large_request_nosec_test/h2_sockpair_1byte_invoke_large_request_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_invoke_large_request_nosec_test/h2_sockpair_1byte_invoke_large_request_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_invoke_large_request_test/h2_sockpair_1byte_invoke_large_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_invoke_large_request_test/h2_sockpair_1byte_invoke_large_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_large_metadata_nosec_test/h2_sockpair_1byte_large_metadata_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_large_metadata_nosec_test/h2_sockpair_1byte_large_metadata_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_large_metadata_test/h2_sockpair_1byte_large_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_large_metadata_test/h2_sockpair_1byte_large_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_max_concurrent_streams_nosec_test/h2_sockpair_1byte_max_concurrent_streams_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_max_concurrent_streams_nosec_test/h2_sockpair_1byte_max_concurrent_streams_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_max_concurrent_streams_test/h2_sockpair_1byte_max_concurrent_streams_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_max_concurrent_streams_test/h2_sockpair_1byte_max_concurrent_streams_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_max_message_length_nosec_test/h2_sockpair_1byte_max_message_length_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_max_message_length_nosec_test/h2_sockpair_1byte_max_message_length_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_max_message_length_test/h2_sockpair_1byte_max_message_length_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_max_message_length_test/h2_sockpair_1byte_max_message_length_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_metadata_nosec_test/h2_sockpair_1byte_metadata_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_metadata_nosec_test/h2_sockpair_1byte_metadata_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_metadata_test/h2_sockpair_1byte_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_metadata_test/h2_sockpair_1byte_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_no_op_nosec_test/h2_sockpair_1byte_no_op_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_no_op_nosec_test/h2_sockpair_1byte_no_op_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_no_op_test/h2_sockpair_1byte_no_op_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_no_op_test/h2_sockpair_1byte_no_op_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_payload_nosec_test/h2_sockpair_1byte_payload_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_payload_nosec_test/h2_sockpair_1byte_payload_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_payload_test/h2_sockpair_1byte_payload_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_payload_test/h2_sockpair_1byte_payload_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_ping_pong_streaming_nosec_test/h2_sockpair_1byte_ping_pong_streaming_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_ping_pong_streaming_nosec_test/h2_sockpair_1byte_ping_pong_streaming_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_ping_pong_streaming_test/h2_sockpair_1byte_ping_pong_streaming_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_ping_pong_streaming_test/h2_sockpair_1byte_ping_pong_streaming_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_registered_call_nosec_test/h2_sockpair_1byte_registered_call_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_registered_call_nosec_test/h2_sockpair_1byte_registered_call_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_registered_call_test/h2_sockpair_1byte_registered_call_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_registered_call_test/h2_sockpair_1byte_registered_call_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_request_with_flags_nosec_test/h2_sockpair_1byte_request_with_flags_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_request_with_flags_nosec_test/h2_sockpair_1byte_request_with_flags_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_request_with_flags_test/h2_sockpair_1byte_request_with_flags_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_request_with_flags_test/h2_sockpair_1byte_request_with_flags_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_request_with_payload_nosec_test/h2_sockpair_1byte_request_with_payload_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_request_with_payload_nosec_test/h2_sockpair_1byte_request_with_payload_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_request_with_payload_test/h2_sockpair_1byte_request_with_payload_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_request_with_payload_test/h2_sockpair_1byte_request_with_payload_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_server_finishes_request_nosec_test/h2_sockpair_1byte_server_finishes_request_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_server_finishes_request_nosec_test/h2_sockpair_1byte_server_finishes_request_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_server_finishes_request_test/h2_sockpair_1byte_server_finishes_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_server_finishes_request_test/h2_sockpair_1byte_server_finishes_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_shutdown_finishes_calls_nosec_test/h2_sockpair_1byte_shutdown_finishes_calls_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_shutdown_finishes_calls_nosec_test/h2_sockpair_1byte_shutdown_finishes_calls_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_shutdown_finishes_calls_test/h2_sockpair_1byte_shutdown_finishes_calls_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_shutdown_finishes_calls_test/h2_sockpair_1byte_shutdown_finishes_calls_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_shutdown_finishes_tags_nosec_test/h2_sockpair_1byte_shutdown_finishes_tags_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_shutdown_finishes_tags_nosec_test/h2_sockpair_1byte_shutdown_finishes_tags_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_shutdown_finishes_tags_test/h2_sockpair_1byte_shutdown_finishes_tags_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_shutdown_finishes_tags_test/h2_sockpair_1byte_shutdown_finishes_tags_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_simple_request_nosec_test/h2_sockpair_1byte_simple_request_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_simple_request_nosec_test/h2_sockpair_1byte_simple_request_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_simple_request_test/h2_sockpair_1byte_simple_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_simple_request_test/h2_sockpair_1byte_simple_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_trailing_metadata_nosec_test/h2_sockpair_1byte_trailing_metadata_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_trailing_metadata_nosec_test/h2_sockpair_1byte_trailing_metadata_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_trailing_metadata_test/h2_sockpair_1byte_trailing_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_1byte_trailing_metadata_test/h2_sockpair_1byte_trailing_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_bad_hostname_nosec_test/h2_sockpair_bad_hostname_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_bad_hostname_nosec_test/h2_sockpair_bad_hostname_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_bad_hostname_test/h2_sockpair_bad_hostname_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_bad_hostname_test/h2_sockpair_bad_hostname_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_binary_metadata_nosec_test/h2_sockpair_binary_metadata_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_binary_metadata_nosec_test/h2_sockpair_binary_metadata_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_binary_metadata_test/h2_sockpair_binary_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_binary_metadata_test/h2_sockpair_binary_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_call_creds_test/h2_sockpair_call_creds_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_call_creds_test/h2_sockpair_call_creds_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_cancel_after_accept_nosec_test/h2_sockpair_cancel_after_accept_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_cancel_after_accept_nosec_test/h2_sockpair_cancel_after_accept_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_cancel_after_accept_test/h2_sockpair_cancel_after_accept_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_cancel_after_accept_test/h2_sockpair_cancel_after_accept_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_cancel_after_client_done_nosec_test/h2_sockpair_cancel_after_client_done_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_cancel_after_client_done_nosec_test/h2_sockpair_cancel_after_client_done_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_cancel_after_client_done_test/h2_sockpair_cancel_after_client_done_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_cancel_after_client_done_test/h2_sockpair_cancel_after_client_done_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_cancel_after_invoke_nosec_test/h2_sockpair_cancel_after_invoke_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_cancel_after_invoke_nosec_test/h2_sockpair_cancel_after_invoke_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_cancel_after_invoke_test/h2_sockpair_cancel_after_invoke_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_cancel_after_invoke_test/h2_sockpair_cancel_after_invoke_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_cancel_before_invoke_nosec_test/h2_sockpair_cancel_before_invoke_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_cancel_before_invoke_nosec_test/h2_sockpair_cancel_before_invoke_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_cancel_before_invoke_test/h2_sockpair_cancel_before_invoke_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_cancel_before_invoke_test/h2_sockpair_cancel_before_invoke_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_cancel_in_a_vacuum_nosec_test/h2_sockpair_cancel_in_a_vacuum_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_cancel_in_a_vacuum_nosec_test/h2_sockpair_cancel_in_a_vacuum_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_cancel_in_a_vacuum_test/h2_sockpair_cancel_in_a_vacuum_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_cancel_in_a_vacuum_test/h2_sockpair_cancel_in_a_vacuum_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_census_simple_request_nosec_test/h2_sockpair_census_simple_request_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_census_simple_request_nosec_test/h2_sockpair_census_simple_request_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_census_simple_request_test/h2_sockpair_census_simple_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_census_simple_request_test/h2_sockpair_census_simple_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_compressed_payload_nosec_test/h2_sockpair_compressed_payload_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_compressed_payload_nosec_test/h2_sockpair_compressed_payload_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_compressed_payload_test/h2_sockpair_compressed_payload_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_compressed_payload_test/h2_sockpair_compressed_payload_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_empty_batch_nosec_test/h2_sockpair_empty_batch_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_empty_batch_nosec_test/h2_sockpair_empty_batch_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_empty_batch_test/h2_sockpair_empty_batch_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_empty_batch_test/h2_sockpair_empty_batch_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_graceful_server_shutdown_nosec_test/h2_sockpair_graceful_server_shutdown_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_graceful_server_shutdown_nosec_test/h2_sockpair_graceful_server_shutdown_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_graceful_server_shutdown_test/h2_sockpair_graceful_server_shutdown_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_graceful_server_shutdown_test/h2_sockpair_graceful_server_shutdown_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_high_initial_seqno_nosec_test/h2_sockpair_high_initial_seqno_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_high_initial_seqno_nosec_test/h2_sockpair_high_initial_seqno_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_high_initial_seqno_test/h2_sockpair_high_initial_seqno_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_high_initial_seqno_test/h2_sockpair_high_initial_seqno_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_invoke_large_request_nosec_test/h2_sockpair_invoke_large_request_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_invoke_large_request_nosec_test/h2_sockpair_invoke_large_request_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_invoke_large_request_test/h2_sockpair_invoke_large_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_invoke_large_request_test/h2_sockpair_invoke_large_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_large_metadata_nosec_test/h2_sockpair_large_metadata_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_large_metadata_nosec_test/h2_sockpair_large_metadata_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_large_metadata_test/h2_sockpair_large_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_large_metadata_test/h2_sockpair_large_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_max_concurrent_streams_nosec_test/h2_sockpair_max_concurrent_streams_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_max_concurrent_streams_nosec_test/h2_sockpair_max_concurrent_streams_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_max_concurrent_streams_test/h2_sockpair_max_concurrent_streams_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_max_concurrent_streams_test/h2_sockpair_max_concurrent_streams_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_max_message_length_nosec_test/h2_sockpair_max_message_length_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_max_message_length_nosec_test/h2_sockpair_max_message_length_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_max_message_length_test/h2_sockpair_max_message_length_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_max_message_length_test/h2_sockpair_max_message_length_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_metadata_nosec_test/h2_sockpair_metadata_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_metadata_nosec_test/h2_sockpair_metadata_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_metadata_test/h2_sockpair_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_metadata_test/h2_sockpair_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_no_op_nosec_test/h2_sockpair_no_op_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_no_op_nosec_test/h2_sockpair_no_op_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_no_op_test/h2_sockpair_no_op_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_no_op_test/h2_sockpair_no_op_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_payload_nosec_test/h2_sockpair_payload_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_payload_nosec_test/h2_sockpair_payload_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_payload_test/h2_sockpair_payload_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_payload_test/h2_sockpair_payload_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_ping_pong_streaming_nosec_test/h2_sockpair_ping_pong_streaming_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_ping_pong_streaming_nosec_test/h2_sockpair_ping_pong_streaming_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_ping_pong_streaming_test/h2_sockpair_ping_pong_streaming_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_ping_pong_streaming_test/h2_sockpair_ping_pong_streaming_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_registered_call_nosec_test/h2_sockpair_registered_call_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_registered_call_nosec_test/h2_sockpair_registered_call_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_registered_call_test/h2_sockpair_registered_call_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_registered_call_test/h2_sockpair_registered_call_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_request_with_flags_nosec_test/h2_sockpair_request_with_flags_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_request_with_flags_nosec_test/h2_sockpair_request_with_flags_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_request_with_flags_test/h2_sockpair_request_with_flags_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_request_with_flags_test/h2_sockpair_request_with_flags_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_request_with_payload_nosec_test/h2_sockpair_request_with_payload_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_request_with_payload_nosec_test/h2_sockpair_request_with_payload_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_request_with_payload_test/h2_sockpair_request_with_payload_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_request_with_payload_test/h2_sockpair_request_with_payload_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_server_finishes_request_nosec_test/h2_sockpair_server_finishes_request_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_server_finishes_request_nosec_test/h2_sockpair_server_finishes_request_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_server_finishes_request_test/h2_sockpair_server_finishes_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_server_finishes_request_test/h2_sockpair_server_finishes_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_shutdown_finishes_calls_nosec_test/h2_sockpair_shutdown_finishes_calls_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_shutdown_finishes_calls_nosec_test/h2_sockpair_shutdown_finishes_calls_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_shutdown_finishes_calls_test/h2_sockpair_shutdown_finishes_calls_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_shutdown_finishes_calls_test/h2_sockpair_shutdown_finishes_calls_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_shutdown_finishes_tags_nosec_test/h2_sockpair_shutdown_finishes_tags_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_shutdown_finishes_tags_nosec_test/h2_sockpair_shutdown_finishes_tags_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_shutdown_finishes_tags_test/h2_sockpair_shutdown_finishes_tags_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_shutdown_finishes_tags_test/h2_sockpair_shutdown_finishes_tags_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_simple_request_nosec_test/h2_sockpair_simple_request_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_simple_request_nosec_test/h2_sockpair_simple_request_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_simple_request_test/h2_sockpair_simple_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_simple_request_test/h2_sockpair_simple_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_trailing_metadata_nosec_test/h2_sockpair_trailing_metadata_nosec_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_trailing_metadata_nosec_test/h2_sockpair_trailing_metadata_nosec_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_sockpair_trailing_metadata_test/h2_sockpair_trailing_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_sockpair_trailing_metadata_test/h2_sockpair_trailing_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_bad_hostname_test/h2_ssl_bad_hostname_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_bad_hostname_test/h2_ssl_bad_hostname_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_binary_metadata_test/h2_ssl_binary_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_binary_metadata_test/h2_ssl_binary_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_call_creds_test/h2_ssl_call_creds_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_call_creds_test/h2_ssl_call_creds_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_cancel_after_accept_test/h2_ssl_cancel_after_accept_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_cancel_after_accept_test/h2_ssl_cancel_after_accept_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_cancel_after_client_done_test/h2_ssl_cancel_after_client_done_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_cancel_after_client_done_test/h2_ssl_cancel_after_client_done_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_cancel_after_invoke_test/h2_ssl_cancel_after_invoke_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_cancel_after_invoke_test/h2_ssl_cancel_after_invoke_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_cancel_before_invoke_test/h2_ssl_cancel_before_invoke_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_cancel_before_invoke_test/h2_ssl_cancel_before_invoke_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_cancel_in_a_vacuum_test/h2_ssl_cancel_in_a_vacuum_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_cancel_in_a_vacuum_test/h2_ssl_cancel_in_a_vacuum_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_census_simple_request_test/h2_ssl_census_simple_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_census_simple_request_test/h2_ssl_census_simple_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_channel_connectivity_test/h2_ssl_channel_connectivity_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_channel_connectivity_test/h2_ssl_channel_connectivity_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_compressed_payload_test/h2_ssl_compressed_payload_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_compressed_payload_test/h2_ssl_compressed_payload_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_default_host_test/h2_ssl_default_host_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_default_host_test/h2_ssl_default_host_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_disappearing_server_test/h2_ssl_disappearing_server_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_disappearing_server_test/h2_ssl_disappearing_server_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_empty_batch_test/h2_ssl_empty_batch_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_empty_batch_test/h2_ssl_empty_batch_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_graceful_server_shutdown_test/h2_ssl_graceful_server_shutdown_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_graceful_server_shutdown_test/h2_ssl_graceful_server_shutdown_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_high_initial_seqno_test/h2_ssl_high_initial_seqno_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_high_initial_seqno_test/h2_ssl_high_initial_seqno_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_invoke_large_request_test/h2_ssl_invoke_large_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_invoke_large_request_test/h2_ssl_invoke_large_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_large_metadata_test/h2_ssl_large_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_large_metadata_test/h2_ssl_large_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_max_concurrent_streams_test/h2_ssl_max_concurrent_streams_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_max_concurrent_streams_test/h2_ssl_max_concurrent_streams_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_max_message_length_test/h2_ssl_max_message_length_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_max_message_length_test/h2_ssl_max_message_length_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_metadata_test/h2_ssl_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_metadata_test/h2_ssl_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_no_op_test/h2_ssl_no_op_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_no_op_test/h2_ssl_no_op_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_payload_test/h2_ssl_payload_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_payload_test/h2_ssl_payload_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_ping_pong_streaming_test/h2_ssl_ping_pong_streaming_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_ping_pong_streaming_test/h2_ssl_ping_pong_streaming_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_bad_hostname_test/h2_ssl_proxy_bad_hostname_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_bad_hostname_test/h2_ssl_proxy_bad_hostname_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_binary_metadata_test/h2_ssl_proxy_binary_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_binary_metadata_test/h2_ssl_proxy_binary_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_call_creds_test/h2_ssl_proxy_call_creds_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_call_creds_test/h2_ssl_proxy_call_creds_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_cancel_after_accept_test/h2_ssl_proxy_cancel_after_accept_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_cancel_after_accept_test/h2_ssl_proxy_cancel_after_accept_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_cancel_after_client_done_test/h2_ssl_proxy_cancel_after_client_done_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_cancel_after_client_done_test/h2_ssl_proxy_cancel_after_client_done_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_cancel_after_invoke_test/h2_ssl_proxy_cancel_after_invoke_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_cancel_after_invoke_test/h2_ssl_proxy_cancel_after_invoke_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_cancel_before_invoke_test/h2_ssl_proxy_cancel_before_invoke_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_cancel_before_invoke_test/h2_ssl_proxy_cancel_before_invoke_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_cancel_in_a_vacuum_test/h2_ssl_proxy_cancel_in_a_vacuum_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_cancel_in_a_vacuum_test/h2_ssl_proxy_cancel_in_a_vacuum_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_census_simple_request_test/h2_ssl_proxy_census_simple_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_census_simple_request_test/h2_ssl_proxy_census_simple_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_default_host_test/h2_ssl_proxy_default_host_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_default_host_test/h2_ssl_proxy_default_host_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_disappearing_server_test/h2_ssl_proxy_disappearing_server_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_disappearing_server_test/h2_ssl_proxy_disappearing_server_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_empty_batch_test/h2_ssl_proxy_empty_batch_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_empty_batch_test/h2_ssl_proxy_empty_batch_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_graceful_server_shutdown_test/h2_ssl_proxy_graceful_server_shutdown_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_graceful_server_shutdown_test/h2_ssl_proxy_graceful_server_shutdown_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_high_initial_seqno_test/h2_ssl_proxy_high_initial_seqno_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_high_initial_seqno_test/h2_ssl_proxy_high_initial_seqno_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_invoke_large_request_test/h2_ssl_proxy_invoke_large_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_invoke_large_request_test/h2_ssl_proxy_invoke_large_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_large_metadata_test/h2_ssl_proxy_large_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_large_metadata_test/h2_ssl_proxy_large_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_max_message_length_test/h2_ssl_proxy_max_message_length_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_max_message_length_test/h2_ssl_proxy_max_message_length_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_metadata_test/h2_ssl_proxy_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_metadata_test/h2_ssl_proxy_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_no_op_test/h2_ssl_proxy_no_op_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_no_op_test/h2_ssl_proxy_no_op_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_payload_test/h2_ssl_proxy_payload_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_payload_test/h2_ssl_proxy_payload_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_ping_pong_streaming_test/h2_ssl_proxy_ping_pong_streaming_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_ping_pong_streaming_test/h2_ssl_proxy_ping_pong_streaming_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_registered_call_test/h2_ssl_proxy_registered_call_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_registered_call_test/h2_ssl_proxy_registered_call_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_request_with_payload_test/h2_ssl_proxy_request_with_payload_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_request_with_payload_test/h2_ssl_proxy_request_with_payload_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_server_finishes_request_test/h2_ssl_proxy_server_finishes_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_server_finishes_request_test/h2_ssl_proxy_server_finishes_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_shutdown_finishes_calls_test/h2_ssl_proxy_shutdown_finishes_calls_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_shutdown_finishes_calls_test/h2_ssl_proxy_shutdown_finishes_calls_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_shutdown_finishes_tags_test/h2_ssl_proxy_shutdown_finishes_tags_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_shutdown_finishes_tags_test/h2_ssl_proxy_shutdown_finishes_tags_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_simple_delayed_request_test/h2_ssl_proxy_simple_delayed_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_simple_delayed_request_test/h2_ssl_proxy_simple_delayed_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_simple_request_test/h2_ssl_proxy_simple_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_simple_request_test/h2_ssl_proxy_simple_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_trailing_metadata_test/h2_ssl_proxy_trailing_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_proxy_trailing_metadata_test/h2_ssl_proxy_trailing_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_registered_call_test/h2_ssl_registered_call_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_registered_call_test/h2_ssl_registered_call_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_request_with_flags_test/h2_ssl_request_with_flags_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_request_with_flags_test/h2_ssl_request_with_flags_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_request_with_payload_test/h2_ssl_request_with_payload_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_request_with_payload_test/h2_ssl_request_with_payload_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_server_finishes_request_test/h2_ssl_server_finishes_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_server_finishes_request_test/h2_ssl_server_finishes_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_shutdown_finishes_calls_test/h2_ssl_shutdown_finishes_calls_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_shutdown_finishes_calls_test/h2_ssl_shutdown_finishes_calls_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_shutdown_finishes_tags_test/h2_ssl_shutdown_finishes_tags_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_shutdown_finishes_tags_test/h2_ssl_shutdown_finishes_tags_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_simple_delayed_request_test/h2_ssl_simple_delayed_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_simple_delayed_request_test/h2_ssl_simple_delayed_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_simple_request_test/h2_ssl_simple_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_simple_request_test/h2_ssl_simple_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/h2_ssl_trailing_metadata_test/h2_ssl_trailing_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/h2_ssl_trailing_metadata_test/h2_ssl_trailing_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/hpack_parser_test/hpack_parser_test.vcxproj create mode 100644 vsprojects/vcxproj/test/hpack_parser_test/hpack_parser_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/hpack_table_test/hpack_table_test.vcxproj create mode 100644 vsprojects/vcxproj/test/hpack_table_test/hpack_table_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/httpcli_format_request_test/httpcli_format_request_test.vcxproj create mode 100644 vsprojects/vcxproj/test/httpcli_format_request_test/httpcli_format_request_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/httpcli_parser_test/httpcli_parser_test.vcxproj create mode 100644 vsprojects/vcxproj/test/httpcli_parser_test/httpcli_parser_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/initial_settings_frame_bad_client_test/initial_settings_frame_bad_client_test.vcxproj create mode 100644 vsprojects/vcxproj/test/initial_settings_frame_bad_client_test/initial_settings_frame_bad_client_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/json_rewrite/json_rewrite.vcxproj create mode 100644 vsprojects/vcxproj/test/json_rewrite/json_rewrite.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/json_rewrite_test/json_rewrite_test.vcxproj create mode 100644 vsprojects/vcxproj/test/json_rewrite_test/json_rewrite_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/json_test/json_test.vcxproj create mode 100644 vsprojects/vcxproj/test/json_test/json_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/lame_client_test/lame_client_test.vcxproj create mode 100644 vsprojects/vcxproj/test/lame_client_test/lame_client_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/message_compress_test/message_compress_test.vcxproj create mode 100644 vsprojects/vcxproj/test/message_compress_test/message_compress_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/mock_test/mock_test.vcxproj create mode 100644 vsprojects/vcxproj/test/mock_test/mock_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/multi_init_test/multi_init_test.vcxproj create mode 100644 vsprojects/vcxproj/test/multi_init_test/multi_init_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/multiple_server_queues_test/multiple_server_queues_test.vcxproj create mode 100644 vsprojects/vcxproj/test/multiple_server_queues_test/multiple_server_queues_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/murmur_hash_test/murmur_hash_test.vcxproj create mode 100644 vsprojects/vcxproj/test/murmur_hash_test/murmur_hash_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/no_server_test/no_server_test.vcxproj create mode 100644 vsprojects/vcxproj/test/no_server_test/no_server_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/reconnect_interop_client/reconnect_interop_client.vcxproj create mode 100644 vsprojects/vcxproj/test/reconnect_interop_client/reconnect_interop_client.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/reconnect_interop_server/reconnect_interop_server.vcxproj create mode 100644 vsprojects/vcxproj/test/reconnect_interop_server/reconnect_interop_server.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/resolve_address_test/resolve_address_test.vcxproj create mode 100644 vsprojects/vcxproj/test/resolve_address_test/resolve_address_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/secure_auth_context_test/secure_auth_context_test.vcxproj create mode 100644 vsprojects/vcxproj/test/secure_auth_context_test/secure_auth_context_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/secure_endpoint_test/secure_endpoint_test.vcxproj create mode 100644 vsprojects/vcxproj/test/secure_endpoint_test/secure_endpoint_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/server_crash_test_client/server_crash_test_client.vcxproj create mode 100644 vsprojects/vcxproj/test/server_crash_test_client/server_crash_test_client.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/shutdown_test/shutdown_test.vcxproj create mode 100644 vsprojects/vcxproj/test/shutdown_test/shutdown_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/sockaddr_utils_test/sockaddr_utils_test.vcxproj create mode 100644 vsprojects/vcxproj/test/sockaddr_utils_test/sockaddr_utils_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/status_test/status_test.vcxproj create mode 100644 vsprojects/vcxproj/test/status_test/status_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/thread_stress_test/thread_stress_test.vcxproj create mode 100644 vsprojects/vcxproj/test/thread_stress_test/thread_stress_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/time_averaged_stats_test/time_averaged_stats_test.vcxproj create mode 100644 vsprojects/vcxproj/test/time_averaged_stats_test/time_averaged_stats_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/timeout_encoding_test/timeout_encoding_test.vcxproj create mode 100644 vsprojects/vcxproj/test/timeout_encoding_test/timeout_encoding_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/timers_test/timers_test.vcxproj create mode 100644 vsprojects/vcxproj/test/timers_test/timers_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/transport_metadata_test/transport_metadata_test.vcxproj create mode 100644 vsprojects/vcxproj/test/transport_metadata_test/transport_metadata_test.vcxproj.filters create mode 100644 vsprojects/vcxproj/test/uri_parser_test/uri_parser_test.vcxproj create mode 100644 vsprojects/vcxproj/test/uri_parser_test/uri_parser_test.vcxproj.filters create mode 100644 vsprojects/winsock.props create mode 100644 vsprojects/zlib-dll.props create mode 100644 vsprojects/zlib.props diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..4b3f13fa --- /dev/null +++ b/.clang-format @@ -0,0 +1,5 @@ +--- +Language: Cpp +BasedOnStyle: Google +... + diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..f7445169 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,7 @@ +root = true +[**] +end_of_line = LF +indent_style = space +indent_size = 2 +insert_final_newline = true +tab_width = 8 diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..45f8fdaa --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +# C/C++ build outputs +bins +gens +libs +objs + +# Python virtual environments +python*_virtual_environment + +# gcov coverage data +coverage +*.gcno + +# profiler output +*.prof + +# python compiled objects +*.pyc + +#eclipse project files +.cproject +.project +.settings + +# cache for run_tests.py +.run_tests_cache + +# emacs temp files +*~ + +# vim temp files +.*.swp + +# Makefile's cache +cache.mk + +# Temporary test reports +report.xml + +# port server log +portlog.txt diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..4d35fdec --- /dev/null +++ b/.gitmodules @@ -0,0 +1,17 @@ +[submodule "third_party/zlib"] + path = third_party/zlib + url = https://github.com/madler/zlib +[submodule "third_party/openssl"] + path = third_party/openssl + url = https://github.com/openssl/openssl.git + branch = OpenSSL_1_0_2-stable +[submodule "third_party/protobuf"] + path = third_party/protobuf + url = https://github.com/google/protobuf.git + branch = v3.0.0-alpha-4.1 +[submodule "third_party/gflags"] + path = third_party/gflags + url = https://github.com/gflags/gflags.git +[submodule "third_party/googletest"] + path = third_party/googletest + url = https://github.com/google/googletest.git diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..b6c80629 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,42 @@ +language: cpp +before_install: + - sudo add-apt-repository ppa:yjwong/gflags -y + - sudo add-apt-repository ppa:h-rayflood/llvm -y + - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF + - echo "deb http://download.mono-project.com/repo/debian wheezy main" | sudo tee /etc/apt/sources.list.d/mono-xamarin.list + - echo "deb http://download.mono-project.com/repo/debian wheezy-libtiff-compat main" | sudo tee -a /etc/apt/sources.list.d/mono-xamarin.list + - sudo apt-get update -qq + - sudo apt-get install -qq libgtest-dev libgflags-dev python-virtualenv python-dev python3-dev clang-3.5 + - sudo pip install --upgrade virtualenv + - sudo pip install cpp-coveralls mako simplejson + - sudo apt-get install -qq mono-devel nunit + - wget www.nuget.org/NuGet.exe -O nuget.exe +env: + global: + - RUBY_VERSION=2.1 + - COVERALLS_PARALLEL=true + - CPPFLAGS=-I/tmp/prebuilt/include + - NUGET="mono nuget.exe" + matrix: + - CONFIG=opt TEST=sanity JOBS=1 + - CONFIG=gcov TEST=c JOBS=16 + - CONFIG=gcov TEST=c++ JOBS=16 + - CONFIG=opt TEST=c JOBS=16 + - CONFIG=opt TEST=c++ JOBS=16 + - CONFIG=opt TEST=node JOBS=16 + - CONFIG=opt TEST=ruby JOBS=16 + - CONFIG=opt TEST=python JOBS=1 + - CONFIG=opt TEST=csharp JOBS=16 + - USE_GCC=4.4 CONFIG=opt TEST=build JOBS=16 +script: + - rvm use $RUBY_VERSION + - gem install bundler + - ./tools/run_tests/prepare_travis.sh + - if [ ! -z "$USE_GCC" ] ; then export CC=gcc-$USE_GCC ; export CXX=g++-$USE_GCC ; fi + - ./tools/run_tests/run_tests.py -l $TEST -t -j $JOBS -c $CONFIG -s 4.0 +after_success: + - if [ "$CONFIG" = "gcov" ] ; then coveralls --exclude third_party --exclude gens --exclude test --exclude tools --exclude src/compiler -b. --gcov-options '\-p' ; fi +notifications: + email: false + webhooks: + - https://coveralls.io/webhook?repo_token=54IxAHPjJNdQJzJAhPU0MFpCtg7KvcydQ diff --git a/BUILD b/BUILD new file mode 100644 index 00000000..7ee84383 --- /dev/null +++ b/BUILD @@ -0,0 +1,1433 @@ +# GRPC Bazel BUILD file. +# This currently builds C, C++ and Objective-C code. +# This file has been automatically generated from a template file. +# Please look at the templates directory instead. +# This file can be regenerated from the template by running +# tools/buildgen/generate_projects.sh + +# Copyright 2015, 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. + +licenses(["notice"]) # 3-clause BSD + +package(default_visibility = ["//visibility:public"]) + + + + +cc_library( + name = "gpr", + srcs = [ + "src/core/support/env.h", + "src/core/support/file.h", + "src/core/support/murmur_hash.h", + "src/core/support/stack_lockfree.h", + "src/core/support/string.h", + "src/core/support/string_win32.h", + "src/core/support/thd_internal.h", + "src/core/support/time_precise.h", + "src/core/support/alloc.c", + "src/core/support/cmdline.c", + "src/core/support/cpu_iphone.c", + "src/core/support/cpu_linux.c", + "src/core/support/cpu_posix.c", + "src/core/support/cpu_windows.c", + "src/core/support/env_linux.c", + "src/core/support/env_posix.c", + "src/core/support/env_win32.c", + "src/core/support/file.c", + "src/core/support/file_posix.c", + "src/core/support/file_win32.c", + "src/core/support/histogram.c", + "src/core/support/host_port.c", + "src/core/support/log.c", + "src/core/support/log_android.c", + "src/core/support/log_linux.c", + "src/core/support/log_posix.c", + "src/core/support/log_win32.c", + "src/core/support/murmur_hash.c", + "src/core/support/slice.c", + "src/core/support/slice_buffer.c", + "src/core/support/stack_lockfree.c", + "src/core/support/string.c", + "src/core/support/string_posix.c", + "src/core/support/string_win32.c", + "src/core/support/subprocess_posix.c", + "src/core/support/sync.c", + "src/core/support/sync_posix.c", + "src/core/support/sync_win32.c", + "src/core/support/thd.c", + "src/core/support/thd_posix.c", + "src/core/support/thd_win32.c", + "src/core/support/time.c", + "src/core/support/time_posix.c", + "src/core/support/time_win32.c", + "src/core/support/tls_pthread.c", + ], + hdrs = [ + "include/grpc/support/alloc.h", + "include/grpc/support/atm.h", + "include/grpc/support/atm_gcc_atomic.h", + "include/grpc/support/atm_gcc_sync.h", + "include/grpc/support/atm_win32.h", + "include/grpc/support/cmdline.h", + "include/grpc/support/cpu.h", + "include/grpc/support/histogram.h", + "include/grpc/support/host_port.h", + "include/grpc/support/log.h", + "include/grpc/support/log_win32.h", + "include/grpc/support/port_platform.h", + "include/grpc/support/slice.h", + "include/grpc/support/slice_buffer.h", + "include/grpc/support/string_util.h", + "include/grpc/support/subprocess.h", + "include/grpc/support/sync.h", + "include/grpc/support/sync_generic.h", + "include/grpc/support/sync_posix.h", + "include/grpc/support/sync_win32.h", + "include/grpc/support/thd.h", + "include/grpc/support/time.h", + "include/grpc/support/tls.h", + "include/grpc/support/tls_gcc.h", + "include/grpc/support/tls_msvc.h", + "include/grpc/support/tls_pthread.h", + "include/grpc/support/useful.h", + ], + includes = [ + "include", + ".", + ], + deps = [ + ], +) + + +cc_library( + name = "grpc", + srcs = [ + "src/core/security/auth_filters.h", + "src/core/security/base64.h", + "src/core/security/credentials.h", + "src/core/security/json_token.h", + "src/core/security/jwt_verifier.h", + "src/core/security/secure_endpoint.h", + "src/core/security/secure_transport_setup.h", + "src/core/security/security_connector.h", + "src/core/security/security_context.h", + "src/core/tsi/fake_transport_security.h", + "src/core/tsi/ssl_transport_security.h", + "src/core/tsi/transport_security.h", + "src/core/tsi/transport_security_interface.h", + "src/core/census/grpc_filter.h", + "src/core/channel/channel_args.h", + "src/core/channel/channel_stack.h", + "src/core/channel/client_channel.h", + "src/core/channel/compress_filter.h", + "src/core/channel/connected_channel.h", + "src/core/channel/context.h", + "src/core/channel/http_client_filter.h", + "src/core/channel/http_server_filter.h", + "src/core/channel/noop_filter.h", + "src/core/client_config/client_config.h", + "src/core/client_config/connector.h", + "src/core/client_config/lb_policies/pick_first.h", + "src/core/client_config/lb_policy.h", + "src/core/client_config/resolver.h", + "src/core/client_config/resolver_factory.h", + "src/core/client_config/resolver_registry.h", + "src/core/client_config/resolvers/dns_resolver.h", + "src/core/client_config/resolvers/sockaddr_resolver.h", + "src/core/client_config/subchannel.h", + "src/core/client_config/subchannel_factory.h", + "src/core/client_config/subchannel_factory_decorators/add_channel_arg.h", + "src/core/client_config/subchannel_factory_decorators/merge_channel_args.h", + "src/core/client_config/uri_parser.h", + "src/core/compression/message_compress.h", + "src/core/debug/trace.h", + "src/core/httpcli/format_request.h", + "src/core/httpcli/httpcli.h", + "src/core/httpcli/parser.h", + "src/core/iomgr/alarm.h", + "src/core/iomgr/alarm_heap.h", + "src/core/iomgr/alarm_internal.h", + "src/core/iomgr/endpoint.h", + "src/core/iomgr/endpoint_pair.h", + "src/core/iomgr/fd_posix.h", + "src/core/iomgr/iocp_windows.h", + "src/core/iomgr/iomgr.h", + "src/core/iomgr/iomgr_internal.h", + "src/core/iomgr/iomgr_posix.h", + "src/core/iomgr/pollset.h", + "src/core/iomgr/pollset_posix.h", + "src/core/iomgr/pollset_set.h", + "src/core/iomgr/pollset_set_posix.h", + "src/core/iomgr/pollset_set_windows.h", + "src/core/iomgr/pollset_windows.h", + "src/core/iomgr/resolve_address.h", + "src/core/iomgr/sockaddr.h", + "src/core/iomgr/sockaddr_posix.h", + "src/core/iomgr/sockaddr_utils.h", + "src/core/iomgr/sockaddr_win32.h", + "src/core/iomgr/socket_utils_posix.h", + "src/core/iomgr/socket_windows.h", + "src/core/iomgr/tcp_client.h", + "src/core/iomgr/tcp_posix.h", + "src/core/iomgr/tcp_server.h", + "src/core/iomgr/tcp_windows.h", + "src/core/iomgr/time_averaged_stats.h", + "src/core/iomgr/udp_server.h", + "src/core/iomgr/wakeup_fd_pipe.h", + "src/core/iomgr/wakeup_fd_posix.h", + "src/core/json/json.h", + "src/core/json/json_common.h", + "src/core/json/json_reader.h", + "src/core/json/json_writer.h", + "src/core/profiling/timers.h", + "src/core/statistics/census_interface.h", + "src/core/statistics/census_rpc_stats.h", + "src/core/surface/byte_buffer_queue.h", + "src/core/surface/call.h", + "src/core/surface/channel.h", + "src/core/surface/completion_queue.h", + "src/core/surface/event_string.h", + "src/core/surface/init.h", + "src/core/surface/server.h", + "src/core/surface/surface_trace.h", + "src/core/transport/chttp2/alpn.h", + "src/core/transport/chttp2/bin_encoder.h", + "src/core/transport/chttp2/frame.h", + "src/core/transport/chttp2/frame_data.h", + "src/core/transport/chttp2/frame_goaway.h", + "src/core/transport/chttp2/frame_ping.h", + "src/core/transport/chttp2/frame_rst_stream.h", + "src/core/transport/chttp2/frame_settings.h", + "src/core/transport/chttp2/frame_window_update.h", + "src/core/transport/chttp2/hpack_parser.h", + "src/core/transport/chttp2/hpack_table.h", + "src/core/transport/chttp2/http2_errors.h", + "src/core/transport/chttp2/huffsyms.h", + "src/core/transport/chttp2/incoming_metadata.h", + "src/core/transport/chttp2/internal.h", + "src/core/transport/chttp2/status_conversion.h", + "src/core/transport/chttp2/stream_encoder.h", + "src/core/transport/chttp2/stream_map.h", + "src/core/transport/chttp2/timeout_encoding.h", + "src/core/transport/chttp2/varint.h", + "src/core/transport/chttp2_transport.h", + "src/core/transport/connectivity_state.h", + "src/core/transport/metadata.h", + "src/core/transport/stream_op.h", + "src/core/transport/transport.h", + "src/core/transport/transport_impl.h", + "src/core/census/aggregation.h", + "src/core/census/context.h", + "src/core/census/rpc_metric_id.h", + "src/core/httpcli/httpcli_security_connector.c", + "src/core/security/base64.c", + "src/core/security/client_auth_filter.c", + "src/core/security/credentials.c", + "src/core/security/credentials_metadata.c", + "src/core/security/credentials_posix.c", + "src/core/security/credentials_win32.c", + "src/core/security/google_default_credentials.c", + "src/core/security/json_token.c", + "src/core/security/jwt_verifier.c", + "src/core/security/secure_endpoint.c", + "src/core/security/secure_transport_setup.c", + "src/core/security/security_connector.c", + "src/core/security/security_context.c", + "src/core/security/server_auth_filter.c", + "src/core/security/server_secure_chttp2.c", + "src/core/surface/init_secure.c", + "src/core/surface/secure_channel_create.c", + "src/core/tsi/fake_transport_security.c", + "src/core/tsi/ssl_transport_security.c", + "src/core/tsi/transport_security.c", + "src/core/census/grpc_context.c", + "src/core/census/grpc_filter.c", + "src/core/channel/channel_args.c", + "src/core/channel/channel_stack.c", + "src/core/channel/client_channel.c", + "src/core/channel/compress_filter.c", + "src/core/channel/connected_channel.c", + "src/core/channel/http_client_filter.c", + "src/core/channel/http_server_filter.c", + "src/core/channel/noop_filter.c", + "src/core/client_config/client_config.c", + "src/core/client_config/connector.c", + "src/core/client_config/lb_policies/pick_first.c", + "src/core/client_config/lb_policy.c", + "src/core/client_config/resolver.c", + "src/core/client_config/resolver_factory.c", + "src/core/client_config/resolver_registry.c", + "src/core/client_config/resolvers/dns_resolver.c", + "src/core/client_config/resolvers/sockaddr_resolver.c", + "src/core/client_config/subchannel.c", + "src/core/client_config/subchannel_factory.c", + "src/core/client_config/subchannel_factory_decorators/add_channel_arg.c", + "src/core/client_config/subchannel_factory_decorators/merge_channel_args.c", + "src/core/client_config/uri_parser.c", + "src/core/compression/algorithm.c", + "src/core/compression/message_compress.c", + "src/core/debug/trace.c", + "src/core/httpcli/format_request.c", + "src/core/httpcli/httpcli.c", + "src/core/httpcli/parser.c", + "src/core/iomgr/alarm.c", + "src/core/iomgr/alarm_heap.c", + "src/core/iomgr/endpoint.c", + "src/core/iomgr/endpoint_pair_posix.c", + "src/core/iomgr/endpoint_pair_windows.c", + "src/core/iomgr/fd_posix.c", + "src/core/iomgr/iocp_windows.c", + "src/core/iomgr/iomgr.c", + "src/core/iomgr/iomgr_posix.c", + "src/core/iomgr/iomgr_windows.c", + "src/core/iomgr/pollset_multipoller_with_epoll.c", + "src/core/iomgr/pollset_multipoller_with_poll_posix.c", + "src/core/iomgr/pollset_posix.c", + "src/core/iomgr/pollset_set_posix.c", + "src/core/iomgr/pollset_set_windows.c", + "src/core/iomgr/pollset_windows.c", + "src/core/iomgr/resolve_address_posix.c", + "src/core/iomgr/resolve_address_windows.c", + "src/core/iomgr/sockaddr_utils.c", + "src/core/iomgr/socket_utils_common_posix.c", + "src/core/iomgr/socket_utils_linux.c", + "src/core/iomgr/socket_utils_posix.c", + "src/core/iomgr/socket_windows.c", + "src/core/iomgr/tcp_client_posix.c", + "src/core/iomgr/tcp_client_windows.c", + "src/core/iomgr/tcp_posix.c", + "src/core/iomgr/tcp_server_posix.c", + "src/core/iomgr/tcp_server_windows.c", + "src/core/iomgr/tcp_windows.c", + "src/core/iomgr/time_averaged_stats.c", + "src/core/iomgr/udp_server.c", + "src/core/iomgr/wakeup_fd_eventfd.c", + "src/core/iomgr/wakeup_fd_nospecial.c", + "src/core/iomgr/wakeup_fd_pipe.c", + "src/core/iomgr/wakeup_fd_posix.c", + "src/core/json/json.c", + "src/core/json/json_reader.c", + "src/core/json/json_string.c", + "src/core/json/json_writer.c", + "src/core/profiling/basic_timers.c", + "src/core/profiling/stap_timers.c", + "src/core/surface/byte_buffer.c", + "src/core/surface/byte_buffer_queue.c", + "src/core/surface/byte_buffer_reader.c", + "src/core/surface/call.c", + "src/core/surface/call_details.c", + "src/core/surface/call_log_batch.c", + "src/core/surface/channel.c", + "src/core/surface/channel_connectivity.c", + "src/core/surface/channel_create.c", + "src/core/surface/completion_queue.c", + "src/core/surface/event_string.c", + "src/core/surface/init.c", + "src/core/surface/lame_client.c", + "src/core/surface/metadata_array.c", + "src/core/surface/server.c", + "src/core/surface/server_chttp2.c", + "src/core/surface/server_create.c", + "src/core/surface/surface_trace.c", + "src/core/surface/version.c", + "src/core/transport/chttp2/alpn.c", + "src/core/transport/chttp2/bin_encoder.c", + "src/core/transport/chttp2/frame_data.c", + "src/core/transport/chttp2/frame_goaway.c", + "src/core/transport/chttp2/frame_ping.c", + "src/core/transport/chttp2/frame_rst_stream.c", + "src/core/transport/chttp2/frame_settings.c", + "src/core/transport/chttp2/frame_window_update.c", + "src/core/transport/chttp2/hpack_parser.c", + "src/core/transport/chttp2/hpack_table.c", + "src/core/transport/chttp2/huffsyms.c", + "src/core/transport/chttp2/incoming_metadata.c", + "src/core/transport/chttp2/parsing.c", + "src/core/transport/chttp2/status_conversion.c", + "src/core/transport/chttp2/stream_encoder.c", + "src/core/transport/chttp2/stream_lists.c", + "src/core/transport/chttp2/stream_map.c", + "src/core/transport/chttp2/timeout_encoding.c", + "src/core/transport/chttp2/varint.c", + "src/core/transport/chttp2/writing.c", + "src/core/transport/chttp2_transport.c", + "src/core/transport/connectivity_state.c", + "src/core/transport/metadata.c", + "src/core/transport/stream_op.c", + "src/core/transport/transport.c", + "src/core/transport/transport_op_string.c", + "src/core/census/context.c", + "src/core/census/initialize.c", + "src/core/census/operation.c", + "src/core/census/tracing.c", + ], + hdrs = [ + "include/grpc/grpc_security.h", + "include/grpc/byte_buffer.h", + "include/grpc/byte_buffer_reader.h", + "include/grpc/compression.h", + "include/grpc/grpc.h", + "include/grpc/status.h", + "include/grpc/census.h", + ], + includes = [ + "include", + ".", + ], + deps = [ + "//external:libssl", + "//external:zlib", + ":gpr", + ], +) + + +cc_library( + name = "grpc_unsecure", + srcs = [ + "src/core/census/grpc_filter.h", + "src/core/channel/channel_args.h", + "src/core/channel/channel_stack.h", + "src/core/channel/client_channel.h", + "src/core/channel/compress_filter.h", + "src/core/channel/connected_channel.h", + "src/core/channel/context.h", + "src/core/channel/http_client_filter.h", + "src/core/channel/http_server_filter.h", + "src/core/channel/noop_filter.h", + "src/core/client_config/client_config.h", + "src/core/client_config/connector.h", + "src/core/client_config/lb_policies/pick_first.h", + "src/core/client_config/lb_policy.h", + "src/core/client_config/resolver.h", + "src/core/client_config/resolver_factory.h", + "src/core/client_config/resolver_registry.h", + "src/core/client_config/resolvers/dns_resolver.h", + "src/core/client_config/resolvers/sockaddr_resolver.h", + "src/core/client_config/subchannel.h", + "src/core/client_config/subchannel_factory.h", + "src/core/client_config/subchannel_factory_decorators/add_channel_arg.h", + "src/core/client_config/subchannel_factory_decorators/merge_channel_args.h", + "src/core/client_config/uri_parser.h", + "src/core/compression/message_compress.h", + "src/core/debug/trace.h", + "src/core/httpcli/format_request.h", + "src/core/httpcli/httpcli.h", + "src/core/httpcli/parser.h", + "src/core/iomgr/alarm.h", + "src/core/iomgr/alarm_heap.h", + "src/core/iomgr/alarm_internal.h", + "src/core/iomgr/endpoint.h", + "src/core/iomgr/endpoint_pair.h", + "src/core/iomgr/fd_posix.h", + "src/core/iomgr/iocp_windows.h", + "src/core/iomgr/iomgr.h", + "src/core/iomgr/iomgr_internal.h", + "src/core/iomgr/iomgr_posix.h", + "src/core/iomgr/pollset.h", + "src/core/iomgr/pollset_posix.h", + "src/core/iomgr/pollset_set.h", + "src/core/iomgr/pollset_set_posix.h", + "src/core/iomgr/pollset_set_windows.h", + "src/core/iomgr/pollset_windows.h", + "src/core/iomgr/resolve_address.h", + "src/core/iomgr/sockaddr.h", + "src/core/iomgr/sockaddr_posix.h", + "src/core/iomgr/sockaddr_utils.h", + "src/core/iomgr/sockaddr_win32.h", + "src/core/iomgr/socket_utils_posix.h", + "src/core/iomgr/socket_windows.h", + "src/core/iomgr/tcp_client.h", + "src/core/iomgr/tcp_posix.h", + "src/core/iomgr/tcp_server.h", + "src/core/iomgr/tcp_windows.h", + "src/core/iomgr/time_averaged_stats.h", + "src/core/iomgr/udp_server.h", + "src/core/iomgr/wakeup_fd_pipe.h", + "src/core/iomgr/wakeup_fd_posix.h", + "src/core/json/json.h", + "src/core/json/json_common.h", + "src/core/json/json_reader.h", + "src/core/json/json_writer.h", + "src/core/profiling/timers.h", + "src/core/statistics/census_interface.h", + "src/core/statistics/census_rpc_stats.h", + "src/core/surface/byte_buffer_queue.h", + "src/core/surface/call.h", + "src/core/surface/channel.h", + "src/core/surface/completion_queue.h", + "src/core/surface/event_string.h", + "src/core/surface/init.h", + "src/core/surface/server.h", + "src/core/surface/surface_trace.h", + "src/core/transport/chttp2/alpn.h", + "src/core/transport/chttp2/bin_encoder.h", + "src/core/transport/chttp2/frame.h", + "src/core/transport/chttp2/frame_data.h", + "src/core/transport/chttp2/frame_goaway.h", + "src/core/transport/chttp2/frame_ping.h", + "src/core/transport/chttp2/frame_rst_stream.h", + "src/core/transport/chttp2/frame_settings.h", + "src/core/transport/chttp2/frame_window_update.h", + "src/core/transport/chttp2/hpack_parser.h", + "src/core/transport/chttp2/hpack_table.h", + "src/core/transport/chttp2/http2_errors.h", + "src/core/transport/chttp2/huffsyms.h", + "src/core/transport/chttp2/incoming_metadata.h", + "src/core/transport/chttp2/internal.h", + "src/core/transport/chttp2/status_conversion.h", + "src/core/transport/chttp2/stream_encoder.h", + "src/core/transport/chttp2/stream_map.h", + "src/core/transport/chttp2/timeout_encoding.h", + "src/core/transport/chttp2/varint.h", + "src/core/transport/chttp2_transport.h", + "src/core/transport/connectivity_state.h", + "src/core/transport/metadata.h", + "src/core/transport/stream_op.h", + "src/core/transport/transport.h", + "src/core/transport/transport_impl.h", + "src/core/census/aggregation.h", + "src/core/census/context.h", + "src/core/census/rpc_metric_id.h", + "src/core/surface/init_unsecure.c", + "src/core/census/grpc_context.c", + "src/core/census/grpc_filter.c", + "src/core/channel/channel_args.c", + "src/core/channel/channel_stack.c", + "src/core/channel/client_channel.c", + "src/core/channel/compress_filter.c", + "src/core/channel/connected_channel.c", + "src/core/channel/http_client_filter.c", + "src/core/channel/http_server_filter.c", + "src/core/channel/noop_filter.c", + "src/core/client_config/client_config.c", + "src/core/client_config/connector.c", + "src/core/client_config/lb_policies/pick_first.c", + "src/core/client_config/lb_policy.c", + "src/core/client_config/resolver.c", + "src/core/client_config/resolver_factory.c", + "src/core/client_config/resolver_registry.c", + "src/core/client_config/resolvers/dns_resolver.c", + "src/core/client_config/resolvers/sockaddr_resolver.c", + "src/core/client_config/subchannel.c", + "src/core/client_config/subchannel_factory.c", + "src/core/client_config/subchannel_factory_decorators/add_channel_arg.c", + "src/core/client_config/subchannel_factory_decorators/merge_channel_args.c", + "src/core/client_config/uri_parser.c", + "src/core/compression/algorithm.c", + "src/core/compression/message_compress.c", + "src/core/debug/trace.c", + "src/core/httpcli/format_request.c", + "src/core/httpcli/httpcli.c", + "src/core/httpcli/parser.c", + "src/core/iomgr/alarm.c", + "src/core/iomgr/alarm_heap.c", + "src/core/iomgr/endpoint.c", + "src/core/iomgr/endpoint_pair_posix.c", + "src/core/iomgr/endpoint_pair_windows.c", + "src/core/iomgr/fd_posix.c", + "src/core/iomgr/iocp_windows.c", + "src/core/iomgr/iomgr.c", + "src/core/iomgr/iomgr_posix.c", + "src/core/iomgr/iomgr_windows.c", + "src/core/iomgr/pollset_multipoller_with_epoll.c", + "src/core/iomgr/pollset_multipoller_with_poll_posix.c", + "src/core/iomgr/pollset_posix.c", + "src/core/iomgr/pollset_set_posix.c", + "src/core/iomgr/pollset_set_windows.c", + "src/core/iomgr/pollset_windows.c", + "src/core/iomgr/resolve_address_posix.c", + "src/core/iomgr/resolve_address_windows.c", + "src/core/iomgr/sockaddr_utils.c", + "src/core/iomgr/socket_utils_common_posix.c", + "src/core/iomgr/socket_utils_linux.c", + "src/core/iomgr/socket_utils_posix.c", + "src/core/iomgr/socket_windows.c", + "src/core/iomgr/tcp_client_posix.c", + "src/core/iomgr/tcp_client_windows.c", + "src/core/iomgr/tcp_posix.c", + "src/core/iomgr/tcp_server_posix.c", + "src/core/iomgr/tcp_server_windows.c", + "src/core/iomgr/tcp_windows.c", + "src/core/iomgr/time_averaged_stats.c", + "src/core/iomgr/udp_server.c", + "src/core/iomgr/wakeup_fd_eventfd.c", + "src/core/iomgr/wakeup_fd_nospecial.c", + "src/core/iomgr/wakeup_fd_pipe.c", + "src/core/iomgr/wakeup_fd_posix.c", + "src/core/json/json.c", + "src/core/json/json_reader.c", + "src/core/json/json_string.c", + "src/core/json/json_writer.c", + "src/core/profiling/basic_timers.c", + "src/core/profiling/stap_timers.c", + "src/core/surface/byte_buffer.c", + "src/core/surface/byte_buffer_queue.c", + "src/core/surface/byte_buffer_reader.c", + "src/core/surface/call.c", + "src/core/surface/call_details.c", + "src/core/surface/call_log_batch.c", + "src/core/surface/channel.c", + "src/core/surface/channel_connectivity.c", + "src/core/surface/channel_create.c", + "src/core/surface/completion_queue.c", + "src/core/surface/event_string.c", + "src/core/surface/init.c", + "src/core/surface/lame_client.c", + "src/core/surface/metadata_array.c", + "src/core/surface/server.c", + "src/core/surface/server_chttp2.c", + "src/core/surface/server_create.c", + "src/core/surface/surface_trace.c", + "src/core/surface/version.c", + "src/core/transport/chttp2/alpn.c", + "src/core/transport/chttp2/bin_encoder.c", + "src/core/transport/chttp2/frame_data.c", + "src/core/transport/chttp2/frame_goaway.c", + "src/core/transport/chttp2/frame_ping.c", + "src/core/transport/chttp2/frame_rst_stream.c", + "src/core/transport/chttp2/frame_settings.c", + "src/core/transport/chttp2/frame_window_update.c", + "src/core/transport/chttp2/hpack_parser.c", + "src/core/transport/chttp2/hpack_table.c", + "src/core/transport/chttp2/huffsyms.c", + "src/core/transport/chttp2/incoming_metadata.c", + "src/core/transport/chttp2/parsing.c", + "src/core/transport/chttp2/status_conversion.c", + "src/core/transport/chttp2/stream_encoder.c", + "src/core/transport/chttp2/stream_lists.c", + "src/core/transport/chttp2/stream_map.c", + "src/core/transport/chttp2/timeout_encoding.c", + "src/core/transport/chttp2/varint.c", + "src/core/transport/chttp2/writing.c", + "src/core/transport/chttp2_transport.c", + "src/core/transport/connectivity_state.c", + "src/core/transport/metadata.c", + "src/core/transport/stream_op.c", + "src/core/transport/transport.c", + "src/core/transport/transport_op_string.c", + "src/core/census/context.c", + "src/core/census/initialize.c", + "src/core/census/operation.c", + "src/core/census/tracing.c", + ], + hdrs = [ + "include/grpc/byte_buffer.h", + "include/grpc/byte_buffer_reader.h", + "include/grpc/compression.h", + "include/grpc/grpc.h", + "include/grpc/status.h", + "include/grpc/census.h", + ], + includes = [ + "include", + ".", + ], + deps = [ + ":gpr", + ], +) + + +cc_library( + name = "grpc_zookeeper", + srcs = [ + "src/core/client_config/resolvers/zookeeper_resolver.h", + "src/core/client_config/resolvers/zookeeper_resolver.c", + ], + hdrs = [ + "include/grpc/grpc_zookeeper.h", + ], + includes = [ + "include", + ".", + ], + deps = [ + ":gpr", + ":grpc", + ], +) + + +cc_library( + name = "grpc++", + srcs = [ + "src/cpp/client/secure_credentials.h", + "src/cpp/common/secure_auth_context.h", + "src/cpp/server/secure_server_credentials.h", + "src/cpp/client/create_channel_internal.h", + "src/cpp/common/create_auth_context.h", + "src/cpp/server/dynamic_thread_pool.h", + "src/cpp/server/fixed_size_thread_pool.h", + "src/cpp/server/thread_pool_interface.h", + "src/cpp/client/secure_channel_arguments.cc", + "src/cpp/client/secure_credentials.cc", + "src/cpp/common/auth_property_iterator.cc", + "src/cpp/common/secure_auth_context.cc", + "src/cpp/common/secure_create_auth_context.cc", + "src/cpp/server/secure_server_credentials.cc", + "src/cpp/client/channel.cc", + "src/cpp/client/channel_arguments.cc", + "src/cpp/client/client_context.cc", + "src/cpp/client/create_channel.cc", + "src/cpp/client/create_channel_internal.cc", + "src/cpp/client/credentials.cc", + "src/cpp/client/generic_stub.cc", + "src/cpp/client/insecure_credentials.cc", + "src/cpp/common/call.cc", + "src/cpp/common/completion_queue.cc", + "src/cpp/common/rpc_method.cc", + "src/cpp/proto/proto_utils.cc", + "src/cpp/server/async_generic_service.cc", + "src/cpp/server/create_default_thread_pool.cc", + "src/cpp/server/dynamic_thread_pool.cc", + "src/cpp/server/fixed_size_thread_pool.cc", + "src/cpp/server/insecure_server_credentials.cc", + "src/cpp/server/server.cc", + "src/cpp/server/server_builder.cc", + "src/cpp/server/server_context.cc", + "src/cpp/server/server_credentials.cc", + "src/cpp/util/byte_buffer.cc", + "src/cpp/util/slice.cc", + "src/cpp/util/status.cc", + "src/cpp/util/string_ref.cc", + "src/cpp/util/time.cc", + ], + hdrs = [ + "include/grpc++/channel.h", + "include/grpc++/client_context.h", + "include/grpc++/completion_queue.h", + "include/grpc++/create_channel.h", + "include/grpc++/generic/async_generic_service.h", + "include/grpc++/generic/generic_stub.h", + "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/proto_utils.h", + "include/grpc++/impl/rpc_method.h", + "include/grpc++/impl/rpc_service_method.h", + "include/grpc++/impl/serialization_traits.h", + "include/grpc++/impl/service_type.h", + "include/grpc++/impl/sync.h", + "include/grpc++/impl/sync_cxx11.h", + "include/grpc++/impl/sync_no_cxx11.h", + "include/grpc++/impl/thd.h", + "include/grpc++/impl/thd_cxx11.h", + "include/grpc++/impl/thd_no_cxx11.h", + "include/grpc++/security/auth_context.h", + "include/grpc++/security/auth_metadata_processor.h", + "include/grpc++/security/credentials.h", + "include/grpc++/security/server_credentials.h", + "include/grpc++/server.h", + "include/grpc++/server_builder.h", + "include/grpc++/server_context.h", + "include/grpc++/support/async_stream.h", + "include/grpc++/support/async_unary_call.h", + "include/grpc++/support/byte_buffer.h", + "include/grpc++/support/channel_arguments.h", + "include/grpc++/support/config.h", + "include/grpc++/support/config_protobuf.h", + "include/grpc++/support/slice.h", + "include/grpc++/support/status.h", + "include/grpc++/support/status_code_enum.h", + "include/grpc++/support/string_ref.h", + "include/grpc++/support/stub_options.h", + "include/grpc++/support/sync_stream.h", + "include/grpc++/support/time.h", + ], + includes = [ + "include", + ".", + ], + deps = [ + "//external:libssl", + "//external:protobuf_clib", + ":gpr", + ":grpc", + ], +) + + +cc_library( + name = "grpc++_unsecure", + srcs = [ + "src/cpp/client/create_channel_internal.h", + "src/cpp/common/create_auth_context.h", + "src/cpp/server/dynamic_thread_pool.h", + "src/cpp/server/fixed_size_thread_pool.h", + "src/cpp/server/thread_pool_interface.h", + "src/cpp/common/insecure_create_auth_context.cc", + "src/cpp/client/channel.cc", + "src/cpp/client/channel_arguments.cc", + "src/cpp/client/client_context.cc", + "src/cpp/client/create_channel.cc", + "src/cpp/client/create_channel_internal.cc", + "src/cpp/client/credentials.cc", + "src/cpp/client/generic_stub.cc", + "src/cpp/client/insecure_credentials.cc", + "src/cpp/common/call.cc", + "src/cpp/common/completion_queue.cc", + "src/cpp/common/rpc_method.cc", + "src/cpp/proto/proto_utils.cc", + "src/cpp/server/async_generic_service.cc", + "src/cpp/server/create_default_thread_pool.cc", + "src/cpp/server/dynamic_thread_pool.cc", + "src/cpp/server/fixed_size_thread_pool.cc", + "src/cpp/server/insecure_server_credentials.cc", + "src/cpp/server/server.cc", + "src/cpp/server/server_builder.cc", + "src/cpp/server/server_context.cc", + "src/cpp/server/server_credentials.cc", + "src/cpp/util/byte_buffer.cc", + "src/cpp/util/slice.cc", + "src/cpp/util/status.cc", + "src/cpp/util/string_ref.cc", + "src/cpp/util/time.cc", + ], + hdrs = [ + "include/grpc++/channel.h", + "include/grpc++/client_context.h", + "include/grpc++/completion_queue.h", + "include/grpc++/create_channel.h", + "include/grpc++/generic/async_generic_service.h", + "include/grpc++/generic/generic_stub.h", + "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/proto_utils.h", + "include/grpc++/impl/rpc_method.h", + "include/grpc++/impl/rpc_service_method.h", + "include/grpc++/impl/serialization_traits.h", + "include/grpc++/impl/service_type.h", + "include/grpc++/impl/sync.h", + "include/grpc++/impl/sync_cxx11.h", + "include/grpc++/impl/sync_no_cxx11.h", + "include/grpc++/impl/thd.h", + "include/grpc++/impl/thd_cxx11.h", + "include/grpc++/impl/thd_no_cxx11.h", + "include/grpc++/security/auth_context.h", + "include/grpc++/security/auth_metadata_processor.h", + "include/grpc++/security/credentials.h", + "include/grpc++/security/server_credentials.h", + "include/grpc++/server.h", + "include/grpc++/server_builder.h", + "include/grpc++/server_context.h", + "include/grpc++/support/async_stream.h", + "include/grpc++/support/async_unary_call.h", + "include/grpc++/support/byte_buffer.h", + "include/grpc++/support/channel_arguments.h", + "include/grpc++/support/config.h", + "include/grpc++/support/config_protobuf.h", + "include/grpc++/support/slice.h", + "include/grpc++/support/status.h", + "include/grpc++/support/status_code_enum.h", + "include/grpc++/support/string_ref.h", + "include/grpc++/support/stub_options.h", + "include/grpc++/support/sync_stream.h", + "include/grpc++/support/time.h", + ], + includes = [ + "include", + ".", + ], + deps = [ + "//external:protobuf_clib", + ":gpr", + ":grpc_unsecure", + ], +) + + +cc_library( + name = "grpc_plugin_support", + srcs = [ + "include/grpc++/support/config.h", + "include/grpc++/support/config_protobuf.h", + "src/compiler/config.h", + "src/compiler/cpp_generator.h", + "src/compiler/cpp_generator_helpers.h", + "src/compiler/csharp_generator.h", + "src/compiler/csharp_generator_helpers.h", + "src/compiler/generator_helpers.h", + "src/compiler/objective_c_generator.h", + "src/compiler/objective_c_generator_helpers.h", + "src/compiler/python_generator.h", + "src/compiler/ruby_generator.h", + "src/compiler/ruby_generator_helpers-inl.h", + "src/compiler/ruby_generator_map-inl.h", + "src/compiler/ruby_generator_string-inl.h", + "src/compiler/cpp_generator.cc", + "src/compiler/csharp_generator.cc", + "src/compiler/objective_c_generator.cc", + "src/compiler/python_generator.cc", + "src/compiler/ruby_generator.cc", + ], + hdrs = [ + ], + includes = [ + "include", + ".", + ], + deps = [ + "//external:protobuf_compiler", + ], +) + + +cc_library( + name = "grpc_csharp_ext", + srcs = [ + "src/csharp/ext/grpc_csharp_ext.c", + ], + hdrs = [ + ], + includes = [ + "include", + ".", + ], + deps = [ + ":gpr", + ":grpc", + ], +) + + + +objc_library( + name = "gpr_objc", + srcs = [ + "src/core/support/alloc.c", + "src/core/support/cmdline.c", + "src/core/support/cpu_iphone.c", + "src/core/support/cpu_linux.c", + "src/core/support/cpu_posix.c", + "src/core/support/cpu_windows.c", + "src/core/support/env_linux.c", + "src/core/support/env_posix.c", + "src/core/support/env_win32.c", + "src/core/support/file.c", + "src/core/support/file_posix.c", + "src/core/support/file_win32.c", + "src/core/support/histogram.c", + "src/core/support/host_port.c", + "src/core/support/log.c", + "src/core/support/log_android.c", + "src/core/support/log_linux.c", + "src/core/support/log_posix.c", + "src/core/support/log_win32.c", + "src/core/support/murmur_hash.c", + "src/core/support/slice.c", + "src/core/support/slice_buffer.c", + "src/core/support/stack_lockfree.c", + "src/core/support/string.c", + "src/core/support/string_posix.c", + "src/core/support/string_win32.c", + "src/core/support/subprocess_posix.c", + "src/core/support/sync.c", + "src/core/support/sync_posix.c", + "src/core/support/sync_win32.c", + "src/core/support/thd.c", + "src/core/support/thd_posix.c", + "src/core/support/thd_win32.c", + "src/core/support/time.c", + "src/core/support/time_posix.c", + "src/core/support/time_win32.c", + "src/core/support/tls_pthread.c", + ], + hdrs = [ + "include/grpc/support/alloc.h", + "include/grpc/support/atm.h", + "include/grpc/support/atm_gcc_atomic.h", + "include/grpc/support/atm_gcc_sync.h", + "include/grpc/support/atm_win32.h", + "include/grpc/support/cmdline.h", + "include/grpc/support/cpu.h", + "include/grpc/support/histogram.h", + "include/grpc/support/host_port.h", + "include/grpc/support/log.h", + "include/grpc/support/log_win32.h", + "include/grpc/support/port_platform.h", + "include/grpc/support/slice.h", + "include/grpc/support/slice_buffer.h", + "include/grpc/support/string_util.h", + "include/grpc/support/subprocess.h", + "include/grpc/support/sync.h", + "include/grpc/support/sync_generic.h", + "include/grpc/support/sync_posix.h", + "include/grpc/support/sync_win32.h", + "include/grpc/support/thd.h", + "include/grpc/support/time.h", + "include/grpc/support/tls.h", + "include/grpc/support/tls_gcc.h", + "include/grpc/support/tls_msvc.h", + "include/grpc/support/tls_pthread.h", + "include/grpc/support/useful.h", + "src/core/support/env.h", + "src/core/support/file.h", + "src/core/support/murmur_hash.h", + "src/core/support/stack_lockfree.h", + "src/core/support/string.h", + "src/core/support/string_win32.h", + "src/core/support/thd_internal.h", + "src/core/support/time_precise.h", + ], + includes = [ + "include", + ".", + ], + deps = [ + ], +) + + +objc_library( + name = "grpc_objc", + srcs = [ + "src/core/httpcli/httpcli_security_connector.c", + "src/core/security/base64.c", + "src/core/security/client_auth_filter.c", + "src/core/security/credentials.c", + "src/core/security/credentials_metadata.c", + "src/core/security/credentials_posix.c", + "src/core/security/credentials_win32.c", + "src/core/security/google_default_credentials.c", + "src/core/security/json_token.c", + "src/core/security/jwt_verifier.c", + "src/core/security/secure_endpoint.c", + "src/core/security/secure_transport_setup.c", + "src/core/security/security_connector.c", + "src/core/security/security_context.c", + "src/core/security/server_auth_filter.c", + "src/core/security/server_secure_chttp2.c", + "src/core/surface/init_secure.c", + "src/core/surface/secure_channel_create.c", + "src/core/tsi/fake_transport_security.c", + "src/core/tsi/ssl_transport_security.c", + "src/core/tsi/transport_security.c", + "src/core/census/grpc_context.c", + "src/core/census/grpc_filter.c", + "src/core/channel/channel_args.c", + "src/core/channel/channel_stack.c", + "src/core/channel/client_channel.c", + "src/core/channel/compress_filter.c", + "src/core/channel/connected_channel.c", + "src/core/channel/http_client_filter.c", + "src/core/channel/http_server_filter.c", + "src/core/channel/noop_filter.c", + "src/core/client_config/client_config.c", + "src/core/client_config/connector.c", + "src/core/client_config/lb_policies/pick_first.c", + "src/core/client_config/lb_policy.c", + "src/core/client_config/resolver.c", + "src/core/client_config/resolver_factory.c", + "src/core/client_config/resolver_registry.c", + "src/core/client_config/resolvers/dns_resolver.c", + "src/core/client_config/resolvers/sockaddr_resolver.c", + "src/core/client_config/subchannel.c", + "src/core/client_config/subchannel_factory.c", + "src/core/client_config/subchannel_factory_decorators/add_channel_arg.c", + "src/core/client_config/subchannel_factory_decorators/merge_channel_args.c", + "src/core/client_config/uri_parser.c", + "src/core/compression/algorithm.c", + "src/core/compression/message_compress.c", + "src/core/debug/trace.c", + "src/core/httpcli/format_request.c", + "src/core/httpcli/httpcli.c", + "src/core/httpcli/parser.c", + "src/core/iomgr/alarm.c", + "src/core/iomgr/alarm_heap.c", + "src/core/iomgr/endpoint.c", + "src/core/iomgr/endpoint_pair_posix.c", + "src/core/iomgr/endpoint_pair_windows.c", + "src/core/iomgr/fd_posix.c", + "src/core/iomgr/iocp_windows.c", + "src/core/iomgr/iomgr.c", + "src/core/iomgr/iomgr_posix.c", + "src/core/iomgr/iomgr_windows.c", + "src/core/iomgr/pollset_multipoller_with_epoll.c", + "src/core/iomgr/pollset_multipoller_with_poll_posix.c", + "src/core/iomgr/pollset_posix.c", + "src/core/iomgr/pollset_set_posix.c", + "src/core/iomgr/pollset_set_windows.c", + "src/core/iomgr/pollset_windows.c", + "src/core/iomgr/resolve_address_posix.c", + "src/core/iomgr/resolve_address_windows.c", + "src/core/iomgr/sockaddr_utils.c", + "src/core/iomgr/socket_utils_common_posix.c", + "src/core/iomgr/socket_utils_linux.c", + "src/core/iomgr/socket_utils_posix.c", + "src/core/iomgr/socket_windows.c", + "src/core/iomgr/tcp_client_posix.c", + "src/core/iomgr/tcp_client_windows.c", + "src/core/iomgr/tcp_posix.c", + "src/core/iomgr/tcp_server_posix.c", + "src/core/iomgr/tcp_server_windows.c", + "src/core/iomgr/tcp_windows.c", + "src/core/iomgr/time_averaged_stats.c", + "src/core/iomgr/udp_server.c", + "src/core/iomgr/wakeup_fd_eventfd.c", + "src/core/iomgr/wakeup_fd_nospecial.c", + "src/core/iomgr/wakeup_fd_pipe.c", + "src/core/iomgr/wakeup_fd_posix.c", + "src/core/json/json.c", + "src/core/json/json_reader.c", + "src/core/json/json_string.c", + "src/core/json/json_writer.c", + "src/core/profiling/basic_timers.c", + "src/core/profiling/stap_timers.c", + "src/core/surface/byte_buffer.c", + "src/core/surface/byte_buffer_queue.c", + "src/core/surface/byte_buffer_reader.c", + "src/core/surface/call.c", + "src/core/surface/call_details.c", + "src/core/surface/call_log_batch.c", + "src/core/surface/channel.c", + "src/core/surface/channel_connectivity.c", + "src/core/surface/channel_create.c", + "src/core/surface/completion_queue.c", + "src/core/surface/event_string.c", + "src/core/surface/init.c", + "src/core/surface/lame_client.c", + "src/core/surface/metadata_array.c", + "src/core/surface/server.c", + "src/core/surface/server_chttp2.c", + "src/core/surface/server_create.c", + "src/core/surface/surface_trace.c", + "src/core/surface/version.c", + "src/core/transport/chttp2/alpn.c", + "src/core/transport/chttp2/bin_encoder.c", + "src/core/transport/chttp2/frame_data.c", + "src/core/transport/chttp2/frame_goaway.c", + "src/core/transport/chttp2/frame_ping.c", + "src/core/transport/chttp2/frame_rst_stream.c", + "src/core/transport/chttp2/frame_settings.c", + "src/core/transport/chttp2/frame_window_update.c", + "src/core/transport/chttp2/hpack_parser.c", + "src/core/transport/chttp2/hpack_table.c", + "src/core/transport/chttp2/huffsyms.c", + "src/core/transport/chttp2/incoming_metadata.c", + "src/core/transport/chttp2/parsing.c", + "src/core/transport/chttp2/status_conversion.c", + "src/core/transport/chttp2/stream_encoder.c", + "src/core/transport/chttp2/stream_lists.c", + "src/core/transport/chttp2/stream_map.c", + "src/core/transport/chttp2/timeout_encoding.c", + "src/core/transport/chttp2/varint.c", + "src/core/transport/chttp2/writing.c", + "src/core/transport/chttp2_transport.c", + "src/core/transport/connectivity_state.c", + "src/core/transport/metadata.c", + "src/core/transport/stream_op.c", + "src/core/transport/transport.c", + "src/core/transport/transport_op_string.c", + "src/core/census/context.c", + "src/core/census/initialize.c", + "src/core/census/operation.c", + "src/core/census/tracing.c", + ], + hdrs = [ + "include/grpc/grpc_security.h", + "include/grpc/byte_buffer.h", + "include/grpc/byte_buffer_reader.h", + "include/grpc/compression.h", + "include/grpc/grpc.h", + "include/grpc/status.h", + "include/grpc/census.h", + "src/core/security/auth_filters.h", + "src/core/security/base64.h", + "src/core/security/credentials.h", + "src/core/security/json_token.h", + "src/core/security/jwt_verifier.h", + "src/core/security/secure_endpoint.h", + "src/core/security/secure_transport_setup.h", + "src/core/security/security_connector.h", + "src/core/security/security_context.h", + "src/core/tsi/fake_transport_security.h", + "src/core/tsi/ssl_transport_security.h", + "src/core/tsi/transport_security.h", + "src/core/tsi/transport_security_interface.h", + "src/core/census/grpc_filter.h", + "src/core/channel/channel_args.h", + "src/core/channel/channel_stack.h", + "src/core/channel/client_channel.h", + "src/core/channel/compress_filter.h", + "src/core/channel/connected_channel.h", + "src/core/channel/context.h", + "src/core/channel/http_client_filter.h", + "src/core/channel/http_server_filter.h", + "src/core/channel/noop_filter.h", + "src/core/client_config/client_config.h", + "src/core/client_config/connector.h", + "src/core/client_config/lb_policies/pick_first.h", + "src/core/client_config/lb_policy.h", + "src/core/client_config/resolver.h", + "src/core/client_config/resolver_factory.h", + "src/core/client_config/resolver_registry.h", + "src/core/client_config/resolvers/dns_resolver.h", + "src/core/client_config/resolvers/sockaddr_resolver.h", + "src/core/client_config/subchannel.h", + "src/core/client_config/subchannel_factory.h", + "src/core/client_config/subchannel_factory_decorators/add_channel_arg.h", + "src/core/client_config/subchannel_factory_decorators/merge_channel_args.h", + "src/core/client_config/uri_parser.h", + "src/core/compression/message_compress.h", + "src/core/debug/trace.h", + "src/core/httpcli/format_request.h", + "src/core/httpcli/httpcli.h", + "src/core/httpcli/parser.h", + "src/core/iomgr/alarm.h", + "src/core/iomgr/alarm_heap.h", + "src/core/iomgr/alarm_internal.h", + "src/core/iomgr/endpoint.h", + "src/core/iomgr/endpoint_pair.h", + "src/core/iomgr/fd_posix.h", + "src/core/iomgr/iocp_windows.h", + "src/core/iomgr/iomgr.h", + "src/core/iomgr/iomgr_internal.h", + "src/core/iomgr/iomgr_posix.h", + "src/core/iomgr/pollset.h", + "src/core/iomgr/pollset_posix.h", + "src/core/iomgr/pollset_set.h", + "src/core/iomgr/pollset_set_posix.h", + "src/core/iomgr/pollset_set_windows.h", + "src/core/iomgr/pollset_windows.h", + "src/core/iomgr/resolve_address.h", + "src/core/iomgr/sockaddr.h", + "src/core/iomgr/sockaddr_posix.h", + "src/core/iomgr/sockaddr_utils.h", + "src/core/iomgr/sockaddr_win32.h", + "src/core/iomgr/socket_utils_posix.h", + "src/core/iomgr/socket_windows.h", + "src/core/iomgr/tcp_client.h", + "src/core/iomgr/tcp_posix.h", + "src/core/iomgr/tcp_server.h", + "src/core/iomgr/tcp_windows.h", + "src/core/iomgr/time_averaged_stats.h", + "src/core/iomgr/udp_server.h", + "src/core/iomgr/wakeup_fd_pipe.h", + "src/core/iomgr/wakeup_fd_posix.h", + "src/core/json/json.h", + "src/core/json/json_common.h", + "src/core/json/json_reader.h", + "src/core/json/json_writer.h", + "src/core/profiling/timers.h", + "src/core/statistics/census_interface.h", + "src/core/statistics/census_rpc_stats.h", + "src/core/surface/byte_buffer_queue.h", + "src/core/surface/call.h", + "src/core/surface/channel.h", + "src/core/surface/completion_queue.h", + "src/core/surface/event_string.h", + "src/core/surface/init.h", + "src/core/surface/server.h", + "src/core/surface/surface_trace.h", + "src/core/transport/chttp2/alpn.h", + "src/core/transport/chttp2/bin_encoder.h", + "src/core/transport/chttp2/frame.h", + "src/core/transport/chttp2/frame_data.h", + "src/core/transport/chttp2/frame_goaway.h", + "src/core/transport/chttp2/frame_ping.h", + "src/core/transport/chttp2/frame_rst_stream.h", + "src/core/transport/chttp2/frame_settings.h", + "src/core/transport/chttp2/frame_window_update.h", + "src/core/transport/chttp2/hpack_parser.h", + "src/core/transport/chttp2/hpack_table.h", + "src/core/transport/chttp2/http2_errors.h", + "src/core/transport/chttp2/huffsyms.h", + "src/core/transport/chttp2/incoming_metadata.h", + "src/core/transport/chttp2/internal.h", + "src/core/transport/chttp2/status_conversion.h", + "src/core/transport/chttp2/stream_encoder.h", + "src/core/transport/chttp2/stream_map.h", + "src/core/transport/chttp2/timeout_encoding.h", + "src/core/transport/chttp2/varint.h", + "src/core/transport/chttp2_transport.h", + "src/core/transport/connectivity_state.h", + "src/core/transport/metadata.h", + "src/core/transport/stream_op.h", + "src/core/transport/transport.h", + "src/core/transport/transport_impl.h", + "src/core/census/aggregation.h", + "src/core/census/context.h", + "src/core/census/rpc_metric_id.h", + ], + includes = [ + "include", + ".", + ], + deps = [ + ":gpr_objc", + "//external:libssl_objc", + ], + sdk_dylibs = ["libz"], +) + + + +cc_binary( + name = "grpc_cpp_plugin", + srcs = [ + "src/compiler/cpp_plugin.cc", + ], + deps = [ + "//external:protobuf_compiler", + ":grpc_plugin_support", + ], +) + + +cc_binary( + name = "grpc_csharp_plugin", + srcs = [ + "src/compiler/csharp_plugin.cc", + ], + deps = [ + "//external:protobuf_compiler", + ":grpc_plugin_support", + ], +) + + +cc_binary( + name = "grpc_objective_c_plugin", + srcs = [ + "src/compiler/objective_c_plugin.cc", + ], + deps = [ + "//external:protobuf_compiler", + ":grpc_plugin_support", + ], +) + + +cc_binary( + name = "grpc_python_plugin", + srcs = [ + "src/compiler/python_plugin.cc", + ], + deps = [ + "//external:protobuf_compiler", + ":grpc_plugin_support", + ], +) + + +cc_binary( + name = "grpc_ruby_plugin", + srcs = [ + "src/compiler/ruby_plugin.cc", + ], + deps = [ + "//external:protobuf_compiler", + ":grpc_plugin_support", + ], +) + + + + + + + + +objc_path = "src/objective-c" + +rx_library_path = objc_path + "/RxLibrary" + +objc_library( + name = "rx_library", + hdrs = glob([ + rx_library_path + "/*.h", + rx_library_path + "/transformations/*.h", + ]), + srcs = glob([ + rx_library_path + "/*.m", + rx_library_path + "/transformations/*.m", + ]), + includes = [objc_path], + deps = [ + ":rx_library_private", + ], +) + +objc_library( + name = "rx_library_private", + hdrs = glob([rx_library_path + "/private/*.h"]), + srcs = glob([rx_library_path + "/private/*.m"]), + visibility = ["//visibility:private"], +) + +objc_client_path = objc_path + "/GRPCClient" + +objc_library( + name = "grpc_client", + hdrs = glob([ + objc_client_path + "/*.h", + objc_client_path + "/private/*.h", + ]), + srcs = glob([ + objc_client_path + "/*.m", + objc_client_path + "/private/*.m", + ]), + includes = [objc_path], + bundles = [":gRPCCertificates"], + deps = [ + ":grpc_objc", + ":rx_library", + ], +) + +objc_bundle_library( + # The choice of name is signicant here, since it determines the bundle name. + name = "gRPCCertificates", + resources = ["etc/roots.pem"], +) + +proto_objc_rpc_path = objc_path + "/ProtoRPC" + +objc_library( + name = "proto_objc_rpc", + hdrs = glob([ + proto_objc_rpc_path + "/*.h", + ]), + srcs = glob([ + proto_objc_rpc_path + "/*.m", + ]), + includes = [objc_path], + deps = [ + ":grpc_client", + ":rx_library", + "//external:protobuf_objc", + ], +) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..9423c465 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,54 @@ +# How to contribute + +We definitely welcome patches and contribution to grpc! Here is some guideline +and information about how to do so. + +## Getting started + +### Legal requirements + +In order to protect both you and ourselves, you will need to sign the +[Contributor License Agreement](https://cla.developers.google.com/clas). + +### Technical requirements + +You will need several tools to work with this repository. In addition to all of +the packages described in the [INSTALL](INSTALL) file, you will also need +python, and the mako template renderer. To install the latter, using pip, one +should simply be able to do `pip install mako`. + +In order to run all of the tests we provide, you will need valgrind and clang. +More specifically, under debian, you will need the package libc++-dev to +properly run all the tests. + +If you are planning to work on any of the languages other than C and C++, you +will also need their appropriate development environments. + +If you want to work under Windows, we recommend the use of Visual Studio 2013. +The [Community or Express editions](http://www.visualstudio.com/en-us/downloads/download-visual-studio-vs.aspx) +are free and suitable for developing with grpc. Note however that our test +environment and tools are available for Unix environments only at the moment. + +## Testing your changes + +We provide a tool to help run the suite of tests in various environments. +In order to run most of the available tests, one would need to run: + +`./tools/run_tests/run_tests.py` + +If you want to run all the possible tests for any of the languages {c, c++, node, php, python}, do this: + +`./tools/run_tests/run_tests.py -l -c all` + +## Adding or removing source code + +Each language uses its own build system to work. Currently, the root's Makefile +and the Visual Studio project files are building only the C and C++ source code. +In order to ease the maintenance of these files, we have a +template system. Please do not contribute manual changes to any of the generated +files. Instead, modify the template files, or the build.json file, and +re-generate the project files using the following command: + +`./tools/buildgen/generate_projects.sh` + +You'll find more information about this in the [templates](templates) folder. diff --git a/INSTALL b/INSTALL new file mode 100644 index 00000000..a0df57dc --- /dev/null +++ b/INSTALL @@ -0,0 +1,225 @@ +These instructions only cover building grpc C and C++ libraries under +typical unix systems. If you need more information, please try grpc's +wiki pages: + + https://github.com/google/grpc/wiki + + +************************* +* If you are in a hurry * +************************* + +On Linux (Debian): + + Note: you will need to add the Debian 'jessie-backports' distribution to your sources + file first. + + Add the following line to your `/etc/apt/sources.list` file: + + deb http://http.debian.net/debian jessie-backports main + + Install the gRPC library: + + $ [sudo] apt-get install libgrpc-dev + +OR + + $ git clone https://github.com/grpc/grpc.git + $ cd grpc + $ git submodule update --init + $ make + $ [sudo] make install + +You don't need anything else than GNU Make, gcc and autotools. Under a Debian +or Ubuntu system, this should boil down to the following packages: + + $ [sudo] apt-get install build-essential autoconf libtool + +Building the python wrapper requires the following: + + $ [sudo] apt-get install python-all-dev python-virtualenv + +If you want to install in a different directory than the default /usr/lib, you can +override it on the command line: + + $ [sudo] make install prefix=/opt + + +******************************* +* More detailled instructions * +******************************* + +Setting up dependencies +======================= + +Dependencies to compile the libraries +------------------------------------- + +grpc libraries have few external dependencies. If you need to compile and +install them, they are present in the third_party directory if you have +cloned the github repository recursively. If you didn't clone recursively, +you can still get them later by running the following command: + + $ git submodule update --init + +Note that the Makefile makes it much easier for you to compile from sources +if you were to clone recursively our git repository: it will automatically +compile zlib and OpenSSL, which are core requirements for grpc. Note this +creates grpc libraries that will have zlib and OpenSSL built-in inside of them, +which significantly increases the libraries' size. + +In order to decrease that size, you can manually install zlib and OpenSSL on +your system, so that the Makefile can use them instead. + +Under a Debian or Ubuntu system, one can acquire the development package +for zlib this way: + + # apt-get install zlib1g-dev + +To the best of our knowledge, no distribution has an OpenSSL package that +supports ALPN yet, so you would still have to depend on installing from source +for that particular dependency if you want to reduce the libraries' size. + +The recommended version of OpenSSL that provides ALPN support is available +at this URL: + + https://www.openssl.org/source/openssl-1.0.2.tar.gz + + +Dependencies to compile and run the tests +----------------------------------------- + +Compiling and running grpc plain-C tests dont't require any more dependency. + + +Compiling and running grpc C++ tests depend on protobuf 3.0.0, gtest and +gflags. Although gflags is provided in third_party, you will need to manually +install that dependency on your system to run these tests. + +Under a Debian or Ubuntu system, you can install the gtests and gflags packages +using apt-get: + + # apt-get install libgflags-dev libgtest-dev + +However, protobuf 3.0.0 isn't in a debian package yet, but the Makefile will +automatically try and compile the one present in third_party if you cloned the +repository recursively, and that it detects your system is lacking it. + +Compiling and installing protobuf 3.0.0 requires a few more dependencies in +itself, notably the autoconf suite. If you have apt-get, you can install +these dependencies this way: + + # apt-get install autoconf libtool + +If you want to run the tests using one of the sanitized configurations, you +will need clang and its instrumented libc++: + + # apt-get install clang libc++-dev + +Mac-specific notes: +------------------- + +For a Mac system, git is not available by default. You will first need to +install Xcode from the Mac AppStore and then run the following command from a +terminal: + + $ sudo xcode-select --install + +You should also install "port" following the instructions at +https://www.macports.org . This will reside in /opt/local/bin/port for +most Mac installations. Do the "git submodule" command listed above. + +Then execute the following for all the needed build dependencies + + $ sudo /opt/local/bin/port install autoconf automake libtool gflags cmake + $ mkdir ~/gtest-svn + $ svn checkout http://googletest.googlecode.com/svn/trunk/ gtest-svn + $ mkdir mybuild + $ cd mybuild + $ cmake ../gtest-svn + $ make + $ make gtest.a gtest_main.a + $ sudo cp libgtest.a libgtest_main.a /opt/local/lib + $ sudo mkdir /opt/local/include/gtest + $ sudo cp -pr ../gtest-svn/include/gtest /opt/local/include/gtest + +We will also need to make openssl and install it appropriately + + $ cd + $ cd third_party/openssl + $ ./config + $ sudo make install + $ cd ../../ + +If you are going to make changes and need to regenerate the projects file, +you will need to install certain modules for python. + + $ sudo easy_install simplejson mako + +Mingw-specific notes: +--------------------- + +While gRPC compiles properly under mingw, some more preparation work is needed. +The recommendation is to use msys2. The installation instructions are available +at that address: http://msys2.github.io/ + +Once this is installed, make sure you are using the following: MinGW-w64 Win64. +You'll be required to install a few more packages: + + $ pacman -S make mingw-w64-x86_64-gcc mingw-w64-x86_64-zlib autoconf automake libtool + +Please also install OpenSSL from that website: + + http://slproweb.com/products/Win32OpenSSL.html + +The package Win64 OpenSSL v1.0.2a should do. At that point you should be able +to compile gRPC with the following: + + $ export LDFLAGS="-L/mingw64/lib -L/c/OpenSSL-Win64" + $ export CPPFLAGS="-I/mingw64/include -I/c/OpenSSL-Win64/include" + $ make + +A word on OpenSSL +----------------- + +Secure HTTP2 requires the TLS extension ALPN (see rfc 7301 and +http://http2.github.io/http2-spec/ section 3.3). Our HTTP2 implementation +relies on OpenSSL's implementation. OpenSSL 1.0.2 is the first released version +of OpenSSL that has ALPN support, and this explains our dependency on it. + +Note that the Makefile supports compiling only the unsecure elements of grpc, +and if you do not have OpenSSL and do not want it, you can still proceed +with installing only the elements you require. However, we strongly recommend +the use of encryption for all network traffic, and discourage the use of grpc +without TLS. + + +Compiling +========= + +If you have all the dependencies mentioned above, you should simply be able +to go ahead and run "make" to compile grpc's C and C++ libraries: + + $ make + + +Testing +======= + +To build and run the tests, you can run the command: + + $ make test + +If you want to be able to run them in parallel, and get better output, you can +also use the python tool we have written: + + $ ./tools/run_tests/run_tests.py + + +Installing +========== + +Once everything is compiled, you should be able to install grpc C and C++ +libraries and headers: + + # make install diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..0209b570 --- /dev/null +++ b/LICENSE @@ -0,0 +1,28 @@ +Copyright 2015, 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. diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..bba9592a --- /dev/null +++ b/Makefile @@ -0,0 +1,20573 @@ +# GRPC global makefile +# This currently builds C and C++ code. +# This file has been automatically generated from a template file. +# Please look at the templates directory instead. +# This file can be regenerated from the template by running +# tools/buildgen/generate_projects.sh + +# Copyright 2015, 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. + + + +# Basic platform detection +HOST_SYSTEM = $(shell uname | cut -f 1 -d_) +ifeq ($(SYSTEM),) +SYSTEM = $(HOST_SYSTEM) +endif +ifeq ($(SYSTEM),MSYS) +SYSTEM = MINGW32 +endif +ifeq ($(SYSTEM),MINGW64) +SYSTEM = MINGW32 +endif + + +MAKEFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) +ifndef BUILDDIR +BUILDDIR_ABSOLUTE = $(patsubst %/,%,$(dir $(MAKEFILE_PATH))) +else +BUILDDIR_ABSOLUTE = $(abspath $(BUILDDIR)) +endif + +HAS_GCC = $(shell which gcc > /dev/null 2> /dev/null && echo true || echo false) +HAS_CC = $(shell which cc > /dev/null 2> /dev/null && echo true || echo false) +HAS_CLANG = $(shell which clang > /dev/null 2> /dev/null && echo true || echo false) + +ifeq ($(HAS_CC),true) +DEFAULT_CC = cc +DEFAULT_CXX = c++ +else +ifeq ($(HAS_GCC),true) +DEFAULT_CC = gcc +DEFAULT_CXX = g++ +else +ifeq ($(HAS_CLANG),true) +DEFAULT_CC = clang +DEFAULT_CXX = clang++ +else +DEFAULT_CC = no_c_compiler +DEFAULT_CXX = no_c++_compiler +endif +endif +endif + + +BINDIR = $(BUILDDIR_ABSOLUTE)/bins +OBJDIR = $(BUILDDIR_ABSOLUTE)/objs +LIBDIR = $(BUILDDIR_ABSOLUTE)/libs +GENDIR = $(BUILDDIR_ABSOLUTE)/gens + +# Configurations + +VALID_CONFIG_opt = 1 +CC_opt = $(DEFAULT_CC) +CXX_opt = $(DEFAULT_CXX) +LD_opt = $(DEFAULT_CC) +LDXX_opt = $(DEFAULT_CXX) +CPPFLAGS_opt = -O2 +LDFLAGS_opt = +DEFINES_opt = NDEBUG + +VALID_CONFIG_basicprof = 1 +CC_basicprof = $(DEFAULT_CC) +CXX_basicprof = $(DEFAULT_CXX) +LD_basicprof = $(DEFAULT_CC) +LDXX_basicprof = $(DEFAULT_CXX) +CPPFLAGS_basicprof = -O2 -DGRPC_BASIC_PROFILER -DGRPC_TIMERS_RDTSC +LDFLAGS_basicprof = +DEFINES_basicprof = NDEBUG + +VALID_CONFIG_stapprof = 1 +CC_stapprof = $(DEFAULT_CC) +CXX_stapprof = $(DEFAULT_CXX) +LD_stapprof = $(DEFAULT_CC) +LDXX_stapprof = $(DEFAULT_CXX) +CPPFLAGS_stapprof = -O2 -DGRPC_STAP_PROFILER +LDFLAGS_stapprof = +DEFINES_stapprof = NDEBUG + +VALID_CONFIG_dbg = 1 +CC_dbg = $(DEFAULT_CC) +CXX_dbg = $(DEFAULT_CXX) +LD_dbg = $(DEFAULT_CC) +LDXX_dbg = $(DEFAULT_CXX) +CPPFLAGS_dbg = -O0 +LDFLAGS_dbg = +DEFINES_dbg = _DEBUG DEBUG + +VALID_CONFIG_mutrace = 1 +CC_mutrace = $(DEFAULT_CC) +CXX_mutrace = $(DEFAULT_CXX) +LD_mutrace = $(DEFAULT_CC) +LDXX_mutrace = $(DEFAULT_CXX) +CPPFLAGS_mutrace = -O0 +LDFLAGS_mutrace = -rdynamic +DEFINES_mutrace = _DEBUG DEBUG + +VALID_CONFIG_valgrind = 1 +REQUIRE_CUSTOM_LIBRARIES_valgrind = 1 +CC_valgrind = $(DEFAULT_CC) +CXX_valgrind = $(DEFAULT_CXX) +LD_valgrind = $(DEFAULT_CC) +LDXX_valgrind = $(DEFAULT_CXX) +CPPFLAGS_valgrind = -O0 +OPENSSL_CFLAGS_valgrind = -DPURIFY +LDFLAGS_valgrind = +DEFINES_valgrind = _DEBUG DEBUG GRPC_TEST_SLOWDOWN_BUILD_FACTOR=20 + +VALID_CONFIG_tsan = 1 +REQUIRE_CUSTOM_LIBRARIES_tsan = 1 +CC_tsan = clang +CXX_tsan = clang++ +LD_tsan = clang +LDXX_tsan = clang++ +CPPFLAGS_tsan = -O0 -fsanitize=thread -fno-omit-frame-pointer -Wno-error=unused-command-line-argument +LDFLAGS_tsan = -fsanitize=thread +DEFINES_tsan = NDEBUG GRPC_TEST_SLOWDOWN_BUILD_FACTOR=10 + +VALID_CONFIG_asan = 1 +REQUIRE_CUSTOM_LIBRARIES_asan = 1 +CC_asan = clang +CXX_asan = clang++ +LD_asan = clang +LDXX_asan = clang++ +CPPFLAGS_asan = -O0 -fsanitize=address -fno-omit-frame-pointer -Wno-error=unused-command-line-argument +LDFLAGS_asan = -fsanitize=address +DEFINES_asan = GRPC_TEST_SLOWDOWN_BUILD_FACTOR=3 + +VALID_CONFIG_msan = 1 +REQUIRE_CUSTOM_LIBRARIES_msan = 1 +CC_msan = clang +CXX_msan = clang++-libc++ +LD_msan = clang +LDXX_msan = clang++-libc++ +CPPFLAGS_msan = -O0 -fsanitize=memory -fsanitize-memory-track-origins -fno-omit-frame-pointer -DGTEST_HAS_TR1_TUPLE=0 -DGTEST_USE_OWN_TR1_TUPLE=1 -Wno-error=unused-command-line-argument +OPENSSL_CFLAGS_msan = -DPURIFY +LDFLAGS_msan = -fsanitize=memory -DGTEST_HAS_TR1_TUPLE=0 -DGTEST_USE_OWN_TR1_TUPLE=1 +DEFINES_msan = NDEBUG GRPC_TEST_SLOWDOWN_BUILD_FACTOR=4 + +VALID_CONFIG_ubsan = 1 +REQUIRE_CUSTOM_LIBRARIES_ubsan = 1 +CC_ubsan = clang +CXX_ubsan = clang++ +LD_ubsan = clang +LDXX_ubsan = clang++ +CPPFLAGS_ubsan = -O1 -fsanitize=undefined -fno-omit-frame-pointer -Wno-error=unused-command-line-argument +OPENSSL_CFLAGS_ubsan = -DPURIFY +LDFLAGS_ubsan = -fsanitize=undefined +DEFINES_ubsan = NDEBUG GRPC_TEST_SLOWDOWN_BUILD_FACTOR=3 + +VALID_CONFIG_gcov = 1 +CC_gcov = gcc +CXX_gcov = g++ +LD_gcov = gcc +LDXX_gcov = g++ +CPPFLAGS_gcov = -O0 -fprofile-arcs -ftest-coverage +LDFLAGS_gcov = -fprofile-arcs -ftest-coverage +DEFINES_gcov = _DEBUG DEBUG + + +# General settings. +# You may want to change these depending on your system. + +prefix ?= /usr/local + +PROTOC = protoc +DTRACE = dtrace +CONFIG ?= opt +CC = $(CC_$(CONFIG)) +CXX = $(CXX_$(CONFIG)) +LD = $(LD_$(CONFIG)) +LDXX = $(LDXX_$(CONFIG)) +AR = ar +ifeq ($(SYSTEM),Linux) +STRIP = strip --strip-unneeded +else +ifeq ($(SYSTEM),Darwin) +STRIP = strip -x +else +STRIP = strip +endif +endif +INSTALL = install +RM = rm -f +PKG_CONFIG = pkg-config + +ifndef VALID_CONFIG_$(CONFIG) +$(error Invalid CONFIG value '$(CONFIG)') +endif + +ifeq ($(SYSTEM),Linux) +TMPOUT = /dev/null +else +TMPOUT = `mktemp /tmp/test-out-XXXXXX` +endif + +# Detect if we can use C++11 +CXX11_CHECK_CMD = $(CXX) -std=c++11 -o $(TMPOUT) -c test/build/c++11.cc +HAS_CXX11 = $(shell $(CXX11_CHECK_CMD) 2> /dev/null && echo true || echo false) + +# The HOST compiler settings are used to compile the protoc plugins. +# In most cases, you won't have to change anything, but if you are +# cross-compiling, you can override these variables from GNU make's +# command line: make CC=cross-gcc HOST_CC=gcc + +HOST_CC = $(CC) +HOST_CXX = $(CXX) +HOST_LD = $(LD) +HOST_LDXX = $(LDXX) + +ifdef EXTRA_DEFINES +DEFINES += $(EXTRA_DEFINES) +endif + +CFLAGS += -std=c89 -pedantic +ifeq ($(HAS_CXX11),true) +CXXFLAGS += -std=c++11 +else +CXXFLAGS += -std=c++0x +endif +CPPFLAGS += -g -Wall -Wextra -Werror -Wno-long-long -Wno-unused-parameter +LDFLAGS += -g + +CPPFLAGS += $(CPPFLAGS_$(CONFIG)) +DEFINES += $(DEFINES_$(CONFIG)) INSTALL_PREFIX=\"$(prefix)\" +LDFLAGS += $(LDFLAGS_$(CONFIG)) + +ifneq ($(SYSTEM),MINGW32) +PIC_CPPFLAGS = -fPIC +CPPFLAGS += -fPIC +LDFLAGS += -fPIC +endif + +INCLUDES = . include $(GENDIR) +LDFLAGS += -Llibs/$(CONFIG) + +ifeq ($(SYSTEM),Darwin) +ifneq ($(wildcard /usr/local/ssl/include),) +INCLUDES += /usr/local/ssl/include +endif +ifneq ($(wildcard /opt/local/include),) +INCLUDES += /opt/local/include +endif +ifneq ($(wildcard /usr/local/include),) +INCLUDES += /usr/local/include +endif +LIBS = m z +ifneq ($(wildcard /usr/local/ssl/lib),) +LDFLAGS += -L/usr/local/ssl/lib +endif +ifneq ($(wildcard /opt/local/lib),) +LDFLAGS += -L/opt/local/lib +endif +ifneq ($(wildcard /usr/local/lib),) +LDFLAGS += -L/usr/local/lib +endif +endif + +ifeq ($(SYSTEM),Linux) +LIBS = rt m z pthread +LDFLAGS += -pthread +endif + +ifeq ($(SYSTEM),MINGW32) +LIBS = m z pthread +LDFLAGS += -pthread +endif + +GTEST_LIB = -Ithird_party/googletest/include -Ithird_party/googletest third_party/googletest/src/gtest-all.cc +GTEST_LIB += -lgflags +ifeq ($(V),1) +E = @: +Q = +else +E = @echo +Q = @ +endif + +VERSION = 0.11.0.0 + +CPPFLAGS_NO_ARCH += $(addprefix -I, $(INCLUDES)) $(addprefix -D, $(DEFINES)) +CPPFLAGS += $(CPPFLAGS_NO_ARCH) $(ARCH_FLAGS) + +LDFLAGS += $(ARCH_FLAGS) +LDLIBS += $(addprefix -l, $(LIBS)) +LDLIBSXX += $(addprefix -l, $(LIBSXX)) + +HOST_CPPFLAGS = $(CPPFLAGS) +HOST_CFLAGS = $(CFLAGS) +HOST_CXXFLAGS = $(CXXFLAGS) +HOST_LDFLAGS = $(LDFLAGS) +HOST_LDLIBS = $(LDLIBS) + + +# These are automatically computed variables. +# There shouldn't be any need to change anything from now on. + +-include cache.mk + +CACHE_MK = + +HAS_PKG_CONFIG ?= $(shell command -v $(PKG_CONFIG) >/dev/null 2>&1 && echo true || echo false) + +ifeq ($(HAS_PKG_CONFIG), true) +CACHE_MK += HAS_PKG_CONFIG = true, +endif + +PC_TEMPLATE = prefix=$(prefix),exec_prefix=\$${prefix},includedir=\$${prefix}/include,libdir=\$${exec_prefix}/lib,,Name: $(PC_NAME),Description: $(PC_DESCRIPTION),Version: $(VERSION),Cflags: -I\$${includedir} $(PC_CFLAGS),Requires.private: $(PC_REQUIRES_PRIVATE),Libs: -L\$${libdir} $(PC_LIB),Libs.private: $(PC_LIBS_PRIVATE) + +# gpr .pc file +PC_NAME = gRPC Portable Runtime +PC_DESCRIPTION = gRPC Portable Runtime +PC_CFLAGS = -pthread +PC_REQUIRES_PRIVATE = +PC_LIBS_PRIVATE = -lpthread +PC_LIB = -lgpr +ifneq ($(SYSTEM),Darwin) +PC_LIBS_PRIVATE += -lrt +endif +GPR_PC_FILE := $(PC_TEMPLATE) + +ifeq ($(SYSTEM),MINGW32) +SHARED_EXT = dll +endif +ifeq ($(SYSTEM),Darwin) +SHARED_EXT = dylib +endif +ifeq ($(SHARED_EXT),) +SHARED_EXT = so.$(VERSION) +endif + +ifeq ($(wildcard .git),) +IS_GIT_FOLDER = false +else +IS_GIT_FOLDER = true +endif + +ifeq ($(SYSTEM),Linux) +OPENSSL_REQUIRES_DL = true +endif + +ifeq ($(SYSTEM),Darwin) +OPENSSL_REQUIRES_DL = true +endif + +ifeq ($(HAS_PKG_CONFIG),true) +OPENSSL_ALPN_CHECK_CMD = $(PKG_CONFIG) --atleast-version=1.0.2 openssl +OPENSSL_NPN_CHECK_CMD = $(PKG_CONFIG) --atleast-version=1.0.1 openssl +ZLIB_CHECK_CMD = $(PKG_CONFIG) --exists zlib +PROTOBUF_CHECK_CMD = $(PKG_CONFIG) --atleast-version=3.0.0-alpha-3 protobuf +else # HAS_PKG_CONFIG + +ifeq ($(SYSTEM),MINGW32) +OPENSSL_LIBS = ssl32 eay32 +else +OPENSSL_LIBS = ssl crypto +endif + +OPENSSL_ALPN_CHECK_CMD = $(CC) $(CFLAGS) $(CPPFLAGS) -o $(TMPOUT) test/build/openssl-alpn.c $(addprefix -l, $(OPENSSL_LIBS)) $(LDFLAGS) +OPENSSL_NPN_CHECK_CMD = $(CC) $(CFLAGS) $(CPPFLAGS) -o $(TMPOUT) test/build/openssl-npn.c $(addprefix -l, $(OPENSSL_LIBS)) $(LDFLAGS) +ZLIB_CHECK_CMD = $(CC) $(CFLAGS) $(CPPFLAGS) -o $(TMPOUT) test/build/zlib.c -lz $(LDFLAGS) +PROTOBUF_CHECK_CMD = $(CXX) $(CXXFLAGS) $(CPPFLAGS) -o $(TMPOUT) test/build/protobuf.cc -lprotobuf $(LDFLAGS) + +ifeq ($(OPENSSL_REQUIRES_DL),true) +OPENSSL_ALPN_CHECK_CMD += -ldl +OPENSSL_NPN_CHECK_CMD += -ldl +endif + +endif # HAS_PKG_CONFIG + +PERFTOOLS_CHECK_CMD = $(CC) $(CFLAGS) $(CPPFLAGS) -o $(TMPOUT) test/build/perftools.c -lprofiler $(LDFLAGS) + +PROTOC_CHECK_CMD = which protoc > /dev/null +PROTOC_CHECK_VERSION_CMD = protoc --version | grep -q libprotoc.3 +DTRACE_CHECK_CMD = which dtrace > /dev/null +SYSTEMTAP_HEADERS_CHECK_CMD = $(CC) $(CFLAGS) $(CPPFLAGS) -o $(TMPOUT) test/build/systemtap.c $(LDFLAGS) +ZOOKEEPER_CHECK_CMD = $(CC) $(CFLAGS) $(CPPFLAGS) -o $(TMPOUT) test/build/zookeeper.c $(LDFLAGS) -lzookeeper_mt + +ifndef REQUIRE_CUSTOM_LIBRARIES_$(CONFIG) +HAS_SYSTEM_PERFTOOLS ?= $(shell $(PERFTOOLS_CHECK_CMD) 2> /dev/null && echo true || echo false) +ifeq ($(HAS_SYSTEM_PERFTOOLS),true) +DEFINES += GRPC_HAVE_PERFTOOLS +LIBS += profiler +CACHE_MK += HAS_SYSTEM_PERFTOOLS = true, +endif +endif + +HAS_SYSTEM_PROTOBUF_VERIFY = $(shell $(PROTOBUF_CHECK_CMD) 2> /dev/null && echo true || echo false) +ifndef REQUIRE_CUSTOM_LIBRARIES_$(CONFIG) +HAS_SYSTEM_OPENSSL_ALPN ?= $(shell $(OPENSSL_ALPN_CHECK_CMD) 2> /dev/null && echo true || echo false) +ifeq ($(HAS_SYSTEM_OPENSSL_ALPN),true) +HAS_SYSTEM_OPENSSL_NPN = true +CACHE_MK += HAS_SYSTEM_OPENSSL_ALPN = true, +else +HAS_SYSTEM_OPENSSL_NPN ?= $(shell $(OPENSSL_NPN_CHECK_CMD) 2> /dev/null && echo true || echo false) +endif +ifeq ($(HAS_SYSTEM_OPENSSL_NPN),true) +CACHE_MK += HAS_SYSTEM_OPENSSL_NPN = true, +endif +HAS_SYSTEM_ZLIB ?= $(shell $(ZLIB_CHECK_CMD) 2> /dev/null && echo true || echo false) +ifeq ($(HAS_SYSTEM_ZLIB),true) +CACHE_MK += HAS_SYSTEM_ZLIB = true, +endif +HAS_SYSTEM_PROTOBUF ?= $(HAS_SYSTEM_PROTOBUF_VERIFY) +ifeq ($(HAS_SYSTEM_PROTOBUF),true) +CACHE_MK += HAS_SYSTEM_PROTOBUF = true, +endif +else +# override system libraries if the config requires a custom compiled library +HAS_SYSTEM_OPENSSL_ALPN = false +HAS_SYSTEM_OPENSSL_NPN = false +HAS_SYSTEM_ZLIB = false +HAS_SYSTEM_PROTOBUF = false +endif + +HAS_PROTOC ?= $(shell $(PROTOC_CHECK_CMD) 2> /dev/null && echo true || echo false) +ifeq ($(HAS_PROTOC),true) +CACHE_MK += HAS_PROTOC = true, +HAS_VALID_PROTOC ?= $(shell $(PROTOC_CHECK_VERSION_CMD) 2> /dev/null && echo true || echo false) +ifeq ($(HAS_VALID_PROTOC),true) +CACHE_MK += HAS_VALID_PROTOC = true, +endif +else +HAS_VALID_PROTOC = false +endif + +# Check for Systemtap (https://sourceware.org/systemtap/), first by making sure is present +# in the system and secondly by checking for the "dtrace" binary (on Linux, this is part of the Systemtap +# distribution. It's part of the base system on BSD/Solaris machines). +ifndef HAS_SYSTEMTAP +HAS_SYSTEMTAP_HEADERS = $(shell $(SYSTEMTAP_HEADERS_CHECK_CMD) 2> /dev/null && echo true || echo false) +HAS_DTRACE = $(shell $(DTRACE_CHECK_CMD) 2> /dev/null && echo true || echo false) +HAS_SYSTEMTAP = false +ifeq ($(HAS_SYSTEMTAP_HEADERS),true) +ifeq ($(HAS_DTRACE),true) +HAS_SYSTEMTAP = true +endif +endif +endif + +ifeq ($(HAS_SYSTEMTAP),true) +CACHE_MK += HAS_SYSTEMTAP = true, +endif + +HAS_ZOOKEEPER = $(shell $(ZOOKEEPER_CHECK_CMD) 2> /dev/null && echo true || echo false) + +# Note that for testing purposes, one can do: +# make HAS_EMBEDDED_OPENSSL_ALPN=false +# to emulate the fact we do not have OpenSSL in the third_party folder. +ifeq ($(wildcard third_party/openssl/ssl/ssl.h),) +HAS_EMBEDDED_OPENSSL_ALPN = false +else +HAS_EMBEDDED_OPENSSL_ALPN = true +endif + +ifeq ($(wildcard third_party/zlib/zlib.h),) +HAS_EMBEDDED_ZLIB = false +else +HAS_EMBEDDED_ZLIB = true +endif + +ifeq ($(wildcard third_party/protobuf/src/google/protobuf/descriptor.pb.h),) +HAS_EMBEDDED_PROTOBUF = false +ifneq ($(HAS_VALID_PROTOC),true) +NO_PROTOC = true +endif +else +HAS_EMBEDDED_PROTOBUF = true +endif + +PC_REQUIRES_GRPC = gpr +PC_LIBS_GRPC = + +ifeq ($(HAS_SYSTEM_ZLIB),false) +ifeq ($(HAS_EMBEDDED_ZLIB),true) +ZLIB_DEP = $(LIBDIR)/$(CONFIG)/zlib/libz.a +CPPFLAGS += -Ithird_party/zlib +LDFLAGS += -L$(LIBDIR)/$(CONFIG)/zlib +else +DEP_MISSING += zlib +endif +else +ifeq ($(HAS_PKG_CONFIG),true) +CPPFLAGS += $(shell $(PKG_CONFIG) --cflags zlib) +LDFLAGS += $(shell $(PKG_CONFIG) --libs-only-L zlib) +PC_REQUIRES_GRPC += zlib +else +PC_LIBS_GRPC += -lz +endif +endif + +OPENSSL_PKG_CONFIG = false + +PC_REQUIRES_SECURE = +PC_LIBS_SECURE = + +ifeq ($(HAS_SYSTEM_OPENSSL_ALPN),true) +ifeq ($(HAS_PKG_CONFIG),true) +OPENSSL_PKG_CONFIG = true +PC_REQUIRES_SECURE = openssl +CPPFLAGS := $(shell $(PKG_CONFIG) --cflags openssl) $(CPPFLAGS) +LDFLAGS_OPENSSL_PKG_CONFIG = $(shell $(PKG_CONFIG) --libs-only-L openssl) +ifeq ($(SYSTEM),Linux) +ifneq ($(LDFLAGS_OPENSSL_PKG_CONFIG),) +LDFLAGS_OPENSSL_PKG_CONFIG += $(shell $(PKG_CONFIG) --libs-only-L openssl | sed s/L/Wl,-rpath,/) +endif +endif +LDFLAGS := $(LDFLAGS_OPENSSL_PKG_CONFIG) $(LDFLAGS) +else +LIBS_SECURE = $(OPENSSL_LIBS) +ifeq ($(OPENSSL_REQUIRES_DL),true) +LIBS_SECURE += dl +PC_LIBS_SECURE = $(addprefix -l, $(LIBS_SECURE)) +endif +endif +else +ifeq ($(HAS_EMBEDDED_OPENSSL_ALPN),true) +USE_SYSTEM_OPENSSL = false +OPENSSL_DEP = $(LIBDIR)/$(CONFIG)/openssl/libssl.a +OPENSSL_MERGE_LIBS += $(LIBDIR)/$(CONFIG)/openssl/libssl.a $(LIBDIR)/$(CONFIG)/openssl/libcrypto.a +# need to prefix these to ensure overriding system libraries +CPPFLAGS := -Ithird_party/openssl/include $(CPPFLAGS) +LDFLAGS := -L$(LIBDIR)/$(CONFIG)/openssl $(LDFLAGS) +ifeq ($(OPENSSL_REQUIRES_DL),true) +LIBS_SECURE = dl +endif +else +ifeq ($(HAS_SYSTEM_OPENSSL_NPN),true) +USE_SYSTEM_OPENSSL = true +CPPFLAGS += -DTSI_OPENSSL_ALPN_SUPPORT=0 +LIBS_SECURE = $(OPENSSL_LIBS) +ifeq ($(OPENSSL_REQUIRES_DL),true) +LIBS_SECURE += dl +endif +else +NO_SECURE = true +endif +endif +endif + +ifeq ($(OPENSSL_PKG_CONFIG),true) +LDLIBS_SECURE += $(shell $(PKG_CONFIG) --libs-only-l openssl) +else +LDLIBS_SECURE += $(addprefix -l, $(LIBS_SECURE)) +endif + +# grpc .pc file +PC_NAME = gRPC +PC_DESCRIPTION = high performance general RPC framework +PC_CFLAGS = +PC_REQUIRES_PRIVATE = $(PC_REQUIRES_GRPC) $(PC_REQUIRES_SECURE) +PC_LIBS_PRIVATE = $(PC_LIBS_GRPC) $(PC_LIBS_SECURE) +PC_LIB = -lgrpc +GRPC_PC_FILE := $(PC_TEMPLATE) + +# gprc_unsecure .pc file +PC_NAME = gRPC unsecure +PC_DESCRIPTION = high performance general RPC framework without SSL +PC_CFLAGS = +PC_REQUIRES_PRIVATE = $(PC_REQUIRES_GRPC) +PC_LIBS_PRIVATE = $(PC_LIBS_GRPC) +PC_LIB = -lgrpc +GRPC_UNSECURE_PC_FILE := $(PC_TEMPLATE) + +# gprc_zookeeper .pc file +PC_NAME = gRPC zookeeper +PC_DESCRIPTION = gRPC's zookeeper plugin +PC_CFLAGS = +PC_REQUIRES_PRIVATE = +PC_LIBS_PRIVATE = -lzookeeper_mt +GRPC_ZOOKEEPER_PC_FILE := $(PC_TEMPLATE) + +PROTOBUF_PKG_CONFIG = false + +PC_REQUIRES_GRPCXX = +PC_LIBS_GRPCXX = + +CPPFLAGS := -Ithird_party/googletest/include $(CPPFLAGS) + +ifeq ($(HAS_SYSTEM_PROTOBUF),true) +ifeq ($(HAS_PKG_CONFIG),true) +PROTOBUF_PKG_CONFIG = true +PC_REQUIRES_GRPCXX = protobuf +CPPFLAGS := $(shell $(PKG_CONFIG) --cflags protobuf) $(CPPFLAGS) +LDFLAGS_PROTOBUF_PKG_CONFIG = $(shell $(PKG_CONFIG) --libs-only-L protobuf) +ifeq ($(SYSTEM),Linux) +ifneq ($(LDFLAGS_PROTOBUF_PKG_CONFIG),) +LDFLAGS_PROTOBUF_PKG_CONFIG += $(shell $(PKG_CONFIG) --libs-only-L protobuf | sed s/L/Wl,-rpath,/) +endif +endif +else +PC_LIBS_GRPCXX = -lprotobuf +endif +else +ifeq ($(HAS_EMBEDDED_PROTOBUF),true) +PROTOBUF_DEP = $(LIBDIR)/$(CONFIG)/protobuf/libprotobuf.a +CPPFLAGS := -Ithird_party/protobuf/src $(CPPFLAGS) +LDFLAGS := -L$(LIBDIR)/$(CONFIG)/protobuf $(LDFLAGS) +PROTOC = $(BINDIR)/$(CONFIG)/protobuf/protoc +else +NO_PROTOBUF = true +endif +endif + +LIBS_PROTOBUF = protobuf +LIBS_PROTOC = protoc protobuf + +HOST_LDLIBS_PROTOC += $(addprefix -l, $(LIBS_PROTOC)) + +ifeq ($(PROTOBUF_PKG_CONFIG),true) +LDLIBS_PROTOBUF += $(shell $(PKG_CONFIG) --libs-only-l protobuf) +else +LDLIBS_PROTOBUF += $(addprefix -l, $(LIBS_PROTOBUF)) +endif + +# grpc++ .pc file +PC_NAME = gRPC++ +PC_DESCRIPTION = C++ wrapper for gRPC +PC_CFLAGS = +PC_REQUIRES_PRIVATE = grpc $(PC_REQUIRES_GRPCXX) +PC_LIBS_PRIVATE = $(PC_LIBS_GRPCXX) +PC_LIB = -lgrpc++ +GRPCXX_PC_FILE := $(PC_TEMPLATE) + +# grpc++_unsecure .pc file +PC_NAME = gRPC++ unsecure +PC_DESCRIPTION = C++ wrapper for gRPC without SSL +PC_CFLAGS = +PC_REQUIRES_PRIVATE = grpc_unsecure $(PC_REQUIRES_GRPCXX) +PC_LIBS_PRIVATE = $(PC_LIBS_GRPCXX) +PC_LIB = -lgrpc++ +GRPCXX_UNSECURE_PC_FILE := $(PC_TEMPLATE) + +ifeq ($(MAKECMDGOALS),clean) +NO_DEPS = true +endif + +INSTALL_OK = false +ifeq ($(HAS_VALID_PROTOC),true) +ifeq ($(HAS_SYSTEM_PROTOBUF_VERIFY),true) +INSTALL_OK = true +endif +endif + +.SECONDARY = %.pb.h %.pb.cc + +PROTOC_PLUGINS = $(BINDIR)/$(CONFIG)/grpc_cpp_plugin $(BINDIR)/$(CONFIG)/grpc_csharp_plugin $(BINDIR)/$(CONFIG)/grpc_objective_c_plugin $(BINDIR)/$(CONFIG)/grpc_python_plugin $(BINDIR)/$(CONFIG)/grpc_ruby_plugin +ifeq ($(DEP_MISSING),) +all: static shared plugins +dep_error: + @echo "You shouldn't see this message - all of your dependencies are correct." +else +all: dep_error git_update stop + +dep_error: + @echo + @echo "DEPENDENCY ERROR" + @echo + @echo "You are missing system dependencies that are essential to build grpc," + @echo "and the third_party directory doesn't have them:" + @echo + @echo " $(DEP_MISSING)" + @echo + @echo "Installing the development packages for your system will solve" + @echo "this issue. Please consult INSTALL to get more information." + @echo + @echo "If you need information about why these tests failed, run:" + @echo + @echo " make run_dep_checks" + @echo +endif + +git_update: +ifeq ($(IS_GIT_FOLDER),true) + @echo "Additionally, since you are in a git clone, you can download the" + @echo "missing dependencies in third_party by running the following command:" + @echo + @echo " git submodule update --init" + @echo +endif + +openssl_dep_error: openssl_dep_message git_update stop + +protobuf_dep_error: protobuf_dep_message git_update stop + +protoc_dep_error: protoc_dep_message git_update stop + +openssl_dep_message: + @echo + @echo "DEPENDENCY ERROR" + @echo + @echo "The target you are trying to run requires OpenSSL." + @echo "Your system doesn't have it, and neither does the third_party directory." + @echo + @echo "Please consult INSTALL to get more information." + @echo + @echo "If you need information about why these tests failed, run:" + @echo + @echo " make run_dep_checks" + @echo + +protobuf_dep_message: + @echo + @echo "DEPENDENCY ERROR" + @echo + @echo "The target you are trying to run requires protobuf 3.0.0+" + @echo "Your system doesn't have it, and neither does the third_party directory." + @echo + @echo "Please consult INSTALL to get more information." + @echo + @echo "If you need information about why these tests failed, run:" + @echo + @echo " make run_dep_checks" + @echo + +protoc_dep_message: + @echo + @echo "DEPENDENCY ERROR" + @echo + @echo "The target you are trying to run requires protobuf-compiler 3.0.0+" + @echo "Your system doesn't have it, and neither does the third_party directory." + @echo + @echo "Please consult INSTALL to get more information." + @echo + @echo "If you need information about why these tests failed, run:" + @echo + @echo " make run_dep_checks" + @echo + +systemtap_dep_error: + @echo + @echo "DEPENDENCY ERROR" + @echo + @echo "Under the '$(CONFIG)' configutation, the target you are trying " + @echo "to build requires systemtap 2.7+ (on Linux) or dtrace (on other " + @echo "platforms such as Solaris and *BSD). " + @echo + @echo "Please consult INSTALL to get more information." + @echo + +stop: + @false + +alarm_heap_test: $(BINDIR)/$(CONFIG)/alarm_heap_test +alarm_list_test: $(BINDIR)/$(CONFIG)/alarm_list_test +alarm_test: $(BINDIR)/$(CONFIG)/alarm_test +alpn_test: $(BINDIR)/$(CONFIG)/alpn_test +bin_encoder_test: $(BINDIR)/$(CONFIG)/bin_encoder_test +chttp2_status_conversion_test: $(BINDIR)/$(CONFIG)/chttp2_status_conversion_test +chttp2_stream_encoder_test: $(BINDIR)/$(CONFIG)/chttp2_stream_encoder_test +chttp2_stream_map_test: $(BINDIR)/$(CONFIG)/chttp2_stream_map_test +compression_test: $(BINDIR)/$(CONFIG)/compression_test +dualstack_socket_test: $(BINDIR)/$(CONFIG)/dualstack_socket_test +endpoint_pair_test: $(BINDIR)/$(CONFIG)/endpoint_pair_test +fd_conservation_posix_test: $(BINDIR)/$(CONFIG)/fd_conservation_posix_test +fd_posix_test: $(BINDIR)/$(CONFIG)/fd_posix_test +fling_client: $(BINDIR)/$(CONFIG)/fling_client +fling_server: $(BINDIR)/$(CONFIG)/fling_server +fling_stream_test: $(BINDIR)/$(CONFIG)/fling_stream_test +fling_test: $(BINDIR)/$(CONFIG)/fling_test +gen_hpack_tables: $(BINDIR)/$(CONFIG)/gen_hpack_tables +gen_legal_metadata_characters: $(BINDIR)/$(CONFIG)/gen_legal_metadata_characters +gpr_cmdline_test: $(BINDIR)/$(CONFIG)/gpr_cmdline_test +gpr_env_test: $(BINDIR)/$(CONFIG)/gpr_env_test +gpr_file_test: $(BINDIR)/$(CONFIG)/gpr_file_test +gpr_histogram_test: $(BINDIR)/$(CONFIG)/gpr_histogram_test +gpr_host_port_test: $(BINDIR)/$(CONFIG)/gpr_host_port_test +gpr_log_test: $(BINDIR)/$(CONFIG)/gpr_log_test +gpr_slice_buffer_test: $(BINDIR)/$(CONFIG)/gpr_slice_buffer_test +gpr_slice_test: $(BINDIR)/$(CONFIG)/gpr_slice_test +gpr_stack_lockfree_test: $(BINDIR)/$(CONFIG)/gpr_stack_lockfree_test +gpr_string_test: $(BINDIR)/$(CONFIG)/gpr_string_test +gpr_sync_test: $(BINDIR)/$(CONFIG)/gpr_sync_test +gpr_thd_test: $(BINDIR)/$(CONFIG)/gpr_thd_test +gpr_time_test: $(BINDIR)/$(CONFIG)/gpr_time_test +gpr_tls_test: $(BINDIR)/$(CONFIG)/gpr_tls_test +gpr_useful_test: $(BINDIR)/$(CONFIG)/gpr_useful_test +grpc_auth_context_test: $(BINDIR)/$(CONFIG)/grpc_auth_context_test +grpc_base64_test: $(BINDIR)/$(CONFIG)/grpc_base64_test +grpc_byte_buffer_reader_test: $(BINDIR)/$(CONFIG)/grpc_byte_buffer_reader_test +grpc_channel_args_test: $(BINDIR)/$(CONFIG)/grpc_channel_args_test +grpc_channel_stack_test: $(BINDIR)/$(CONFIG)/grpc_channel_stack_test +grpc_completion_queue_test: $(BINDIR)/$(CONFIG)/grpc_completion_queue_test +grpc_create_jwt: $(BINDIR)/$(CONFIG)/grpc_create_jwt +grpc_credentials_test: $(BINDIR)/$(CONFIG)/grpc_credentials_test +grpc_fetch_oauth2: $(BINDIR)/$(CONFIG)/grpc_fetch_oauth2 +grpc_json_token_test: $(BINDIR)/$(CONFIG)/grpc_json_token_test +grpc_jwt_verifier_test: $(BINDIR)/$(CONFIG)/grpc_jwt_verifier_test +grpc_print_google_default_creds_token: $(BINDIR)/$(CONFIG)/grpc_print_google_default_creds_token +grpc_security_connector_test: $(BINDIR)/$(CONFIG)/grpc_security_connector_test +grpc_stream_op_test: $(BINDIR)/$(CONFIG)/grpc_stream_op_test +grpc_verify_jwt: $(BINDIR)/$(CONFIG)/grpc_verify_jwt +hpack_parser_test: $(BINDIR)/$(CONFIG)/hpack_parser_test +hpack_table_test: $(BINDIR)/$(CONFIG)/hpack_table_test +httpcli_format_request_test: $(BINDIR)/$(CONFIG)/httpcli_format_request_test +httpcli_parser_test: $(BINDIR)/$(CONFIG)/httpcli_parser_test +httpcli_test: $(BINDIR)/$(CONFIG)/httpcli_test +json_rewrite: $(BINDIR)/$(CONFIG)/json_rewrite +json_rewrite_test: $(BINDIR)/$(CONFIG)/json_rewrite_test +json_test: $(BINDIR)/$(CONFIG)/json_test +lame_client_test: $(BINDIR)/$(CONFIG)/lame_client_test +low_level_ping_pong_benchmark: $(BINDIR)/$(CONFIG)/low_level_ping_pong_benchmark +message_compress_test: $(BINDIR)/$(CONFIG)/message_compress_test +multi_init_test: $(BINDIR)/$(CONFIG)/multi_init_test +multiple_server_queues_test: $(BINDIR)/$(CONFIG)/multiple_server_queues_test +murmur_hash_test: $(BINDIR)/$(CONFIG)/murmur_hash_test +no_server_test: $(BINDIR)/$(CONFIG)/no_server_test +resolve_address_test: $(BINDIR)/$(CONFIG)/resolve_address_test +secure_endpoint_test: $(BINDIR)/$(CONFIG)/secure_endpoint_test +sockaddr_utils_test: $(BINDIR)/$(CONFIG)/sockaddr_utils_test +tcp_client_posix_test: $(BINDIR)/$(CONFIG)/tcp_client_posix_test +tcp_posix_test: $(BINDIR)/$(CONFIG)/tcp_posix_test +tcp_server_posix_test: $(BINDIR)/$(CONFIG)/tcp_server_posix_test +time_averaged_stats_test: $(BINDIR)/$(CONFIG)/time_averaged_stats_test +timeout_encoding_test: $(BINDIR)/$(CONFIG)/timeout_encoding_test +timers_test: $(BINDIR)/$(CONFIG)/timers_test +transport_metadata_test: $(BINDIR)/$(CONFIG)/transport_metadata_test +transport_security_test: $(BINDIR)/$(CONFIG)/transport_security_test +udp_server_test: $(BINDIR)/$(CONFIG)/udp_server_test +uri_parser_test: $(BINDIR)/$(CONFIG)/uri_parser_test +async_end2end_test: $(BINDIR)/$(CONFIG)/async_end2end_test +async_streaming_ping_pong_test: $(BINDIR)/$(CONFIG)/async_streaming_ping_pong_test +async_unary_ping_pong_test: $(BINDIR)/$(CONFIG)/async_unary_ping_pong_test +auth_property_iterator_test: $(BINDIR)/$(CONFIG)/auth_property_iterator_test +channel_arguments_test: $(BINDIR)/$(CONFIG)/channel_arguments_test +cli_call_test: $(BINDIR)/$(CONFIG)/cli_call_test +client_crash_test: $(BINDIR)/$(CONFIG)/client_crash_test +client_crash_test_server: $(BINDIR)/$(CONFIG)/client_crash_test_server +credentials_test: $(BINDIR)/$(CONFIG)/credentials_test +cxx_byte_buffer_test: $(BINDIR)/$(CONFIG)/cxx_byte_buffer_test +cxx_slice_test: $(BINDIR)/$(CONFIG)/cxx_slice_test +cxx_string_ref_test: $(BINDIR)/$(CONFIG)/cxx_string_ref_test +cxx_time_test: $(BINDIR)/$(CONFIG)/cxx_time_test +end2end_test: $(BINDIR)/$(CONFIG)/end2end_test +generic_end2end_test: $(BINDIR)/$(CONFIG)/generic_end2end_test +grpc_cli: $(BINDIR)/$(CONFIG)/grpc_cli +grpc_cpp_plugin: $(BINDIR)/$(CONFIG)/grpc_cpp_plugin +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 +interop_client: $(BINDIR)/$(CONFIG)/interop_client +interop_server: $(BINDIR)/$(CONFIG)/interop_server +interop_test: $(BINDIR)/$(CONFIG)/interop_test +mock_test: $(BINDIR)/$(CONFIG)/mock_test +qps_driver: $(BINDIR)/$(CONFIG)/qps_driver +qps_interarrival_test: $(BINDIR)/$(CONFIG)/qps_interarrival_test +qps_openloop_test: $(BINDIR)/$(CONFIG)/qps_openloop_test +qps_test: $(BINDIR)/$(CONFIG)/qps_test +qps_worker: $(BINDIR)/$(CONFIG)/qps_worker +reconnect_interop_client: $(BINDIR)/$(CONFIG)/reconnect_interop_client +reconnect_interop_server: $(BINDIR)/$(CONFIG)/reconnect_interop_server +secure_auth_context_test: $(BINDIR)/$(CONFIG)/secure_auth_context_test +server_crash_test: $(BINDIR)/$(CONFIG)/server_crash_test +server_crash_test_client: $(BINDIR)/$(CONFIG)/server_crash_test_client +shutdown_test: $(BINDIR)/$(CONFIG)/shutdown_test +status_test: $(BINDIR)/$(CONFIG)/status_test +streaming_throughput_test: $(BINDIR)/$(CONFIG)/streaming_throughput_test +sync_streaming_ping_pong_test: $(BINDIR)/$(CONFIG)/sync_streaming_ping_pong_test +sync_unary_ping_pong_test: $(BINDIR)/$(CONFIG)/sync_unary_ping_pong_test +thread_stress_test: $(BINDIR)/$(CONFIG)/thread_stress_test +zookeeper_test: $(BINDIR)/$(CONFIG)/zookeeper_test +h2_compress_bad_hostname_test: $(BINDIR)/$(CONFIG)/h2_compress_bad_hostname_test +h2_compress_binary_metadata_test: $(BINDIR)/$(CONFIG)/h2_compress_binary_metadata_test +h2_compress_call_creds_test: $(BINDIR)/$(CONFIG)/h2_compress_call_creds_test +h2_compress_cancel_after_accept_test: $(BINDIR)/$(CONFIG)/h2_compress_cancel_after_accept_test +h2_compress_cancel_after_client_done_test: $(BINDIR)/$(CONFIG)/h2_compress_cancel_after_client_done_test +h2_compress_cancel_after_invoke_test: $(BINDIR)/$(CONFIG)/h2_compress_cancel_after_invoke_test +h2_compress_cancel_before_invoke_test: $(BINDIR)/$(CONFIG)/h2_compress_cancel_before_invoke_test +h2_compress_cancel_in_a_vacuum_test: $(BINDIR)/$(CONFIG)/h2_compress_cancel_in_a_vacuum_test +h2_compress_census_simple_request_test: $(BINDIR)/$(CONFIG)/h2_compress_census_simple_request_test +h2_compress_channel_connectivity_test: $(BINDIR)/$(CONFIG)/h2_compress_channel_connectivity_test +h2_compress_compressed_payload_test: $(BINDIR)/$(CONFIG)/h2_compress_compressed_payload_test +h2_compress_default_host_test: $(BINDIR)/$(CONFIG)/h2_compress_default_host_test +h2_compress_disappearing_server_test: $(BINDIR)/$(CONFIG)/h2_compress_disappearing_server_test +h2_compress_empty_batch_test: $(BINDIR)/$(CONFIG)/h2_compress_empty_batch_test +h2_compress_graceful_server_shutdown_test: $(BINDIR)/$(CONFIG)/h2_compress_graceful_server_shutdown_test +h2_compress_high_initial_seqno_test: $(BINDIR)/$(CONFIG)/h2_compress_high_initial_seqno_test +h2_compress_invoke_large_request_test: $(BINDIR)/$(CONFIG)/h2_compress_invoke_large_request_test +h2_compress_large_metadata_test: $(BINDIR)/$(CONFIG)/h2_compress_large_metadata_test +h2_compress_max_concurrent_streams_test: $(BINDIR)/$(CONFIG)/h2_compress_max_concurrent_streams_test +h2_compress_max_message_length_test: $(BINDIR)/$(CONFIG)/h2_compress_max_message_length_test +h2_compress_metadata_test: $(BINDIR)/$(CONFIG)/h2_compress_metadata_test +h2_compress_no_op_test: $(BINDIR)/$(CONFIG)/h2_compress_no_op_test +h2_compress_payload_test: $(BINDIR)/$(CONFIG)/h2_compress_payload_test +h2_compress_ping_pong_streaming_test: $(BINDIR)/$(CONFIG)/h2_compress_ping_pong_streaming_test +h2_compress_registered_call_test: $(BINDIR)/$(CONFIG)/h2_compress_registered_call_test +h2_compress_request_with_flags_test: $(BINDIR)/$(CONFIG)/h2_compress_request_with_flags_test +h2_compress_request_with_payload_test: $(BINDIR)/$(CONFIG)/h2_compress_request_with_payload_test +h2_compress_server_finishes_request_test: $(BINDIR)/$(CONFIG)/h2_compress_server_finishes_request_test +h2_compress_shutdown_finishes_calls_test: $(BINDIR)/$(CONFIG)/h2_compress_shutdown_finishes_calls_test +h2_compress_shutdown_finishes_tags_test: $(BINDIR)/$(CONFIG)/h2_compress_shutdown_finishes_tags_test +h2_compress_simple_delayed_request_test: $(BINDIR)/$(CONFIG)/h2_compress_simple_delayed_request_test +h2_compress_simple_request_test: $(BINDIR)/$(CONFIG)/h2_compress_simple_request_test +h2_compress_trailing_metadata_test: $(BINDIR)/$(CONFIG)/h2_compress_trailing_metadata_test +h2_fakesec_bad_hostname_test: $(BINDIR)/$(CONFIG)/h2_fakesec_bad_hostname_test +h2_fakesec_binary_metadata_test: $(BINDIR)/$(CONFIG)/h2_fakesec_binary_metadata_test +h2_fakesec_call_creds_test: $(BINDIR)/$(CONFIG)/h2_fakesec_call_creds_test +h2_fakesec_cancel_after_accept_test: $(BINDIR)/$(CONFIG)/h2_fakesec_cancel_after_accept_test +h2_fakesec_cancel_after_client_done_test: $(BINDIR)/$(CONFIG)/h2_fakesec_cancel_after_client_done_test +h2_fakesec_cancel_after_invoke_test: $(BINDIR)/$(CONFIG)/h2_fakesec_cancel_after_invoke_test +h2_fakesec_cancel_before_invoke_test: $(BINDIR)/$(CONFIG)/h2_fakesec_cancel_before_invoke_test +h2_fakesec_cancel_in_a_vacuum_test: $(BINDIR)/$(CONFIG)/h2_fakesec_cancel_in_a_vacuum_test +h2_fakesec_census_simple_request_test: $(BINDIR)/$(CONFIG)/h2_fakesec_census_simple_request_test +h2_fakesec_channel_connectivity_test: $(BINDIR)/$(CONFIG)/h2_fakesec_channel_connectivity_test +h2_fakesec_compressed_payload_test: $(BINDIR)/$(CONFIG)/h2_fakesec_compressed_payload_test +h2_fakesec_default_host_test: $(BINDIR)/$(CONFIG)/h2_fakesec_default_host_test +h2_fakesec_disappearing_server_test: $(BINDIR)/$(CONFIG)/h2_fakesec_disappearing_server_test +h2_fakesec_empty_batch_test: $(BINDIR)/$(CONFIG)/h2_fakesec_empty_batch_test +h2_fakesec_graceful_server_shutdown_test: $(BINDIR)/$(CONFIG)/h2_fakesec_graceful_server_shutdown_test +h2_fakesec_high_initial_seqno_test: $(BINDIR)/$(CONFIG)/h2_fakesec_high_initial_seqno_test +h2_fakesec_invoke_large_request_test: $(BINDIR)/$(CONFIG)/h2_fakesec_invoke_large_request_test +h2_fakesec_large_metadata_test: $(BINDIR)/$(CONFIG)/h2_fakesec_large_metadata_test +h2_fakesec_max_concurrent_streams_test: $(BINDIR)/$(CONFIG)/h2_fakesec_max_concurrent_streams_test +h2_fakesec_max_message_length_test: $(BINDIR)/$(CONFIG)/h2_fakesec_max_message_length_test +h2_fakesec_metadata_test: $(BINDIR)/$(CONFIG)/h2_fakesec_metadata_test +h2_fakesec_no_op_test: $(BINDIR)/$(CONFIG)/h2_fakesec_no_op_test +h2_fakesec_payload_test: $(BINDIR)/$(CONFIG)/h2_fakesec_payload_test +h2_fakesec_ping_pong_streaming_test: $(BINDIR)/$(CONFIG)/h2_fakesec_ping_pong_streaming_test +h2_fakesec_registered_call_test: $(BINDIR)/$(CONFIG)/h2_fakesec_registered_call_test +h2_fakesec_request_with_flags_test: $(BINDIR)/$(CONFIG)/h2_fakesec_request_with_flags_test +h2_fakesec_request_with_payload_test: $(BINDIR)/$(CONFIG)/h2_fakesec_request_with_payload_test +h2_fakesec_server_finishes_request_test: $(BINDIR)/$(CONFIG)/h2_fakesec_server_finishes_request_test +h2_fakesec_shutdown_finishes_calls_test: $(BINDIR)/$(CONFIG)/h2_fakesec_shutdown_finishes_calls_test +h2_fakesec_shutdown_finishes_tags_test: $(BINDIR)/$(CONFIG)/h2_fakesec_shutdown_finishes_tags_test +h2_fakesec_simple_delayed_request_test: $(BINDIR)/$(CONFIG)/h2_fakesec_simple_delayed_request_test +h2_fakesec_simple_request_test: $(BINDIR)/$(CONFIG)/h2_fakesec_simple_request_test +h2_fakesec_trailing_metadata_test: $(BINDIR)/$(CONFIG)/h2_fakesec_trailing_metadata_test +h2_full_bad_hostname_test: $(BINDIR)/$(CONFIG)/h2_full_bad_hostname_test +h2_full_binary_metadata_test: $(BINDIR)/$(CONFIG)/h2_full_binary_metadata_test +h2_full_call_creds_test: $(BINDIR)/$(CONFIG)/h2_full_call_creds_test +h2_full_cancel_after_accept_test: $(BINDIR)/$(CONFIG)/h2_full_cancel_after_accept_test +h2_full_cancel_after_client_done_test: $(BINDIR)/$(CONFIG)/h2_full_cancel_after_client_done_test +h2_full_cancel_after_invoke_test: $(BINDIR)/$(CONFIG)/h2_full_cancel_after_invoke_test +h2_full_cancel_before_invoke_test: $(BINDIR)/$(CONFIG)/h2_full_cancel_before_invoke_test +h2_full_cancel_in_a_vacuum_test: $(BINDIR)/$(CONFIG)/h2_full_cancel_in_a_vacuum_test +h2_full_census_simple_request_test: $(BINDIR)/$(CONFIG)/h2_full_census_simple_request_test +h2_full_channel_connectivity_test: $(BINDIR)/$(CONFIG)/h2_full_channel_connectivity_test +h2_full_compressed_payload_test: $(BINDIR)/$(CONFIG)/h2_full_compressed_payload_test +h2_full_default_host_test: $(BINDIR)/$(CONFIG)/h2_full_default_host_test +h2_full_disappearing_server_test: $(BINDIR)/$(CONFIG)/h2_full_disappearing_server_test +h2_full_empty_batch_test: $(BINDIR)/$(CONFIG)/h2_full_empty_batch_test +h2_full_graceful_server_shutdown_test: $(BINDIR)/$(CONFIG)/h2_full_graceful_server_shutdown_test +h2_full_high_initial_seqno_test: $(BINDIR)/$(CONFIG)/h2_full_high_initial_seqno_test +h2_full_invoke_large_request_test: $(BINDIR)/$(CONFIG)/h2_full_invoke_large_request_test +h2_full_large_metadata_test: $(BINDIR)/$(CONFIG)/h2_full_large_metadata_test +h2_full_max_concurrent_streams_test: $(BINDIR)/$(CONFIG)/h2_full_max_concurrent_streams_test +h2_full_max_message_length_test: $(BINDIR)/$(CONFIG)/h2_full_max_message_length_test +h2_full_metadata_test: $(BINDIR)/$(CONFIG)/h2_full_metadata_test +h2_full_no_op_test: $(BINDIR)/$(CONFIG)/h2_full_no_op_test +h2_full_payload_test: $(BINDIR)/$(CONFIG)/h2_full_payload_test +h2_full_ping_pong_streaming_test: $(BINDIR)/$(CONFIG)/h2_full_ping_pong_streaming_test +h2_full_registered_call_test: $(BINDIR)/$(CONFIG)/h2_full_registered_call_test +h2_full_request_with_flags_test: $(BINDIR)/$(CONFIG)/h2_full_request_with_flags_test +h2_full_request_with_payload_test: $(BINDIR)/$(CONFIG)/h2_full_request_with_payload_test +h2_full_server_finishes_request_test: $(BINDIR)/$(CONFIG)/h2_full_server_finishes_request_test +h2_full_shutdown_finishes_calls_test: $(BINDIR)/$(CONFIG)/h2_full_shutdown_finishes_calls_test +h2_full_shutdown_finishes_tags_test: $(BINDIR)/$(CONFIG)/h2_full_shutdown_finishes_tags_test +h2_full_simple_delayed_request_test: $(BINDIR)/$(CONFIG)/h2_full_simple_delayed_request_test +h2_full_simple_request_test: $(BINDIR)/$(CONFIG)/h2_full_simple_request_test +h2_full_trailing_metadata_test: $(BINDIR)/$(CONFIG)/h2_full_trailing_metadata_test +h2_full+poll_bad_hostname_test: $(BINDIR)/$(CONFIG)/h2_full+poll_bad_hostname_test +h2_full+poll_binary_metadata_test: $(BINDIR)/$(CONFIG)/h2_full+poll_binary_metadata_test +h2_full+poll_call_creds_test: $(BINDIR)/$(CONFIG)/h2_full+poll_call_creds_test +h2_full+poll_cancel_after_accept_test: $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_accept_test +h2_full+poll_cancel_after_client_done_test: $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_client_done_test +h2_full+poll_cancel_after_invoke_test: $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_invoke_test +h2_full+poll_cancel_before_invoke_test: $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_before_invoke_test +h2_full+poll_cancel_in_a_vacuum_test: $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_in_a_vacuum_test +h2_full+poll_census_simple_request_test: $(BINDIR)/$(CONFIG)/h2_full+poll_census_simple_request_test +h2_full+poll_channel_connectivity_test: $(BINDIR)/$(CONFIG)/h2_full+poll_channel_connectivity_test +h2_full+poll_compressed_payload_test: $(BINDIR)/$(CONFIG)/h2_full+poll_compressed_payload_test +h2_full+poll_default_host_test: $(BINDIR)/$(CONFIG)/h2_full+poll_default_host_test +h2_full+poll_disappearing_server_test: $(BINDIR)/$(CONFIG)/h2_full+poll_disappearing_server_test +h2_full+poll_empty_batch_test: $(BINDIR)/$(CONFIG)/h2_full+poll_empty_batch_test +h2_full+poll_graceful_server_shutdown_test: $(BINDIR)/$(CONFIG)/h2_full+poll_graceful_server_shutdown_test +h2_full+poll_high_initial_seqno_test: $(BINDIR)/$(CONFIG)/h2_full+poll_high_initial_seqno_test +h2_full+poll_invoke_large_request_test: $(BINDIR)/$(CONFIG)/h2_full+poll_invoke_large_request_test +h2_full+poll_large_metadata_test: $(BINDIR)/$(CONFIG)/h2_full+poll_large_metadata_test +h2_full+poll_max_concurrent_streams_test: $(BINDIR)/$(CONFIG)/h2_full+poll_max_concurrent_streams_test +h2_full+poll_max_message_length_test: $(BINDIR)/$(CONFIG)/h2_full+poll_max_message_length_test +h2_full+poll_metadata_test: $(BINDIR)/$(CONFIG)/h2_full+poll_metadata_test +h2_full+poll_no_op_test: $(BINDIR)/$(CONFIG)/h2_full+poll_no_op_test +h2_full+poll_payload_test: $(BINDIR)/$(CONFIG)/h2_full+poll_payload_test +h2_full+poll_ping_pong_streaming_test: $(BINDIR)/$(CONFIG)/h2_full+poll_ping_pong_streaming_test +h2_full+poll_registered_call_test: $(BINDIR)/$(CONFIG)/h2_full+poll_registered_call_test +h2_full+poll_request_with_flags_test: $(BINDIR)/$(CONFIG)/h2_full+poll_request_with_flags_test +h2_full+poll_request_with_payload_test: $(BINDIR)/$(CONFIG)/h2_full+poll_request_with_payload_test +h2_full+poll_server_finishes_request_test: $(BINDIR)/$(CONFIG)/h2_full+poll_server_finishes_request_test +h2_full+poll_shutdown_finishes_calls_test: $(BINDIR)/$(CONFIG)/h2_full+poll_shutdown_finishes_calls_test +h2_full+poll_shutdown_finishes_tags_test: $(BINDIR)/$(CONFIG)/h2_full+poll_shutdown_finishes_tags_test +h2_full+poll_simple_delayed_request_test: $(BINDIR)/$(CONFIG)/h2_full+poll_simple_delayed_request_test +h2_full+poll_simple_request_test: $(BINDIR)/$(CONFIG)/h2_full+poll_simple_request_test +h2_full+poll_trailing_metadata_test: $(BINDIR)/$(CONFIG)/h2_full+poll_trailing_metadata_test +h2_oauth2_bad_hostname_test: $(BINDIR)/$(CONFIG)/h2_oauth2_bad_hostname_test +h2_oauth2_binary_metadata_test: $(BINDIR)/$(CONFIG)/h2_oauth2_binary_metadata_test +h2_oauth2_call_creds_test: $(BINDIR)/$(CONFIG)/h2_oauth2_call_creds_test +h2_oauth2_cancel_after_accept_test: $(BINDIR)/$(CONFIG)/h2_oauth2_cancel_after_accept_test +h2_oauth2_cancel_after_client_done_test: $(BINDIR)/$(CONFIG)/h2_oauth2_cancel_after_client_done_test +h2_oauth2_cancel_after_invoke_test: $(BINDIR)/$(CONFIG)/h2_oauth2_cancel_after_invoke_test +h2_oauth2_cancel_before_invoke_test: $(BINDIR)/$(CONFIG)/h2_oauth2_cancel_before_invoke_test +h2_oauth2_cancel_in_a_vacuum_test: $(BINDIR)/$(CONFIG)/h2_oauth2_cancel_in_a_vacuum_test +h2_oauth2_census_simple_request_test: $(BINDIR)/$(CONFIG)/h2_oauth2_census_simple_request_test +h2_oauth2_channel_connectivity_test: $(BINDIR)/$(CONFIG)/h2_oauth2_channel_connectivity_test +h2_oauth2_compressed_payload_test: $(BINDIR)/$(CONFIG)/h2_oauth2_compressed_payload_test +h2_oauth2_default_host_test: $(BINDIR)/$(CONFIG)/h2_oauth2_default_host_test +h2_oauth2_disappearing_server_test: $(BINDIR)/$(CONFIG)/h2_oauth2_disappearing_server_test +h2_oauth2_empty_batch_test: $(BINDIR)/$(CONFIG)/h2_oauth2_empty_batch_test +h2_oauth2_graceful_server_shutdown_test: $(BINDIR)/$(CONFIG)/h2_oauth2_graceful_server_shutdown_test +h2_oauth2_high_initial_seqno_test: $(BINDIR)/$(CONFIG)/h2_oauth2_high_initial_seqno_test +h2_oauth2_invoke_large_request_test: $(BINDIR)/$(CONFIG)/h2_oauth2_invoke_large_request_test +h2_oauth2_large_metadata_test: $(BINDIR)/$(CONFIG)/h2_oauth2_large_metadata_test +h2_oauth2_max_concurrent_streams_test: $(BINDIR)/$(CONFIG)/h2_oauth2_max_concurrent_streams_test +h2_oauth2_max_message_length_test: $(BINDIR)/$(CONFIG)/h2_oauth2_max_message_length_test +h2_oauth2_metadata_test: $(BINDIR)/$(CONFIG)/h2_oauth2_metadata_test +h2_oauth2_no_op_test: $(BINDIR)/$(CONFIG)/h2_oauth2_no_op_test +h2_oauth2_payload_test: $(BINDIR)/$(CONFIG)/h2_oauth2_payload_test +h2_oauth2_ping_pong_streaming_test: $(BINDIR)/$(CONFIG)/h2_oauth2_ping_pong_streaming_test +h2_oauth2_registered_call_test: $(BINDIR)/$(CONFIG)/h2_oauth2_registered_call_test +h2_oauth2_request_with_flags_test: $(BINDIR)/$(CONFIG)/h2_oauth2_request_with_flags_test +h2_oauth2_request_with_payload_test: $(BINDIR)/$(CONFIG)/h2_oauth2_request_with_payload_test +h2_oauth2_server_finishes_request_test: $(BINDIR)/$(CONFIG)/h2_oauth2_server_finishes_request_test +h2_oauth2_shutdown_finishes_calls_test: $(BINDIR)/$(CONFIG)/h2_oauth2_shutdown_finishes_calls_test +h2_oauth2_shutdown_finishes_tags_test: $(BINDIR)/$(CONFIG)/h2_oauth2_shutdown_finishes_tags_test +h2_oauth2_simple_delayed_request_test: $(BINDIR)/$(CONFIG)/h2_oauth2_simple_delayed_request_test +h2_oauth2_simple_request_test: $(BINDIR)/$(CONFIG)/h2_oauth2_simple_request_test +h2_oauth2_trailing_metadata_test: $(BINDIR)/$(CONFIG)/h2_oauth2_trailing_metadata_test +h2_proxy_bad_hostname_test: $(BINDIR)/$(CONFIG)/h2_proxy_bad_hostname_test +h2_proxy_binary_metadata_test: $(BINDIR)/$(CONFIG)/h2_proxy_binary_metadata_test +h2_proxy_call_creds_test: $(BINDIR)/$(CONFIG)/h2_proxy_call_creds_test +h2_proxy_cancel_after_accept_test: $(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_accept_test +h2_proxy_cancel_after_client_done_test: $(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_client_done_test +h2_proxy_cancel_after_invoke_test: $(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_invoke_test +h2_proxy_cancel_before_invoke_test: $(BINDIR)/$(CONFIG)/h2_proxy_cancel_before_invoke_test +h2_proxy_cancel_in_a_vacuum_test: $(BINDIR)/$(CONFIG)/h2_proxy_cancel_in_a_vacuum_test +h2_proxy_census_simple_request_test: $(BINDIR)/$(CONFIG)/h2_proxy_census_simple_request_test +h2_proxy_default_host_test: $(BINDIR)/$(CONFIG)/h2_proxy_default_host_test +h2_proxy_disappearing_server_test: $(BINDIR)/$(CONFIG)/h2_proxy_disappearing_server_test +h2_proxy_empty_batch_test: $(BINDIR)/$(CONFIG)/h2_proxy_empty_batch_test +h2_proxy_graceful_server_shutdown_test: $(BINDIR)/$(CONFIG)/h2_proxy_graceful_server_shutdown_test +h2_proxy_high_initial_seqno_test: $(BINDIR)/$(CONFIG)/h2_proxy_high_initial_seqno_test +h2_proxy_invoke_large_request_test: $(BINDIR)/$(CONFIG)/h2_proxy_invoke_large_request_test +h2_proxy_large_metadata_test: $(BINDIR)/$(CONFIG)/h2_proxy_large_metadata_test +h2_proxy_max_message_length_test: $(BINDIR)/$(CONFIG)/h2_proxy_max_message_length_test +h2_proxy_metadata_test: $(BINDIR)/$(CONFIG)/h2_proxy_metadata_test +h2_proxy_no_op_test: $(BINDIR)/$(CONFIG)/h2_proxy_no_op_test +h2_proxy_payload_test: $(BINDIR)/$(CONFIG)/h2_proxy_payload_test +h2_proxy_ping_pong_streaming_test: $(BINDIR)/$(CONFIG)/h2_proxy_ping_pong_streaming_test +h2_proxy_registered_call_test: $(BINDIR)/$(CONFIG)/h2_proxy_registered_call_test +h2_proxy_request_with_payload_test: $(BINDIR)/$(CONFIG)/h2_proxy_request_with_payload_test +h2_proxy_server_finishes_request_test: $(BINDIR)/$(CONFIG)/h2_proxy_server_finishes_request_test +h2_proxy_shutdown_finishes_calls_test: $(BINDIR)/$(CONFIG)/h2_proxy_shutdown_finishes_calls_test +h2_proxy_shutdown_finishes_tags_test: $(BINDIR)/$(CONFIG)/h2_proxy_shutdown_finishes_tags_test +h2_proxy_simple_delayed_request_test: $(BINDIR)/$(CONFIG)/h2_proxy_simple_delayed_request_test +h2_proxy_simple_request_test: $(BINDIR)/$(CONFIG)/h2_proxy_simple_request_test +h2_proxy_trailing_metadata_test: $(BINDIR)/$(CONFIG)/h2_proxy_trailing_metadata_test +h2_sockpair_bad_hostname_test: $(BINDIR)/$(CONFIG)/h2_sockpair_bad_hostname_test +h2_sockpair_binary_metadata_test: $(BINDIR)/$(CONFIG)/h2_sockpair_binary_metadata_test +h2_sockpair_call_creds_test: $(BINDIR)/$(CONFIG)/h2_sockpair_call_creds_test +h2_sockpair_cancel_after_accept_test: $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_accept_test +h2_sockpair_cancel_after_client_done_test: $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_client_done_test +h2_sockpair_cancel_after_invoke_test: $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_invoke_test +h2_sockpair_cancel_before_invoke_test: $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_before_invoke_test +h2_sockpair_cancel_in_a_vacuum_test: $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_in_a_vacuum_test +h2_sockpair_census_simple_request_test: $(BINDIR)/$(CONFIG)/h2_sockpair_census_simple_request_test +h2_sockpair_compressed_payload_test: $(BINDIR)/$(CONFIG)/h2_sockpair_compressed_payload_test +h2_sockpair_empty_batch_test: $(BINDIR)/$(CONFIG)/h2_sockpair_empty_batch_test +h2_sockpair_graceful_server_shutdown_test: $(BINDIR)/$(CONFIG)/h2_sockpair_graceful_server_shutdown_test +h2_sockpair_high_initial_seqno_test: $(BINDIR)/$(CONFIG)/h2_sockpair_high_initial_seqno_test +h2_sockpair_invoke_large_request_test: $(BINDIR)/$(CONFIG)/h2_sockpair_invoke_large_request_test +h2_sockpair_large_metadata_test: $(BINDIR)/$(CONFIG)/h2_sockpair_large_metadata_test +h2_sockpair_max_concurrent_streams_test: $(BINDIR)/$(CONFIG)/h2_sockpair_max_concurrent_streams_test +h2_sockpair_max_message_length_test: $(BINDIR)/$(CONFIG)/h2_sockpair_max_message_length_test +h2_sockpair_metadata_test: $(BINDIR)/$(CONFIG)/h2_sockpair_metadata_test +h2_sockpair_no_op_test: $(BINDIR)/$(CONFIG)/h2_sockpair_no_op_test +h2_sockpair_payload_test: $(BINDIR)/$(CONFIG)/h2_sockpair_payload_test +h2_sockpair_ping_pong_streaming_test: $(BINDIR)/$(CONFIG)/h2_sockpair_ping_pong_streaming_test +h2_sockpair_registered_call_test: $(BINDIR)/$(CONFIG)/h2_sockpair_registered_call_test +h2_sockpair_request_with_flags_test: $(BINDIR)/$(CONFIG)/h2_sockpair_request_with_flags_test +h2_sockpair_request_with_payload_test: $(BINDIR)/$(CONFIG)/h2_sockpair_request_with_payload_test +h2_sockpair_server_finishes_request_test: $(BINDIR)/$(CONFIG)/h2_sockpair_server_finishes_request_test +h2_sockpair_shutdown_finishes_calls_test: $(BINDIR)/$(CONFIG)/h2_sockpair_shutdown_finishes_calls_test +h2_sockpair_shutdown_finishes_tags_test: $(BINDIR)/$(CONFIG)/h2_sockpair_shutdown_finishes_tags_test +h2_sockpair_simple_request_test: $(BINDIR)/$(CONFIG)/h2_sockpair_simple_request_test +h2_sockpair_trailing_metadata_test: $(BINDIR)/$(CONFIG)/h2_sockpair_trailing_metadata_test +h2_sockpair+trace_bad_hostname_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_bad_hostname_test +h2_sockpair+trace_binary_metadata_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_binary_metadata_test +h2_sockpair+trace_call_creds_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_call_creds_test +h2_sockpair+trace_cancel_after_accept_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_accept_test +h2_sockpair+trace_cancel_after_client_done_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_client_done_test +h2_sockpair+trace_cancel_after_invoke_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_invoke_test +h2_sockpair+trace_cancel_before_invoke_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_before_invoke_test +h2_sockpair+trace_cancel_in_a_vacuum_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_in_a_vacuum_test +h2_sockpair+trace_census_simple_request_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_census_simple_request_test +h2_sockpair+trace_compressed_payload_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_compressed_payload_test +h2_sockpair+trace_empty_batch_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_empty_batch_test +h2_sockpair+trace_graceful_server_shutdown_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_graceful_server_shutdown_test +h2_sockpair+trace_high_initial_seqno_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_high_initial_seqno_test +h2_sockpair+trace_invoke_large_request_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_invoke_large_request_test +h2_sockpair+trace_large_metadata_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_large_metadata_test +h2_sockpair+trace_max_concurrent_streams_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_max_concurrent_streams_test +h2_sockpair+trace_max_message_length_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_max_message_length_test +h2_sockpair+trace_metadata_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_metadata_test +h2_sockpair+trace_no_op_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_no_op_test +h2_sockpair+trace_payload_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_payload_test +h2_sockpair+trace_ping_pong_streaming_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_ping_pong_streaming_test +h2_sockpair+trace_registered_call_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_registered_call_test +h2_sockpair+trace_request_with_flags_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_request_with_flags_test +h2_sockpair+trace_request_with_payload_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_request_with_payload_test +h2_sockpair+trace_server_finishes_request_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_server_finishes_request_test +h2_sockpair+trace_shutdown_finishes_calls_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_shutdown_finishes_calls_test +h2_sockpair+trace_shutdown_finishes_tags_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_shutdown_finishes_tags_test +h2_sockpair+trace_simple_request_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_simple_request_test +h2_sockpair+trace_trailing_metadata_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_trailing_metadata_test +h2_sockpair_1byte_bad_hostname_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_bad_hostname_test +h2_sockpair_1byte_binary_metadata_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_binary_metadata_test +h2_sockpair_1byte_call_creds_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_call_creds_test +h2_sockpair_1byte_cancel_after_accept_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_accept_test +h2_sockpair_1byte_cancel_after_client_done_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_client_done_test +h2_sockpair_1byte_cancel_after_invoke_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_invoke_test +h2_sockpair_1byte_cancel_before_invoke_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_before_invoke_test +h2_sockpair_1byte_cancel_in_a_vacuum_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_in_a_vacuum_test +h2_sockpair_1byte_census_simple_request_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_census_simple_request_test +h2_sockpair_1byte_compressed_payload_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_compressed_payload_test +h2_sockpair_1byte_empty_batch_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_empty_batch_test +h2_sockpair_1byte_graceful_server_shutdown_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_graceful_server_shutdown_test +h2_sockpair_1byte_high_initial_seqno_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_high_initial_seqno_test +h2_sockpair_1byte_invoke_large_request_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_invoke_large_request_test +h2_sockpair_1byte_large_metadata_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_large_metadata_test +h2_sockpair_1byte_max_concurrent_streams_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_max_concurrent_streams_test +h2_sockpair_1byte_max_message_length_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_max_message_length_test +h2_sockpair_1byte_metadata_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_metadata_test +h2_sockpair_1byte_no_op_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_no_op_test +h2_sockpair_1byte_payload_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_payload_test +h2_sockpair_1byte_ping_pong_streaming_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_ping_pong_streaming_test +h2_sockpair_1byte_registered_call_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_registered_call_test +h2_sockpair_1byte_request_with_flags_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_request_with_flags_test +h2_sockpair_1byte_request_with_payload_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_request_with_payload_test +h2_sockpair_1byte_server_finishes_request_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_server_finishes_request_test +h2_sockpair_1byte_shutdown_finishes_calls_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_shutdown_finishes_calls_test +h2_sockpair_1byte_shutdown_finishes_tags_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_shutdown_finishes_tags_test +h2_sockpair_1byte_simple_request_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_simple_request_test +h2_sockpair_1byte_trailing_metadata_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_trailing_metadata_test +h2_ssl_bad_hostname_test: $(BINDIR)/$(CONFIG)/h2_ssl_bad_hostname_test +h2_ssl_binary_metadata_test: $(BINDIR)/$(CONFIG)/h2_ssl_binary_metadata_test +h2_ssl_call_creds_test: $(BINDIR)/$(CONFIG)/h2_ssl_call_creds_test +h2_ssl_cancel_after_accept_test: $(BINDIR)/$(CONFIG)/h2_ssl_cancel_after_accept_test +h2_ssl_cancel_after_client_done_test: $(BINDIR)/$(CONFIG)/h2_ssl_cancel_after_client_done_test +h2_ssl_cancel_after_invoke_test: $(BINDIR)/$(CONFIG)/h2_ssl_cancel_after_invoke_test +h2_ssl_cancel_before_invoke_test: $(BINDIR)/$(CONFIG)/h2_ssl_cancel_before_invoke_test +h2_ssl_cancel_in_a_vacuum_test: $(BINDIR)/$(CONFIG)/h2_ssl_cancel_in_a_vacuum_test +h2_ssl_census_simple_request_test: $(BINDIR)/$(CONFIG)/h2_ssl_census_simple_request_test +h2_ssl_channel_connectivity_test: $(BINDIR)/$(CONFIG)/h2_ssl_channel_connectivity_test +h2_ssl_compressed_payload_test: $(BINDIR)/$(CONFIG)/h2_ssl_compressed_payload_test +h2_ssl_default_host_test: $(BINDIR)/$(CONFIG)/h2_ssl_default_host_test +h2_ssl_disappearing_server_test: $(BINDIR)/$(CONFIG)/h2_ssl_disappearing_server_test +h2_ssl_empty_batch_test: $(BINDIR)/$(CONFIG)/h2_ssl_empty_batch_test +h2_ssl_graceful_server_shutdown_test: $(BINDIR)/$(CONFIG)/h2_ssl_graceful_server_shutdown_test +h2_ssl_high_initial_seqno_test: $(BINDIR)/$(CONFIG)/h2_ssl_high_initial_seqno_test +h2_ssl_invoke_large_request_test: $(BINDIR)/$(CONFIG)/h2_ssl_invoke_large_request_test +h2_ssl_large_metadata_test: $(BINDIR)/$(CONFIG)/h2_ssl_large_metadata_test +h2_ssl_max_concurrent_streams_test: $(BINDIR)/$(CONFIG)/h2_ssl_max_concurrent_streams_test +h2_ssl_max_message_length_test: $(BINDIR)/$(CONFIG)/h2_ssl_max_message_length_test +h2_ssl_metadata_test: $(BINDIR)/$(CONFIG)/h2_ssl_metadata_test +h2_ssl_no_op_test: $(BINDIR)/$(CONFIG)/h2_ssl_no_op_test +h2_ssl_payload_test: $(BINDIR)/$(CONFIG)/h2_ssl_payload_test +h2_ssl_ping_pong_streaming_test: $(BINDIR)/$(CONFIG)/h2_ssl_ping_pong_streaming_test +h2_ssl_registered_call_test: $(BINDIR)/$(CONFIG)/h2_ssl_registered_call_test +h2_ssl_request_with_flags_test: $(BINDIR)/$(CONFIG)/h2_ssl_request_with_flags_test +h2_ssl_request_with_payload_test: $(BINDIR)/$(CONFIG)/h2_ssl_request_with_payload_test +h2_ssl_server_finishes_request_test: $(BINDIR)/$(CONFIG)/h2_ssl_server_finishes_request_test +h2_ssl_shutdown_finishes_calls_test: $(BINDIR)/$(CONFIG)/h2_ssl_shutdown_finishes_calls_test +h2_ssl_shutdown_finishes_tags_test: $(BINDIR)/$(CONFIG)/h2_ssl_shutdown_finishes_tags_test +h2_ssl_simple_delayed_request_test: $(BINDIR)/$(CONFIG)/h2_ssl_simple_delayed_request_test +h2_ssl_simple_request_test: $(BINDIR)/$(CONFIG)/h2_ssl_simple_request_test +h2_ssl_trailing_metadata_test: $(BINDIR)/$(CONFIG)/h2_ssl_trailing_metadata_test +h2_ssl+poll_bad_hostname_test: $(BINDIR)/$(CONFIG)/h2_ssl+poll_bad_hostname_test +h2_ssl+poll_binary_metadata_test: $(BINDIR)/$(CONFIG)/h2_ssl+poll_binary_metadata_test +h2_ssl+poll_call_creds_test: $(BINDIR)/$(CONFIG)/h2_ssl+poll_call_creds_test +h2_ssl+poll_cancel_after_accept_test: $(BINDIR)/$(CONFIG)/h2_ssl+poll_cancel_after_accept_test +h2_ssl+poll_cancel_after_client_done_test: $(BINDIR)/$(CONFIG)/h2_ssl+poll_cancel_after_client_done_test +h2_ssl+poll_cancel_after_invoke_test: $(BINDIR)/$(CONFIG)/h2_ssl+poll_cancel_after_invoke_test +h2_ssl+poll_cancel_before_invoke_test: $(BINDIR)/$(CONFIG)/h2_ssl+poll_cancel_before_invoke_test +h2_ssl+poll_cancel_in_a_vacuum_test: $(BINDIR)/$(CONFIG)/h2_ssl+poll_cancel_in_a_vacuum_test +h2_ssl+poll_census_simple_request_test: $(BINDIR)/$(CONFIG)/h2_ssl+poll_census_simple_request_test +h2_ssl+poll_channel_connectivity_test: $(BINDIR)/$(CONFIG)/h2_ssl+poll_channel_connectivity_test +h2_ssl+poll_compressed_payload_test: $(BINDIR)/$(CONFIG)/h2_ssl+poll_compressed_payload_test +h2_ssl+poll_default_host_test: $(BINDIR)/$(CONFIG)/h2_ssl+poll_default_host_test +h2_ssl+poll_disappearing_server_test: $(BINDIR)/$(CONFIG)/h2_ssl+poll_disappearing_server_test +h2_ssl+poll_empty_batch_test: $(BINDIR)/$(CONFIG)/h2_ssl+poll_empty_batch_test +h2_ssl+poll_graceful_server_shutdown_test: $(BINDIR)/$(CONFIG)/h2_ssl+poll_graceful_server_shutdown_test +h2_ssl+poll_high_initial_seqno_test: $(BINDIR)/$(CONFIG)/h2_ssl+poll_high_initial_seqno_test +h2_ssl+poll_invoke_large_request_test: $(BINDIR)/$(CONFIG)/h2_ssl+poll_invoke_large_request_test +h2_ssl+poll_large_metadata_test: $(BINDIR)/$(CONFIG)/h2_ssl+poll_large_metadata_test +h2_ssl+poll_max_concurrent_streams_test: $(BINDIR)/$(CONFIG)/h2_ssl+poll_max_concurrent_streams_test +h2_ssl+poll_max_message_length_test: $(BINDIR)/$(CONFIG)/h2_ssl+poll_max_message_length_test +h2_ssl+poll_metadata_test: $(BINDIR)/$(CONFIG)/h2_ssl+poll_metadata_test +h2_ssl+poll_no_op_test: $(BINDIR)/$(CONFIG)/h2_ssl+poll_no_op_test +h2_ssl+poll_payload_test: $(BINDIR)/$(CONFIG)/h2_ssl+poll_payload_test +h2_ssl+poll_ping_pong_streaming_test: $(BINDIR)/$(CONFIG)/h2_ssl+poll_ping_pong_streaming_test +h2_ssl+poll_registered_call_test: $(BINDIR)/$(CONFIG)/h2_ssl+poll_registered_call_test +h2_ssl+poll_request_with_flags_test: $(BINDIR)/$(CONFIG)/h2_ssl+poll_request_with_flags_test +h2_ssl+poll_request_with_payload_test: $(BINDIR)/$(CONFIG)/h2_ssl+poll_request_with_payload_test +h2_ssl+poll_server_finishes_request_test: $(BINDIR)/$(CONFIG)/h2_ssl+poll_server_finishes_request_test +h2_ssl+poll_shutdown_finishes_calls_test: $(BINDIR)/$(CONFIG)/h2_ssl+poll_shutdown_finishes_calls_test +h2_ssl+poll_shutdown_finishes_tags_test: $(BINDIR)/$(CONFIG)/h2_ssl+poll_shutdown_finishes_tags_test +h2_ssl+poll_simple_delayed_request_test: $(BINDIR)/$(CONFIG)/h2_ssl+poll_simple_delayed_request_test +h2_ssl+poll_simple_request_test: $(BINDIR)/$(CONFIG)/h2_ssl+poll_simple_request_test +h2_ssl+poll_trailing_metadata_test: $(BINDIR)/$(CONFIG)/h2_ssl+poll_trailing_metadata_test +h2_ssl_proxy_bad_hostname_test: $(BINDIR)/$(CONFIG)/h2_ssl_proxy_bad_hostname_test +h2_ssl_proxy_binary_metadata_test: $(BINDIR)/$(CONFIG)/h2_ssl_proxy_binary_metadata_test +h2_ssl_proxy_call_creds_test: $(BINDIR)/$(CONFIG)/h2_ssl_proxy_call_creds_test +h2_ssl_proxy_cancel_after_accept_test: $(BINDIR)/$(CONFIG)/h2_ssl_proxy_cancel_after_accept_test +h2_ssl_proxy_cancel_after_client_done_test: $(BINDIR)/$(CONFIG)/h2_ssl_proxy_cancel_after_client_done_test +h2_ssl_proxy_cancel_after_invoke_test: $(BINDIR)/$(CONFIG)/h2_ssl_proxy_cancel_after_invoke_test +h2_ssl_proxy_cancel_before_invoke_test: $(BINDIR)/$(CONFIG)/h2_ssl_proxy_cancel_before_invoke_test +h2_ssl_proxy_cancel_in_a_vacuum_test: $(BINDIR)/$(CONFIG)/h2_ssl_proxy_cancel_in_a_vacuum_test +h2_ssl_proxy_census_simple_request_test: $(BINDIR)/$(CONFIG)/h2_ssl_proxy_census_simple_request_test +h2_ssl_proxy_default_host_test: $(BINDIR)/$(CONFIG)/h2_ssl_proxy_default_host_test +h2_ssl_proxy_disappearing_server_test: $(BINDIR)/$(CONFIG)/h2_ssl_proxy_disappearing_server_test +h2_ssl_proxy_empty_batch_test: $(BINDIR)/$(CONFIG)/h2_ssl_proxy_empty_batch_test +h2_ssl_proxy_graceful_server_shutdown_test: $(BINDIR)/$(CONFIG)/h2_ssl_proxy_graceful_server_shutdown_test +h2_ssl_proxy_high_initial_seqno_test: $(BINDIR)/$(CONFIG)/h2_ssl_proxy_high_initial_seqno_test +h2_ssl_proxy_invoke_large_request_test: $(BINDIR)/$(CONFIG)/h2_ssl_proxy_invoke_large_request_test +h2_ssl_proxy_large_metadata_test: $(BINDIR)/$(CONFIG)/h2_ssl_proxy_large_metadata_test +h2_ssl_proxy_max_message_length_test: $(BINDIR)/$(CONFIG)/h2_ssl_proxy_max_message_length_test +h2_ssl_proxy_metadata_test: $(BINDIR)/$(CONFIG)/h2_ssl_proxy_metadata_test +h2_ssl_proxy_no_op_test: $(BINDIR)/$(CONFIG)/h2_ssl_proxy_no_op_test +h2_ssl_proxy_payload_test: $(BINDIR)/$(CONFIG)/h2_ssl_proxy_payload_test +h2_ssl_proxy_ping_pong_streaming_test: $(BINDIR)/$(CONFIG)/h2_ssl_proxy_ping_pong_streaming_test +h2_ssl_proxy_registered_call_test: $(BINDIR)/$(CONFIG)/h2_ssl_proxy_registered_call_test +h2_ssl_proxy_request_with_payload_test: $(BINDIR)/$(CONFIG)/h2_ssl_proxy_request_with_payload_test +h2_ssl_proxy_server_finishes_request_test: $(BINDIR)/$(CONFIG)/h2_ssl_proxy_server_finishes_request_test +h2_ssl_proxy_shutdown_finishes_calls_test: $(BINDIR)/$(CONFIG)/h2_ssl_proxy_shutdown_finishes_calls_test +h2_ssl_proxy_shutdown_finishes_tags_test: $(BINDIR)/$(CONFIG)/h2_ssl_proxy_shutdown_finishes_tags_test +h2_ssl_proxy_simple_delayed_request_test: $(BINDIR)/$(CONFIG)/h2_ssl_proxy_simple_delayed_request_test +h2_ssl_proxy_simple_request_test: $(BINDIR)/$(CONFIG)/h2_ssl_proxy_simple_request_test +h2_ssl_proxy_trailing_metadata_test: $(BINDIR)/$(CONFIG)/h2_ssl_proxy_trailing_metadata_test +h2_uds_bad_hostname_test: $(BINDIR)/$(CONFIG)/h2_uds_bad_hostname_test +h2_uds_binary_metadata_test: $(BINDIR)/$(CONFIG)/h2_uds_binary_metadata_test +h2_uds_call_creds_test: $(BINDIR)/$(CONFIG)/h2_uds_call_creds_test +h2_uds_cancel_after_accept_test: $(BINDIR)/$(CONFIG)/h2_uds_cancel_after_accept_test +h2_uds_cancel_after_client_done_test: $(BINDIR)/$(CONFIG)/h2_uds_cancel_after_client_done_test +h2_uds_cancel_after_invoke_test: $(BINDIR)/$(CONFIG)/h2_uds_cancel_after_invoke_test +h2_uds_cancel_before_invoke_test: $(BINDIR)/$(CONFIG)/h2_uds_cancel_before_invoke_test +h2_uds_cancel_in_a_vacuum_test: $(BINDIR)/$(CONFIG)/h2_uds_cancel_in_a_vacuum_test +h2_uds_census_simple_request_test: $(BINDIR)/$(CONFIG)/h2_uds_census_simple_request_test +h2_uds_channel_connectivity_test: $(BINDIR)/$(CONFIG)/h2_uds_channel_connectivity_test +h2_uds_compressed_payload_test: $(BINDIR)/$(CONFIG)/h2_uds_compressed_payload_test +h2_uds_disappearing_server_test: $(BINDIR)/$(CONFIG)/h2_uds_disappearing_server_test +h2_uds_empty_batch_test: $(BINDIR)/$(CONFIG)/h2_uds_empty_batch_test +h2_uds_graceful_server_shutdown_test: $(BINDIR)/$(CONFIG)/h2_uds_graceful_server_shutdown_test +h2_uds_high_initial_seqno_test: $(BINDIR)/$(CONFIG)/h2_uds_high_initial_seqno_test +h2_uds_invoke_large_request_test: $(BINDIR)/$(CONFIG)/h2_uds_invoke_large_request_test +h2_uds_large_metadata_test: $(BINDIR)/$(CONFIG)/h2_uds_large_metadata_test +h2_uds_max_concurrent_streams_test: $(BINDIR)/$(CONFIG)/h2_uds_max_concurrent_streams_test +h2_uds_max_message_length_test: $(BINDIR)/$(CONFIG)/h2_uds_max_message_length_test +h2_uds_metadata_test: $(BINDIR)/$(CONFIG)/h2_uds_metadata_test +h2_uds_no_op_test: $(BINDIR)/$(CONFIG)/h2_uds_no_op_test +h2_uds_payload_test: $(BINDIR)/$(CONFIG)/h2_uds_payload_test +h2_uds_ping_pong_streaming_test: $(BINDIR)/$(CONFIG)/h2_uds_ping_pong_streaming_test +h2_uds_registered_call_test: $(BINDIR)/$(CONFIG)/h2_uds_registered_call_test +h2_uds_request_with_flags_test: $(BINDIR)/$(CONFIG)/h2_uds_request_with_flags_test +h2_uds_request_with_payload_test: $(BINDIR)/$(CONFIG)/h2_uds_request_with_payload_test +h2_uds_server_finishes_request_test: $(BINDIR)/$(CONFIG)/h2_uds_server_finishes_request_test +h2_uds_shutdown_finishes_calls_test: $(BINDIR)/$(CONFIG)/h2_uds_shutdown_finishes_calls_test +h2_uds_shutdown_finishes_tags_test: $(BINDIR)/$(CONFIG)/h2_uds_shutdown_finishes_tags_test +h2_uds_simple_delayed_request_test: $(BINDIR)/$(CONFIG)/h2_uds_simple_delayed_request_test +h2_uds_simple_request_test: $(BINDIR)/$(CONFIG)/h2_uds_simple_request_test +h2_uds_trailing_metadata_test: $(BINDIR)/$(CONFIG)/h2_uds_trailing_metadata_test +h2_uds+poll_bad_hostname_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_bad_hostname_test +h2_uds+poll_binary_metadata_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_binary_metadata_test +h2_uds+poll_call_creds_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_call_creds_test +h2_uds+poll_cancel_after_accept_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_accept_test +h2_uds+poll_cancel_after_client_done_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_client_done_test +h2_uds+poll_cancel_after_invoke_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_invoke_test +h2_uds+poll_cancel_before_invoke_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_before_invoke_test +h2_uds+poll_cancel_in_a_vacuum_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_in_a_vacuum_test +h2_uds+poll_census_simple_request_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_census_simple_request_test +h2_uds+poll_channel_connectivity_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_channel_connectivity_test +h2_uds+poll_compressed_payload_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_compressed_payload_test +h2_uds+poll_disappearing_server_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_disappearing_server_test +h2_uds+poll_empty_batch_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_empty_batch_test +h2_uds+poll_graceful_server_shutdown_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_graceful_server_shutdown_test +h2_uds+poll_high_initial_seqno_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_high_initial_seqno_test +h2_uds+poll_invoke_large_request_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_invoke_large_request_test +h2_uds+poll_large_metadata_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_large_metadata_test +h2_uds+poll_max_concurrent_streams_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_max_concurrent_streams_test +h2_uds+poll_max_message_length_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_max_message_length_test +h2_uds+poll_metadata_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_metadata_test +h2_uds+poll_no_op_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_no_op_test +h2_uds+poll_payload_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_payload_test +h2_uds+poll_ping_pong_streaming_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_ping_pong_streaming_test +h2_uds+poll_registered_call_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_registered_call_test +h2_uds+poll_request_with_flags_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_request_with_flags_test +h2_uds+poll_request_with_payload_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_request_with_payload_test +h2_uds+poll_server_finishes_request_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_server_finishes_request_test +h2_uds+poll_shutdown_finishes_calls_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_shutdown_finishes_calls_test +h2_uds+poll_shutdown_finishes_tags_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_shutdown_finishes_tags_test +h2_uds+poll_simple_delayed_request_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_simple_delayed_request_test +h2_uds+poll_simple_request_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_simple_request_test +h2_uds+poll_trailing_metadata_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_trailing_metadata_test +h2_compress_bad_hostname_nosec_test: $(BINDIR)/$(CONFIG)/h2_compress_bad_hostname_nosec_test +h2_compress_binary_metadata_nosec_test: $(BINDIR)/$(CONFIG)/h2_compress_binary_metadata_nosec_test +h2_compress_cancel_after_accept_nosec_test: $(BINDIR)/$(CONFIG)/h2_compress_cancel_after_accept_nosec_test +h2_compress_cancel_after_client_done_nosec_test: $(BINDIR)/$(CONFIG)/h2_compress_cancel_after_client_done_nosec_test +h2_compress_cancel_after_invoke_nosec_test: $(BINDIR)/$(CONFIG)/h2_compress_cancel_after_invoke_nosec_test +h2_compress_cancel_before_invoke_nosec_test: $(BINDIR)/$(CONFIG)/h2_compress_cancel_before_invoke_nosec_test +h2_compress_cancel_in_a_vacuum_nosec_test: $(BINDIR)/$(CONFIG)/h2_compress_cancel_in_a_vacuum_nosec_test +h2_compress_census_simple_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_compress_census_simple_request_nosec_test +h2_compress_channel_connectivity_nosec_test: $(BINDIR)/$(CONFIG)/h2_compress_channel_connectivity_nosec_test +h2_compress_compressed_payload_nosec_test: $(BINDIR)/$(CONFIG)/h2_compress_compressed_payload_nosec_test +h2_compress_default_host_nosec_test: $(BINDIR)/$(CONFIG)/h2_compress_default_host_nosec_test +h2_compress_disappearing_server_nosec_test: $(BINDIR)/$(CONFIG)/h2_compress_disappearing_server_nosec_test +h2_compress_empty_batch_nosec_test: $(BINDIR)/$(CONFIG)/h2_compress_empty_batch_nosec_test +h2_compress_graceful_server_shutdown_nosec_test: $(BINDIR)/$(CONFIG)/h2_compress_graceful_server_shutdown_nosec_test +h2_compress_high_initial_seqno_nosec_test: $(BINDIR)/$(CONFIG)/h2_compress_high_initial_seqno_nosec_test +h2_compress_invoke_large_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_compress_invoke_large_request_nosec_test +h2_compress_large_metadata_nosec_test: $(BINDIR)/$(CONFIG)/h2_compress_large_metadata_nosec_test +h2_compress_max_concurrent_streams_nosec_test: $(BINDIR)/$(CONFIG)/h2_compress_max_concurrent_streams_nosec_test +h2_compress_max_message_length_nosec_test: $(BINDIR)/$(CONFIG)/h2_compress_max_message_length_nosec_test +h2_compress_metadata_nosec_test: $(BINDIR)/$(CONFIG)/h2_compress_metadata_nosec_test +h2_compress_no_op_nosec_test: $(BINDIR)/$(CONFIG)/h2_compress_no_op_nosec_test +h2_compress_payload_nosec_test: $(BINDIR)/$(CONFIG)/h2_compress_payload_nosec_test +h2_compress_ping_pong_streaming_nosec_test: $(BINDIR)/$(CONFIG)/h2_compress_ping_pong_streaming_nosec_test +h2_compress_registered_call_nosec_test: $(BINDIR)/$(CONFIG)/h2_compress_registered_call_nosec_test +h2_compress_request_with_flags_nosec_test: $(BINDIR)/$(CONFIG)/h2_compress_request_with_flags_nosec_test +h2_compress_request_with_payload_nosec_test: $(BINDIR)/$(CONFIG)/h2_compress_request_with_payload_nosec_test +h2_compress_server_finishes_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_compress_server_finishes_request_nosec_test +h2_compress_shutdown_finishes_calls_nosec_test: $(BINDIR)/$(CONFIG)/h2_compress_shutdown_finishes_calls_nosec_test +h2_compress_shutdown_finishes_tags_nosec_test: $(BINDIR)/$(CONFIG)/h2_compress_shutdown_finishes_tags_nosec_test +h2_compress_simple_delayed_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_compress_simple_delayed_request_nosec_test +h2_compress_simple_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_compress_simple_request_nosec_test +h2_compress_trailing_metadata_nosec_test: $(BINDIR)/$(CONFIG)/h2_compress_trailing_metadata_nosec_test +h2_full_bad_hostname_nosec_test: $(BINDIR)/$(CONFIG)/h2_full_bad_hostname_nosec_test +h2_full_binary_metadata_nosec_test: $(BINDIR)/$(CONFIG)/h2_full_binary_metadata_nosec_test +h2_full_cancel_after_accept_nosec_test: $(BINDIR)/$(CONFIG)/h2_full_cancel_after_accept_nosec_test +h2_full_cancel_after_client_done_nosec_test: $(BINDIR)/$(CONFIG)/h2_full_cancel_after_client_done_nosec_test +h2_full_cancel_after_invoke_nosec_test: $(BINDIR)/$(CONFIG)/h2_full_cancel_after_invoke_nosec_test +h2_full_cancel_before_invoke_nosec_test: $(BINDIR)/$(CONFIG)/h2_full_cancel_before_invoke_nosec_test +h2_full_cancel_in_a_vacuum_nosec_test: $(BINDIR)/$(CONFIG)/h2_full_cancel_in_a_vacuum_nosec_test +h2_full_census_simple_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_full_census_simple_request_nosec_test +h2_full_channel_connectivity_nosec_test: $(BINDIR)/$(CONFIG)/h2_full_channel_connectivity_nosec_test +h2_full_compressed_payload_nosec_test: $(BINDIR)/$(CONFIG)/h2_full_compressed_payload_nosec_test +h2_full_default_host_nosec_test: $(BINDIR)/$(CONFIG)/h2_full_default_host_nosec_test +h2_full_disappearing_server_nosec_test: $(BINDIR)/$(CONFIG)/h2_full_disappearing_server_nosec_test +h2_full_empty_batch_nosec_test: $(BINDIR)/$(CONFIG)/h2_full_empty_batch_nosec_test +h2_full_graceful_server_shutdown_nosec_test: $(BINDIR)/$(CONFIG)/h2_full_graceful_server_shutdown_nosec_test +h2_full_high_initial_seqno_nosec_test: $(BINDIR)/$(CONFIG)/h2_full_high_initial_seqno_nosec_test +h2_full_invoke_large_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_full_invoke_large_request_nosec_test +h2_full_large_metadata_nosec_test: $(BINDIR)/$(CONFIG)/h2_full_large_metadata_nosec_test +h2_full_max_concurrent_streams_nosec_test: $(BINDIR)/$(CONFIG)/h2_full_max_concurrent_streams_nosec_test +h2_full_max_message_length_nosec_test: $(BINDIR)/$(CONFIG)/h2_full_max_message_length_nosec_test +h2_full_metadata_nosec_test: $(BINDIR)/$(CONFIG)/h2_full_metadata_nosec_test +h2_full_no_op_nosec_test: $(BINDIR)/$(CONFIG)/h2_full_no_op_nosec_test +h2_full_payload_nosec_test: $(BINDIR)/$(CONFIG)/h2_full_payload_nosec_test +h2_full_ping_pong_streaming_nosec_test: $(BINDIR)/$(CONFIG)/h2_full_ping_pong_streaming_nosec_test +h2_full_registered_call_nosec_test: $(BINDIR)/$(CONFIG)/h2_full_registered_call_nosec_test +h2_full_request_with_flags_nosec_test: $(BINDIR)/$(CONFIG)/h2_full_request_with_flags_nosec_test +h2_full_request_with_payload_nosec_test: $(BINDIR)/$(CONFIG)/h2_full_request_with_payload_nosec_test +h2_full_server_finishes_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_full_server_finishes_request_nosec_test +h2_full_shutdown_finishes_calls_nosec_test: $(BINDIR)/$(CONFIG)/h2_full_shutdown_finishes_calls_nosec_test +h2_full_shutdown_finishes_tags_nosec_test: $(BINDIR)/$(CONFIG)/h2_full_shutdown_finishes_tags_nosec_test +h2_full_simple_delayed_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_full_simple_delayed_request_nosec_test +h2_full_simple_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_full_simple_request_nosec_test +h2_full_trailing_metadata_nosec_test: $(BINDIR)/$(CONFIG)/h2_full_trailing_metadata_nosec_test +h2_full+poll_bad_hostname_nosec_test: $(BINDIR)/$(CONFIG)/h2_full+poll_bad_hostname_nosec_test +h2_full+poll_binary_metadata_nosec_test: $(BINDIR)/$(CONFIG)/h2_full+poll_binary_metadata_nosec_test +h2_full+poll_cancel_after_accept_nosec_test: $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_accept_nosec_test +h2_full+poll_cancel_after_client_done_nosec_test: $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_client_done_nosec_test +h2_full+poll_cancel_after_invoke_nosec_test: $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_invoke_nosec_test +h2_full+poll_cancel_before_invoke_nosec_test: $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_before_invoke_nosec_test +h2_full+poll_cancel_in_a_vacuum_nosec_test: $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_in_a_vacuum_nosec_test +h2_full+poll_census_simple_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_full+poll_census_simple_request_nosec_test +h2_full+poll_channel_connectivity_nosec_test: $(BINDIR)/$(CONFIG)/h2_full+poll_channel_connectivity_nosec_test +h2_full+poll_compressed_payload_nosec_test: $(BINDIR)/$(CONFIG)/h2_full+poll_compressed_payload_nosec_test +h2_full+poll_default_host_nosec_test: $(BINDIR)/$(CONFIG)/h2_full+poll_default_host_nosec_test +h2_full+poll_disappearing_server_nosec_test: $(BINDIR)/$(CONFIG)/h2_full+poll_disappearing_server_nosec_test +h2_full+poll_empty_batch_nosec_test: $(BINDIR)/$(CONFIG)/h2_full+poll_empty_batch_nosec_test +h2_full+poll_graceful_server_shutdown_nosec_test: $(BINDIR)/$(CONFIG)/h2_full+poll_graceful_server_shutdown_nosec_test +h2_full+poll_high_initial_seqno_nosec_test: $(BINDIR)/$(CONFIG)/h2_full+poll_high_initial_seqno_nosec_test +h2_full+poll_invoke_large_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_full+poll_invoke_large_request_nosec_test +h2_full+poll_large_metadata_nosec_test: $(BINDIR)/$(CONFIG)/h2_full+poll_large_metadata_nosec_test +h2_full+poll_max_concurrent_streams_nosec_test: $(BINDIR)/$(CONFIG)/h2_full+poll_max_concurrent_streams_nosec_test +h2_full+poll_max_message_length_nosec_test: $(BINDIR)/$(CONFIG)/h2_full+poll_max_message_length_nosec_test +h2_full+poll_metadata_nosec_test: $(BINDIR)/$(CONFIG)/h2_full+poll_metadata_nosec_test +h2_full+poll_no_op_nosec_test: $(BINDIR)/$(CONFIG)/h2_full+poll_no_op_nosec_test +h2_full+poll_payload_nosec_test: $(BINDIR)/$(CONFIG)/h2_full+poll_payload_nosec_test +h2_full+poll_ping_pong_streaming_nosec_test: $(BINDIR)/$(CONFIG)/h2_full+poll_ping_pong_streaming_nosec_test +h2_full+poll_registered_call_nosec_test: $(BINDIR)/$(CONFIG)/h2_full+poll_registered_call_nosec_test +h2_full+poll_request_with_flags_nosec_test: $(BINDIR)/$(CONFIG)/h2_full+poll_request_with_flags_nosec_test +h2_full+poll_request_with_payload_nosec_test: $(BINDIR)/$(CONFIG)/h2_full+poll_request_with_payload_nosec_test +h2_full+poll_server_finishes_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_full+poll_server_finishes_request_nosec_test +h2_full+poll_shutdown_finishes_calls_nosec_test: $(BINDIR)/$(CONFIG)/h2_full+poll_shutdown_finishes_calls_nosec_test +h2_full+poll_shutdown_finishes_tags_nosec_test: $(BINDIR)/$(CONFIG)/h2_full+poll_shutdown_finishes_tags_nosec_test +h2_full+poll_simple_delayed_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_full+poll_simple_delayed_request_nosec_test +h2_full+poll_simple_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_full+poll_simple_request_nosec_test +h2_full+poll_trailing_metadata_nosec_test: $(BINDIR)/$(CONFIG)/h2_full+poll_trailing_metadata_nosec_test +h2_proxy_bad_hostname_nosec_test: $(BINDIR)/$(CONFIG)/h2_proxy_bad_hostname_nosec_test +h2_proxy_binary_metadata_nosec_test: $(BINDIR)/$(CONFIG)/h2_proxy_binary_metadata_nosec_test +h2_proxy_cancel_after_accept_nosec_test: $(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_accept_nosec_test +h2_proxy_cancel_after_client_done_nosec_test: $(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_client_done_nosec_test +h2_proxy_cancel_after_invoke_nosec_test: $(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_invoke_nosec_test +h2_proxy_cancel_before_invoke_nosec_test: $(BINDIR)/$(CONFIG)/h2_proxy_cancel_before_invoke_nosec_test +h2_proxy_cancel_in_a_vacuum_nosec_test: $(BINDIR)/$(CONFIG)/h2_proxy_cancel_in_a_vacuum_nosec_test +h2_proxy_census_simple_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_proxy_census_simple_request_nosec_test +h2_proxy_default_host_nosec_test: $(BINDIR)/$(CONFIG)/h2_proxy_default_host_nosec_test +h2_proxy_disappearing_server_nosec_test: $(BINDIR)/$(CONFIG)/h2_proxy_disappearing_server_nosec_test +h2_proxy_empty_batch_nosec_test: $(BINDIR)/$(CONFIG)/h2_proxy_empty_batch_nosec_test +h2_proxy_graceful_server_shutdown_nosec_test: $(BINDIR)/$(CONFIG)/h2_proxy_graceful_server_shutdown_nosec_test +h2_proxy_high_initial_seqno_nosec_test: $(BINDIR)/$(CONFIG)/h2_proxy_high_initial_seqno_nosec_test +h2_proxy_invoke_large_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_proxy_invoke_large_request_nosec_test +h2_proxy_large_metadata_nosec_test: $(BINDIR)/$(CONFIG)/h2_proxy_large_metadata_nosec_test +h2_proxy_max_message_length_nosec_test: $(BINDIR)/$(CONFIG)/h2_proxy_max_message_length_nosec_test +h2_proxy_metadata_nosec_test: $(BINDIR)/$(CONFIG)/h2_proxy_metadata_nosec_test +h2_proxy_no_op_nosec_test: $(BINDIR)/$(CONFIG)/h2_proxy_no_op_nosec_test +h2_proxy_payload_nosec_test: $(BINDIR)/$(CONFIG)/h2_proxy_payload_nosec_test +h2_proxy_ping_pong_streaming_nosec_test: $(BINDIR)/$(CONFIG)/h2_proxy_ping_pong_streaming_nosec_test +h2_proxy_registered_call_nosec_test: $(BINDIR)/$(CONFIG)/h2_proxy_registered_call_nosec_test +h2_proxy_request_with_payload_nosec_test: $(BINDIR)/$(CONFIG)/h2_proxy_request_with_payload_nosec_test +h2_proxy_server_finishes_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_proxy_server_finishes_request_nosec_test +h2_proxy_shutdown_finishes_calls_nosec_test: $(BINDIR)/$(CONFIG)/h2_proxy_shutdown_finishes_calls_nosec_test +h2_proxy_shutdown_finishes_tags_nosec_test: $(BINDIR)/$(CONFIG)/h2_proxy_shutdown_finishes_tags_nosec_test +h2_proxy_simple_delayed_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_proxy_simple_delayed_request_nosec_test +h2_proxy_simple_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_proxy_simple_request_nosec_test +h2_proxy_trailing_metadata_nosec_test: $(BINDIR)/$(CONFIG)/h2_proxy_trailing_metadata_nosec_test +h2_sockpair_bad_hostname_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_bad_hostname_nosec_test +h2_sockpair_binary_metadata_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_binary_metadata_nosec_test +h2_sockpair_cancel_after_accept_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_accept_nosec_test +h2_sockpair_cancel_after_client_done_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_client_done_nosec_test +h2_sockpair_cancel_after_invoke_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_invoke_nosec_test +h2_sockpair_cancel_before_invoke_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_before_invoke_nosec_test +h2_sockpair_cancel_in_a_vacuum_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_in_a_vacuum_nosec_test +h2_sockpair_census_simple_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_census_simple_request_nosec_test +h2_sockpair_compressed_payload_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_compressed_payload_nosec_test +h2_sockpair_empty_batch_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_empty_batch_nosec_test +h2_sockpair_graceful_server_shutdown_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_graceful_server_shutdown_nosec_test +h2_sockpair_high_initial_seqno_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_high_initial_seqno_nosec_test +h2_sockpair_invoke_large_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_invoke_large_request_nosec_test +h2_sockpair_large_metadata_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_large_metadata_nosec_test +h2_sockpair_max_concurrent_streams_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_max_concurrent_streams_nosec_test +h2_sockpair_max_message_length_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_max_message_length_nosec_test +h2_sockpair_metadata_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_metadata_nosec_test +h2_sockpair_no_op_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_no_op_nosec_test +h2_sockpair_payload_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_payload_nosec_test +h2_sockpair_ping_pong_streaming_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_ping_pong_streaming_nosec_test +h2_sockpair_registered_call_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_registered_call_nosec_test +h2_sockpair_request_with_flags_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_request_with_flags_nosec_test +h2_sockpair_request_with_payload_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_request_with_payload_nosec_test +h2_sockpair_server_finishes_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_server_finishes_request_nosec_test +h2_sockpair_shutdown_finishes_calls_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_shutdown_finishes_calls_nosec_test +h2_sockpair_shutdown_finishes_tags_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_shutdown_finishes_tags_nosec_test +h2_sockpair_simple_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_simple_request_nosec_test +h2_sockpair_trailing_metadata_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_trailing_metadata_nosec_test +h2_sockpair+trace_bad_hostname_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_bad_hostname_nosec_test +h2_sockpair+trace_binary_metadata_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_binary_metadata_nosec_test +h2_sockpair+trace_cancel_after_accept_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_accept_nosec_test +h2_sockpair+trace_cancel_after_client_done_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_client_done_nosec_test +h2_sockpair+trace_cancel_after_invoke_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_invoke_nosec_test +h2_sockpair+trace_cancel_before_invoke_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_before_invoke_nosec_test +h2_sockpair+trace_cancel_in_a_vacuum_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_in_a_vacuum_nosec_test +h2_sockpair+trace_census_simple_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_census_simple_request_nosec_test +h2_sockpair+trace_compressed_payload_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_compressed_payload_nosec_test +h2_sockpair+trace_empty_batch_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_empty_batch_nosec_test +h2_sockpair+trace_graceful_server_shutdown_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_graceful_server_shutdown_nosec_test +h2_sockpair+trace_high_initial_seqno_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_high_initial_seqno_nosec_test +h2_sockpair+trace_invoke_large_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_invoke_large_request_nosec_test +h2_sockpair+trace_large_metadata_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_large_metadata_nosec_test +h2_sockpair+trace_max_concurrent_streams_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_max_concurrent_streams_nosec_test +h2_sockpair+trace_max_message_length_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_max_message_length_nosec_test +h2_sockpair+trace_metadata_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_metadata_nosec_test +h2_sockpair+trace_no_op_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_no_op_nosec_test +h2_sockpair+trace_payload_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_payload_nosec_test +h2_sockpair+trace_ping_pong_streaming_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_ping_pong_streaming_nosec_test +h2_sockpair+trace_registered_call_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_registered_call_nosec_test +h2_sockpair+trace_request_with_flags_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_request_with_flags_nosec_test +h2_sockpair+trace_request_with_payload_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_request_with_payload_nosec_test +h2_sockpair+trace_server_finishes_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_server_finishes_request_nosec_test +h2_sockpair+trace_shutdown_finishes_calls_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_shutdown_finishes_calls_nosec_test +h2_sockpair+trace_shutdown_finishes_tags_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_shutdown_finishes_tags_nosec_test +h2_sockpair+trace_simple_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_simple_request_nosec_test +h2_sockpair+trace_trailing_metadata_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair+trace_trailing_metadata_nosec_test +h2_sockpair_1byte_bad_hostname_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_bad_hostname_nosec_test +h2_sockpair_1byte_binary_metadata_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_binary_metadata_nosec_test +h2_sockpair_1byte_cancel_after_accept_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_accept_nosec_test +h2_sockpair_1byte_cancel_after_client_done_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_client_done_nosec_test +h2_sockpair_1byte_cancel_after_invoke_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_invoke_nosec_test +h2_sockpair_1byte_cancel_before_invoke_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_before_invoke_nosec_test +h2_sockpair_1byte_cancel_in_a_vacuum_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_in_a_vacuum_nosec_test +h2_sockpair_1byte_census_simple_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_census_simple_request_nosec_test +h2_sockpair_1byte_compressed_payload_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_compressed_payload_nosec_test +h2_sockpair_1byte_empty_batch_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_empty_batch_nosec_test +h2_sockpair_1byte_graceful_server_shutdown_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_graceful_server_shutdown_nosec_test +h2_sockpair_1byte_high_initial_seqno_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_high_initial_seqno_nosec_test +h2_sockpair_1byte_invoke_large_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_invoke_large_request_nosec_test +h2_sockpair_1byte_large_metadata_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_large_metadata_nosec_test +h2_sockpair_1byte_max_concurrent_streams_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_max_concurrent_streams_nosec_test +h2_sockpair_1byte_max_message_length_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_max_message_length_nosec_test +h2_sockpair_1byte_metadata_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_metadata_nosec_test +h2_sockpair_1byte_no_op_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_no_op_nosec_test +h2_sockpair_1byte_payload_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_payload_nosec_test +h2_sockpair_1byte_ping_pong_streaming_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_ping_pong_streaming_nosec_test +h2_sockpair_1byte_registered_call_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_registered_call_nosec_test +h2_sockpair_1byte_request_with_flags_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_request_with_flags_nosec_test +h2_sockpair_1byte_request_with_payload_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_request_with_payload_nosec_test +h2_sockpair_1byte_server_finishes_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_server_finishes_request_nosec_test +h2_sockpair_1byte_shutdown_finishes_calls_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_shutdown_finishes_calls_nosec_test +h2_sockpair_1byte_shutdown_finishes_tags_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_shutdown_finishes_tags_nosec_test +h2_sockpair_1byte_simple_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_simple_request_nosec_test +h2_sockpair_1byte_trailing_metadata_nosec_test: $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_trailing_metadata_nosec_test +h2_uds_bad_hostname_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds_bad_hostname_nosec_test +h2_uds_binary_metadata_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds_binary_metadata_nosec_test +h2_uds_cancel_after_accept_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds_cancel_after_accept_nosec_test +h2_uds_cancel_after_client_done_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds_cancel_after_client_done_nosec_test +h2_uds_cancel_after_invoke_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds_cancel_after_invoke_nosec_test +h2_uds_cancel_before_invoke_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds_cancel_before_invoke_nosec_test +h2_uds_cancel_in_a_vacuum_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds_cancel_in_a_vacuum_nosec_test +h2_uds_census_simple_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds_census_simple_request_nosec_test +h2_uds_channel_connectivity_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds_channel_connectivity_nosec_test +h2_uds_compressed_payload_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds_compressed_payload_nosec_test +h2_uds_disappearing_server_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds_disappearing_server_nosec_test +h2_uds_empty_batch_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds_empty_batch_nosec_test +h2_uds_graceful_server_shutdown_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds_graceful_server_shutdown_nosec_test +h2_uds_high_initial_seqno_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds_high_initial_seqno_nosec_test +h2_uds_invoke_large_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds_invoke_large_request_nosec_test +h2_uds_large_metadata_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds_large_metadata_nosec_test +h2_uds_max_concurrent_streams_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds_max_concurrent_streams_nosec_test +h2_uds_max_message_length_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds_max_message_length_nosec_test +h2_uds_metadata_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds_metadata_nosec_test +h2_uds_no_op_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds_no_op_nosec_test +h2_uds_payload_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds_payload_nosec_test +h2_uds_ping_pong_streaming_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds_ping_pong_streaming_nosec_test +h2_uds_registered_call_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds_registered_call_nosec_test +h2_uds_request_with_flags_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds_request_with_flags_nosec_test +h2_uds_request_with_payload_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds_request_with_payload_nosec_test +h2_uds_server_finishes_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds_server_finishes_request_nosec_test +h2_uds_shutdown_finishes_calls_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds_shutdown_finishes_calls_nosec_test +h2_uds_shutdown_finishes_tags_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds_shutdown_finishes_tags_nosec_test +h2_uds_simple_delayed_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds_simple_delayed_request_nosec_test +h2_uds_simple_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds_simple_request_nosec_test +h2_uds_trailing_metadata_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds_trailing_metadata_nosec_test +h2_uds+poll_bad_hostname_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_bad_hostname_nosec_test +h2_uds+poll_binary_metadata_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_binary_metadata_nosec_test +h2_uds+poll_cancel_after_accept_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_accept_nosec_test +h2_uds+poll_cancel_after_client_done_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_client_done_nosec_test +h2_uds+poll_cancel_after_invoke_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_invoke_nosec_test +h2_uds+poll_cancel_before_invoke_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_before_invoke_nosec_test +h2_uds+poll_cancel_in_a_vacuum_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_in_a_vacuum_nosec_test +h2_uds+poll_census_simple_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_census_simple_request_nosec_test +h2_uds+poll_channel_connectivity_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_channel_connectivity_nosec_test +h2_uds+poll_compressed_payload_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_compressed_payload_nosec_test +h2_uds+poll_disappearing_server_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_disappearing_server_nosec_test +h2_uds+poll_empty_batch_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_empty_batch_nosec_test +h2_uds+poll_graceful_server_shutdown_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_graceful_server_shutdown_nosec_test +h2_uds+poll_high_initial_seqno_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_high_initial_seqno_nosec_test +h2_uds+poll_invoke_large_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_invoke_large_request_nosec_test +h2_uds+poll_large_metadata_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_large_metadata_nosec_test +h2_uds+poll_max_concurrent_streams_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_max_concurrent_streams_nosec_test +h2_uds+poll_max_message_length_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_max_message_length_nosec_test +h2_uds+poll_metadata_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_metadata_nosec_test +h2_uds+poll_no_op_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_no_op_nosec_test +h2_uds+poll_payload_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_payload_nosec_test +h2_uds+poll_ping_pong_streaming_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_ping_pong_streaming_nosec_test +h2_uds+poll_registered_call_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_registered_call_nosec_test +h2_uds+poll_request_with_flags_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_request_with_flags_nosec_test +h2_uds+poll_request_with_payload_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_request_with_payload_nosec_test +h2_uds+poll_server_finishes_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_server_finishes_request_nosec_test +h2_uds+poll_shutdown_finishes_calls_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_shutdown_finishes_calls_nosec_test +h2_uds+poll_shutdown_finishes_tags_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_shutdown_finishes_tags_nosec_test +h2_uds+poll_simple_delayed_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_simple_delayed_request_nosec_test +h2_uds+poll_simple_request_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_simple_request_nosec_test +h2_uds+poll_trailing_metadata_nosec_test: $(BINDIR)/$(CONFIG)/h2_uds+poll_trailing_metadata_nosec_test +connection_prefix_bad_client_test: $(BINDIR)/$(CONFIG)/connection_prefix_bad_client_test +initial_settings_frame_bad_client_test: $(BINDIR)/$(CONFIG)/initial_settings_frame_bad_client_test + +run_dep_checks: + $(OPENSSL_ALPN_CHECK_CMD) || true + $(OPENSSL_NPN_CHECK_CMD) || true + $(ZLIB_CHECK_CMD) || true + $(PERFTOOLS_CHECK_CMD) || true + $(PROTOBUF_CHECK_CMD) || true + $(PROTOC_CHECK_VERSION_CMD) || true + $(ZOOKEEPER_CHECK_CMD) || true + +$(LIBDIR)/$(CONFIG)/zlib/libz.a: + $(E) "[MAKE] Building zlib" + $(Q)(cd third_party/zlib ; CC="$(CC)" CFLAGS="$(PIC_CPPFLAGS) -fvisibility=hidden $(CPPFLAGS_$(CONFIG)) $(ZLIB_CFLAGS_EXTRA)" ./configure --static) + $(Q)$(MAKE) -C third_party/zlib clean + $(Q)$(MAKE) -C third_party/zlib + $(Q)mkdir -p $(LIBDIR)/$(CONFIG)/zlib + $(Q)cp third_party/zlib/libz.a $(LIBDIR)/$(CONFIG)/zlib + +$(LIBDIR)/$(CONFIG)/openssl/libssl.a: + $(E) "[MAKE] Building openssl for $(SYSTEM)" +ifeq ($(SYSTEM),Darwin) + $(Q)(cd third_party/openssl ; CC="$(CC) $(PIC_CPPFLAGS) -fvisibility=hidden $(CPPFLAGS_$(CONFIG)) $(OPENSSL_CFLAGS_$(CONFIG)) $(OPENSSL_CFLAGS_EXTRA)" ./Configure darwin64-x86_64-cc) +else +ifeq ($(SYSTEM),MINGW32) + @echo "We currently don't have a good way to compile OpenSSL in-place under msys." + @echo "Please provide a OpenSSL in your mingw32 system." + @echo + @echo "Note that you can find a compatible version of the libraries here:" + @echo + @echo "http://slproweb.com/products/Win32OpenSSL.html" + @echo + @echo "If you decide to install that one, take the full version. The light" + @echo "version only contains compiled DLLs, without the development files." + @echo + @echo "When installing, chose to copy the OpenSSL dlls to the OpenSSL binaries" + @echo "directory. This way we'll link to them directly." + @echo + @echo "You can then re-start the build the following way:" + @echo + @echo " CPPFLAGS=-I/c/OpenSSL-Win64/include LDFLAGS=-L/c/OpenSSL-Win64 make" + @false +else + $(Q)(cd third_party/openssl ; CC="$(CC) $(PIC_CPPFLAGS) -fvisibility=hidden $(CPPFLAGS_$(CONFIG)) $(OPENSSL_CFLAGS_$(CONFIG)) $(OPENSSL_CFLAGS_EXTRA)" ./config no-asm $(OPENSSL_CONFIG_$(CONFIG))) +endif +endif + $(Q)$(MAKE) -j 1 -C third_party/openssl clean + $(Q)(unset CPPFLAGS; $(MAKE) -j 1 -C third_party/openssl build_crypto build_ssl) + $(Q)mkdir -p $(LIBDIR)/$(CONFIG)/openssl + $(Q)cp third_party/openssl/libssl.a third_party/openssl/libcrypto.a $(LIBDIR)/$(CONFIG)/openssl + +third_party/protobuf/configure: + $(E) "[AUTOGEN] Preparing protobuf" + $(Q)(cd third_party/protobuf ; autoreconf -f -i -Wall,no-obsolete) + +$(LIBDIR)/$(CONFIG)/protobuf/libprotobuf.a: third_party/protobuf/configure + $(E) "[MAKE] Building protobuf" + $(Q)(cd third_party/protobuf ; CC="$(CC)" CXX="$(CXX)" LDFLAGS="$(LDFLAGS_$(CONFIG)) -g $(PROTOBUF_LDFLAGS_EXTRA)" CPPFLAGS="$(PIC_CPPFLAGS) $(CPPFLAGS_$(CONFIG)) -g $(PROTOBUF_CPPFLAGS_EXTRA)" ./configure --disable-shared --enable-static) + $(Q)$(MAKE) -C third_party/protobuf clean + $(Q)$(MAKE) -C third_party/protobuf + $(Q)mkdir -p $(LIBDIR)/$(CONFIG)/protobuf + $(Q)mkdir -p $(BINDIR)/$(CONFIG)/protobuf + $(Q)cp third_party/protobuf/src/.libs/libprotoc.a $(LIBDIR)/$(CONFIG)/protobuf + $(Q)cp third_party/protobuf/src/.libs/libprotobuf.a $(LIBDIR)/$(CONFIG)/protobuf + $(Q)cp third_party/protobuf/src/protoc $(BINDIR)/$(CONFIG)/protobuf + +static: static_c static_cxx + +static_c: pc_c pc_c_unsecure cache.mk pc_gpr pc_c_zookeeper $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a static_zookeeper_libs + + +static_cxx: pc_cxx pc_cxx_unsecure pc_gpr cache.mk $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.a + +shared: shared_c shared_cxx + +shared_c: pc_c pc_c_unsecure pc_gpr cache.mk pc_c_zookeeper $(LIBDIR)/$(CONFIG)/libgpr.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.$(SHARED_EXT) shared_zookeeper_libs + +shared_cxx: pc_cxx pc_cxx_unsecure cache.mk $(LIBDIR)/$(CONFIG)/libgrpc++.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.$(SHARED_EXT) + +shared_csharp: shared_c $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext.$(SHARED_EXT) +ifeq ($(HAS_ZOOKEEPER),true) +static_zookeeper_libs: $(LIBDIR)/$(CONFIG)/libgrpc_zookeeper.a +shared_zookeeper_libs: $(LIBDIR)/$(CONFIG)/libgrpc_zookeeper.$(SHARED_EXT) +else + +static_zookeeper_libs: + +shared_zookeeper_libs: + +endif + +grpc_csharp_ext: shared_csharp + +plugins: $(PROTOC_PLUGINS) + +privatelibs: privatelibs_c privatelibs_cxx + +privatelibs_c: $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libreconnect_server.a $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_test_call_creds.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_test_channel_connectivity.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libend2end_test_default_host.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libbad_client_test.a +pc_gpr: $(LIBDIR)/$(CONFIG)/pkgconfig/gpr.pc + +pc_c: $(LIBDIR)/$(CONFIG)/pkgconfig/grpc.pc + +pc_c_unsecure: $(LIBDIR)/$(CONFIG)/pkgconfig/grpc_unsecure.pc + +ifeq ($(HAS_ZOOKEEPER),true) +pc_c_zookeeper: $(LIBDIR)/$(CONFIG)/pkgconfig/grpc_zookeeper.pc +else +pc_c_zookeeper: +endif + +pc_cxx: $(LIBDIR)/$(CONFIG)/pkgconfig/grpc++.pc + +pc_cxx_unsecure: $(LIBDIR)/$(CONFIG)/pkgconfig/grpc++_unsecure.pc + +privatelibs_cxx: $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libinterop_client_helper.a $(LIBDIR)/$(CONFIG)/libinterop_client_main.a $(LIBDIR)/$(CONFIG)/libinterop_server_helper.a $(LIBDIR)/$(CONFIG)/libinterop_server_main.a $(LIBDIR)/$(CONFIG)/libqps.a + +ifeq ($(HAS_ZOOKEEPER),true) +privatelibs_zookeeper: +else +privatelibs_zookeeper: +endif + + +buildtests: buildtests_c buildtests_cxx buildtests_zookeeper + +buildtests_c: privatelibs_c $(BINDIR)/$(CONFIG)/alarm_heap_test $(BINDIR)/$(CONFIG)/alarm_list_test $(BINDIR)/$(CONFIG)/alarm_test $(BINDIR)/$(CONFIG)/alpn_test $(BINDIR)/$(CONFIG)/bin_encoder_test $(BINDIR)/$(CONFIG)/chttp2_status_conversion_test $(BINDIR)/$(CONFIG)/chttp2_stream_encoder_test $(BINDIR)/$(CONFIG)/chttp2_stream_map_test $(BINDIR)/$(CONFIG)/compression_test $(BINDIR)/$(CONFIG)/dualstack_socket_test $(BINDIR)/$(CONFIG)/endpoint_pair_test $(BINDIR)/$(CONFIG)/fd_conservation_posix_test $(BINDIR)/$(CONFIG)/fd_posix_test $(BINDIR)/$(CONFIG)/fling_client $(BINDIR)/$(CONFIG)/fling_server $(BINDIR)/$(CONFIG)/fling_stream_test $(BINDIR)/$(CONFIG)/fling_test $(BINDIR)/$(CONFIG)/gpr_cmdline_test $(BINDIR)/$(CONFIG)/gpr_env_test $(BINDIR)/$(CONFIG)/gpr_file_test $(BINDIR)/$(CONFIG)/gpr_histogram_test $(BINDIR)/$(CONFIG)/gpr_host_port_test $(BINDIR)/$(CONFIG)/gpr_log_test $(BINDIR)/$(CONFIG)/gpr_slice_buffer_test $(BINDIR)/$(CONFIG)/gpr_slice_test $(BINDIR)/$(CONFIG)/gpr_stack_lockfree_test $(BINDIR)/$(CONFIG)/gpr_string_test $(BINDIR)/$(CONFIG)/gpr_sync_test $(BINDIR)/$(CONFIG)/gpr_thd_test $(BINDIR)/$(CONFIG)/gpr_time_test $(BINDIR)/$(CONFIG)/gpr_tls_test $(BINDIR)/$(CONFIG)/gpr_useful_test $(BINDIR)/$(CONFIG)/grpc_auth_context_test $(BINDIR)/$(CONFIG)/grpc_base64_test $(BINDIR)/$(CONFIG)/grpc_byte_buffer_reader_test $(BINDIR)/$(CONFIG)/grpc_channel_args_test $(BINDIR)/$(CONFIG)/grpc_channel_stack_test $(BINDIR)/$(CONFIG)/grpc_completion_queue_test $(BINDIR)/$(CONFIG)/grpc_credentials_test $(BINDIR)/$(CONFIG)/grpc_json_token_test $(BINDIR)/$(CONFIG)/grpc_jwt_verifier_test $(BINDIR)/$(CONFIG)/grpc_security_connector_test $(BINDIR)/$(CONFIG)/grpc_stream_op_test $(BINDIR)/$(CONFIG)/hpack_parser_test $(BINDIR)/$(CONFIG)/hpack_table_test $(BINDIR)/$(CONFIG)/httpcli_format_request_test $(BINDIR)/$(CONFIG)/httpcli_parser_test $(BINDIR)/$(CONFIG)/httpcli_test $(BINDIR)/$(CONFIG)/json_rewrite $(BINDIR)/$(CONFIG)/json_rewrite_test $(BINDIR)/$(CONFIG)/json_test $(BINDIR)/$(CONFIG)/lame_client_test $(BINDIR)/$(CONFIG)/message_compress_test $(BINDIR)/$(CONFIG)/multi_init_test $(BINDIR)/$(CONFIG)/multiple_server_queues_test $(BINDIR)/$(CONFIG)/murmur_hash_test $(BINDIR)/$(CONFIG)/no_server_test $(BINDIR)/$(CONFIG)/resolve_address_test $(BINDIR)/$(CONFIG)/secure_endpoint_test $(BINDIR)/$(CONFIG)/sockaddr_utils_test $(BINDIR)/$(CONFIG)/tcp_client_posix_test $(BINDIR)/$(CONFIG)/tcp_posix_test $(BINDIR)/$(CONFIG)/tcp_server_posix_test $(BINDIR)/$(CONFIG)/time_averaged_stats_test $(BINDIR)/$(CONFIG)/timeout_encoding_test $(BINDIR)/$(CONFIG)/timers_test $(BINDIR)/$(CONFIG)/transport_metadata_test $(BINDIR)/$(CONFIG)/transport_security_test $(BINDIR)/$(CONFIG)/udp_server_test $(BINDIR)/$(CONFIG)/uri_parser_test $(BINDIR)/$(CONFIG)/h2_compress_bad_hostname_test $(BINDIR)/$(CONFIG)/h2_compress_binary_metadata_test $(BINDIR)/$(CONFIG)/h2_compress_call_creds_test $(BINDIR)/$(CONFIG)/h2_compress_cancel_after_accept_test $(BINDIR)/$(CONFIG)/h2_compress_cancel_after_client_done_test $(BINDIR)/$(CONFIG)/h2_compress_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/h2_compress_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/h2_compress_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/h2_compress_census_simple_request_test $(BINDIR)/$(CONFIG)/h2_compress_channel_connectivity_test $(BINDIR)/$(CONFIG)/h2_compress_compressed_payload_test $(BINDIR)/$(CONFIG)/h2_compress_default_host_test $(BINDIR)/$(CONFIG)/h2_compress_disappearing_server_test $(BINDIR)/$(CONFIG)/h2_compress_empty_batch_test $(BINDIR)/$(CONFIG)/h2_compress_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/h2_compress_high_initial_seqno_test $(BINDIR)/$(CONFIG)/h2_compress_invoke_large_request_test $(BINDIR)/$(CONFIG)/h2_compress_large_metadata_test $(BINDIR)/$(CONFIG)/h2_compress_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/h2_compress_max_message_length_test $(BINDIR)/$(CONFIG)/h2_compress_metadata_test $(BINDIR)/$(CONFIG)/h2_compress_no_op_test $(BINDIR)/$(CONFIG)/h2_compress_payload_test $(BINDIR)/$(CONFIG)/h2_compress_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/h2_compress_registered_call_test $(BINDIR)/$(CONFIG)/h2_compress_request_with_flags_test $(BINDIR)/$(CONFIG)/h2_compress_request_with_payload_test $(BINDIR)/$(CONFIG)/h2_compress_server_finishes_request_test $(BINDIR)/$(CONFIG)/h2_compress_shutdown_finishes_calls_test $(BINDIR)/$(CONFIG)/h2_compress_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/h2_compress_simple_delayed_request_test $(BINDIR)/$(CONFIG)/h2_compress_simple_request_test $(BINDIR)/$(CONFIG)/h2_compress_trailing_metadata_test $(BINDIR)/$(CONFIG)/h2_fakesec_bad_hostname_test $(BINDIR)/$(CONFIG)/h2_fakesec_binary_metadata_test $(BINDIR)/$(CONFIG)/h2_fakesec_call_creds_test $(BINDIR)/$(CONFIG)/h2_fakesec_cancel_after_accept_test $(BINDIR)/$(CONFIG)/h2_fakesec_cancel_after_client_done_test $(BINDIR)/$(CONFIG)/h2_fakesec_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/h2_fakesec_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/h2_fakesec_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/h2_fakesec_census_simple_request_test $(BINDIR)/$(CONFIG)/h2_fakesec_channel_connectivity_test $(BINDIR)/$(CONFIG)/h2_fakesec_compressed_payload_test $(BINDIR)/$(CONFIG)/h2_fakesec_default_host_test $(BINDIR)/$(CONFIG)/h2_fakesec_disappearing_server_test $(BINDIR)/$(CONFIG)/h2_fakesec_empty_batch_test $(BINDIR)/$(CONFIG)/h2_fakesec_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/h2_fakesec_high_initial_seqno_test $(BINDIR)/$(CONFIG)/h2_fakesec_invoke_large_request_test $(BINDIR)/$(CONFIG)/h2_fakesec_large_metadata_test $(BINDIR)/$(CONFIG)/h2_fakesec_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/h2_fakesec_max_message_length_test $(BINDIR)/$(CONFIG)/h2_fakesec_metadata_test $(BINDIR)/$(CONFIG)/h2_fakesec_no_op_test $(BINDIR)/$(CONFIG)/h2_fakesec_payload_test $(BINDIR)/$(CONFIG)/h2_fakesec_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/h2_fakesec_registered_call_test $(BINDIR)/$(CONFIG)/h2_fakesec_request_with_flags_test $(BINDIR)/$(CONFIG)/h2_fakesec_request_with_payload_test $(BINDIR)/$(CONFIG)/h2_fakesec_server_finishes_request_test $(BINDIR)/$(CONFIG)/h2_fakesec_shutdown_finishes_calls_test $(BINDIR)/$(CONFIG)/h2_fakesec_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/h2_fakesec_simple_delayed_request_test $(BINDIR)/$(CONFIG)/h2_fakesec_simple_request_test $(BINDIR)/$(CONFIG)/h2_fakesec_trailing_metadata_test $(BINDIR)/$(CONFIG)/h2_full_bad_hostname_test $(BINDIR)/$(CONFIG)/h2_full_binary_metadata_test $(BINDIR)/$(CONFIG)/h2_full_call_creds_test $(BINDIR)/$(CONFIG)/h2_full_cancel_after_accept_test $(BINDIR)/$(CONFIG)/h2_full_cancel_after_client_done_test $(BINDIR)/$(CONFIG)/h2_full_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/h2_full_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/h2_full_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/h2_full_census_simple_request_test $(BINDIR)/$(CONFIG)/h2_full_channel_connectivity_test $(BINDIR)/$(CONFIG)/h2_full_compressed_payload_test $(BINDIR)/$(CONFIG)/h2_full_default_host_test $(BINDIR)/$(CONFIG)/h2_full_disappearing_server_test $(BINDIR)/$(CONFIG)/h2_full_empty_batch_test $(BINDIR)/$(CONFIG)/h2_full_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/h2_full_high_initial_seqno_test $(BINDIR)/$(CONFIG)/h2_full_invoke_large_request_test $(BINDIR)/$(CONFIG)/h2_full_large_metadata_test $(BINDIR)/$(CONFIG)/h2_full_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/h2_full_max_message_length_test $(BINDIR)/$(CONFIG)/h2_full_metadata_test $(BINDIR)/$(CONFIG)/h2_full_no_op_test $(BINDIR)/$(CONFIG)/h2_full_payload_test $(BINDIR)/$(CONFIG)/h2_full_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/h2_full_registered_call_test $(BINDIR)/$(CONFIG)/h2_full_request_with_flags_test $(BINDIR)/$(CONFIG)/h2_full_request_with_payload_test $(BINDIR)/$(CONFIG)/h2_full_server_finishes_request_test $(BINDIR)/$(CONFIG)/h2_full_shutdown_finishes_calls_test $(BINDIR)/$(CONFIG)/h2_full_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/h2_full_simple_delayed_request_test $(BINDIR)/$(CONFIG)/h2_full_simple_request_test $(BINDIR)/$(CONFIG)/h2_full_trailing_metadata_test $(BINDIR)/$(CONFIG)/h2_full+poll_bad_hostname_test $(BINDIR)/$(CONFIG)/h2_full+poll_binary_metadata_test $(BINDIR)/$(CONFIG)/h2_full+poll_call_creds_test $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_accept_test $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_client_done_test $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/h2_full+poll_census_simple_request_test $(BINDIR)/$(CONFIG)/h2_full+poll_channel_connectivity_test $(BINDIR)/$(CONFIG)/h2_full+poll_compressed_payload_test $(BINDIR)/$(CONFIG)/h2_full+poll_default_host_test $(BINDIR)/$(CONFIG)/h2_full+poll_disappearing_server_test $(BINDIR)/$(CONFIG)/h2_full+poll_empty_batch_test $(BINDIR)/$(CONFIG)/h2_full+poll_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/h2_full+poll_high_initial_seqno_test $(BINDIR)/$(CONFIG)/h2_full+poll_invoke_large_request_test $(BINDIR)/$(CONFIG)/h2_full+poll_large_metadata_test $(BINDIR)/$(CONFIG)/h2_full+poll_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/h2_full+poll_max_message_length_test $(BINDIR)/$(CONFIG)/h2_full+poll_metadata_test $(BINDIR)/$(CONFIG)/h2_full+poll_no_op_test $(BINDIR)/$(CONFIG)/h2_full+poll_payload_test $(BINDIR)/$(CONFIG)/h2_full+poll_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/h2_full+poll_registered_call_test $(BINDIR)/$(CONFIG)/h2_full+poll_request_with_flags_test $(BINDIR)/$(CONFIG)/h2_full+poll_request_with_payload_test $(BINDIR)/$(CONFIG)/h2_full+poll_server_finishes_request_test $(BINDIR)/$(CONFIG)/h2_full+poll_shutdown_finishes_calls_test $(BINDIR)/$(CONFIG)/h2_full+poll_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/h2_full+poll_simple_delayed_request_test $(BINDIR)/$(CONFIG)/h2_full+poll_simple_request_test $(BINDIR)/$(CONFIG)/h2_full+poll_trailing_metadata_test $(BINDIR)/$(CONFIG)/h2_oauth2_bad_hostname_test $(BINDIR)/$(CONFIG)/h2_oauth2_binary_metadata_test $(BINDIR)/$(CONFIG)/h2_oauth2_call_creds_test $(BINDIR)/$(CONFIG)/h2_oauth2_cancel_after_accept_test $(BINDIR)/$(CONFIG)/h2_oauth2_cancel_after_client_done_test $(BINDIR)/$(CONFIG)/h2_oauth2_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/h2_oauth2_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/h2_oauth2_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/h2_oauth2_census_simple_request_test $(BINDIR)/$(CONFIG)/h2_oauth2_channel_connectivity_test $(BINDIR)/$(CONFIG)/h2_oauth2_compressed_payload_test $(BINDIR)/$(CONFIG)/h2_oauth2_default_host_test $(BINDIR)/$(CONFIG)/h2_oauth2_disappearing_server_test $(BINDIR)/$(CONFIG)/h2_oauth2_empty_batch_test $(BINDIR)/$(CONFIG)/h2_oauth2_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/h2_oauth2_high_initial_seqno_test $(BINDIR)/$(CONFIG)/h2_oauth2_invoke_large_request_test $(BINDIR)/$(CONFIG)/h2_oauth2_large_metadata_test $(BINDIR)/$(CONFIG)/h2_oauth2_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/h2_oauth2_max_message_length_test $(BINDIR)/$(CONFIG)/h2_oauth2_metadata_test $(BINDIR)/$(CONFIG)/h2_oauth2_no_op_test $(BINDIR)/$(CONFIG)/h2_oauth2_payload_test $(BINDIR)/$(CONFIG)/h2_oauth2_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/h2_oauth2_registered_call_test $(BINDIR)/$(CONFIG)/h2_oauth2_request_with_flags_test $(BINDIR)/$(CONFIG)/h2_oauth2_request_with_payload_test $(BINDIR)/$(CONFIG)/h2_oauth2_server_finishes_request_test $(BINDIR)/$(CONFIG)/h2_oauth2_shutdown_finishes_calls_test $(BINDIR)/$(CONFIG)/h2_oauth2_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/h2_oauth2_simple_delayed_request_test $(BINDIR)/$(CONFIG)/h2_oauth2_simple_request_test $(BINDIR)/$(CONFIG)/h2_oauth2_trailing_metadata_test $(BINDIR)/$(CONFIG)/h2_proxy_bad_hostname_test $(BINDIR)/$(CONFIG)/h2_proxy_binary_metadata_test $(BINDIR)/$(CONFIG)/h2_proxy_call_creds_test $(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_accept_test $(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_client_done_test $(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/h2_proxy_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/h2_proxy_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/h2_proxy_census_simple_request_test $(BINDIR)/$(CONFIG)/h2_proxy_default_host_test $(BINDIR)/$(CONFIG)/h2_proxy_disappearing_server_test $(BINDIR)/$(CONFIG)/h2_proxy_empty_batch_test $(BINDIR)/$(CONFIG)/h2_proxy_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/h2_proxy_high_initial_seqno_test $(BINDIR)/$(CONFIG)/h2_proxy_invoke_large_request_test $(BINDIR)/$(CONFIG)/h2_proxy_large_metadata_test $(BINDIR)/$(CONFIG)/h2_proxy_max_message_length_test $(BINDIR)/$(CONFIG)/h2_proxy_metadata_test $(BINDIR)/$(CONFIG)/h2_proxy_no_op_test $(BINDIR)/$(CONFIG)/h2_proxy_payload_test $(BINDIR)/$(CONFIG)/h2_proxy_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/h2_proxy_registered_call_test $(BINDIR)/$(CONFIG)/h2_proxy_request_with_payload_test $(BINDIR)/$(CONFIG)/h2_proxy_server_finishes_request_test $(BINDIR)/$(CONFIG)/h2_proxy_shutdown_finishes_calls_test $(BINDIR)/$(CONFIG)/h2_proxy_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/h2_proxy_simple_delayed_request_test $(BINDIR)/$(CONFIG)/h2_proxy_simple_request_test $(BINDIR)/$(CONFIG)/h2_proxy_trailing_metadata_test $(BINDIR)/$(CONFIG)/h2_sockpair_bad_hostname_test $(BINDIR)/$(CONFIG)/h2_sockpair_binary_metadata_test $(BINDIR)/$(CONFIG)/h2_sockpair_call_creds_test $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_accept_test $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_client_done_test $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/h2_sockpair_census_simple_request_test $(BINDIR)/$(CONFIG)/h2_sockpair_compressed_payload_test $(BINDIR)/$(CONFIG)/h2_sockpair_empty_batch_test $(BINDIR)/$(CONFIG)/h2_sockpair_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/h2_sockpair_high_initial_seqno_test $(BINDIR)/$(CONFIG)/h2_sockpair_invoke_large_request_test $(BINDIR)/$(CONFIG)/h2_sockpair_large_metadata_test $(BINDIR)/$(CONFIG)/h2_sockpair_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/h2_sockpair_max_message_length_test $(BINDIR)/$(CONFIG)/h2_sockpair_metadata_test $(BINDIR)/$(CONFIG)/h2_sockpair_no_op_test $(BINDIR)/$(CONFIG)/h2_sockpair_payload_test $(BINDIR)/$(CONFIG)/h2_sockpair_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/h2_sockpair_registered_call_test $(BINDIR)/$(CONFIG)/h2_sockpair_request_with_flags_test $(BINDIR)/$(CONFIG)/h2_sockpair_request_with_payload_test $(BINDIR)/$(CONFIG)/h2_sockpair_server_finishes_request_test $(BINDIR)/$(CONFIG)/h2_sockpair_shutdown_finishes_calls_test $(BINDIR)/$(CONFIG)/h2_sockpair_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/h2_sockpair_simple_request_test $(BINDIR)/$(CONFIG)/h2_sockpair_trailing_metadata_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_bad_hostname_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_binary_metadata_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_call_creds_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_accept_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_client_done_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_census_simple_request_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_compressed_payload_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_empty_batch_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_high_initial_seqno_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_invoke_large_request_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_large_metadata_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_max_message_length_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_metadata_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_no_op_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_payload_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_registered_call_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_request_with_flags_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_request_with_payload_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_server_finishes_request_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_shutdown_finishes_calls_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_simple_request_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_trailing_metadata_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_bad_hostname_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_binary_metadata_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_call_creds_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_accept_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_client_done_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_census_simple_request_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_compressed_payload_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_empty_batch_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_high_initial_seqno_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_invoke_large_request_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_large_metadata_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_max_message_length_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_metadata_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_no_op_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_payload_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_registered_call_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_request_with_flags_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_request_with_payload_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_server_finishes_request_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_shutdown_finishes_calls_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_simple_request_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_trailing_metadata_test $(BINDIR)/$(CONFIG)/h2_ssl_bad_hostname_test $(BINDIR)/$(CONFIG)/h2_ssl_binary_metadata_test $(BINDIR)/$(CONFIG)/h2_ssl_call_creds_test $(BINDIR)/$(CONFIG)/h2_ssl_cancel_after_accept_test $(BINDIR)/$(CONFIG)/h2_ssl_cancel_after_client_done_test $(BINDIR)/$(CONFIG)/h2_ssl_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/h2_ssl_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/h2_ssl_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/h2_ssl_census_simple_request_test $(BINDIR)/$(CONFIG)/h2_ssl_channel_connectivity_test $(BINDIR)/$(CONFIG)/h2_ssl_compressed_payload_test $(BINDIR)/$(CONFIG)/h2_ssl_default_host_test $(BINDIR)/$(CONFIG)/h2_ssl_disappearing_server_test $(BINDIR)/$(CONFIG)/h2_ssl_empty_batch_test $(BINDIR)/$(CONFIG)/h2_ssl_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/h2_ssl_high_initial_seqno_test $(BINDIR)/$(CONFIG)/h2_ssl_invoke_large_request_test $(BINDIR)/$(CONFIG)/h2_ssl_large_metadata_test $(BINDIR)/$(CONFIG)/h2_ssl_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/h2_ssl_max_message_length_test $(BINDIR)/$(CONFIG)/h2_ssl_metadata_test $(BINDIR)/$(CONFIG)/h2_ssl_no_op_test $(BINDIR)/$(CONFIG)/h2_ssl_payload_test $(BINDIR)/$(CONFIG)/h2_ssl_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/h2_ssl_registered_call_test $(BINDIR)/$(CONFIG)/h2_ssl_request_with_flags_test $(BINDIR)/$(CONFIG)/h2_ssl_request_with_payload_test $(BINDIR)/$(CONFIG)/h2_ssl_server_finishes_request_test $(BINDIR)/$(CONFIG)/h2_ssl_shutdown_finishes_calls_test $(BINDIR)/$(CONFIG)/h2_ssl_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/h2_ssl_simple_delayed_request_test $(BINDIR)/$(CONFIG)/h2_ssl_simple_request_test $(BINDIR)/$(CONFIG)/h2_ssl_trailing_metadata_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_bad_hostname_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_binary_metadata_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_call_creds_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_cancel_after_accept_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_cancel_after_client_done_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_census_simple_request_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_channel_connectivity_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_compressed_payload_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_default_host_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_disappearing_server_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_empty_batch_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_high_initial_seqno_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_invoke_large_request_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_large_metadata_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_max_message_length_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_metadata_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_no_op_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_payload_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_registered_call_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_request_with_flags_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_request_with_payload_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_server_finishes_request_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_shutdown_finishes_calls_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_simple_delayed_request_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_simple_request_test $(BINDIR)/$(CONFIG)/h2_ssl+poll_trailing_metadata_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_bad_hostname_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_binary_metadata_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_call_creds_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_cancel_after_accept_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_cancel_after_client_done_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_census_simple_request_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_default_host_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_disappearing_server_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_empty_batch_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_high_initial_seqno_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_invoke_large_request_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_large_metadata_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_max_message_length_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_metadata_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_no_op_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_payload_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_registered_call_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_request_with_payload_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_server_finishes_request_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_shutdown_finishes_calls_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_simple_delayed_request_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_simple_request_test $(BINDIR)/$(CONFIG)/h2_ssl_proxy_trailing_metadata_test $(BINDIR)/$(CONFIG)/h2_uds_bad_hostname_test $(BINDIR)/$(CONFIG)/h2_uds_binary_metadata_test $(BINDIR)/$(CONFIG)/h2_uds_call_creds_test $(BINDIR)/$(CONFIG)/h2_uds_cancel_after_accept_test $(BINDIR)/$(CONFIG)/h2_uds_cancel_after_client_done_test $(BINDIR)/$(CONFIG)/h2_uds_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/h2_uds_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/h2_uds_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/h2_uds_census_simple_request_test $(BINDIR)/$(CONFIG)/h2_uds_channel_connectivity_test $(BINDIR)/$(CONFIG)/h2_uds_compressed_payload_test $(BINDIR)/$(CONFIG)/h2_uds_disappearing_server_test $(BINDIR)/$(CONFIG)/h2_uds_empty_batch_test $(BINDIR)/$(CONFIG)/h2_uds_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/h2_uds_high_initial_seqno_test $(BINDIR)/$(CONFIG)/h2_uds_invoke_large_request_test $(BINDIR)/$(CONFIG)/h2_uds_large_metadata_test $(BINDIR)/$(CONFIG)/h2_uds_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/h2_uds_max_message_length_test $(BINDIR)/$(CONFIG)/h2_uds_metadata_test $(BINDIR)/$(CONFIG)/h2_uds_no_op_test $(BINDIR)/$(CONFIG)/h2_uds_payload_test $(BINDIR)/$(CONFIG)/h2_uds_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/h2_uds_registered_call_test $(BINDIR)/$(CONFIG)/h2_uds_request_with_flags_test $(BINDIR)/$(CONFIG)/h2_uds_request_with_payload_test $(BINDIR)/$(CONFIG)/h2_uds_server_finishes_request_test $(BINDIR)/$(CONFIG)/h2_uds_shutdown_finishes_calls_test $(BINDIR)/$(CONFIG)/h2_uds_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/h2_uds_simple_delayed_request_test $(BINDIR)/$(CONFIG)/h2_uds_simple_request_test $(BINDIR)/$(CONFIG)/h2_uds_trailing_metadata_test $(BINDIR)/$(CONFIG)/h2_uds+poll_bad_hostname_test $(BINDIR)/$(CONFIG)/h2_uds+poll_binary_metadata_test $(BINDIR)/$(CONFIG)/h2_uds+poll_call_creds_test $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_accept_test $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_client_done_test $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_invoke_test $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_before_invoke_test $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_in_a_vacuum_test $(BINDIR)/$(CONFIG)/h2_uds+poll_census_simple_request_test $(BINDIR)/$(CONFIG)/h2_uds+poll_channel_connectivity_test $(BINDIR)/$(CONFIG)/h2_uds+poll_compressed_payload_test $(BINDIR)/$(CONFIG)/h2_uds+poll_disappearing_server_test $(BINDIR)/$(CONFIG)/h2_uds+poll_empty_batch_test $(BINDIR)/$(CONFIG)/h2_uds+poll_graceful_server_shutdown_test $(BINDIR)/$(CONFIG)/h2_uds+poll_high_initial_seqno_test $(BINDIR)/$(CONFIG)/h2_uds+poll_invoke_large_request_test $(BINDIR)/$(CONFIG)/h2_uds+poll_large_metadata_test $(BINDIR)/$(CONFIG)/h2_uds+poll_max_concurrent_streams_test $(BINDIR)/$(CONFIG)/h2_uds+poll_max_message_length_test $(BINDIR)/$(CONFIG)/h2_uds+poll_metadata_test $(BINDIR)/$(CONFIG)/h2_uds+poll_no_op_test $(BINDIR)/$(CONFIG)/h2_uds+poll_payload_test $(BINDIR)/$(CONFIG)/h2_uds+poll_ping_pong_streaming_test $(BINDIR)/$(CONFIG)/h2_uds+poll_registered_call_test $(BINDIR)/$(CONFIG)/h2_uds+poll_request_with_flags_test $(BINDIR)/$(CONFIG)/h2_uds+poll_request_with_payload_test $(BINDIR)/$(CONFIG)/h2_uds+poll_server_finishes_request_test $(BINDIR)/$(CONFIG)/h2_uds+poll_shutdown_finishes_calls_test $(BINDIR)/$(CONFIG)/h2_uds+poll_shutdown_finishes_tags_test $(BINDIR)/$(CONFIG)/h2_uds+poll_simple_delayed_request_test $(BINDIR)/$(CONFIG)/h2_uds+poll_simple_request_test $(BINDIR)/$(CONFIG)/h2_uds+poll_trailing_metadata_test $(BINDIR)/$(CONFIG)/h2_compress_bad_hostname_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_binary_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_cancel_after_accept_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_cancel_after_client_done_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_cancel_after_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_cancel_before_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_cancel_in_a_vacuum_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_census_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_channel_connectivity_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_compressed_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_default_host_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_disappearing_server_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_empty_batch_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_graceful_server_shutdown_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_high_initial_seqno_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_invoke_large_request_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_large_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_max_concurrent_streams_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_max_message_length_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_no_op_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_ping_pong_streaming_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_registered_call_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_request_with_flags_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_request_with_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_server_finishes_request_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_shutdown_finishes_calls_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_shutdown_finishes_tags_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_simple_delayed_request_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_compress_trailing_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_full_bad_hostname_nosec_test $(BINDIR)/$(CONFIG)/h2_full_binary_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_full_cancel_after_accept_nosec_test $(BINDIR)/$(CONFIG)/h2_full_cancel_after_client_done_nosec_test $(BINDIR)/$(CONFIG)/h2_full_cancel_after_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_full_cancel_before_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_full_cancel_in_a_vacuum_nosec_test $(BINDIR)/$(CONFIG)/h2_full_census_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_full_channel_connectivity_nosec_test $(BINDIR)/$(CONFIG)/h2_full_compressed_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_full_default_host_nosec_test $(BINDIR)/$(CONFIG)/h2_full_disappearing_server_nosec_test $(BINDIR)/$(CONFIG)/h2_full_empty_batch_nosec_test $(BINDIR)/$(CONFIG)/h2_full_graceful_server_shutdown_nosec_test $(BINDIR)/$(CONFIG)/h2_full_high_initial_seqno_nosec_test $(BINDIR)/$(CONFIG)/h2_full_invoke_large_request_nosec_test $(BINDIR)/$(CONFIG)/h2_full_large_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_full_max_concurrent_streams_nosec_test $(BINDIR)/$(CONFIG)/h2_full_max_message_length_nosec_test $(BINDIR)/$(CONFIG)/h2_full_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_full_no_op_nosec_test $(BINDIR)/$(CONFIG)/h2_full_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_full_ping_pong_streaming_nosec_test $(BINDIR)/$(CONFIG)/h2_full_registered_call_nosec_test $(BINDIR)/$(CONFIG)/h2_full_request_with_flags_nosec_test $(BINDIR)/$(CONFIG)/h2_full_request_with_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_full_server_finishes_request_nosec_test $(BINDIR)/$(CONFIG)/h2_full_shutdown_finishes_calls_nosec_test $(BINDIR)/$(CONFIG)/h2_full_shutdown_finishes_tags_nosec_test $(BINDIR)/$(CONFIG)/h2_full_simple_delayed_request_nosec_test $(BINDIR)/$(CONFIG)/h2_full_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_full_trailing_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_bad_hostname_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_binary_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_accept_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_client_done_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_before_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_in_a_vacuum_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_census_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_channel_connectivity_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_compressed_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_default_host_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_disappearing_server_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_empty_batch_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_graceful_server_shutdown_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_high_initial_seqno_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_invoke_large_request_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_large_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_max_concurrent_streams_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_max_message_length_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_no_op_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_ping_pong_streaming_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_registered_call_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_request_with_flags_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_request_with_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_server_finishes_request_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_shutdown_finishes_calls_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_shutdown_finishes_tags_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_simple_delayed_request_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_full+poll_trailing_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_bad_hostname_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_binary_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_accept_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_client_done_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_cancel_before_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_cancel_in_a_vacuum_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_census_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_default_host_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_disappearing_server_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_empty_batch_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_graceful_server_shutdown_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_high_initial_seqno_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_invoke_large_request_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_large_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_max_message_length_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_no_op_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_ping_pong_streaming_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_registered_call_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_request_with_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_server_finishes_request_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_shutdown_finishes_calls_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_shutdown_finishes_tags_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_simple_delayed_request_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_proxy_trailing_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_bad_hostname_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_binary_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_accept_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_client_done_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_before_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_in_a_vacuum_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_census_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_compressed_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_empty_batch_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_graceful_server_shutdown_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_high_initial_seqno_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_invoke_large_request_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_large_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_max_concurrent_streams_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_max_message_length_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_no_op_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_ping_pong_streaming_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_registered_call_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_request_with_flags_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_request_with_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_server_finishes_request_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_shutdown_finishes_calls_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_shutdown_finishes_tags_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_trailing_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_bad_hostname_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_binary_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_accept_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_client_done_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_before_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_in_a_vacuum_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_census_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_compressed_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_empty_batch_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_graceful_server_shutdown_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_high_initial_seqno_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_invoke_large_request_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_large_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_max_concurrent_streams_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_max_message_length_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_no_op_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_ping_pong_streaming_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_registered_call_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_request_with_flags_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_request_with_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_server_finishes_request_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_shutdown_finishes_calls_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_shutdown_finishes_tags_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair+trace_trailing_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_bad_hostname_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_binary_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_accept_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_client_done_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_before_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_in_a_vacuum_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_census_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_compressed_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_empty_batch_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_graceful_server_shutdown_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_high_initial_seqno_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_invoke_large_request_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_large_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_max_concurrent_streams_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_max_message_length_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_no_op_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_ping_pong_streaming_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_registered_call_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_request_with_flags_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_request_with_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_server_finishes_request_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_shutdown_finishes_calls_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_shutdown_finishes_tags_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_trailing_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_bad_hostname_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_binary_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_cancel_after_accept_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_cancel_after_client_done_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_cancel_after_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_cancel_before_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_cancel_in_a_vacuum_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_census_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_channel_connectivity_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_compressed_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_disappearing_server_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_empty_batch_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_graceful_server_shutdown_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_high_initial_seqno_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_invoke_large_request_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_large_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_max_concurrent_streams_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_max_message_length_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_no_op_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_ping_pong_streaming_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_registered_call_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_request_with_flags_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_request_with_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_server_finishes_request_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_shutdown_finishes_calls_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_shutdown_finishes_tags_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_simple_delayed_request_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_uds_trailing_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_bad_hostname_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_binary_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_accept_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_client_done_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_before_invoke_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_in_a_vacuum_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_census_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_channel_connectivity_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_compressed_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_disappearing_server_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_empty_batch_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_graceful_server_shutdown_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_high_initial_seqno_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_invoke_large_request_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_large_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_max_concurrent_streams_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_max_message_length_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_metadata_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_no_op_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_ping_pong_streaming_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_registered_call_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_request_with_flags_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_request_with_payload_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_server_finishes_request_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_shutdown_finishes_calls_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_shutdown_finishes_tags_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_simple_delayed_request_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_simple_request_nosec_test $(BINDIR)/$(CONFIG)/h2_uds+poll_trailing_metadata_nosec_test $(BINDIR)/$(CONFIG)/connection_prefix_bad_client_test $(BINDIR)/$(CONFIG)/initial_settings_frame_bad_client_test + +buildtests_cxx: buildtests_zookeeper privatelibs_cxx $(BINDIR)/$(CONFIG)/async_end2end_test $(BINDIR)/$(CONFIG)/async_streaming_ping_pong_test $(BINDIR)/$(CONFIG)/async_unary_ping_pong_test $(BINDIR)/$(CONFIG)/auth_property_iterator_test $(BINDIR)/$(CONFIG)/channel_arguments_test $(BINDIR)/$(CONFIG)/cli_call_test $(BINDIR)/$(CONFIG)/client_crash_test $(BINDIR)/$(CONFIG)/client_crash_test_server $(BINDIR)/$(CONFIG)/credentials_test $(BINDIR)/$(CONFIG)/cxx_byte_buffer_test $(BINDIR)/$(CONFIG)/cxx_slice_test $(BINDIR)/$(CONFIG)/cxx_string_ref_test $(BINDIR)/$(CONFIG)/cxx_time_test $(BINDIR)/$(CONFIG)/end2end_test $(BINDIR)/$(CONFIG)/generic_end2end_test $(BINDIR)/$(CONFIG)/grpc_cli $(BINDIR)/$(CONFIG)/interop_client $(BINDIR)/$(CONFIG)/interop_server $(BINDIR)/$(CONFIG)/interop_test $(BINDIR)/$(CONFIG)/mock_test $(BINDIR)/$(CONFIG)/qps_interarrival_test $(BINDIR)/$(CONFIG)/qps_openloop_test $(BINDIR)/$(CONFIG)/qps_test $(BINDIR)/$(CONFIG)/reconnect_interop_client $(BINDIR)/$(CONFIG)/reconnect_interop_server $(BINDIR)/$(CONFIG)/secure_auth_context_test $(BINDIR)/$(CONFIG)/server_crash_test $(BINDIR)/$(CONFIG)/server_crash_test_client $(BINDIR)/$(CONFIG)/shutdown_test $(BINDIR)/$(CONFIG)/status_test $(BINDIR)/$(CONFIG)/streaming_throughput_test $(BINDIR)/$(CONFIG)/sync_streaming_ping_pong_test $(BINDIR)/$(CONFIG)/sync_unary_ping_pong_test $(BINDIR)/$(CONFIG)/thread_stress_test + +ifeq ($(HAS_ZOOKEEPER),true) +buildtests_zookeeper: privatelibs_zookeeper $(BINDIR)/$(CONFIG)/zookeeper_test +else +buildtests_zookeeper: +endif + + +test: test_c test_cxx test_zookeeper + +flaky_test: flaky_test_c flaky_test_cxx flaky_test_zookeeper + +test_c: buildtests_c + $(E) "[RUN] Testing alarm_heap_test" + $(Q) $(BINDIR)/$(CONFIG)/alarm_heap_test || ( echo test alarm_heap_test failed ; exit 1 ) + $(E) "[RUN] Testing alarm_list_test" + $(Q) $(BINDIR)/$(CONFIG)/alarm_list_test || ( echo test alarm_list_test failed ; exit 1 ) + $(E) "[RUN] Testing alarm_test" + $(Q) $(BINDIR)/$(CONFIG)/alarm_test || ( echo test alarm_test failed ; exit 1 ) + $(E) "[RUN] Testing alpn_test" + $(Q) $(BINDIR)/$(CONFIG)/alpn_test || ( echo test alpn_test failed ; exit 1 ) + $(E) "[RUN] Testing bin_encoder_test" + $(Q) $(BINDIR)/$(CONFIG)/bin_encoder_test || ( echo test bin_encoder_test failed ; exit 1 ) + $(E) "[RUN] Testing chttp2_status_conversion_test" + $(Q) $(BINDIR)/$(CONFIG)/chttp2_status_conversion_test || ( echo test chttp2_status_conversion_test failed ; exit 1 ) + $(E) "[RUN] Testing chttp2_stream_encoder_test" + $(Q) $(BINDIR)/$(CONFIG)/chttp2_stream_encoder_test || ( echo test chttp2_stream_encoder_test failed ; exit 1 ) + $(E) "[RUN] Testing chttp2_stream_map_test" + $(Q) $(BINDIR)/$(CONFIG)/chttp2_stream_map_test || ( echo test chttp2_stream_map_test failed ; exit 1 ) + $(E) "[RUN] Testing compression_test" + $(Q) $(BINDIR)/$(CONFIG)/compression_test || ( echo test compression_test failed ; exit 1 ) + $(E) "[RUN] Testing dualstack_socket_test" + $(Q) $(BINDIR)/$(CONFIG)/dualstack_socket_test || ( echo test dualstack_socket_test failed ; exit 1 ) + $(E) "[RUN] Testing endpoint_pair_test" + $(Q) $(BINDIR)/$(CONFIG)/endpoint_pair_test || ( echo test endpoint_pair_test failed ; exit 1 ) + $(E) "[RUN] Testing fd_conservation_posix_test" + $(Q) $(BINDIR)/$(CONFIG)/fd_conservation_posix_test || ( echo test fd_conservation_posix_test failed ; exit 1 ) + $(E) "[RUN] Testing fd_posix_test" + $(Q) $(BINDIR)/$(CONFIG)/fd_posix_test || ( echo test fd_posix_test failed ; exit 1 ) + $(E) "[RUN] Testing fling_stream_test" + $(Q) $(BINDIR)/$(CONFIG)/fling_stream_test || ( echo test fling_stream_test failed ; exit 1 ) + $(E) "[RUN] Testing fling_test" + $(Q) $(BINDIR)/$(CONFIG)/fling_test || ( echo test fling_test failed ; exit 1 ) + $(E) "[RUN] Testing gpr_cmdline_test" + $(Q) $(BINDIR)/$(CONFIG)/gpr_cmdline_test || ( echo test gpr_cmdline_test failed ; exit 1 ) + $(E) "[RUN] Testing gpr_env_test" + $(Q) $(BINDIR)/$(CONFIG)/gpr_env_test || ( echo test gpr_env_test failed ; exit 1 ) + $(E) "[RUN] Testing gpr_file_test" + $(Q) $(BINDIR)/$(CONFIG)/gpr_file_test || ( echo test gpr_file_test failed ; exit 1 ) + $(E) "[RUN] Testing gpr_histogram_test" + $(Q) $(BINDIR)/$(CONFIG)/gpr_histogram_test || ( echo test gpr_histogram_test failed ; exit 1 ) + $(E) "[RUN] Testing gpr_host_port_test" + $(Q) $(BINDIR)/$(CONFIG)/gpr_host_port_test || ( echo test gpr_host_port_test failed ; exit 1 ) + $(E) "[RUN] Testing gpr_log_test" + $(Q) $(BINDIR)/$(CONFIG)/gpr_log_test || ( echo test gpr_log_test failed ; exit 1 ) + $(E) "[RUN] Testing gpr_slice_buffer_test" + $(Q) $(BINDIR)/$(CONFIG)/gpr_slice_buffer_test || ( echo test gpr_slice_buffer_test failed ; exit 1 ) + $(E) "[RUN] Testing gpr_slice_test" + $(Q) $(BINDIR)/$(CONFIG)/gpr_slice_test || ( echo test gpr_slice_test failed ; exit 1 ) + $(E) "[RUN] Testing gpr_stack_lockfree_test" + $(Q) $(BINDIR)/$(CONFIG)/gpr_stack_lockfree_test || ( echo test gpr_stack_lockfree_test failed ; exit 1 ) + $(E) "[RUN] Testing gpr_string_test" + $(Q) $(BINDIR)/$(CONFIG)/gpr_string_test || ( echo test gpr_string_test failed ; exit 1 ) + $(E) "[RUN] Testing gpr_sync_test" + $(Q) $(BINDIR)/$(CONFIG)/gpr_sync_test || ( echo test gpr_sync_test failed ; exit 1 ) + $(E) "[RUN] Testing gpr_thd_test" + $(Q) $(BINDIR)/$(CONFIG)/gpr_thd_test || ( echo test gpr_thd_test failed ; exit 1 ) + $(E) "[RUN] Testing gpr_time_test" + $(Q) $(BINDIR)/$(CONFIG)/gpr_time_test || ( echo test gpr_time_test failed ; exit 1 ) + $(E) "[RUN] Testing gpr_tls_test" + $(Q) $(BINDIR)/$(CONFIG)/gpr_tls_test || ( echo test gpr_tls_test failed ; exit 1 ) + $(E) "[RUN] Testing gpr_useful_test" + $(Q) $(BINDIR)/$(CONFIG)/gpr_useful_test || ( echo test gpr_useful_test failed ; exit 1 ) + $(E) "[RUN] Testing grpc_auth_context_test" + $(Q) $(BINDIR)/$(CONFIG)/grpc_auth_context_test || ( echo test grpc_auth_context_test failed ; exit 1 ) + $(E) "[RUN] Testing grpc_base64_test" + $(Q) $(BINDIR)/$(CONFIG)/grpc_base64_test || ( echo test grpc_base64_test failed ; exit 1 ) + $(E) "[RUN] Testing grpc_byte_buffer_reader_test" + $(Q) $(BINDIR)/$(CONFIG)/grpc_byte_buffer_reader_test || ( echo test grpc_byte_buffer_reader_test failed ; exit 1 ) + $(E) "[RUN] Testing grpc_channel_args_test" + $(Q) $(BINDIR)/$(CONFIG)/grpc_channel_args_test || ( echo test grpc_channel_args_test failed ; exit 1 ) + $(E) "[RUN] Testing grpc_channel_stack_test" + $(Q) $(BINDIR)/$(CONFIG)/grpc_channel_stack_test || ( echo test grpc_channel_stack_test failed ; exit 1 ) + $(E) "[RUN] Testing grpc_completion_queue_test" + $(Q) $(BINDIR)/$(CONFIG)/grpc_completion_queue_test || ( echo test grpc_completion_queue_test failed ; exit 1 ) + $(E) "[RUN] Testing grpc_credentials_test" + $(Q) $(BINDIR)/$(CONFIG)/grpc_credentials_test || ( echo test grpc_credentials_test failed ; exit 1 ) + $(E) "[RUN] Testing grpc_json_token_test" + $(Q) $(BINDIR)/$(CONFIG)/grpc_json_token_test || ( echo test grpc_json_token_test failed ; exit 1 ) + $(E) "[RUN] Testing grpc_jwt_verifier_test" + $(Q) $(BINDIR)/$(CONFIG)/grpc_jwt_verifier_test || ( echo test grpc_jwt_verifier_test failed ; exit 1 ) + $(E) "[RUN] Testing grpc_security_connector_test" + $(Q) $(BINDIR)/$(CONFIG)/grpc_security_connector_test || ( echo test grpc_security_connector_test failed ; exit 1 ) + $(E) "[RUN] Testing grpc_stream_op_test" + $(Q) $(BINDIR)/$(CONFIG)/grpc_stream_op_test || ( echo test grpc_stream_op_test failed ; exit 1 ) + $(E) "[RUN] Testing hpack_parser_test" + $(Q) $(BINDIR)/$(CONFIG)/hpack_parser_test || ( echo test hpack_parser_test failed ; exit 1 ) + $(E) "[RUN] Testing hpack_table_test" + $(Q) $(BINDIR)/$(CONFIG)/hpack_table_test || ( echo test hpack_table_test failed ; exit 1 ) + $(E) "[RUN] Testing httpcli_format_request_test" + $(Q) $(BINDIR)/$(CONFIG)/httpcli_format_request_test || ( echo test httpcli_format_request_test failed ; exit 1 ) + $(E) "[RUN] Testing httpcli_parser_test" + $(Q) $(BINDIR)/$(CONFIG)/httpcli_parser_test || ( echo test httpcli_parser_test failed ; exit 1 ) + $(E) "[RUN] Testing httpcli_test" + $(Q) $(BINDIR)/$(CONFIG)/httpcli_test || ( echo test httpcli_test failed ; exit 1 ) + $(E) "[RUN] Testing json_rewrite_test" + $(Q) $(BINDIR)/$(CONFIG)/json_rewrite_test || ( echo test json_rewrite_test failed ; exit 1 ) + $(E) "[RUN] Testing json_test" + $(Q) $(BINDIR)/$(CONFIG)/json_test || ( echo test json_test failed ; exit 1 ) + $(E) "[RUN] Testing lame_client_test" + $(Q) $(BINDIR)/$(CONFIG)/lame_client_test || ( echo test lame_client_test failed ; exit 1 ) + $(E) "[RUN] Testing message_compress_test" + $(Q) $(BINDIR)/$(CONFIG)/message_compress_test || ( echo test message_compress_test failed ; exit 1 ) + $(E) "[RUN] Testing multi_init_test" + $(Q) $(BINDIR)/$(CONFIG)/multi_init_test || ( echo test multi_init_test failed ; exit 1 ) + $(E) "[RUN] Testing multiple_server_queues_test" + $(Q) $(BINDIR)/$(CONFIG)/multiple_server_queues_test || ( echo test multiple_server_queues_test failed ; exit 1 ) + $(E) "[RUN] Testing murmur_hash_test" + $(Q) $(BINDIR)/$(CONFIG)/murmur_hash_test || ( echo test murmur_hash_test failed ; exit 1 ) + $(E) "[RUN] Testing no_server_test" + $(Q) $(BINDIR)/$(CONFIG)/no_server_test || ( echo test no_server_test failed ; exit 1 ) + $(E) "[RUN] Testing resolve_address_test" + $(Q) $(BINDIR)/$(CONFIG)/resolve_address_test || ( echo test resolve_address_test failed ; exit 1 ) + $(E) "[RUN] Testing secure_endpoint_test" + $(Q) $(BINDIR)/$(CONFIG)/secure_endpoint_test || ( echo test secure_endpoint_test failed ; exit 1 ) + $(E) "[RUN] Testing sockaddr_utils_test" + $(Q) $(BINDIR)/$(CONFIG)/sockaddr_utils_test || ( echo test sockaddr_utils_test failed ; exit 1 ) + $(E) "[RUN] Testing tcp_client_posix_test" + $(Q) $(BINDIR)/$(CONFIG)/tcp_client_posix_test || ( echo test tcp_client_posix_test failed ; exit 1 ) + $(E) "[RUN] Testing tcp_posix_test" + $(Q) $(BINDIR)/$(CONFIG)/tcp_posix_test || ( echo test tcp_posix_test failed ; exit 1 ) + $(E) "[RUN] Testing tcp_server_posix_test" + $(Q) $(BINDIR)/$(CONFIG)/tcp_server_posix_test || ( echo test tcp_server_posix_test failed ; exit 1 ) + $(E) "[RUN] Testing time_averaged_stats_test" + $(Q) $(BINDIR)/$(CONFIG)/time_averaged_stats_test || ( echo test time_averaged_stats_test failed ; exit 1 ) + $(E) "[RUN] Testing timeout_encoding_test" + $(Q) $(BINDIR)/$(CONFIG)/timeout_encoding_test || ( echo test timeout_encoding_test failed ; exit 1 ) + $(E) "[RUN] Testing timers_test" + $(Q) $(BINDIR)/$(CONFIG)/timers_test || ( echo test timers_test failed ; exit 1 ) + $(E) "[RUN] Testing transport_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/transport_metadata_test || ( echo test transport_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing transport_security_test" + $(Q) $(BINDIR)/$(CONFIG)/transport_security_test || ( echo test transport_security_test failed ; exit 1 ) + $(E) "[RUN] Testing udp_server_test" + $(Q) $(BINDIR)/$(CONFIG)/udp_server_test || ( echo test udp_server_test failed ; exit 1 ) + $(E) "[RUN] Testing uri_parser_test" + $(Q) $(BINDIR)/$(CONFIG)/uri_parser_test || ( echo test uri_parser_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_bad_hostname_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_bad_hostname_test || ( echo test h2_compress_bad_hostname_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_binary_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_binary_metadata_test || ( echo test h2_compress_binary_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_call_creds_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_call_creds_test || ( echo test h2_compress_call_creds_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_cancel_after_accept_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_cancel_after_accept_test || ( echo test h2_compress_cancel_after_accept_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_cancel_after_client_done_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_cancel_after_client_done_test || ( echo test h2_compress_cancel_after_client_done_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_cancel_after_invoke_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_cancel_after_invoke_test || ( echo test h2_compress_cancel_after_invoke_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_cancel_before_invoke_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_cancel_before_invoke_test || ( echo test h2_compress_cancel_before_invoke_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_cancel_in_a_vacuum_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_cancel_in_a_vacuum_test || ( echo test h2_compress_cancel_in_a_vacuum_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_census_simple_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_census_simple_request_test || ( echo test h2_compress_census_simple_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_channel_connectivity_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_channel_connectivity_test || ( echo test h2_compress_channel_connectivity_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_compressed_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_compressed_payload_test || ( echo test h2_compress_compressed_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_default_host_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_default_host_test || ( echo test h2_compress_default_host_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_disappearing_server_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_disappearing_server_test || ( echo test h2_compress_disappearing_server_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_empty_batch_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_empty_batch_test || ( echo test h2_compress_empty_batch_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_graceful_server_shutdown_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_graceful_server_shutdown_test || ( echo test h2_compress_graceful_server_shutdown_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_high_initial_seqno_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_high_initial_seqno_test || ( echo test h2_compress_high_initial_seqno_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_invoke_large_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_invoke_large_request_test || ( echo test h2_compress_invoke_large_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_large_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_large_metadata_test || ( echo test h2_compress_large_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_max_concurrent_streams_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_max_concurrent_streams_test || ( echo test h2_compress_max_concurrent_streams_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_max_message_length_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_max_message_length_test || ( echo test h2_compress_max_message_length_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_metadata_test || ( echo test h2_compress_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_no_op_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_no_op_test || ( echo test h2_compress_no_op_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_payload_test || ( echo test h2_compress_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_ping_pong_streaming_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_ping_pong_streaming_test || ( echo test h2_compress_ping_pong_streaming_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_registered_call_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_registered_call_test || ( echo test h2_compress_registered_call_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_request_with_flags_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_request_with_flags_test || ( echo test h2_compress_request_with_flags_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_request_with_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_request_with_payload_test || ( echo test h2_compress_request_with_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_server_finishes_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_server_finishes_request_test || ( echo test h2_compress_server_finishes_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_shutdown_finishes_calls_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_shutdown_finishes_calls_test || ( echo test h2_compress_shutdown_finishes_calls_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_shutdown_finishes_tags_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_shutdown_finishes_tags_test || ( echo test h2_compress_shutdown_finishes_tags_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_simple_delayed_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_simple_delayed_request_test || ( echo test h2_compress_simple_delayed_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_simple_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_simple_request_test || ( echo test h2_compress_simple_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_trailing_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_trailing_metadata_test || ( echo test h2_compress_trailing_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_fakesec_bad_hostname_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_fakesec_bad_hostname_test || ( echo test h2_fakesec_bad_hostname_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_fakesec_binary_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_fakesec_binary_metadata_test || ( echo test h2_fakesec_binary_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_fakesec_call_creds_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_fakesec_call_creds_test || ( echo test h2_fakesec_call_creds_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_fakesec_cancel_after_accept_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_fakesec_cancel_after_accept_test || ( echo test h2_fakesec_cancel_after_accept_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_fakesec_cancel_after_client_done_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_fakesec_cancel_after_client_done_test || ( echo test h2_fakesec_cancel_after_client_done_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_fakesec_cancel_after_invoke_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_fakesec_cancel_after_invoke_test || ( echo test h2_fakesec_cancel_after_invoke_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_fakesec_cancel_before_invoke_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_fakesec_cancel_before_invoke_test || ( echo test h2_fakesec_cancel_before_invoke_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_fakesec_cancel_in_a_vacuum_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_fakesec_cancel_in_a_vacuum_test || ( echo test h2_fakesec_cancel_in_a_vacuum_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_fakesec_census_simple_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_fakesec_census_simple_request_test || ( echo test h2_fakesec_census_simple_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_fakesec_channel_connectivity_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_fakesec_channel_connectivity_test || ( echo test h2_fakesec_channel_connectivity_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_fakesec_compressed_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_fakesec_compressed_payload_test || ( echo test h2_fakesec_compressed_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_fakesec_default_host_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_fakesec_default_host_test || ( echo test h2_fakesec_default_host_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_fakesec_disappearing_server_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_fakesec_disappearing_server_test || ( echo test h2_fakesec_disappearing_server_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_fakesec_empty_batch_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_fakesec_empty_batch_test || ( echo test h2_fakesec_empty_batch_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_fakesec_graceful_server_shutdown_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_fakesec_graceful_server_shutdown_test || ( echo test h2_fakesec_graceful_server_shutdown_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_fakesec_high_initial_seqno_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_fakesec_high_initial_seqno_test || ( echo test h2_fakesec_high_initial_seqno_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_fakesec_invoke_large_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_fakesec_invoke_large_request_test || ( echo test h2_fakesec_invoke_large_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_fakesec_large_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_fakesec_large_metadata_test || ( echo test h2_fakesec_large_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_fakesec_max_concurrent_streams_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_fakesec_max_concurrent_streams_test || ( echo test h2_fakesec_max_concurrent_streams_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_fakesec_max_message_length_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_fakesec_max_message_length_test || ( echo test h2_fakesec_max_message_length_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_fakesec_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_fakesec_metadata_test || ( echo test h2_fakesec_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_fakesec_no_op_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_fakesec_no_op_test || ( echo test h2_fakesec_no_op_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_fakesec_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_fakesec_payload_test || ( echo test h2_fakesec_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_fakesec_ping_pong_streaming_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_fakesec_ping_pong_streaming_test || ( echo test h2_fakesec_ping_pong_streaming_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_fakesec_registered_call_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_fakesec_registered_call_test || ( echo test h2_fakesec_registered_call_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_fakesec_request_with_flags_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_fakesec_request_with_flags_test || ( echo test h2_fakesec_request_with_flags_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_fakesec_request_with_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_fakesec_request_with_payload_test || ( echo test h2_fakesec_request_with_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_fakesec_server_finishes_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_fakesec_server_finishes_request_test || ( echo test h2_fakesec_server_finishes_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_fakesec_shutdown_finishes_calls_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_fakesec_shutdown_finishes_calls_test || ( echo test h2_fakesec_shutdown_finishes_calls_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_fakesec_shutdown_finishes_tags_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_fakesec_shutdown_finishes_tags_test || ( echo test h2_fakesec_shutdown_finishes_tags_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_fakesec_simple_delayed_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_fakesec_simple_delayed_request_test || ( echo test h2_fakesec_simple_delayed_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_fakesec_simple_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_fakesec_simple_request_test || ( echo test h2_fakesec_simple_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_fakesec_trailing_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_fakesec_trailing_metadata_test || ( echo test h2_fakesec_trailing_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_bad_hostname_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_bad_hostname_test || ( echo test h2_full_bad_hostname_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_binary_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_binary_metadata_test || ( echo test h2_full_binary_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_call_creds_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_call_creds_test || ( echo test h2_full_call_creds_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_cancel_after_accept_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_cancel_after_accept_test || ( echo test h2_full_cancel_after_accept_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_cancel_after_client_done_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_cancel_after_client_done_test || ( echo test h2_full_cancel_after_client_done_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_cancel_after_invoke_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_cancel_after_invoke_test || ( echo test h2_full_cancel_after_invoke_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_cancel_before_invoke_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_cancel_before_invoke_test || ( echo test h2_full_cancel_before_invoke_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_cancel_in_a_vacuum_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_cancel_in_a_vacuum_test || ( echo test h2_full_cancel_in_a_vacuum_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_census_simple_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_census_simple_request_test || ( echo test h2_full_census_simple_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_channel_connectivity_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_channel_connectivity_test || ( echo test h2_full_channel_connectivity_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_compressed_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_compressed_payload_test || ( echo test h2_full_compressed_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_default_host_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_default_host_test || ( echo test h2_full_default_host_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_disappearing_server_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_disappearing_server_test || ( echo test h2_full_disappearing_server_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_empty_batch_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_empty_batch_test || ( echo test h2_full_empty_batch_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_graceful_server_shutdown_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_graceful_server_shutdown_test || ( echo test h2_full_graceful_server_shutdown_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_high_initial_seqno_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_high_initial_seqno_test || ( echo test h2_full_high_initial_seqno_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_invoke_large_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_invoke_large_request_test || ( echo test h2_full_invoke_large_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_large_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_large_metadata_test || ( echo test h2_full_large_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_max_concurrent_streams_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_max_concurrent_streams_test || ( echo test h2_full_max_concurrent_streams_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_max_message_length_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_max_message_length_test || ( echo test h2_full_max_message_length_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_metadata_test || ( echo test h2_full_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_no_op_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_no_op_test || ( echo test h2_full_no_op_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_payload_test || ( echo test h2_full_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_ping_pong_streaming_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_ping_pong_streaming_test || ( echo test h2_full_ping_pong_streaming_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_registered_call_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_registered_call_test || ( echo test h2_full_registered_call_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_request_with_flags_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_request_with_flags_test || ( echo test h2_full_request_with_flags_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_request_with_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_request_with_payload_test || ( echo test h2_full_request_with_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_server_finishes_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_server_finishes_request_test || ( echo test h2_full_server_finishes_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_shutdown_finishes_calls_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_shutdown_finishes_calls_test || ( echo test h2_full_shutdown_finishes_calls_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_shutdown_finishes_tags_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_shutdown_finishes_tags_test || ( echo test h2_full_shutdown_finishes_tags_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_simple_delayed_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_simple_delayed_request_test || ( echo test h2_full_simple_delayed_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_simple_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_simple_request_test || ( echo test h2_full_simple_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_trailing_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_trailing_metadata_test || ( echo test h2_full_trailing_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_bad_hostname_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_bad_hostname_test || ( echo test h2_full+poll_bad_hostname_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_binary_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_binary_metadata_test || ( echo test h2_full+poll_binary_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_call_creds_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_call_creds_test || ( echo test h2_full+poll_call_creds_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_cancel_after_accept_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_accept_test || ( echo test h2_full+poll_cancel_after_accept_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_cancel_after_client_done_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_client_done_test || ( echo test h2_full+poll_cancel_after_client_done_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_cancel_after_invoke_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_invoke_test || ( echo test h2_full+poll_cancel_after_invoke_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_cancel_before_invoke_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_before_invoke_test || ( echo test h2_full+poll_cancel_before_invoke_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_cancel_in_a_vacuum_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_in_a_vacuum_test || ( echo test h2_full+poll_cancel_in_a_vacuum_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_census_simple_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_census_simple_request_test || ( echo test h2_full+poll_census_simple_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_channel_connectivity_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_channel_connectivity_test || ( echo test h2_full+poll_channel_connectivity_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_compressed_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_compressed_payload_test || ( echo test h2_full+poll_compressed_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_default_host_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_default_host_test || ( echo test h2_full+poll_default_host_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_disappearing_server_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_disappearing_server_test || ( echo test h2_full+poll_disappearing_server_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_empty_batch_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_empty_batch_test || ( echo test h2_full+poll_empty_batch_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_graceful_server_shutdown_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_graceful_server_shutdown_test || ( echo test h2_full+poll_graceful_server_shutdown_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_high_initial_seqno_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_high_initial_seqno_test || ( echo test h2_full+poll_high_initial_seqno_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_invoke_large_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_invoke_large_request_test || ( echo test h2_full+poll_invoke_large_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_large_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_large_metadata_test || ( echo test h2_full+poll_large_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_max_concurrent_streams_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_max_concurrent_streams_test || ( echo test h2_full+poll_max_concurrent_streams_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_max_message_length_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_max_message_length_test || ( echo test h2_full+poll_max_message_length_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_metadata_test || ( echo test h2_full+poll_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_no_op_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_no_op_test || ( echo test h2_full+poll_no_op_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_payload_test || ( echo test h2_full+poll_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_ping_pong_streaming_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_ping_pong_streaming_test || ( echo test h2_full+poll_ping_pong_streaming_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_registered_call_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_registered_call_test || ( echo test h2_full+poll_registered_call_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_request_with_flags_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_request_with_flags_test || ( echo test h2_full+poll_request_with_flags_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_request_with_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_request_with_payload_test || ( echo test h2_full+poll_request_with_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_server_finishes_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_server_finishes_request_test || ( echo test h2_full+poll_server_finishes_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_shutdown_finishes_calls_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_shutdown_finishes_calls_test || ( echo test h2_full+poll_shutdown_finishes_calls_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_shutdown_finishes_tags_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_shutdown_finishes_tags_test || ( echo test h2_full+poll_shutdown_finishes_tags_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_simple_delayed_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_simple_delayed_request_test || ( echo test h2_full+poll_simple_delayed_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_simple_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_simple_request_test || ( echo test h2_full+poll_simple_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_trailing_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_trailing_metadata_test || ( echo test h2_full+poll_trailing_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_oauth2_bad_hostname_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_oauth2_bad_hostname_test || ( echo test h2_oauth2_bad_hostname_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_oauth2_binary_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_oauth2_binary_metadata_test || ( echo test h2_oauth2_binary_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_oauth2_call_creds_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_oauth2_call_creds_test || ( echo test h2_oauth2_call_creds_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_oauth2_cancel_after_accept_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_oauth2_cancel_after_accept_test || ( echo test h2_oauth2_cancel_after_accept_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_oauth2_cancel_after_client_done_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_oauth2_cancel_after_client_done_test || ( echo test h2_oauth2_cancel_after_client_done_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_oauth2_cancel_after_invoke_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_oauth2_cancel_after_invoke_test || ( echo test h2_oauth2_cancel_after_invoke_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_oauth2_cancel_before_invoke_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_oauth2_cancel_before_invoke_test || ( echo test h2_oauth2_cancel_before_invoke_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_oauth2_cancel_in_a_vacuum_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_oauth2_cancel_in_a_vacuum_test || ( echo test h2_oauth2_cancel_in_a_vacuum_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_oauth2_census_simple_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_oauth2_census_simple_request_test || ( echo test h2_oauth2_census_simple_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_oauth2_channel_connectivity_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_oauth2_channel_connectivity_test || ( echo test h2_oauth2_channel_connectivity_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_oauth2_compressed_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_oauth2_compressed_payload_test || ( echo test h2_oauth2_compressed_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_oauth2_default_host_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_oauth2_default_host_test || ( echo test h2_oauth2_default_host_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_oauth2_disappearing_server_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_oauth2_disappearing_server_test || ( echo test h2_oauth2_disappearing_server_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_oauth2_empty_batch_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_oauth2_empty_batch_test || ( echo test h2_oauth2_empty_batch_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_oauth2_graceful_server_shutdown_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_oauth2_graceful_server_shutdown_test || ( echo test h2_oauth2_graceful_server_shutdown_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_oauth2_high_initial_seqno_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_oauth2_high_initial_seqno_test || ( echo test h2_oauth2_high_initial_seqno_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_oauth2_invoke_large_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_oauth2_invoke_large_request_test || ( echo test h2_oauth2_invoke_large_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_oauth2_large_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_oauth2_large_metadata_test || ( echo test h2_oauth2_large_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_oauth2_max_concurrent_streams_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_oauth2_max_concurrent_streams_test || ( echo test h2_oauth2_max_concurrent_streams_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_oauth2_max_message_length_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_oauth2_max_message_length_test || ( echo test h2_oauth2_max_message_length_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_oauth2_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_oauth2_metadata_test || ( echo test h2_oauth2_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_oauth2_no_op_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_oauth2_no_op_test || ( echo test h2_oauth2_no_op_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_oauth2_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_oauth2_payload_test || ( echo test h2_oauth2_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_oauth2_ping_pong_streaming_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_oauth2_ping_pong_streaming_test || ( echo test h2_oauth2_ping_pong_streaming_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_oauth2_registered_call_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_oauth2_registered_call_test || ( echo test h2_oauth2_registered_call_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_oauth2_request_with_flags_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_oauth2_request_with_flags_test || ( echo test h2_oauth2_request_with_flags_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_oauth2_request_with_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_oauth2_request_with_payload_test || ( echo test h2_oauth2_request_with_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_oauth2_server_finishes_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_oauth2_server_finishes_request_test || ( echo test h2_oauth2_server_finishes_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_oauth2_shutdown_finishes_calls_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_oauth2_shutdown_finishes_calls_test || ( echo test h2_oauth2_shutdown_finishes_calls_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_oauth2_shutdown_finishes_tags_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_oauth2_shutdown_finishes_tags_test || ( echo test h2_oauth2_shutdown_finishes_tags_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_oauth2_simple_delayed_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_oauth2_simple_delayed_request_test || ( echo test h2_oauth2_simple_delayed_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_oauth2_simple_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_oauth2_simple_request_test || ( echo test h2_oauth2_simple_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_oauth2_trailing_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_oauth2_trailing_metadata_test || ( echo test h2_oauth2_trailing_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_bad_hostname_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_bad_hostname_test || ( echo test h2_proxy_bad_hostname_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_binary_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_binary_metadata_test || ( echo test h2_proxy_binary_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_call_creds_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_call_creds_test || ( echo test h2_proxy_call_creds_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_cancel_after_accept_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_accept_test || ( echo test h2_proxy_cancel_after_accept_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_cancel_after_client_done_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_client_done_test || ( echo test h2_proxy_cancel_after_client_done_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_cancel_after_invoke_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_invoke_test || ( echo test h2_proxy_cancel_after_invoke_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_cancel_before_invoke_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_cancel_before_invoke_test || ( echo test h2_proxy_cancel_before_invoke_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_cancel_in_a_vacuum_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_cancel_in_a_vacuum_test || ( echo test h2_proxy_cancel_in_a_vacuum_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_census_simple_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_census_simple_request_test || ( echo test h2_proxy_census_simple_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_default_host_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_default_host_test || ( echo test h2_proxy_default_host_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_disappearing_server_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_disappearing_server_test || ( echo test h2_proxy_disappearing_server_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_empty_batch_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_empty_batch_test || ( echo test h2_proxy_empty_batch_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_graceful_server_shutdown_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_graceful_server_shutdown_test || ( echo test h2_proxy_graceful_server_shutdown_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_high_initial_seqno_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_high_initial_seqno_test || ( echo test h2_proxy_high_initial_seqno_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_invoke_large_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_invoke_large_request_test || ( echo test h2_proxy_invoke_large_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_large_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_large_metadata_test || ( echo test h2_proxy_large_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_max_message_length_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_max_message_length_test || ( echo test h2_proxy_max_message_length_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_metadata_test || ( echo test h2_proxy_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_no_op_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_no_op_test || ( echo test h2_proxy_no_op_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_payload_test || ( echo test h2_proxy_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_ping_pong_streaming_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_ping_pong_streaming_test || ( echo test h2_proxy_ping_pong_streaming_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_registered_call_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_registered_call_test || ( echo test h2_proxy_registered_call_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_request_with_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_request_with_payload_test || ( echo test h2_proxy_request_with_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_server_finishes_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_server_finishes_request_test || ( echo test h2_proxy_server_finishes_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_shutdown_finishes_calls_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_shutdown_finishes_calls_test || ( echo test h2_proxy_shutdown_finishes_calls_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_shutdown_finishes_tags_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_shutdown_finishes_tags_test || ( echo test h2_proxy_shutdown_finishes_tags_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_simple_delayed_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_simple_delayed_request_test || ( echo test h2_proxy_simple_delayed_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_simple_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_simple_request_test || ( echo test h2_proxy_simple_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_trailing_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_trailing_metadata_test || ( echo test h2_proxy_trailing_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_bad_hostname_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_bad_hostname_test || ( echo test h2_sockpair_bad_hostname_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_binary_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_binary_metadata_test || ( echo test h2_sockpair_binary_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_call_creds_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_call_creds_test || ( echo test h2_sockpair_call_creds_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_cancel_after_accept_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_accept_test || ( echo test h2_sockpair_cancel_after_accept_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_cancel_after_client_done_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_client_done_test || ( echo test h2_sockpair_cancel_after_client_done_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_cancel_after_invoke_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_invoke_test || ( echo test h2_sockpair_cancel_after_invoke_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_cancel_before_invoke_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_before_invoke_test || ( echo test h2_sockpair_cancel_before_invoke_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_cancel_in_a_vacuum_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_in_a_vacuum_test || ( echo test h2_sockpair_cancel_in_a_vacuum_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_census_simple_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_census_simple_request_test || ( echo test h2_sockpair_census_simple_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_compressed_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_compressed_payload_test || ( echo test h2_sockpair_compressed_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_empty_batch_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_empty_batch_test || ( echo test h2_sockpair_empty_batch_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_graceful_server_shutdown_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_graceful_server_shutdown_test || ( echo test h2_sockpair_graceful_server_shutdown_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_high_initial_seqno_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_high_initial_seqno_test || ( echo test h2_sockpair_high_initial_seqno_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_invoke_large_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_invoke_large_request_test || ( echo test h2_sockpair_invoke_large_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_large_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_large_metadata_test || ( echo test h2_sockpair_large_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_max_concurrent_streams_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_max_concurrent_streams_test || ( echo test h2_sockpair_max_concurrent_streams_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_max_message_length_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_max_message_length_test || ( echo test h2_sockpair_max_message_length_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_metadata_test || ( echo test h2_sockpair_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_no_op_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_no_op_test || ( echo test h2_sockpair_no_op_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_payload_test || ( echo test h2_sockpair_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_ping_pong_streaming_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_ping_pong_streaming_test || ( echo test h2_sockpair_ping_pong_streaming_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_registered_call_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_registered_call_test || ( echo test h2_sockpair_registered_call_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_request_with_flags_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_request_with_flags_test || ( echo test h2_sockpair_request_with_flags_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_request_with_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_request_with_payload_test || ( echo test h2_sockpair_request_with_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_server_finishes_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_server_finishes_request_test || ( echo test h2_sockpair_server_finishes_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_shutdown_finishes_calls_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_shutdown_finishes_calls_test || ( echo test h2_sockpair_shutdown_finishes_calls_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_shutdown_finishes_tags_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_shutdown_finishes_tags_test || ( echo test h2_sockpair_shutdown_finishes_tags_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_simple_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_simple_request_test || ( echo test h2_sockpair_simple_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_trailing_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_trailing_metadata_test || ( echo test h2_sockpair_trailing_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_bad_hostname_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_bad_hostname_test || ( echo test h2_sockpair+trace_bad_hostname_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_binary_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_binary_metadata_test || ( echo test h2_sockpair+trace_binary_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_call_creds_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_call_creds_test || ( echo test h2_sockpair+trace_call_creds_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_cancel_after_accept_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_accept_test || ( echo test h2_sockpair+trace_cancel_after_accept_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_cancel_after_client_done_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_client_done_test || ( echo test h2_sockpair+trace_cancel_after_client_done_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_cancel_after_invoke_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_invoke_test || ( echo test h2_sockpair+trace_cancel_after_invoke_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_cancel_before_invoke_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_before_invoke_test || ( echo test h2_sockpair+trace_cancel_before_invoke_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_cancel_in_a_vacuum_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_in_a_vacuum_test || ( echo test h2_sockpair+trace_cancel_in_a_vacuum_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_census_simple_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_census_simple_request_test || ( echo test h2_sockpair+trace_census_simple_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_compressed_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_compressed_payload_test || ( echo test h2_sockpair+trace_compressed_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_empty_batch_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_empty_batch_test || ( echo test h2_sockpair+trace_empty_batch_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_graceful_server_shutdown_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_graceful_server_shutdown_test || ( echo test h2_sockpair+trace_graceful_server_shutdown_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_high_initial_seqno_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_high_initial_seqno_test || ( echo test h2_sockpair+trace_high_initial_seqno_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_invoke_large_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_invoke_large_request_test || ( echo test h2_sockpair+trace_invoke_large_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_large_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_large_metadata_test || ( echo test h2_sockpair+trace_large_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_max_concurrent_streams_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_max_concurrent_streams_test || ( echo test h2_sockpair+trace_max_concurrent_streams_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_max_message_length_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_max_message_length_test || ( echo test h2_sockpair+trace_max_message_length_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_metadata_test || ( echo test h2_sockpair+trace_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_no_op_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_no_op_test || ( echo test h2_sockpair+trace_no_op_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_payload_test || ( echo test h2_sockpair+trace_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_ping_pong_streaming_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_ping_pong_streaming_test || ( echo test h2_sockpair+trace_ping_pong_streaming_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_registered_call_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_registered_call_test || ( echo test h2_sockpair+trace_registered_call_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_request_with_flags_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_request_with_flags_test || ( echo test h2_sockpair+trace_request_with_flags_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_request_with_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_request_with_payload_test || ( echo test h2_sockpair+trace_request_with_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_server_finishes_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_server_finishes_request_test || ( echo test h2_sockpair+trace_server_finishes_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_shutdown_finishes_calls_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_shutdown_finishes_calls_test || ( echo test h2_sockpair+trace_shutdown_finishes_calls_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_shutdown_finishes_tags_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_shutdown_finishes_tags_test || ( echo test h2_sockpair+trace_shutdown_finishes_tags_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_simple_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_simple_request_test || ( echo test h2_sockpair+trace_simple_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_trailing_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_trailing_metadata_test || ( echo test h2_sockpair+trace_trailing_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_bad_hostname_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_bad_hostname_test || ( echo test h2_sockpair_1byte_bad_hostname_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_binary_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_binary_metadata_test || ( echo test h2_sockpair_1byte_binary_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_call_creds_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_call_creds_test || ( echo test h2_sockpair_1byte_call_creds_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_cancel_after_accept_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_accept_test || ( echo test h2_sockpair_1byte_cancel_after_accept_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_cancel_after_client_done_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_client_done_test || ( echo test h2_sockpair_1byte_cancel_after_client_done_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_cancel_after_invoke_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_invoke_test || ( echo test h2_sockpair_1byte_cancel_after_invoke_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_cancel_before_invoke_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_before_invoke_test || ( echo test h2_sockpair_1byte_cancel_before_invoke_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_cancel_in_a_vacuum_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_in_a_vacuum_test || ( echo test h2_sockpair_1byte_cancel_in_a_vacuum_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_census_simple_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_census_simple_request_test || ( echo test h2_sockpair_1byte_census_simple_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_compressed_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_compressed_payload_test || ( echo test h2_sockpair_1byte_compressed_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_empty_batch_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_empty_batch_test || ( echo test h2_sockpair_1byte_empty_batch_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_graceful_server_shutdown_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_graceful_server_shutdown_test || ( echo test h2_sockpair_1byte_graceful_server_shutdown_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_high_initial_seqno_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_high_initial_seqno_test || ( echo test h2_sockpair_1byte_high_initial_seqno_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_invoke_large_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_invoke_large_request_test || ( echo test h2_sockpair_1byte_invoke_large_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_large_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_large_metadata_test || ( echo test h2_sockpair_1byte_large_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_max_concurrent_streams_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_max_concurrent_streams_test || ( echo test h2_sockpair_1byte_max_concurrent_streams_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_max_message_length_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_max_message_length_test || ( echo test h2_sockpair_1byte_max_message_length_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_metadata_test || ( echo test h2_sockpair_1byte_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_no_op_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_no_op_test || ( echo test h2_sockpair_1byte_no_op_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_payload_test || ( echo test h2_sockpair_1byte_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_ping_pong_streaming_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_ping_pong_streaming_test || ( echo test h2_sockpair_1byte_ping_pong_streaming_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_registered_call_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_registered_call_test || ( echo test h2_sockpair_1byte_registered_call_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_request_with_flags_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_request_with_flags_test || ( echo test h2_sockpair_1byte_request_with_flags_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_request_with_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_request_with_payload_test || ( echo test h2_sockpair_1byte_request_with_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_server_finishes_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_server_finishes_request_test || ( echo test h2_sockpair_1byte_server_finishes_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_shutdown_finishes_calls_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_shutdown_finishes_calls_test || ( echo test h2_sockpair_1byte_shutdown_finishes_calls_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_shutdown_finishes_tags_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_shutdown_finishes_tags_test || ( echo test h2_sockpair_1byte_shutdown_finishes_tags_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_simple_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_simple_request_test || ( echo test h2_sockpair_1byte_simple_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_trailing_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_trailing_metadata_test || ( echo test h2_sockpair_1byte_trailing_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_bad_hostname_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_bad_hostname_test || ( echo test h2_ssl_bad_hostname_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_binary_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_binary_metadata_test || ( echo test h2_ssl_binary_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_call_creds_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_call_creds_test || ( echo test h2_ssl_call_creds_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_cancel_after_accept_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_cancel_after_accept_test || ( echo test h2_ssl_cancel_after_accept_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_cancel_after_client_done_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_cancel_after_client_done_test || ( echo test h2_ssl_cancel_after_client_done_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_cancel_after_invoke_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_cancel_after_invoke_test || ( echo test h2_ssl_cancel_after_invoke_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_cancel_before_invoke_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_cancel_before_invoke_test || ( echo test h2_ssl_cancel_before_invoke_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_cancel_in_a_vacuum_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_cancel_in_a_vacuum_test || ( echo test h2_ssl_cancel_in_a_vacuum_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_census_simple_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_census_simple_request_test || ( echo test h2_ssl_census_simple_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_channel_connectivity_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_channel_connectivity_test || ( echo test h2_ssl_channel_connectivity_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_compressed_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_compressed_payload_test || ( echo test h2_ssl_compressed_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_default_host_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_default_host_test || ( echo test h2_ssl_default_host_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_disappearing_server_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_disappearing_server_test || ( echo test h2_ssl_disappearing_server_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_empty_batch_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_empty_batch_test || ( echo test h2_ssl_empty_batch_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_graceful_server_shutdown_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_graceful_server_shutdown_test || ( echo test h2_ssl_graceful_server_shutdown_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_high_initial_seqno_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_high_initial_seqno_test || ( echo test h2_ssl_high_initial_seqno_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_invoke_large_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_invoke_large_request_test || ( echo test h2_ssl_invoke_large_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_large_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_large_metadata_test || ( echo test h2_ssl_large_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_max_concurrent_streams_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_max_concurrent_streams_test || ( echo test h2_ssl_max_concurrent_streams_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_max_message_length_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_max_message_length_test || ( echo test h2_ssl_max_message_length_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_metadata_test || ( echo test h2_ssl_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_no_op_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_no_op_test || ( echo test h2_ssl_no_op_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_payload_test || ( echo test h2_ssl_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_ping_pong_streaming_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_ping_pong_streaming_test || ( echo test h2_ssl_ping_pong_streaming_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_registered_call_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_registered_call_test || ( echo test h2_ssl_registered_call_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_request_with_flags_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_request_with_flags_test || ( echo test h2_ssl_request_with_flags_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_request_with_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_request_with_payload_test || ( echo test h2_ssl_request_with_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_server_finishes_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_server_finishes_request_test || ( echo test h2_ssl_server_finishes_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_shutdown_finishes_calls_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_shutdown_finishes_calls_test || ( echo test h2_ssl_shutdown_finishes_calls_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_shutdown_finishes_tags_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_shutdown_finishes_tags_test || ( echo test h2_ssl_shutdown_finishes_tags_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_simple_delayed_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_simple_delayed_request_test || ( echo test h2_ssl_simple_delayed_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_simple_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_simple_request_test || ( echo test h2_ssl_simple_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_trailing_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_trailing_metadata_test || ( echo test h2_ssl_trailing_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl+poll_bad_hostname_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl+poll_bad_hostname_test || ( echo test h2_ssl+poll_bad_hostname_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl+poll_binary_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl+poll_binary_metadata_test || ( echo test h2_ssl+poll_binary_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl+poll_call_creds_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl+poll_call_creds_test || ( echo test h2_ssl+poll_call_creds_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl+poll_cancel_after_accept_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl+poll_cancel_after_accept_test || ( echo test h2_ssl+poll_cancel_after_accept_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl+poll_cancel_after_client_done_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl+poll_cancel_after_client_done_test || ( echo test h2_ssl+poll_cancel_after_client_done_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl+poll_cancel_after_invoke_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl+poll_cancel_after_invoke_test || ( echo test h2_ssl+poll_cancel_after_invoke_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl+poll_cancel_before_invoke_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl+poll_cancel_before_invoke_test || ( echo test h2_ssl+poll_cancel_before_invoke_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl+poll_cancel_in_a_vacuum_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl+poll_cancel_in_a_vacuum_test || ( echo test h2_ssl+poll_cancel_in_a_vacuum_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl+poll_census_simple_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl+poll_census_simple_request_test || ( echo test h2_ssl+poll_census_simple_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl+poll_channel_connectivity_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl+poll_channel_connectivity_test || ( echo test h2_ssl+poll_channel_connectivity_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl+poll_compressed_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl+poll_compressed_payload_test || ( echo test h2_ssl+poll_compressed_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl+poll_default_host_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl+poll_default_host_test || ( echo test h2_ssl+poll_default_host_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl+poll_disappearing_server_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl+poll_disappearing_server_test || ( echo test h2_ssl+poll_disappearing_server_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl+poll_empty_batch_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl+poll_empty_batch_test || ( echo test h2_ssl+poll_empty_batch_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl+poll_graceful_server_shutdown_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl+poll_graceful_server_shutdown_test || ( echo test h2_ssl+poll_graceful_server_shutdown_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl+poll_high_initial_seqno_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl+poll_high_initial_seqno_test || ( echo test h2_ssl+poll_high_initial_seqno_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl+poll_invoke_large_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl+poll_invoke_large_request_test || ( echo test h2_ssl+poll_invoke_large_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl+poll_large_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl+poll_large_metadata_test || ( echo test h2_ssl+poll_large_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl+poll_max_concurrent_streams_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl+poll_max_concurrent_streams_test || ( echo test h2_ssl+poll_max_concurrent_streams_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl+poll_max_message_length_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl+poll_max_message_length_test || ( echo test h2_ssl+poll_max_message_length_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl+poll_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl+poll_metadata_test || ( echo test h2_ssl+poll_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl+poll_no_op_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl+poll_no_op_test || ( echo test h2_ssl+poll_no_op_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl+poll_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl+poll_payload_test || ( echo test h2_ssl+poll_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl+poll_ping_pong_streaming_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl+poll_ping_pong_streaming_test || ( echo test h2_ssl+poll_ping_pong_streaming_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl+poll_registered_call_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl+poll_registered_call_test || ( echo test h2_ssl+poll_registered_call_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl+poll_request_with_flags_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl+poll_request_with_flags_test || ( echo test h2_ssl+poll_request_with_flags_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl+poll_request_with_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl+poll_request_with_payload_test || ( echo test h2_ssl+poll_request_with_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl+poll_server_finishes_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl+poll_server_finishes_request_test || ( echo test h2_ssl+poll_server_finishes_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl+poll_shutdown_finishes_calls_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl+poll_shutdown_finishes_calls_test || ( echo test h2_ssl+poll_shutdown_finishes_calls_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl+poll_shutdown_finishes_tags_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl+poll_shutdown_finishes_tags_test || ( echo test h2_ssl+poll_shutdown_finishes_tags_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl+poll_simple_delayed_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl+poll_simple_delayed_request_test || ( echo test h2_ssl+poll_simple_delayed_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl+poll_simple_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl+poll_simple_request_test || ( echo test h2_ssl+poll_simple_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl+poll_trailing_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl+poll_trailing_metadata_test || ( echo test h2_ssl+poll_trailing_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_proxy_bad_hostname_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_proxy_bad_hostname_test || ( echo test h2_ssl_proxy_bad_hostname_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_proxy_binary_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_proxy_binary_metadata_test || ( echo test h2_ssl_proxy_binary_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_proxy_call_creds_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_proxy_call_creds_test || ( echo test h2_ssl_proxy_call_creds_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_proxy_cancel_after_accept_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_proxy_cancel_after_accept_test || ( echo test h2_ssl_proxy_cancel_after_accept_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_proxy_cancel_after_client_done_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_proxy_cancel_after_client_done_test || ( echo test h2_ssl_proxy_cancel_after_client_done_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_proxy_cancel_after_invoke_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_proxy_cancel_after_invoke_test || ( echo test h2_ssl_proxy_cancel_after_invoke_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_proxy_cancel_before_invoke_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_proxy_cancel_before_invoke_test || ( echo test h2_ssl_proxy_cancel_before_invoke_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_proxy_cancel_in_a_vacuum_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_proxy_cancel_in_a_vacuum_test || ( echo test h2_ssl_proxy_cancel_in_a_vacuum_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_proxy_census_simple_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_proxy_census_simple_request_test || ( echo test h2_ssl_proxy_census_simple_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_proxy_default_host_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_proxy_default_host_test || ( echo test h2_ssl_proxy_default_host_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_proxy_disappearing_server_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_proxy_disappearing_server_test || ( echo test h2_ssl_proxy_disappearing_server_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_proxy_empty_batch_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_proxy_empty_batch_test || ( echo test h2_ssl_proxy_empty_batch_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_proxy_graceful_server_shutdown_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_proxy_graceful_server_shutdown_test || ( echo test h2_ssl_proxy_graceful_server_shutdown_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_proxy_high_initial_seqno_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_proxy_high_initial_seqno_test || ( echo test h2_ssl_proxy_high_initial_seqno_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_proxy_invoke_large_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_proxy_invoke_large_request_test || ( echo test h2_ssl_proxy_invoke_large_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_proxy_large_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_proxy_large_metadata_test || ( echo test h2_ssl_proxy_large_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_proxy_max_message_length_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_proxy_max_message_length_test || ( echo test h2_ssl_proxy_max_message_length_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_proxy_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_proxy_metadata_test || ( echo test h2_ssl_proxy_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_proxy_no_op_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_proxy_no_op_test || ( echo test h2_ssl_proxy_no_op_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_proxy_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_proxy_payload_test || ( echo test h2_ssl_proxy_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_proxy_ping_pong_streaming_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_proxy_ping_pong_streaming_test || ( echo test h2_ssl_proxy_ping_pong_streaming_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_proxy_registered_call_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_proxy_registered_call_test || ( echo test h2_ssl_proxy_registered_call_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_proxy_request_with_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_proxy_request_with_payload_test || ( echo test h2_ssl_proxy_request_with_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_proxy_server_finishes_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_proxy_server_finishes_request_test || ( echo test h2_ssl_proxy_server_finishes_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_proxy_shutdown_finishes_calls_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_proxy_shutdown_finishes_calls_test || ( echo test h2_ssl_proxy_shutdown_finishes_calls_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_proxy_shutdown_finishes_tags_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_proxy_shutdown_finishes_tags_test || ( echo test h2_ssl_proxy_shutdown_finishes_tags_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_proxy_simple_delayed_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_proxy_simple_delayed_request_test || ( echo test h2_ssl_proxy_simple_delayed_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_proxy_simple_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_proxy_simple_request_test || ( echo test h2_ssl_proxy_simple_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_proxy_trailing_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_proxy_trailing_metadata_test || ( echo test h2_ssl_proxy_trailing_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_bad_hostname_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_bad_hostname_test || ( echo test h2_uds_bad_hostname_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_binary_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_binary_metadata_test || ( echo test h2_uds_binary_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_call_creds_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_call_creds_test || ( echo test h2_uds_call_creds_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_cancel_after_accept_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_cancel_after_accept_test || ( echo test h2_uds_cancel_after_accept_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_cancel_after_client_done_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_cancel_after_client_done_test || ( echo test h2_uds_cancel_after_client_done_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_cancel_after_invoke_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_cancel_after_invoke_test || ( echo test h2_uds_cancel_after_invoke_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_cancel_before_invoke_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_cancel_before_invoke_test || ( echo test h2_uds_cancel_before_invoke_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_cancel_in_a_vacuum_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_cancel_in_a_vacuum_test || ( echo test h2_uds_cancel_in_a_vacuum_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_census_simple_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_census_simple_request_test || ( echo test h2_uds_census_simple_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_channel_connectivity_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_channel_connectivity_test || ( echo test h2_uds_channel_connectivity_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_compressed_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_compressed_payload_test || ( echo test h2_uds_compressed_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_disappearing_server_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_disappearing_server_test || ( echo test h2_uds_disappearing_server_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_empty_batch_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_empty_batch_test || ( echo test h2_uds_empty_batch_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_graceful_server_shutdown_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_graceful_server_shutdown_test || ( echo test h2_uds_graceful_server_shutdown_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_high_initial_seqno_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_high_initial_seqno_test || ( echo test h2_uds_high_initial_seqno_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_invoke_large_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_invoke_large_request_test || ( echo test h2_uds_invoke_large_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_large_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_large_metadata_test || ( echo test h2_uds_large_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_max_concurrent_streams_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_max_concurrent_streams_test || ( echo test h2_uds_max_concurrent_streams_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_max_message_length_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_max_message_length_test || ( echo test h2_uds_max_message_length_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_metadata_test || ( echo test h2_uds_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_no_op_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_no_op_test || ( echo test h2_uds_no_op_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_payload_test || ( echo test h2_uds_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_ping_pong_streaming_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_ping_pong_streaming_test || ( echo test h2_uds_ping_pong_streaming_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_registered_call_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_registered_call_test || ( echo test h2_uds_registered_call_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_request_with_flags_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_request_with_flags_test || ( echo test h2_uds_request_with_flags_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_request_with_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_request_with_payload_test || ( echo test h2_uds_request_with_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_server_finishes_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_server_finishes_request_test || ( echo test h2_uds_server_finishes_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_shutdown_finishes_calls_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_shutdown_finishes_calls_test || ( echo test h2_uds_shutdown_finishes_calls_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_shutdown_finishes_tags_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_shutdown_finishes_tags_test || ( echo test h2_uds_shutdown_finishes_tags_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_simple_delayed_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_simple_delayed_request_test || ( echo test h2_uds_simple_delayed_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_simple_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_simple_request_test || ( echo test h2_uds_simple_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_trailing_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_trailing_metadata_test || ( echo test h2_uds_trailing_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_bad_hostname_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_bad_hostname_test || ( echo test h2_uds+poll_bad_hostname_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_binary_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_binary_metadata_test || ( echo test h2_uds+poll_binary_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_call_creds_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_call_creds_test || ( echo test h2_uds+poll_call_creds_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_cancel_after_accept_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_accept_test || ( echo test h2_uds+poll_cancel_after_accept_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_cancel_after_client_done_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_client_done_test || ( echo test h2_uds+poll_cancel_after_client_done_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_cancel_after_invoke_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_invoke_test || ( echo test h2_uds+poll_cancel_after_invoke_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_cancel_before_invoke_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_before_invoke_test || ( echo test h2_uds+poll_cancel_before_invoke_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_cancel_in_a_vacuum_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_in_a_vacuum_test || ( echo test h2_uds+poll_cancel_in_a_vacuum_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_census_simple_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_census_simple_request_test || ( echo test h2_uds+poll_census_simple_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_channel_connectivity_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_channel_connectivity_test || ( echo test h2_uds+poll_channel_connectivity_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_compressed_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_compressed_payload_test || ( echo test h2_uds+poll_compressed_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_disappearing_server_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_disappearing_server_test || ( echo test h2_uds+poll_disappearing_server_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_empty_batch_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_empty_batch_test || ( echo test h2_uds+poll_empty_batch_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_graceful_server_shutdown_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_graceful_server_shutdown_test || ( echo test h2_uds+poll_graceful_server_shutdown_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_high_initial_seqno_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_high_initial_seqno_test || ( echo test h2_uds+poll_high_initial_seqno_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_invoke_large_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_invoke_large_request_test || ( echo test h2_uds+poll_invoke_large_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_large_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_large_metadata_test || ( echo test h2_uds+poll_large_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_max_concurrent_streams_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_max_concurrent_streams_test || ( echo test h2_uds+poll_max_concurrent_streams_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_max_message_length_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_max_message_length_test || ( echo test h2_uds+poll_max_message_length_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_metadata_test || ( echo test h2_uds+poll_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_no_op_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_no_op_test || ( echo test h2_uds+poll_no_op_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_payload_test || ( echo test h2_uds+poll_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_ping_pong_streaming_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_ping_pong_streaming_test || ( echo test h2_uds+poll_ping_pong_streaming_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_registered_call_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_registered_call_test || ( echo test h2_uds+poll_registered_call_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_request_with_flags_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_request_with_flags_test || ( echo test h2_uds+poll_request_with_flags_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_request_with_payload_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_request_with_payload_test || ( echo test h2_uds+poll_request_with_payload_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_server_finishes_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_server_finishes_request_test || ( echo test h2_uds+poll_server_finishes_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_shutdown_finishes_calls_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_shutdown_finishes_calls_test || ( echo test h2_uds+poll_shutdown_finishes_calls_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_shutdown_finishes_tags_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_shutdown_finishes_tags_test || ( echo test h2_uds+poll_shutdown_finishes_tags_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_simple_delayed_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_simple_delayed_request_test || ( echo test h2_uds+poll_simple_delayed_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_simple_request_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_simple_request_test || ( echo test h2_uds+poll_simple_request_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_trailing_metadata_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_trailing_metadata_test || ( echo test h2_uds+poll_trailing_metadata_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_bad_hostname_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_bad_hostname_nosec_test || ( echo test h2_compress_bad_hostname_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_binary_metadata_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_binary_metadata_nosec_test || ( echo test h2_compress_binary_metadata_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_cancel_after_accept_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_cancel_after_accept_nosec_test || ( echo test h2_compress_cancel_after_accept_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_cancel_after_client_done_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_cancel_after_client_done_nosec_test || ( echo test h2_compress_cancel_after_client_done_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_cancel_after_invoke_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_cancel_after_invoke_nosec_test || ( echo test h2_compress_cancel_after_invoke_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_cancel_before_invoke_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_cancel_before_invoke_nosec_test || ( echo test h2_compress_cancel_before_invoke_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_cancel_in_a_vacuum_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_cancel_in_a_vacuum_nosec_test || ( echo test h2_compress_cancel_in_a_vacuum_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_census_simple_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_census_simple_request_nosec_test || ( echo test h2_compress_census_simple_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_channel_connectivity_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_channel_connectivity_nosec_test || ( echo test h2_compress_channel_connectivity_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_compressed_payload_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_compressed_payload_nosec_test || ( echo test h2_compress_compressed_payload_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_default_host_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_default_host_nosec_test || ( echo test h2_compress_default_host_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_disappearing_server_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_disappearing_server_nosec_test || ( echo test h2_compress_disappearing_server_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_empty_batch_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_empty_batch_nosec_test || ( echo test h2_compress_empty_batch_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_graceful_server_shutdown_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_graceful_server_shutdown_nosec_test || ( echo test h2_compress_graceful_server_shutdown_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_high_initial_seqno_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_high_initial_seqno_nosec_test || ( echo test h2_compress_high_initial_seqno_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_invoke_large_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_invoke_large_request_nosec_test || ( echo test h2_compress_invoke_large_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_large_metadata_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_large_metadata_nosec_test || ( echo test h2_compress_large_metadata_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_max_concurrent_streams_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_max_concurrent_streams_nosec_test || ( echo test h2_compress_max_concurrent_streams_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_max_message_length_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_max_message_length_nosec_test || ( echo test h2_compress_max_message_length_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_metadata_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_metadata_nosec_test || ( echo test h2_compress_metadata_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_no_op_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_no_op_nosec_test || ( echo test h2_compress_no_op_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_payload_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_payload_nosec_test || ( echo test h2_compress_payload_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_ping_pong_streaming_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_ping_pong_streaming_nosec_test || ( echo test h2_compress_ping_pong_streaming_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_registered_call_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_registered_call_nosec_test || ( echo test h2_compress_registered_call_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_request_with_flags_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_request_with_flags_nosec_test || ( echo test h2_compress_request_with_flags_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_request_with_payload_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_request_with_payload_nosec_test || ( echo test h2_compress_request_with_payload_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_server_finishes_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_server_finishes_request_nosec_test || ( echo test h2_compress_server_finishes_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_shutdown_finishes_calls_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_shutdown_finishes_calls_nosec_test || ( echo test h2_compress_shutdown_finishes_calls_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_shutdown_finishes_tags_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_shutdown_finishes_tags_nosec_test || ( echo test h2_compress_shutdown_finishes_tags_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_simple_delayed_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_simple_delayed_request_nosec_test || ( echo test h2_compress_simple_delayed_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_simple_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_simple_request_nosec_test || ( echo test h2_compress_simple_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_compress_trailing_metadata_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_compress_trailing_metadata_nosec_test || ( echo test h2_compress_trailing_metadata_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_bad_hostname_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_bad_hostname_nosec_test || ( echo test h2_full_bad_hostname_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_binary_metadata_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_binary_metadata_nosec_test || ( echo test h2_full_binary_metadata_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_cancel_after_accept_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_cancel_after_accept_nosec_test || ( echo test h2_full_cancel_after_accept_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_cancel_after_client_done_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_cancel_after_client_done_nosec_test || ( echo test h2_full_cancel_after_client_done_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_cancel_after_invoke_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_cancel_after_invoke_nosec_test || ( echo test h2_full_cancel_after_invoke_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_cancel_before_invoke_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_cancel_before_invoke_nosec_test || ( echo test h2_full_cancel_before_invoke_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_cancel_in_a_vacuum_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_cancel_in_a_vacuum_nosec_test || ( echo test h2_full_cancel_in_a_vacuum_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_census_simple_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_census_simple_request_nosec_test || ( echo test h2_full_census_simple_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_channel_connectivity_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_channel_connectivity_nosec_test || ( echo test h2_full_channel_connectivity_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_compressed_payload_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_compressed_payload_nosec_test || ( echo test h2_full_compressed_payload_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_default_host_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_default_host_nosec_test || ( echo test h2_full_default_host_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_disappearing_server_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_disappearing_server_nosec_test || ( echo test h2_full_disappearing_server_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_empty_batch_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_empty_batch_nosec_test || ( echo test h2_full_empty_batch_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_graceful_server_shutdown_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_graceful_server_shutdown_nosec_test || ( echo test h2_full_graceful_server_shutdown_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_high_initial_seqno_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_high_initial_seqno_nosec_test || ( echo test h2_full_high_initial_seqno_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_invoke_large_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_invoke_large_request_nosec_test || ( echo test h2_full_invoke_large_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_large_metadata_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_large_metadata_nosec_test || ( echo test h2_full_large_metadata_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_max_concurrent_streams_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_max_concurrent_streams_nosec_test || ( echo test h2_full_max_concurrent_streams_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_max_message_length_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_max_message_length_nosec_test || ( echo test h2_full_max_message_length_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_metadata_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_metadata_nosec_test || ( echo test h2_full_metadata_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_no_op_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_no_op_nosec_test || ( echo test h2_full_no_op_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_payload_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_payload_nosec_test || ( echo test h2_full_payload_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_ping_pong_streaming_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_ping_pong_streaming_nosec_test || ( echo test h2_full_ping_pong_streaming_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_registered_call_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_registered_call_nosec_test || ( echo test h2_full_registered_call_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_request_with_flags_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_request_with_flags_nosec_test || ( echo test h2_full_request_with_flags_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_request_with_payload_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_request_with_payload_nosec_test || ( echo test h2_full_request_with_payload_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_server_finishes_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_server_finishes_request_nosec_test || ( echo test h2_full_server_finishes_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_shutdown_finishes_calls_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_shutdown_finishes_calls_nosec_test || ( echo test h2_full_shutdown_finishes_calls_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_shutdown_finishes_tags_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_shutdown_finishes_tags_nosec_test || ( echo test h2_full_shutdown_finishes_tags_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_simple_delayed_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_simple_delayed_request_nosec_test || ( echo test h2_full_simple_delayed_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_simple_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_simple_request_nosec_test || ( echo test h2_full_simple_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full_trailing_metadata_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full_trailing_metadata_nosec_test || ( echo test h2_full_trailing_metadata_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_bad_hostname_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_bad_hostname_nosec_test || ( echo test h2_full+poll_bad_hostname_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_binary_metadata_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_binary_metadata_nosec_test || ( echo test h2_full+poll_binary_metadata_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_cancel_after_accept_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_accept_nosec_test || ( echo test h2_full+poll_cancel_after_accept_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_cancel_after_client_done_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_client_done_nosec_test || ( echo test h2_full+poll_cancel_after_client_done_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_cancel_after_invoke_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_invoke_nosec_test || ( echo test h2_full+poll_cancel_after_invoke_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_cancel_before_invoke_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_before_invoke_nosec_test || ( echo test h2_full+poll_cancel_before_invoke_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_cancel_in_a_vacuum_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_in_a_vacuum_nosec_test || ( echo test h2_full+poll_cancel_in_a_vacuum_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_census_simple_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_census_simple_request_nosec_test || ( echo test h2_full+poll_census_simple_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_channel_connectivity_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_channel_connectivity_nosec_test || ( echo test h2_full+poll_channel_connectivity_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_compressed_payload_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_compressed_payload_nosec_test || ( echo test h2_full+poll_compressed_payload_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_default_host_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_default_host_nosec_test || ( echo test h2_full+poll_default_host_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_disappearing_server_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_disappearing_server_nosec_test || ( echo test h2_full+poll_disappearing_server_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_empty_batch_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_empty_batch_nosec_test || ( echo test h2_full+poll_empty_batch_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_graceful_server_shutdown_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_graceful_server_shutdown_nosec_test || ( echo test h2_full+poll_graceful_server_shutdown_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_high_initial_seqno_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_high_initial_seqno_nosec_test || ( echo test h2_full+poll_high_initial_seqno_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_invoke_large_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_invoke_large_request_nosec_test || ( echo test h2_full+poll_invoke_large_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_large_metadata_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_large_metadata_nosec_test || ( echo test h2_full+poll_large_metadata_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_max_concurrent_streams_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_max_concurrent_streams_nosec_test || ( echo test h2_full+poll_max_concurrent_streams_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_max_message_length_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_max_message_length_nosec_test || ( echo test h2_full+poll_max_message_length_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_metadata_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_metadata_nosec_test || ( echo test h2_full+poll_metadata_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_no_op_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_no_op_nosec_test || ( echo test h2_full+poll_no_op_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_payload_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_payload_nosec_test || ( echo test h2_full+poll_payload_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_ping_pong_streaming_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_ping_pong_streaming_nosec_test || ( echo test h2_full+poll_ping_pong_streaming_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_registered_call_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_registered_call_nosec_test || ( echo test h2_full+poll_registered_call_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_request_with_flags_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_request_with_flags_nosec_test || ( echo test h2_full+poll_request_with_flags_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_request_with_payload_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_request_with_payload_nosec_test || ( echo test h2_full+poll_request_with_payload_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_server_finishes_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_server_finishes_request_nosec_test || ( echo test h2_full+poll_server_finishes_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_shutdown_finishes_calls_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_shutdown_finishes_calls_nosec_test || ( echo test h2_full+poll_shutdown_finishes_calls_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_shutdown_finishes_tags_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_shutdown_finishes_tags_nosec_test || ( echo test h2_full+poll_shutdown_finishes_tags_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_simple_delayed_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_simple_delayed_request_nosec_test || ( echo test h2_full+poll_simple_delayed_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_simple_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_simple_request_nosec_test || ( echo test h2_full+poll_simple_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_full+poll_trailing_metadata_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_full+poll_trailing_metadata_nosec_test || ( echo test h2_full+poll_trailing_metadata_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_bad_hostname_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_bad_hostname_nosec_test || ( echo test h2_proxy_bad_hostname_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_binary_metadata_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_binary_metadata_nosec_test || ( echo test h2_proxy_binary_metadata_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_cancel_after_accept_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_accept_nosec_test || ( echo test h2_proxy_cancel_after_accept_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_cancel_after_client_done_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_client_done_nosec_test || ( echo test h2_proxy_cancel_after_client_done_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_cancel_after_invoke_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_invoke_nosec_test || ( echo test h2_proxy_cancel_after_invoke_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_cancel_before_invoke_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_cancel_before_invoke_nosec_test || ( echo test h2_proxy_cancel_before_invoke_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_cancel_in_a_vacuum_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_cancel_in_a_vacuum_nosec_test || ( echo test h2_proxy_cancel_in_a_vacuum_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_census_simple_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_census_simple_request_nosec_test || ( echo test h2_proxy_census_simple_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_default_host_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_default_host_nosec_test || ( echo test h2_proxy_default_host_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_disappearing_server_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_disappearing_server_nosec_test || ( echo test h2_proxy_disappearing_server_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_empty_batch_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_empty_batch_nosec_test || ( echo test h2_proxy_empty_batch_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_graceful_server_shutdown_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_graceful_server_shutdown_nosec_test || ( echo test h2_proxy_graceful_server_shutdown_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_high_initial_seqno_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_high_initial_seqno_nosec_test || ( echo test h2_proxy_high_initial_seqno_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_invoke_large_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_invoke_large_request_nosec_test || ( echo test h2_proxy_invoke_large_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_large_metadata_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_large_metadata_nosec_test || ( echo test h2_proxy_large_metadata_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_max_message_length_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_max_message_length_nosec_test || ( echo test h2_proxy_max_message_length_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_metadata_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_metadata_nosec_test || ( echo test h2_proxy_metadata_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_no_op_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_no_op_nosec_test || ( echo test h2_proxy_no_op_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_payload_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_payload_nosec_test || ( echo test h2_proxy_payload_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_ping_pong_streaming_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_ping_pong_streaming_nosec_test || ( echo test h2_proxy_ping_pong_streaming_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_registered_call_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_registered_call_nosec_test || ( echo test h2_proxy_registered_call_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_request_with_payload_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_request_with_payload_nosec_test || ( echo test h2_proxy_request_with_payload_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_server_finishes_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_server_finishes_request_nosec_test || ( echo test h2_proxy_server_finishes_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_shutdown_finishes_calls_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_shutdown_finishes_calls_nosec_test || ( echo test h2_proxy_shutdown_finishes_calls_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_shutdown_finishes_tags_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_shutdown_finishes_tags_nosec_test || ( echo test h2_proxy_shutdown_finishes_tags_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_simple_delayed_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_simple_delayed_request_nosec_test || ( echo test h2_proxy_simple_delayed_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_simple_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_simple_request_nosec_test || ( echo test h2_proxy_simple_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_proxy_trailing_metadata_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_proxy_trailing_metadata_nosec_test || ( echo test h2_proxy_trailing_metadata_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_bad_hostname_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_bad_hostname_nosec_test || ( echo test h2_sockpair_bad_hostname_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_binary_metadata_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_binary_metadata_nosec_test || ( echo test h2_sockpair_binary_metadata_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_cancel_after_accept_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_accept_nosec_test || ( echo test h2_sockpair_cancel_after_accept_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_cancel_after_client_done_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_client_done_nosec_test || ( echo test h2_sockpair_cancel_after_client_done_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_cancel_after_invoke_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_invoke_nosec_test || ( echo test h2_sockpair_cancel_after_invoke_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_cancel_before_invoke_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_before_invoke_nosec_test || ( echo test h2_sockpair_cancel_before_invoke_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_cancel_in_a_vacuum_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_in_a_vacuum_nosec_test || ( echo test h2_sockpair_cancel_in_a_vacuum_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_census_simple_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_census_simple_request_nosec_test || ( echo test h2_sockpair_census_simple_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_compressed_payload_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_compressed_payload_nosec_test || ( echo test h2_sockpair_compressed_payload_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_empty_batch_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_empty_batch_nosec_test || ( echo test h2_sockpair_empty_batch_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_graceful_server_shutdown_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_graceful_server_shutdown_nosec_test || ( echo test h2_sockpair_graceful_server_shutdown_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_high_initial_seqno_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_high_initial_seqno_nosec_test || ( echo test h2_sockpair_high_initial_seqno_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_invoke_large_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_invoke_large_request_nosec_test || ( echo test h2_sockpair_invoke_large_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_large_metadata_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_large_metadata_nosec_test || ( echo test h2_sockpair_large_metadata_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_max_concurrent_streams_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_max_concurrent_streams_nosec_test || ( echo test h2_sockpair_max_concurrent_streams_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_max_message_length_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_max_message_length_nosec_test || ( echo test h2_sockpair_max_message_length_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_metadata_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_metadata_nosec_test || ( echo test h2_sockpair_metadata_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_no_op_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_no_op_nosec_test || ( echo test h2_sockpair_no_op_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_payload_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_payload_nosec_test || ( echo test h2_sockpair_payload_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_ping_pong_streaming_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_ping_pong_streaming_nosec_test || ( echo test h2_sockpair_ping_pong_streaming_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_registered_call_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_registered_call_nosec_test || ( echo test h2_sockpair_registered_call_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_request_with_flags_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_request_with_flags_nosec_test || ( echo test h2_sockpair_request_with_flags_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_request_with_payload_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_request_with_payload_nosec_test || ( echo test h2_sockpair_request_with_payload_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_server_finishes_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_server_finishes_request_nosec_test || ( echo test h2_sockpair_server_finishes_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_shutdown_finishes_calls_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_shutdown_finishes_calls_nosec_test || ( echo test h2_sockpair_shutdown_finishes_calls_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_shutdown_finishes_tags_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_shutdown_finishes_tags_nosec_test || ( echo test h2_sockpair_shutdown_finishes_tags_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_simple_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_simple_request_nosec_test || ( echo test h2_sockpair_simple_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_trailing_metadata_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_trailing_metadata_nosec_test || ( echo test h2_sockpair_trailing_metadata_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_bad_hostname_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_bad_hostname_nosec_test || ( echo test h2_sockpair+trace_bad_hostname_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_binary_metadata_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_binary_metadata_nosec_test || ( echo test h2_sockpair+trace_binary_metadata_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_cancel_after_accept_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_accept_nosec_test || ( echo test h2_sockpair+trace_cancel_after_accept_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_cancel_after_client_done_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_client_done_nosec_test || ( echo test h2_sockpair+trace_cancel_after_client_done_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_cancel_after_invoke_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_invoke_nosec_test || ( echo test h2_sockpair+trace_cancel_after_invoke_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_cancel_before_invoke_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_before_invoke_nosec_test || ( echo test h2_sockpair+trace_cancel_before_invoke_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_cancel_in_a_vacuum_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_in_a_vacuum_nosec_test || ( echo test h2_sockpair+trace_cancel_in_a_vacuum_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_census_simple_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_census_simple_request_nosec_test || ( echo test h2_sockpair+trace_census_simple_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_compressed_payload_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_compressed_payload_nosec_test || ( echo test h2_sockpair+trace_compressed_payload_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_empty_batch_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_empty_batch_nosec_test || ( echo test h2_sockpair+trace_empty_batch_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_graceful_server_shutdown_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_graceful_server_shutdown_nosec_test || ( echo test h2_sockpair+trace_graceful_server_shutdown_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_high_initial_seqno_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_high_initial_seqno_nosec_test || ( echo test h2_sockpair+trace_high_initial_seqno_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_invoke_large_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_invoke_large_request_nosec_test || ( echo test h2_sockpair+trace_invoke_large_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_large_metadata_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_large_metadata_nosec_test || ( echo test h2_sockpair+trace_large_metadata_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_max_concurrent_streams_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_max_concurrent_streams_nosec_test || ( echo test h2_sockpair+trace_max_concurrent_streams_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_max_message_length_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_max_message_length_nosec_test || ( echo test h2_sockpair+trace_max_message_length_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_metadata_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_metadata_nosec_test || ( echo test h2_sockpair+trace_metadata_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_no_op_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_no_op_nosec_test || ( echo test h2_sockpair+trace_no_op_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_payload_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_payload_nosec_test || ( echo test h2_sockpair+trace_payload_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_ping_pong_streaming_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_ping_pong_streaming_nosec_test || ( echo test h2_sockpair+trace_ping_pong_streaming_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_registered_call_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_registered_call_nosec_test || ( echo test h2_sockpair+trace_registered_call_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_request_with_flags_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_request_with_flags_nosec_test || ( echo test h2_sockpair+trace_request_with_flags_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_request_with_payload_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_request_with_payload_nosec_test || ( echo test h2_sockpair+trace_request_with_payload_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_server_finishes_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_server_finishes_request_nosec_test || ( echo test h2_sockpair+trace_server_finishes_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_shutdown_finishes_calls_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_shutdown_finishes_calls_nosec_test || ( echo test h2_sockpair+trace_shutdown_finishes_calls_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_shutdown_finishes_tags_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_shutdown_finishes_tags_nosec_test || ( echo test h2_sockpair+trace_shutdown_finishes_tags_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_simple_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_simple_request_nosec_test || ( echo test h2_sockpair+trace_simple_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair+trace_trailing_metadata_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair+trace_trailing_metadata_nosec_test || ( echo test h2_sockpair+trace_trailing_metadata_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_bad_hostname_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_bad_hostname_nosec_test || ( echo test h2_sockpair_1byte_bad_hostname_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_binary_metadata_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_binary_metadata_nosec_test || ( echo test h2_sockpair_1byte_binary_metadata_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_cancel_after_accept_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_accept_nosec_test || ( echo test h2_sockpair_1byte_cancel_after_accept_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_cancel_after_client_done_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_client_done_nosec_test || ( echo test h2_sockpair_1byte_cancel_after_client_done_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_cancel_after_invoke_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_invoke_nosec_test || ( echo test h2_sockpair_1byte_cancel_after_invoke_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_cancel_before_invoke_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_before_invoke_nosec_test || ( echo test h2_sockpair_1byte_cancel_before_invoke_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_cancel_in_a_vacuum_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_in_a_vacuum_nosec_test || ( echo test h2_sockpair_1byte_cancel_in_a_vacuum_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_census_simple_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_census_simple_request_nosec_test || ( echo test h2_sockpair_1byte_census_simple_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_compressed_payload_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_compressed_payload_nosec_test || ( echo test h2_sockpair_1byte_compressed_payload_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_empty_batch_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_empty_batch_nosec_test || ( echo test h2_sockpair_1byte_empty_batch_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_graceful_server_shutdown_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_graceful_server_shutdown_nosec_test || ( echo test h2_sockpair_1byte_graceful_server_shutdown_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_high_initial_seqno_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_high_initial_seqno_nosec_test || ( echo test h2_sockpair_1byte_high_initial_seqno_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_invoke_large_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_invoke_large_request_nosec_test || ( echo test h2_sockpair_1byte_invoke_large_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_large_metadata_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_large_metadata_nosec_test || ( echo test h2_sockpair_1byte_large_metadata_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_max_concurrent_streams_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_max_concurrent_streams_nosec_test || ( echo test h2_sockpair_1byte_max_concurrent_streams_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_max_message_length_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_max_message_length_nosec_test || ( echo test h2_sockpair_1byte_max_message_length_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_metadata_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_metadata_nosec_test || ( echo test h2_sockpair_1byte_metadata_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_no_op_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_no_op_nosec_test || ( echo test h2_sockpair_1byte_no_op_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_payload_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_payload_nosec_test || ( echo test h2_sockpair_1byte_payload_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_ping_pong_streaming_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_ping_pong_streaming_nosec_test || ( echo test h2_sockpair_1byte_ping_pong_streaming_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_registered_call_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_registered_call_nosec_test || ( echo test h2_sockpair_1byte_registered_call_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_request_with_flags_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_request_with_flags_nosec_test || ( echo test h2_sockpair_1byte_request_with_flags_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_request_with_payload_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_request_with_payload_nosec_test || ( echo test h2_sockpair_1byte_request_with_payload_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_server_finishes_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_server_finishes_request_nosec_test || ( echo test h2_sockpair_1byte_server_finishes_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_shutdown_finishes_calls_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_shutdown_finishes_calls_nosec_test || ( echo test h2_sockpair_1byte_shutdown_finishes_calls_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_shutdown_finishes_tags_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_shutdown_finishes_tags_nosec_test || ( echo test h2_sockpair_1byte_shutdown_finishes_tags_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_simple_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_simple_request_nosec_test || ( echo test h2_sockpair_1byte_simple_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_sockpair_1byte_trailing_metadata_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_trailing_metadata_nosec_test || ( echo test h2_sockpair_1byte_trailing_metadata_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_bad_hostname_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_bad_hostname_nosec_test || ( echo test h2_uds_bad_hostname_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_binary_metadata_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_binary_metadata_nosec_test || ( echo test h2_uds_binary_metadata_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_cancel_after_accept_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_cancel_after_accept_nosec_test || ( echo test h2_uds_cancel_after_accept_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_cancel_after_client_done_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_cancel_after_client_done_nosec_test || ( echo test h2_uds_cancel_after_client_done_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_cancel_after_invoke_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_cancel_after_invoke_nosec_test || ( echo test h2_uds_cancel_after_invoke_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_cancel_before_invoke_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_cancel_before_invoke_nosec_test || ( echo test h2_uds_cancel_before_invoke_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_cancel_in_a_vacuum_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_cancel_in_a_vacuum_nosec_test || ( echo test h2_uds_cancel_in_a_vacuum_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_census_simple_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_census_simple_request_nosec_test || ( echo test h2_uds_census_simple_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_channel_connectivity_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_channel_connectivity_nosec_test || ( echo test h2_uds_channel_connectivity_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_compressed_payload_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_compressed_payload_nosec_test || ( echo test h2_uds_compressed_payload_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_disappearing_server_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_disappearing_server_nosec_test || ( echo test h2_uds_disappearing_server_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_empty_batch_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_empty_batch_nosec_test || ( echo test h2_uds_empty_batch_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_graceful_server_shutdown_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_graceful_server_shutdown_nosec_test || ( echo test h2_uds_graceful_server_shutdown_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_high_initial_seqno_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_high_initial_seqno_nosec_test || ( echo test h2_uds_high_initial_seqno_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_invoke_large_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_invoke_large_request_nosec_test || ( echo test h2_uds_invoke_large_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_large_metadata_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_large_metadata_nosec_test || ( echo test h2_uds_large_metadata_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_max_concurrent_streams_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_max_concurrent_streams_nosec_test || ( echo test h2_uds_max_concurrent_streams_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_max_message_length_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_max_message_length_nosec_test || ( echo test h2_uds_max_message_length_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_metadata_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_metadata_nosec_test || ( echo test h2_uds_metadata_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_no_op_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_no_op_nosec_test || ( echo test h2_uds_no_op_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_payload_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_payload_nosec_test || ( echo test h2_uds_payload_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_ping_pong_streaming_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_ping_pong_streaming_nosec_test || ( echo test h2_uds_ping_pong_streaming_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_registered_call_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_registered_call_nosec_test || ( echo test h2_uds_registered_call_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_request_with_flags_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_request_with_flags_nosec_test || ( echo test h2_uds_request_with_flags_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_request_with_payload_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_request_with_payload_nosec_test || ( echo test h2_uds_request_with_payload_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_server_finishes_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_server_finishes_request_nosec_test || ( echo test h2_uds_server_finishes_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_shutdown_finishes_calls_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_shutdown_finishes_calls_nosec_test || ( echo test h2_uds_shutdown_finishes_calls_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_shutdown_finishes_tags_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_shutdown_finishes_tags_nosec_test || ( echo test h2_uds_shutdown_finishes_tags_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_simple_delayed_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_simple_delayed_request_nosec_test || ( echo test h2_uds_simple_delayed_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_simple_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_simple_request_nosec_test || ( echo test h2_uds_simple_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds_trailing_metadata_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds_trailing_metadata_nosec_test || ( echo test h2_uds_trailing_metadata_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_bad_hostname_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_bad_hostname_nosec_test || ( echo test h2_uds+poll_bad_hostname_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_binary_metadata_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_binary_metadata_nosec_test || ( echo test h2_uds+poll_binary_metadata_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_cancel_after_accept_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_accept_nosec_test || ( echo test h2_uds+poll_cancel_after_accept_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_cancel_after_client_done_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_client_done_nosec_test || ( echo test h2_uds+poll_cancel_after_client_done_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_cancel_after_invoke_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_invoke_nosec_test || ( echo test h2_uds+poll_cancel_after_invoke_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_cancel_before_invoke_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_before_invoke_nosec_test || ( echo test h2_uds+poll_cancel_before_invoke_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_cancel_in_a_vacuum_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_in_a_vacuum_nosec_test || ( echo test h2_uds+poll_cancel_in_a_vacuum_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_census_simple_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_census_simple_request_nosec_test || ( echo test h2_uds+poll_census_simple_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_channel_connectivity_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_channel_connectivity_nosec_test || ( echo test h2_uds+poll_channel_connectivity_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_compressed_payload_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_compressed_payload_nosec_test || ( echo test h2_uds+poll_compressed_payload_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_disappearing_server_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_disappearing_server_nosec_test || ( echo test h2_uds+poll_disappearing_server_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_empty_batch_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_empty_batch_nosec_test || ( echo test h2_uds+poll_empty_batch_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_graceful_server_shutdown_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_graceful_server_shutdown_nosec_test || ( echo test h2_uds+poll_graceful_server_shutdown_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_high_initial_seqno_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_high_initial_seqno_nosec_test || ( echo test h2_uds+poll_high_initial_seqno_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_invoke_large_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_invoke_large_request_nosec_test || ( echo test h2_uds+poll_invoke_large_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_large_metadata_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_large_metadata_nosec_test || ( echo test h2_uds+poll_large_metadata_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_max_concurrent_streams_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_max_concurrent_streams_nosec_test || ( echo test h2_uds+poll_max_concurrent_streams_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_max_message_length_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_max_message_length_nosec_test || ( echo test h2_uds+poll_max_message_length_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_metadata_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_metadata_nosec_test || ( echo test h2_uds+poll_metadata_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_no_op_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_no_op_nosec_test || ( echo test h2_uds+poll_no_op_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_payload_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_payload_nosec_test || ( echo test h2_uds+poll_payload_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_ping_pong_streaming_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_ping_pong_streaming_nosec_test || ( echo test h2_uds+poll_ping_pong_streaming_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_registered_call_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_registered_call_nosec_test || ( echo test h2_uds+poll_registered_call_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_request_with_flags_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_request_with_flags_nosec_test || ( echo test h2_uds+poll_request_with_flags_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_request_with_payload_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_request_with_payload_nosec_test || ( echo test h2_uds+poll_request_with_payload_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_server_finishes_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_server_finishes_request_nosec_test || ( echo test h2_uds+poll_server_finishes_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_shutdown_finishes_calls_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_shutdown_finishes_calls_nosec_test || ( echo test h2_uds+poll_shutdown_finishes_calls_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_shutdown_finishes_tags_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_shutdown_finishes_tags_nosec_test || ( echo test h2_uds+poll_shutdown_finishes_tags_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_simple_delayed_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_simple_delayed_request_nosec_test || ( echo test h2_uds+poll_simple_delayed_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_simple_request_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_simple_request_nosec_test || ( echo test h2_uds+poll_simple_request_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_uds+poll_trailing_metadata_nosec_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_uds+poll_trailing_metadata_nosec_test || ( echo test h2_uds+poll_trailing_metadata_nosec_test failed ; exit 1 ) + $(E) "[RUN] Testing connection_prefix_bad_client_test" + $(Q) $(BINDIR)/$(CONFIG)/connection_prefix_bad_client_test || ( echo test connection_prefix_bad_client_test failed ; exit 1 ) + $(E) "[RUN] Testing initial_settings_frame_bad_client_test" + $(Q) $(BINDIR)/$(CONFIG)/initial_settings_frame_bad_client_test || ( echo test initial_settings_frame_bad_client_test failed ; exit 1 ) + + +flaky_test_c: buildtests_c + + +test_cxx: test_zookeeper buildtests_cxx + $(E) "[RUN] Testing async_end2end_test" + $(Q) $(BINDIR)/$(CONFIG)/async_end2end_test || ( echo test async_end2end_test failed ; exit 1 ) + $(E) "[RUN] Testing async_streaming_ping_pong_test" + $(Q) $(BINDIR)/$(CONFIG)/async_streaming_ping_pong_test || ( echo test async_streaming_ping_pong_test failed ; exit 1 ) + $(E) "[RUN] Testing async_unary_ping_pong_test" + $(Q) $(BINDIR)/$(CONFIG)/async_unary_ping_pong_test || ( echo test async_unary_ping_pong_test failed ; exit 1 ) + $(E) "[RUN] Testing auth_property_iterator_test" + $(Q) $(BINDIR)/$(CONFIG)/auth_property_iterator_test || ( echo test auth_property_iterator_test failed ; exit 1 ) + $(E) "[RUN] Testing channel_arguments_test" + $(Q) $(BINDIR)/$(CONFIG)/channel_arguments_test || ( echo test channel_arguments_test failed ; exit 1 ) + $(E) "[RUN] Testing cli_call_test" + $(Q) $(BINDIR)/$(CONFIG)/cli_call_test || ( echo test cli_call_test failed ; exit 1 ) + $(E) "[RUN] Testing client_crash_test" + $(Q) $(BINDIR)/$(CONFIG)/client_crash_test || ( echo test client_crash_test failed ; exit 1 ) + $(E) "[RUN] Testing credentials_test" + $(Q) $(BINDIR)/$(CONFIG)/credentials_test || ( echo test credentials_test failed ; exit 1 ) + $(E) "[RUN] Testing cxx_byte_buffer_test" + $(Q) $(BINDIR)/$(CONFIG)/cxx_byte_buffer_test || ( echo test cxx_byte_buffer_test failed ; exit 1 ) + $(E) "[RUN] Testing cxx_slice_test" + $(Q) $(BINDIR)/$(CONFIG)/cxx_slice_test || ( echo test cxx_slice_test failed ; exit 1 ) + $(E) "[RUN] Testing cxx_string_ref_test" + $(Q) $(BINDIR)/$(CONFIG)/cxx_string_ref_test || ( echo test cxx_string_ref_test failed ; exit 1 ) + $(E) "[RUN] Testing cxx_time_test" + $(Q) $(BINDIR)/$(CONFIG)/cxx_time_test || ( echo test cxx_time_test failed ; exit 1 ) + $(E) "[RUN] Testing end2end_test" + $(Q) $(BINDIR)/$(CONFIG)/end2end_test || ( echo test end2end_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 interop_test" + $(Q) $(BINDIR)/$(CONFIG)/interop_test || ( echo test interop_test failed ; exit 1 ) + $(E) "[RUN] Testing mock_test" + $(Q) $(BINDIR)/$(CONFIG)/mock_test || ( echo test mock_test failed ; exit 1 ) + $(E) "[RUN] Testing qps_openloop_test" + $(Q) $(BINDIR)/$(CONFIG)/qps_openloop_test || ( echo test qps_openloop_test failed ; exit 1 ) + $(E) "[RUN] Testing qps_test" + $(Q) $(BINDIR)/$(CONFIG)/qps_test || ( echo test qps_test failed ; exit 1 ) + $(E) "[RUN] Testing secure_auth_context_test" + $(Q) $(BINDIR)/$(CONFIG)/secure_auth_context_test || ( echo test secure_auth_context_test failed ; exit 1 ) + $(E) "[RUN] Testing server_crash_test" + $(Q) $(BINDIR)/$(CONFIG)/server_crash_test || ( echo test server_crash_test failed ; exit 1 ) + $(E) "[RUN] Testing shutdown_test" + $(Q) $(BINDIR)/$(CONFIG)/shutdown_test || ( echo test shutdown_test failed ; exit 1 ) + $(E) "[RUN] Testing status_test" + $(Q) $(BINDIR)/$(CONFIG)/status_test || ( echo test status_test failed ; exit 1 ) + $(E) "[RUN] Testing streaming_throughput_test" + $(Q) $(BINDIR)/$(CONFIG)/streaming_throughput_test || ( echo test streaming_throughput_test failed ; exit 1 ) + $(E) "[RUN] Testing sync_streaming_ping_pong_test" + $(Q) $(BINDIR)/$(CONFIG)/sync_streaming_ping_pong_test || ( echo test sync_streaming_ping_pong_test failed ; exit 1 ) + $(E) "[RUN] Testing sync_unary_ping_pong_test" + $(Q) $(BINDIR)/$(CONFIG)/sync_unary_ping_pong_test || ( echo test sync_unary_ping_pong_test failed ; exit 1 ) + $(E) "[RUN] Testing thread_stress_test" + $(Q) $(BINDIR)/$(CONFIG)/thread_stress_test || ( echo test thread_stress_test failed ; exit 1 ) + + +flaky_test_cxx: buildtests_cxx + + +ifeq ($(HAS_ZOOKEEPER),true) +test_zookeeper: buildtests_zookeeper + $(E) "[RUN] Testing zookeeper_test" + $(Q) $(BINDIR)/$(CONFIG)/zookeeper_test || ( echo test zookeeper_test failed ; exit 1 ) + + +flaky_test_zookeeper: buildtests_zookeeper + +else +test_zookeeper: +flaky_test_zookeeper: +endif + + +test_python: static_c + $(E) "[RUN] Testing python code" + $(Q) tools/run_tests/run_tests.py -lpython -c$(CONFIG) + + +tools: tools_c tools_cxx + + +tools_c: privatelibs_c $(BINDIR)/$(CONFIG)/gen_hpack_tables $(BINDIR)/$(CONFIG)/gen_legal_metadata_characters $(BINDIR)/$(CONFIG)/grpc_create_jwt $(BINDIR)/$(CONFIG)/grpc_fetch_oauth2 $(BINDIR)/$(CONFIG)/grpc_print_google_default_creds_token $(BINDIR)/$(CONFIG)/grpc_verify_jwt + +tools_cxx: privatelibs_cxx + +buildbenchmarks: privatelibs $(BINDIR)/$(CONFIG)/low_level_ping_pong_benchmark $(BINDIR)/$(CONFIG)/qps_driver $(BINDIR)/$(CONFIG)/qps_worker + +benchmarks: buildbenchmarks + +strip: strip-static strip-shared + +strip-static: strip-static_c strip-static_cxx + +strip-shared: strip-shared_c strip-shared_cxx + + +# TODO(nnoble): the strip target is stripping in-place, instead +# of copying files in a temporary folder. +# This prevents proper debugging after running make install. + +strip-static_c: static_c +ifeq ($(CONFIG),opt) + $(E) "[STRIP] Stripping libgpr.a" + $(Q) $(STRIP) $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[STRIP] Stripping libgrpc.a" + $(Q) $(STRIP) $(LIBDIR)/$(CONFIG)/libgrpc.a + $(E) "[STRIP] Stripping libgrpc_unsecure.a" + $(Q) $(STRIP) $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a +ifeq ($(HAS_ZOOKEEPER),true) + $(E) "[STRIP] Stripping libgrpc_zookeeper.a" + $(Q) $(STRIP) $(LIBDIR)/$(CONFIG)/libgrpc_zookeeper.a +endif +endif + +strip-static_cxx: static_cxx +ifeq ($(CONFIG),opt) + $(E) "[STRIP] Stripping libgrpc++.a" + $(Q) $(STRIP) $(LIBDIR)/$(CONFIG)/libgrpc++.a + $(E) "[STRIP] Stripping libgrpc++_unsecure.a" + $(Q) $(STRIP) $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.a +endif + +strip-shared_c: shared_c +ifeq ($(CONFIG),opt) + $(E) "[STRIP] Stripping libgpr.so" + $(Q) $(STRIP) $(LIBDIR)/$(CONFIG)/libgpr.$(SHARED_EXT) + $(E) "[STRIP] Stripping libgrpc.so" + $(Q) $(STRIP) $(LIBDIR)/$(CONFIG)/libgrpc.$(SHARED_EXT) + $(E) "[STRIP] Stripping libgrpc_unsecure.so" + $(Q) $(STRIP) $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.$(SHARED_EXT) +ifeq ($(HAS_ZOOKEEPER),true) + $(E) "[STRIP] Stripping libgrpc_zookeeper.so" + $(Q) $(STRIP) $(LIBDIR)/$(CONFIG)/libgrpc_zookeeper.$(SHARED_EXT) +endif +endif + +strip-shared_cxx: shared_cxx +ifeq ($(CONFIG),opt) + $(E) "[STRIP] Stripping libgrpc++.so" + $(Q) $(STRIP) $(LIBDIR)/$(CONFIG)/libgrpc++.$(SHARED_EXT) + $(E) "[STRIP] Stripping libgrpc++_unsecure.so" + $(Q) $(STRIP) $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.$(SHARED_EXT) +endif + +strip-shared_csharp: shared_csharp +ifeq ($(CONFIG),opt) + $(E) "[STRIP] Stripping libgrpc_csharp_ext.so" + $(Q) $(STRIP) $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext.$(SHARED_EXT) +endif + +cache.mk:: + $(E) "[MAKE] Generating $@" + $(Q) echo "$(CACHE_MK)" | tr , '\n' >$@ + +$(LIBDIR)/$(CONFIG)/pkgconfig/gpr.pc: + $(E) "[MAKE] Generating $@" + $(Q) mkdir -p $(@D) + $(Q) echo "$(GPR_PC_FILE)" | tr , '\n' >$@ + +$(LIBDIR)/$(CONFIG)/pkgconfig/grpc.pc: + $(E) "[MAKE] Generating $@" + $(Q) mkdir -p $(@D) + $(Q) echo "$(GRPC_PC_FILE)" | tr , '\n' >$@ + +$(LIBDIR)/$(CONFIG)/pkgconfig/grpc_unsecure.pc: + $(E) "[MAKE] Generating $@" + $(Q) mkdir -p $(@D) + $(Q) echo "$(GRPC_UNSECURE_PC_FILE)" | tr , '\n' >$@ + +$(LIBDIR)/$(CONFIG)/pkgconfig/grpc_zookeeper.pc: + $(E) "[MAKE] Generating $@" + $(Q) mkdir -p $(@D) + $(Q) echo -e "$(GRPC_ZOOKEEPER_PC_FILE)" >$@ + +$(LIBDIR)/$(CONFIG)/pkgconfig/grpc++.pc: + $(E) "[MAKE] Generating $@" + $(Q) mkdir -p $(@D) + $(Q) echo "$(GRPCXX_PC_FILE)" | tr , '\n' >$@ + +$(LIBDIR)/$(CONFIG)/pkgconfig/grpc++_unsecure.pc: + $(E) "[MAKE] Generating $@" + $(Q) mkdir -p $(@D) + $(Q) echo "$(GRPCXX_UNSECURE_PC_FILE)" | tr , '\n' >$@ + +ifeq ($(NO_PROTOC),true) +$(GENDIR)/test/cpp/qps/perf_db.pb.cc: protoc_dep_error +$(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc: protoc_dep_error +else +$(GENDIR)/test/cpp/qps/perf_db.pb.cc: test/cpp/qps/perf_db.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) + $(E) "[PROTOC] Generating protobuf CC file from $<" + $(Q) mkdir -p `dirname $@` + $(Q) $(PROTOC) --cpp_out=$(GENDIR) $< + +$(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc: test/cpp/qps/perf_db.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) + $(E) "[GRPC] Generating gRPC's protobuf service CC file from $<" + $(Q) mkdir -p `dirname $@` + $(Q) $(PROTOC) --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(BINDIR)/$(CONFIG)/grpc_cpp_plugin $< +endif + +ifeq ($(NO_PROTOC),true) +$(GENDIR)/test/cpp/qps/qpstest.pb.cc: protoc_dep_error +$(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc: protoc_dep_error +else +$(GENDIR)/test/cpp/qps/qpstest.pb.cc: test/cpp/qps/qpstest.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) + $(E) "[PROTOC] Generating protobuf CC file from $<" + $(Q) mkdir -p `dirname $@` + $(Q) $(PROTOC) --cpp_out=$(GENDIR) $< + +$(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc: test/cpp/qps/qpstest.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) + $(E) "[GRPC] Generating gRPC's protobuf service CC file from $<" + $(Q) mkdir -p `dirname $@` + $(Q) $(PROTOC) --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(BINDIR)/$(CONFIG)/grpc_cpp_plugin $< +endif + +ifeq ($(NO_PROTOC),true) +$(GENDIR)/test/cpp/util/echo.pb.cc: protoc_dep_error +$(GENDIR)/test/cpp/util/echo.grpc.pb.cc: protoc_dep_error +else +$(GENDIR)/test/cpp/util/echo.pb.cc: test/cpp/util/echo.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) + $(E) "[PROTOC] Generating protobuf CC file from $<" + $(Q) mkdir -p `dirname $@` + $(Q) $(PROTOC) --cpp_out=$(GENDIR) $< + +$(GENDIR)/test/cpp/util/echo.grpc.pb.cc: test/cpp/util/echo.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) + $(E) "[GRPC] Generating gRPC's protobuf service CC file from $<" + $(Q) mkdir -p `dirname $@` + $(Q) $(PROTOC) --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(BINDIR)/$(CONFIG)/grpc_cpp_plugin $< +endif + +ifeq ($(NO_PROTOC),true) +$(GENDIR)/test/cpp/util/echo_duplicate.pb.cc: protoc_dep_error +$(GENDIR)/test/cpp/util/echo_duplicate.grpc.pb.cc: protoc_dep_error +else +$(GENDIR)/test/cpp/util/echo_duplicate.pb.cc: test/cpp/util/echo_duplicate.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) + $(E) "[PROTOC] Generating protobuf CC file from $<" + $(Q) mkdir -p `dirname $@` + $(Q) $(PROTOC) --cpp_out=$(GENDIR) $< + +$(GENDIR)/test/cpp/util/echo_duplicate.grpc.pb.cc: test/cpp/util/echo_duplicate.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) + $(E) "[GRPC] Generating gRPC's protobuf service CC file from $<" + $(Q) mkdir -p `dirname $@` + $(Q) $(PROTOC) --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(BINDIR)/$(CONFIG)/grpc_cpp_plugin $< +endif + +ifeq ($(NO_PROTOC),true) +$(GENDIR)/test/cpp/util/messages.pb.cc: protoc_dep_error +$(GENDIR)/test/cpp/util/messages.grpc.pb.cc: protoc_dep_error +else +$(GENDIR)/test/cpp/util/messages.pb.cc: test/cpp/util/messages.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) + $(E) "[PROTOC] Generating protobuf CC file from $<" + $(Q) mkdir -p `dirname $@` + $(Q) $(PROTOC) --cpp_out=$(GENDIR) $< + +$(GENDIR)/test/cpp/util/messages.grpc.pb.cc: test/cpp/util/messages.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) + $(E) "[GRPC] Generating gRPC's protobuf service CC file from $<" + $(Q) mkdir -p `dirname $@` + $(Q) $(PROTOC) --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(BINDIR)/$(CONFIG)/grpc_cpp_plugin $< +endif + +ifeq ($(NO_PROTOC),true) +$(GENDIR)/test/proto/empty.pb.cc: protoc_dep_error +$(GENDIR)/test/proto/empty.grpc.pb.cc: protoc_dep_error +else +$(GENDIR)/test/proto/empty.pb.cc: test/proto/empty.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) + $(E) "[PROTOC] Generating protobuf CC file from $<" + $(Q) mkdir -p `dirname $@` + $(Q) $(PROTOC) --cpp_out=$(GENDIR) $< + +$(GENDIR)/test/proto/empty.grpc.pb.cc: test/proto/empty.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) + $(E) "[GRPC] Generating gRPC's protobuf service CC file from $<" + $(Q) mkdir -p `dirname $@` + $(Q) $(PROTOC) --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(BINDIR)/$(CONFIG)/grpc_cpp_plugin $< +endif + +ifeq ($(NO_PROTOC),true) +$(GENDIR)/test/proto/messages.pb.cc: protoc_dep_error +$(GENDIR)/test/proto/messages.grpc.pb.cc: protoc_dep_error +else +$(GENDIR)/test/proto/messages.pb.cc: test/proto/messages.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) + $(E) "[PROTOC] Generating protobuf CC file from $<" + $(Q) mkdir -p `dirname $@` + $(Q) $(PROTOC) --cpp_out=$(GENDIR) $< + +$(GENDIR)/test/proto/messages.grpc.pb.cc: test/proto/messages.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) + $(E) "[GRPC] Generating gRPC's protobuf service CC file from $<" + $(Q) mkdir -p `dirname $@` + $(Q) $(PROTOC) --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(BINDIR)/$(CONFIG)/grpc_cpp_plugin $< +endif + +ifeq ($(NO_PROTOC),true) +$(GENDIR)/test/proto/test.pb.cc: protoc_dep_error +$(GENDIR)/test/proto/test.grpc.pb.cc: protoc_dep_error +else +$(GENDIR)/test/proto/test.pb.cc: test/proto/test.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) + $(E) "[PROTOC] Generating protobuf CC file from $<" + $(Q) mkdir -p `dirname $@` + $(Q) $(PROTOC) --cpp_out=$(GENDIR) $< + +$(GENDIR)/test/proto/test.grpc.pb.cc: test/proto/test.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) + $(E) "[GRPC] Generating gRPC's protobuf service CC file from $<" + $(Q) mkdir -p `dirname $@` + $(Q) $(PROTOC) --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(BINDIR)/$(CONFIG)/grpc_cpp_plugin $< +endif + + +ifeq ($(CONFIG),stapprof) +src/core/profiling/stap_timers.c: $(GENDIR)/src/core/profiling/stap_probes.h +ifeq ($(HAS_SYSTEMTAP),true) +$(GENDIR)/src/core/profiling/stap_probes.h: src/core/profiling/stap_probes.d + $(E) "[DTRACE] Compiling $<" + $(Q) mkdir -p `dirname $@` + $(Q) $(DTRACE) -C -h -s $< -o $@ +else +$(GENDIR)/src/core/profiling/stap_probes.h: systemtap_dep_error stop +endif +endif + +$(OBJDIR)/$(CONFIG)/%.o : %.c + $(E) "[C] Compiling $<" + $(Q) mkdir -p `dirname $@` + $(Q) $(CC) $(CFLAGS) $(CPPFLAGS) -MMD -MF $(addsuffix .dep, $(basename $@)) -c -o $@ $< + +$(OBJDIR)/$(CONFIG)/%.o : $(GENDIR)/%.pb.cc + $(E) "[CXX] Compiling $<" + $(Q) mkdir -p `dirname $@` + $(Q) $(CXX) $(CXXFLAGS) $(CPPFLAGS) -MMD -MF $(addsuffix .dep, $(basename $@)) -c -o $@ $< + +$(OBJDIR)/$(CONFIG)/src/compiler/%.o : src/compiler/%.cc + $(E) "[HOSTCXX] Compiling $<" + $(Q) mkdir -p `dirname $@` + $(Q) $(HOST_CXX) $(HOST_CXXFLAGS) $(HOST_CPPFLAGS) -MMD -MF $(addsuffix .dep, $(basename $@)) -c -o $@ $< + +$(OBJDIR)/$(CONFIG)/%.o : %.cc + $(E) "[CXX] Compiling $<" + $(Q) mkdir -p `dirname $@` + $(Q) $(CXX) $(CXXFLAGS) $(CPPFLAGS) -MMD -MF $(addsuffix .dep, $(basename $@)) -c -o $@ $< + +install: install_c install_cxx install-plugins install-certs verify-install + +install_c: install-headers_c install-static_c install-shared_c + +install_cxx: install-headers_cxx install-static_cxx install-shared_cxx + +install_csharp: install-shared_csharp install_c + +install_grpc_csharp_ext: install_csharp + +install-headers: install-headers_c install-headers_cxx + +install-headers_c: + $(E) "[INSTALL] Installing public C headers" + $(Q) $(foreach h, $(PUBLIC_HEADERS_C), $(INSTALL) -d $(prefix)/$(dir $(h)) && ) exit 0 || exit 1 + $(Q) $(foreach h, $(PUBLIC_HEADERS_C), $(INSTALL) $(h) $(prefix)/$(h) && ) exit 0 || exit 1 + +install-headers_cxx: + $(E) "[INSTALL] Installing public C++ headers" + $(Q) $(foreach h, $(PUBLIC_HEADERS_CXX), $(INSTALL) -d $(prefix)/$(dir $(h)) && ) exit 0 || exit 1 + $(Q) $(foreach h, $(PUBLIC_HEADERS_CXX), $(INSTALL) $(h) $(prefix)/$(h) && ) exit 0 || exit 1 + +install-static: install-static_c install-static_cxx + +install-static_c: static_c strip-static_c install-pkg-config_c + $(E) "[INSTALL] Installing libgpr.a" + $(Q) $(INSTALL) -d $(prefix)/lib + $(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgpr.a $(prefix)/lib/libgpr.a + $(E) "[INSTALL] Installing libgrpc.a" + $(Q) $(INSTALL) -d $(prefix)/lib + $(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc.a $(prefix)/lib/libgrpc.a + $(E) "[INSTALL] Installing libgrpc_unsecure.a" + $(Q) $(INSTALL) -d $(prefix)/lib + $(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(prefix)/lib/libgrpc_unsecure.a +ifeq ($(HAS_ZOOKEEPER),true) + $(E) "[INSTALL] Installing libgrpc_zookeeper.a" + $(Q) $(INSTALL) -d $(prefix)/lib + $(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc_zookeeper.a $(prefix)/lib/libgrpc_zookeeper.a +endif + +install-static_cxx: static_cxx strip-static_cxx install-pkg-config_cxx + $(E) "[INSTALL] Installing libgrpc++.a" + $(Q) $(INSTALL) -d $(prefix)/lib + $(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc++.a $(prefix)/lib/libgrpc++.a + $(E) "[INSTALL] Installing libgrpc++_unsecure.a" + $(Q) $(INSTALL) -d $(prefix)/lib + $(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.a $(prefix)/lib/libgrpc++_unsecure.a + + + +install-shared_c: shared_c strip-shared_c install-pkg-config_c +ifeq ($(SYSTEM),MINGW32) + $(E) "[INSTALL] Installing gpr.$(SHARED_EXT)" + $(Q) $(INSTALL) -d $(prefix)/lib + $(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/gpr.$(SHARED_EXT) $(prefix)/lib/gpr.$(SHARED_EXT) + $(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgpr-imp.a $(prefix)/lib/libgpr-imp.a +else + $(E) "[INSTALL] Installing libgpr.$(SHARED_EXT)" + $(Q) $(INSTALL) -d $(prefix)/lib + $(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgpr.$(SHARED_EXT) $(prefix)/lib/libgpr.$(SHARED_EXT) +ifneq ($(SYSTEM),Darwin) + $(Q) ln -sf libgpr.$(SHARED_EXT) $(prefix)/lib/libgpr.so.0 + $(Q) ln -sf libgpr.$(SHARED_EXT) $(prefix)/lib/libgpr.so +endif +endif +ifeq ($(SYSTEM),MINGW32) + $(E) "[INSTALL] Installing grpc.$(SHARED_EXT)" + $(Q) $(INSTALL) -d $(prefix)/lib + $(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/grpc.$(SHARED_EXT) $(prefix)/lib/grpc.$(SHARED_EXT) + $(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc-imp.a $(prefix)/lib/libgrpc-imp.a +else + $(E) "[INSTALL] Installing libgrpc.$(SHARED_EXT)" + $(Q) $(INSTALL) -d $(prefix)/lib + $(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc.$(SHARED_EXT) $(prefix)/lib/libgrpc.$(SHARED_EXT) +ifneq ($(SYSTEM),Darwin) + $(Q) ln -sf libgrpc.$(SHARED_EXT) $(prefix)/lib/libgrpc.so.0 + $(Q) ln -sf libgrpc.$(SHARED_EXT) $(prefix)/lib/libgrpc.so +endif +endif +ifeq ($(SYSTEM),MINGW32) + $(E) "[INSTALL] Installing grpc_unsecure.$(SHARED_EXT)" + $(Q) $(INSTALL) -d $(prefix)/lib + $(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/grpc_unsecure.$(SHARED_EXT) $(prefix)/lib/grpc_unsecure.$(SHARED_EXT) + $(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc_unsecure-imp.a $(prefix)/lib/libgrpc_unsecure-imp.a +else + $(E) "[INSTALL] Installing libgrpc_unsecure.$(SHARED_EXT)" + $(Q) $(INSTALL) -d $(prefix)/lib + $(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.$(SHARED_EXT) $(prefix)/lib/libgrpc_unsecure.$(SHARED_EXT) +ifneq ($(SYSTEM),Darwin) + $(Q) ln -sf libgrpc_unsecure.$(SHARED_EXT) $(prefix)/lib/libgrpc_unsecure.so.0 + $(Q) ln -sf libgrpc_unsecure.$(SHARED_EXT) $(prefix)/lib/libgrpc_unsecure.so +endif +endif +ifeq ($(HAS_ZOOKEEPER),true) +ifeq ($(SYSTEM),MINGW32) + $(E) "[INSTALL] Installing grpc_zookeeper.$(SHARED_EXT)" + $(Q) $(INSTALL) -d $(prefix)/lib + $(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/grpc_zookeeper.$(SHARED_EXT) $(prefix)/lib/grpc_zookeeper.$(SHARED_EXT) + $(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc_zookeeper-imp.a $(prefix)/lib/libgrpc_zookeeper-imp.a +else + $(E) "[INSTALL] Installing libgrpc_zookeeper.$(SHARED_EXT)" + $(Q) $(INSTALL) -d $(prefix)/lib + $(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc_zookeeper.$(SHARED_EXT) $(prefix)/lib/libgrpc_zookeeper.$(SHARED_EXT) +ifneq ($(SYSTEM),Darwin) + $(Q) ln -sf libgrpc_zookeeper.$(SHARED_EXT) $(prefix)/lib/libgrpc_zookeeper.so.0 + $(Q) ln -sf libgrpc_zookeeper.$(SHARED_EXT) $(prefix)/lib/libgrpc_zookeeper.so +endif +endif +endif +ifneq ($(SYSTEM),MINGW32) +ifneq ($(SYSTEM),Darwin) + $(Q) ldconfig || true +endif +endif + + +install-shared_cxx: shared_cxx strip-shared_cxx install-shared_c install-pkg-config_cxx +ifeq ($(SYSTEM),MINGW32) + $(E) "[INSTALL] Installing grpc++.$(SHARED_EXT)" + $(Q) $(INSTALL) -d $(prefix)/lib + $(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/grpc++.$(SHARED_EXT) $(prefix)/lib/grpc++.$(SHARED_EXT) + $(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc++-imp.a $(prefix)/lib/libgrpc++-imp.a +else + $(E) "[INSTALL] Installing libgrpc++.$(SHARED_EXT)" + $(Q) $(INSTALL) -d $(prefix)/lib + $(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc++.$(SHARED_EXT) $(prefix)/lib/libgrpc++.$(SHARED_EXT) +ifneq ($(SYSTEM),Darwin) + $(Q) ln -sf libgrpc++.$(SHARED_EXT) $(prefix)/lib/libgrpc++.so.0 + $(Q) ln -sf libgrpc++.$(SHARED_EXT) $(prefix)/lib/libgrpc++.so +endif +endif +ifeq ($(SYSTEM),MINGW32) + $(E) "[INSTALL] Installing grpc++_unsecure.$(SHARED_EXT)" + $(Q) $(INSTALL) -d $(prefix)/lib + $(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/grpc++_unsecure.$(SHARED_EXT) $(prefix)/lib/grpc++_unsecure.$(SHARED_EXT) + $(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure-imp.a $(prefix)/lib/libgrpc++_unsecure-imp.a +else + $(E) "[INSTALL] Installing libgrpc++_unsecure.$(SHARED_EXT)" + $(Q) $(INSTALL) -d $(prefix)/lib + $(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.$(SHARED_EXT) $(prefix)/lib/libgrpc++_unsecure.$(SHARED_EXT) +ifneq ($(SYSTEM),Darwin) + $(Q) ln -sf libgrpc++_unsecure.$(SHARED_EXT) $(prefix)/lib/libgrpc++_unsecure.so.0 + $(Q) ln -sf libgrpc++_unsecure.$(SHARED_EXT) $(prefix)/lib/libgrpc++_unsecure.so +endif +endif +ifeq ($(HAS_ZOOKEEPER),true) +endif +ifneq ($(SYSTEM),MINGW32) +ifneq ($(SYSTEM),Darwin) + $(Q) ldconfig || true +endif +endif + + +install-shared_csharp: shared_csharp strip-shared_csharp +ifeq ($(SYSTEM),MINGW32) + $(E) "[INSTALL] Installing grpc_csharp_ext.$(SHARED_EXT)" + $(Q) $(INSTALL) -d $(prefix)/lib + $(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/grpc_csharp_ext.$(SHARED_EXT) $(prefix)/lib/grpc_csharp_ext.$(SHARED_EXT) + $(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext-imp.a $(prefix)/lib/libgrpc_csharp_ext-imp.a +else + $(E) "[INSTALL] Installing libgrpc_csharp_ext.$(SHARED_EXT)" + $(Q) $(INSTALL) -d $(prefix)/lib + $(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext.$(SHARED_EXT) $(prefix)/lib/libgrpc_csharp_ext.$(SHARED_EXT) +ifneq ($(SYSTEM),Darwin) + $(Q) ln -sf libgrpc_csharp_ext.$(SHARED_EXT) $(prefix)/lib/libgrpc_csharp_ext.so.0 + $(Q) ln -sf libgrpc_csharp_ext.$(SHARED_EXT) $(prefix)/lib/libgrpc_csharp_ext.so +endif +endif +ifeq ($(HAS_ZOOKEEPER),true) +endif +ifneq ($(SYSTEM),MINGW32) +ifneq ($(SYSTEM),Darwin) + $(Q) ldconfig || true +endif +endif + + +install-plugins: $(PROTOC_PLUGINS) +ifeq ($(SYSTEM),MINGW32) + $(Q) false +else + $(E) "[INSTALL] Installing grpc protoc plugins" + $(Q) $(INSTALL) -d $(prefix)/bin + $(Q) $(INSTALL) $(BINDIR)/$(CONFIG)/grpc_cpp_plugin $(prefix)/bin/grpc_cpp_plugin + $(Q) $(INSTALL) -d $(prefix)/bin + $(Q) $(INSTALL) $(BINDIR)/$(CONFIG)/grpc_csharp_plugin $(prefix)/bin/grpc_csharp_plugin + $(Q) $(INSTALL) -d $(prefix)/bin + $(Q) $(INSTALL) $(BINDIR)/$(CONFIG)/grpc_objective_c_plugin $(prefix)/bin/grpc_objective_c_plugin + $(Q) $(INSTALL) -d $(prefix)/bin + $(Q) $(INSTALL) $(BINDIR)/$(CONFIG)/grpc_python_plugin $(prefix)/bin/grpc_python_plugin + $(Q) $(INSTALL) -d $(prefix)/bin + $(Q) $(INSTALL) $(BINDIR)/$(CONFIG)/grpc_ruby_plugin $(prefix)/bin/grpc_ruby_plugin +endif + +install-pkg-config_c: pc_gpr pc_c pc_c_unsecure pc_c_zookeeper + $(E) "[INSTALL] Installing C pkg-config files" + $(Q) $(INSTALL) -d $(prefix)/lib/pkgconfig + $(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/pkgconfig/gpr.pc $(prefix)/lib/pkgconfig/gpr.pc + $(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/pkgconfig/grpc.pc $(prefix)/lib/pkgconfig/grpc.pc + $(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/pkgconfig/grpc_unsecure.pc $(prefix)/lib/pkgconfig/grpc_unsecure.pc +ifeq ($(HAS_ZOOKEEPER),true) + $(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/pkgconfig/grpc_zookeeper.pc $(prefix)/lib/pkgconfig/grpc_zookeeper.pc +endif + +install-pkg-config_cxx: pc_cxx pc_cxx_unsecure + $(E) "[INSTALL] Installing C++ pkg-config files" + $(Q) $(INSTALL) -d $(prefix)/lib/pkgconfig + $(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/pkgconfig/grpc++.pc $(prefix)/lib/pkgconfig/grpc++.pc + $(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/pkgconfig/grpc++_unsecure.pc $(prefix)/lib/pkgconfig/grpc++_unsecure.pc + +install-certs: etc/roots.pem + $(E) "[INSTALL] Installing root certificates" + $(Q) $(INSTALL) -d $(prefix)/share/grpc + $(Q) $(INSTALL) etc/roots.pem $(prefix)/share/grpc/roots.pem + +verify-install: +ifeq ($(INSTALL_OK),true) + @echo "Your system looks ready to go." + @echo +else + @echo "We couldn't find protoc 3.0.0+ installed on your system. While this" + @echo "won't prevent grpc from working, you won't be able to compile" + @echo "and run any meaningful code with it." + @echo + @echo + @echo "Please download and install protobuf 3.0.0+ from:" + @echo + @echo " https://github.com/google/protobuf/releases" + @echo + @echo "Once you've done so, or if you think this message is in error," + @echo "you can re-run this check by doing:" + @echo + @echo " make verify-install" +endif + +clean: + $(E) "[CLEAN] Cleaning build directories." + $(Q) $(RM) -rf $(OBJDIR) $(LIBDIR) $(BINDIR) $(GENDIR) cache.mk + + +# The various libraries + + +LIBGPR_SRC = \ + src/core/support/alloc.c \ + src/core/support/cmdline.c \ + src/core/support/cpu_iphone.c \ + src/core/support/cpu_linux.c \ + src/core/support/cpu_posix.c \ + src/core/support/cpu_windows.c \ + src/core/support/env_linux.c \ + src/core/support/env_posix.c \ + src/core/support/env_win32.c \ + src/core/support/file.c \ + src/core/support/file_posix.c \ + src/core/support/file_win32.c \ + src/core/support/histogram.c \ + src/core/support/host_port.c \ + src/core/support/log.c \ + src/core/support/log_android.c \ + src/core/support/log_linux.c \ + src/core/support/log_posix.c \ + src/core/support/log_win32.c \ + src/core/support/murmur_hash.c \ + src/core/support/slice.c \ + src/core/support/slice_buffer.c \ + src/core/support/stack_lockfree.c \ + src/core/support/string.c \ + src/core/support/string_posix.c \ + src/core/support/string_win32.c \ + src/core/support/subprocess_posix.c \ + src/core/support/sync.c \ + src/core/support/sync_posix.c \ + src/core/support/sync_win32.c \ + src/core/support/thd.c \ + src/core/support/thd_posix.c \ + src/core/support/thd_win32.c \ + src/core/support/time.c \ + src/core/support/time_posix.c \ + src/core/support/time_win32.c \ + src/core/support/tls_pthread.c \ + +PUBLIC_HEADERS_C += \ + include/grpc/support/alloc.h \ + include/grpc/support/atm.h \ + include/grpc/support/atm_gcc_atomic.h \ + include/grpc/support/atm_gcc_sync.h \ + include/grpc/support/atm_win32.h \ + include/grpc/support/cmdline.h \ + include/grpc/support/cpu.h \ + include/grpc/support/histogram.h \ + include/grpc/support/host_port.h \ + include/grpc/support/log.h \ + include/grpc/support/log_win32.h \ + include/grpc/support/port_platform.h \ + include/grpc/support/slice.h \ + include/grpc/support/slice_buffer.h \ + include/grpc/support/string_util.h \ + include/grpc/support/subprocess.h \ + include/grpc/support/sync.h \ + include/grpc/support/sync_generic.h \ + include/grpc/support/sync_posix.h \ + include/grpc/support/sync_win32.h \ + include/grpc/support/thd.h \ + include/grpc/support/time.h \ + include/grpc/support/tls.h \ + include/grpc/support/tls_gcc.h \ + include/grpc/support/tls_msvc.h \ + include/grpc/support/tls_pthread.h \ + include/grpc/support/useful.h \ + +LIBGPR_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGPR_SRC)))) + +$(LIBDIR)/$(CONFIG)/libgpr.a: $(ZLIB_DEP) $(LIBGPR_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libgpr.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBGPR_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libgpr.a +endif + + + +ifeq ($(SYSTEM),MINGW32) +$(LIBDIR)/$(CONFIG)/gpr.$(SHARED_EXT): $(LIBGPR_OBJS) $(ZLIB_DEP) + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,--output-def=$(LIBDIR)/$(CONFIG)/gpr.def -Wl,--out-implib=$(LIBDIR)/$(CONFIG)/libgpr-imp.a -o $(LIBDIR)/$(CONFIG)/gpr.$(SHARED_EXT) $(LIBGPR_OBJS) $(LDLIBS) +else +$(LIBDIR)/$(CONFIG)/libgpr.$(SHARED_EXT): $(LIBGPR_OBJS) $(ZLIB_DEP) + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` +ifeq ($(SYSTEM),Darwin) + $(Q) $(LD) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -install_name libgpr.$(SHARED_EXT) -dynamiclib -o $(LIBDIR)/$(CONFIG)/libgpr.$(SHARED_EXT) $(LIBGPR_OBJS) $(LDLIBS) +else + $(Q) $(LD) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,-soname,libgpr.so.0 -o $(LIBDIR)/$(CONFIG)/libgpr.$(SHARED_EXT) $(LIBGPR_OBJS) $(LDLIBS) + $(Q) ln -sf libgpr.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgpr.so.0 + $(Q) ln -sf libgpr.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgpr.so +endif +endif + +ifneq ($(NO_DEPS),true) +-include $(LIBGPR_OBJS:.o=.dep) +endif + + +LIBGPR_TEST_UTIL_SRC = \ + test/core/util/test_config.c \ + + +LIBGPR_TEST_UTIL_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGPR_TEST_UTIL_SRC)))) + +$(LIBDIR)/$(CONFIG)/libgpr_test_util.a: $(ZLIB_DEP) $(LIBGPR_TEST_UTIL_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libgpr_test_util.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBGPR_TEST_UTIL_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libgpr_test_util.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBGPR_TEST_UTIL_OBJS:.o=.dep) +endif + + +LIBGRPC_SRC = \ + src/core/httpcli/httpcli_security_connector.c \ + src/core/security/base64.c \ + src/core/security/client_auth_filter.c \ + src/core/security/credentials.c \ + src/core/security/credentials_metadata.c \ + src/core/security/credentials_posix.c \ + src/core/security/credentials_win32.c \ + src/core/security/google_default_credentials.c \ + src/core/security/json_token.c \ + src/core/security/jwt_verifier.c \ + src/core/security/secure_endpoint.c \ + src/core/security/secure_transport_setup.c \ + src/core/security/security_connector.c \ + src/core/security/security_context.c \ + src/core/security/server_auth_filter.c \ + src/core/security/server_secure_chttp2.c \ + src/core/surface/init_secure.c \ + src/core/surface/secure_channel_create.c \ + src/core/tsi/fake_transport_security.c \ + src/core/tsi/ssl_transport_security.c \ + src/core/tsi/transport_security.c \ + src/core/census/grpc_context.c \ + src/core/census/grpc_filter.c \ + src/core/channel/channel_args.c \ + src/core/channel/channel_stack.c \ + src/core/channel/client_channel.c \ + src/core/channel/compress_filter.c \ + src/core/channel/connected_channel.c \ + src/core/channel/http_client_filter.c \ + src/core/channel/http_server_filter.c \ + src/core/channel/noop_filter.c \ + src/core/client_config/client_config.c \ + src/core/client_config/connector.c \ + src/core/client_config/lb_policies/pick_first.c \ + src/core/client_config/lb_policy.c \ + src/core/client_config/resolver.c \ + src/core/client_config/resolver_factory.c \ + src/core/client_config/resolver_registry.c \ + src/core/client_config/resolvers/dns_resolver.c \ + src/core/client_config/resolvers/sockaddr_resolver.c \ + src/core/client_config/subchannel.c \ + src/core/client_config/subchannel_factory.c \ + src/core/client_config/subchannel_factory_decorators/add_channel_arg.c \ + src/core/client_config/subchannel_factory_decorators/merge_channel_args.c \ + src/core/client_config/uri_parser.c \ + src/core/compression/algorithm.c \ + src/core/compression/message_compress.c \ + src/core/debug/trace.c \ + src/core/httpcli/format_request.c \ + src/core/httpcli/httpcli.c \ + src/core/httpcli/parser.c \ + src/core/iomgr/alarm.c \ + src/core/iomgr/alarm_heap.c \ + src/core/iomgr/endpoint.c \ + src/core/iomgr/endpoint_pair_posix.c \ + src/core/iomgr/endpoint_pair_windows.c \ + src/core/iomgr/fd_posix.c \ + src/core/iomgr/iocp_windows.c \ + src/core/iomgr/iomgr.c \ + src/core/iomgr/iomgr_posix.c \ + src/core/iomgr/iomgr_windows.c \ + src/core/iomgr/pollset_multipoller_with_epoll.c \ + src/core/iomgr/pollset_multipoller_with_poll_posix.c \ + src/core/iomgr/pollset_posix.c \ + src/core/iomgr/pollset_set_posix.c \ + src/core/iomgr/pollset_set_windows.c \ + src/core/iomgr/pollset_windows.c \ + src/core/iomgr/resolve_address_posix.c \ + src/core/iomgr/resolve_address_windows.c \ + src/core/iomgr/sockaddr_utils.c \ + src/core/iomgr/socket_utils_common_posix.c \ + src/core/iomgr/socket_utils_linux.c \ + src/core/iomgr/socket_utils_posix.c \ + src/core/iomgr/socket_windows.c \ + src/core/iomgr/tcp_client_posix.c \ + src/core/iomgr/tcp_client_windows.c \ + src/core/iomgr/tcp_posix.c \ + src/core/iomgr/tcp_server_posix.c \ + src/core/iomgr/tcp_server_windows.c \ + src/core/iomgr/tcp_windows.c \ + src/core/iomgr/time_averaged_stats.c \ + src/core/iomgr/udp_server.c \ + src/core/iomgr/wakeup_fd_eventfd.c \ + src/core/iomgr/wakeup_fd_nospecial.c \ + src/core/iomgr/wakeup_fd_pipe.c \ + src/core/iomgr/wakeup_fd_posix.c \ + src/core/json/json.c \ + src/core/json/json_reader.c \ + src/core/json/json_string.c \ + src/core/json/json_writer.c \ + src/core/profiling/basic_timers.c \ + src/core/profiling/stap_timers.c \ + src/core/surface/byte_buffer.c \ + src/core/surface/byte_buffer_queue.c \ + src/core/surface/byte_buffer_reader.c \ + src/core/surface/call.c \ + src/core/surface/call_details.c \ + src/core/surface/call_log_batch.c \ + src/core/surface/channel.c \ + src/core/surface/channel_connectivity.c \ + src/core/surface/channel_create.c \ + src/core/surface/completion_queue.c \ + src/core/surface/event_string.c \ + src/core/surface/init.c \ + src/core/surface/lame_client.c \ + src/core/surface/metadata_array.c \ + src/core/surface/server.c \ + src/core/surface/server_chttp2.c \ + src/core/surface/server_create.c \ + src/core/surface/surface_trace.c \ + src/core/surface/version.c \ + src/core/transport/chttp2/alpn.c \ + src/core/transport/chttp2/bin_encoder.c \ + src/core/transport/chttp2/frame_data.c \ + src/core/transport/chttp2/frame_goaway.c \ + src/core/transport/chttp2/frame_ping.c \ + src/core/transport/chttp2/frame_rst_stream.c \ + src/core/transport/chttp2/frame_settings.c \ + src/core/transport/chttp2/frame_window_update.c \ + src/core/transport/chttp2/hpack_parser.c \ + src/core/transport/chttp2/hpack_table.c \ + src/core/transport/chttp2/huffsyms.c \ + src/core/transport/chttp2/incoming_metadata.c \ + src/core/transport/chttp2/parsing.c \ + src/core/transport/chttp2/status_conversion.c \ + src/core/transport/chttp2/stream_encoder.c \ + src/core/transport/chttp2/stream_lists.c \ + src/core/transport/chttp2/stream_map.c \ + src/core/transport/chttp2/timeout_encoding.c \ + src/core/transport/chttp2/varint.c \ + src/core/transport/chttp2/writing.c \ + src/core/transport/chttp2_transport.c \ + src/core/transport/connectivity_state.c \ + src/core/transport/metadata.c \ + src/core/transport/stream_op.c \ + src/core/transport/transport.c \ + src/core/transport/transport_op_string.c \ + src/core/census/context.c \ + src/core/census/initialize.c \ + src/core/census/operation.c \ + src/core/census/tracing.c \ + +PUBLIC_HEADERS_C += \ + include/grpc/grpc_security.h \ + include/grpc/byte_buffer.h \ + include/grpc/byte_buffer_reader.h \ + include/grpc/compression.h \ + include/grpc/grpc.h \ + include/grpc/status.h \ + include/grpc/census.h \ + +LIBGRPC_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC_SRC)))) + +ifeq ($(NO_SECURE),true) + +# You can't build secure libraries if you don't have OpenSSL. + +$(LIBDIR)/$(CONFIG)/libgrpc.a: openssl_dep_error + +ifeq ($(SYSTEM),MINGW32) +$(LIBDIR)/$(CONFIG)/grpc.$(SHARED_EXT): openssl_dep_error +else +$(LIBDIR)/$(CONFIG)/libgrpc.$(SHARED_EXT): openssl_dep_error +endif + +else + + +$(LIBDIR)/$(CONFIG)/libgrpc.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBGRPC_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libgrpc.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBGRPC_OBJS) + $(Q) rm -rf $(BUILDDIR_ABSOLUTE)/tmp-merge-grpc + $(Q) mkdir $(BUILDDIR_ABSOLUTE)/tmp-merge-grpc + $(Q) ( cd $(BUILDDIR_ABSOLUTE)/tmp-merge-grpc ; $(AR) x $(LIBDIR)/$(CONFIG)/libgrpc.a ) + $(Q) for l in $(OPENSSL_MERGE_LIBS) ; do ( cd $(BUILDDIR_ABSOLUTE)/tmp-merge-grpc ; ar x $${l} ) ; done + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libgrpc.a $(BUILDDIR_ABSOLUTE)/tmp-merge-grpc/__.SYMDEF* + $(Q) ar rcs $(LIBDIR)/$(CONFIG)/libgrpc.a $(BUILDDIR_ABSOLUTE)/tmp-merge-grpc/* + $(Q) rm -rf $(BUILDDIR_ABSOLUTE)/tmp-merge-grpc +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libgrpc.a +endif + + + +ifeq ($(SYSTEM),MINGW32) +$(LIBDIR)/$(CONFIG)/grpc.$(SHARED_EXT): $(LIBGRPC_OBJS) $(ZLIB_DEP) $(LIBDIR)/$(CONFIG)/gpr.$(SHARED_EXT) $(OPENSSL_DEP) + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,--output-def=$(LIBDIR)/$(CONFIG)/grpc.def -Wl,--out-implib=$(LIBDIR)/$(CONFIG)/libgrpc-imp.a -o $(LIBDIR)/$(CONFIG)/grpc.$(SHARED_EXT) $(LIBGRPC_OBJS) $(LDLIBS) $(OPENSSL_MERGE_LIBS) $(LDLIBS_SECURE) -lgpr-imp +else +$(LIBDIR)/$(CONFIG)/libgrpc.$(SHARED_EXT): $(LIBGRPC_OBJS) $(ZLIB_DEP) $(LIBDIR)/$(CONFIG)/libgpr.$(SHARED_EXT) $(OPENSSL_DEP) + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` +ifeq ($(SYSTEM),Darwin) + $(Q) $(LD) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -install_name libgrpc.$(SHARED_EXT) -dynamiclib -o $(LIBDIR)/$(CONFIG)/libgrpc.$(SHARED_EXT) $(LIBGRPC_OBJS) $(LDLIBS) $(OPENSSL_MERGE_LIBS) $(LDLIBS_SECURE) -lgpr +else + $(Q) $(LD) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,-soname,libgrpc.so.0 -o $(LIBDIR)/$(CONFIG)/libgrpc.$(SHARED_EXT) $(LIBGRPC_OBJS) $(LDLIBS) $(OPENSSL_MERGE_LIBS) $(LDLIBS_SECURE) -lgpr + $(Q) ln -sf libgrpc.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc.so.0 + $(Q) ln -sf libgrpc.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc.so +endif +endif + +endif + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(LIBGRPC_OBJS:.o=.dep) +endif +endif + + +LIBGRPC_TEST_UTIL_SRC = \ + test/core/end2end/data/server1_cert.c \ + test/core/end2end/data/server1_key.c \ + test/core/end2end/data/test_root_cert.c \ + test/core/end2end/cq_verifier.c \ + test/core/end2end/fixtures/proxy.c \ + test/core/iomgr/endpoint_tests.c \ + test/core/security/oauth2_utils.c \ + test/core/util/grpc_profiler.c \ + test/core/util/parse_hexstring.c \ + test/core/util/port_posix.c \ + test/core/util/port_windows.c \ + test/core/util/slice_splitter.c \ + +PUBLIC_HEADERS_C += \ + +LIBGRPC_TEST_UTIL_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC_TEST_UTIL_SRC)))) + +ifeq ($(NO_SECURE),true) + +# You can't build secure libraries if you don't have OpenSSL. + +$(LIBDIR)/$(CONFIG)/libgrpc_test_util.a: openssl_dep_error + + +else + + +$(LIBDIR)/$(CONFIG)/libgrpc_test_util.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBGRPC_TEST_UTIL_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBGRPC_TEST_UTIL_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a +endif + + + + +endif + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(LIBGRPC_TEST_UTIL_OBJS:.o=.dep) +endif +endif + + +LIBGRPC_TEST_UTIL_UNSECURE_SRC = \ + test/core/end2end/cq_verifier.c \ + test/core/end2end/fixtures/proxy.c \ + test/core/iomgr/endpoint_tests.c \ + test/core/security/oauth2_utils.c \ + test/core/util/grpc_profiler.c \ + test/core/util/parse_hexstring.c \ + test/core/util/port_posix.c \ + test/core/util/port_windows.c \ + test/core/util/slice_splitter.c \ + +PUBLIC_HEADERS_C += \ + +LIBGRPC_TEST_UTIL_UNSECURE_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC_TEST_UTIL_UNSECURE_SRC)))) + +$(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a: $(ZLIB_DEP) $(LIBGRPC_TEST_UTIL_UNSECURE_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBGRPC_TEST_UTIL_UNSECURE_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBGRPC_TEST_UTIL_UNSECURE_OBJS:.o=.dep) +endif + + +LIBGRPC_UNSECURE_SRC = \ + src/core/surface/init_unsecure.c \ + src/core/census/grpc_context.c \ + src/core/census/grpc_filter.c \ + src/core/channel/channel_args.c \ + src/core/channel/channel_stack.c \ + src/core/channel/client_channel.c \ + src/core/channel/compress_filter.c \ + src/core/channel/connected_channel.c \ + src/core/channel/http_client_filter.c \ + src/core/channel/http_server_filter.c \ + src/core/channel/noop_filter.c \ + src/core/client_config/client_config.c \ + src/core/client_config/connector.c \ + src/core/client_config/lb_policies/pick_first.c \ + src/core/client_config/lb_policy.c \ + src/core/client_config/resolver.c \ + src/core/client_config/resolver_factory.c \ + src/core/client_config/resolver_registry.c \ + src/core/client_config/resolvers/dns_resolver.c \ + src/core/client_config/resolvers/sockaddr_resolver.c \ + src/core/client_config/subchannel.c \ + src/core/client_config/subchannel_factory.c \ + src/core/client_config/subchannel_factory_decorators/add_channel_arg.c \ + src/core/client_config/subchannel_factory_decorators/merge_channel_args.c \ + src/core/client_config/uri_parser.c \ + src/core/compression/algorithm.c \ + src/core/compression/message_compress.c \ + src/core/debug/trace.c \ + src/core/httpcli/format_request.c \ + src/core/httpcli/httpcli.c \ + src/core/httpcli/parser.c \ + src/core/iomgr/alarm.c \ + src/core/iomgr/alarm_heap.c \ + src/core/iomgr/endpoint.c \ + src/core/iomgr/endpoint_pair_posix.c \ + src/core/iomgr/endpoint_pair_windows.c \ + src/core/iomgr/fd_posix.c \ + src/core/iomgr/iocp_windows.c \ + src/core/iomgr/iomgr.c \ + src/core/iomgr/iomgr_posix.c \ + src/core/iomgr/iomgr_windows.c \ + src/core/iomgr/pollset_multipoller_with_epoll.c \ + src/core/iomgr/pollset_multipoller_with_poll_posix.c \ + src/core/iomgr/pollset_posix.c \ + src/core/iomgr/pollset_set_posix.c \ + src/core/iomgr/pollset_set_windows.c \ + src/core/iomgr/pollset_windows.c \ + src/core/iomgr/resolve_address_posix.c \ + src/core/iomgr/resolve_address_windows.c \ + src/core/iomgr/sockaddr_utils.c \ + src/core/iomgr/socket_utils_common_posix.c \ + src/core/iomgr/socket_utils_linux.c \ + src/core/iomgr/socket_utils_posix.c \ + src/core/iomgr/socket_windows.c \ + src/core/iomgr/tcp_client_posix.c \ + src/core/iomgr/tcp_client_windows.c \ + src/core/iomgr/tcp_posix.c \ + src/core/iomgr/tcp_server_posix.c \ + src/core/iomgr/tcp_server_windows.c \ + src/core/iomgr/tcp_windows.c \ + src/core/iomgr/time_averaged_stats.c \ + src/core/iomgr/udp_server.c \ + src/core/iomgr/wakeup_fd_eventfd.c \ + src/core/iomgr/wakeup_fd_nospecial.c \ + src/core/iomgr/wakeup_fd_pipe.c \ + src/core/iomgr/wakeup_fd_posix.c \ + src/core/json/json.c \ + src/core/json/json_reader.c \ + src/core/json/json_string.c \ + src/core/json/json_writer.c \ + src/core/profiling/basic_timers.c \ + src/core/profiling/stap_timers.c \ + src/core/surface/byte_buffer.c \ + src/core/surface/byte_buffer_queue.c \ + src/core/surface/byte_buffer_reader.c \ + src/core/surface/call.c \ + src/core/surface/call_details.c \ + src/core/surface/call_log_batch.c \ + src/core/surface/channel.c \ + src/core/surface/channel_connectivity.c \ + src/core/surface/channel_create.c \ + src/core/surface/completion_queue.c \ + src/core/surface/event_string.c \ + src/core/surface/init.c \ + src/core/surface/lame_client.c \ + src/core/surface/metadata_array.c \ + src/core/surface/server.c \ + src/core/surface/server_chttp2.c \ + src/core/surface/server_create.c \ + src/core/surface/surface_trace.c \ + src/core/surface/version.c \ + src/core/transport/chttp2/alpn.c \ + src/core/transport/chttp2/bin_encoder.c \ + src/core/transport/chttp2/frame_data.c \ + src/core/transport/chttp2/frame_goaway.c \ + src/core/transport/chttp2/frame_ping.c \ + src/core/transport/chttp2/frame_rst_stream.c \ + src/core/transport/chttp2/frame_settings.c \ + src/core/transport/chttp2/frame_window_update.c \ + src/core/transport/chttp2/hpack_parser.c \ + src/core/transport/chttp2/hpack_table.c \ + src/core/transport/chttp2/huffsyms.c \ + src/core/transport/chttp2/incoming_metadata.c \ + src/core/transport/chttp2/parsing.c \ + src/core/transport/chttp2/status_conversion.c \ + src/core/transport/chttp2/stream_encoder.c \ + src/core/transport/chttp2/stream_lists.c \ + src/core/transport/chttp2/stream_map.c \ + src/core/transport/chttp2/timeout_encoding.c \ + src/core/transport/chttp2/varint.c \ + src/core/transport/chttp2/writing.c \ + src/core/transport/chttp2_transport.c \ + src/core/transport/connectivity_state.c \ + src/core/transport/metadata.c \ + src/core/transport/stream_op.c \ + src/core/transport/transport.c \ + src/core/transport/transport_op_string.c \ + src/core/census/context.c \ + src/core/census/initialize.c \ + src/core/census/operation.c \ + src/core/census/tracing.c \ + +PUBLIC_HEADERS_C += \ + include/grpc/byte_buffer.h \ + include/grpc/byte_buffer_reader.h \ + include/grpc/compression.h \ + include/grpc/grpc.h \ + include/grpc/status.h \ + include/grpc/census.h \ + +LIBGRPC_UNSECURE_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC_UNSECURE_SRC)))) + +$(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a: $(ZLIB_DEP) $(LIBGRPC_UNSECURE_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBGRPC_UNSECURE_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a +endif + + + +ifeq ($(SYSTEM),MINGW32) +$(LIBDIR)/$(CONFIG)/grpc_unsecure.$(SHARED_EXT): $(LIBGRPC_UNSECURE_OBJS) $(ZLIB_DEP) $(LIBDIR)/$(CONFIG)/gpr.$(SHARED_EXT) + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,--output-def=$(LIBDIR)/$(CONFIG)/grpc_unsecure.def -Wl,--out-implib=$(LIBDIR)/$(CONFIG)/libgrpc_unsecure-imp.a -o $(LIBDIR)/$(CONFIG)/grpc_unsecure.$(SHARED_EXT) $(LIBGRPC_UNSECURE_OBJS) $(LDLIBS) -lgpr-imp +else +$(LIBDIR)/$(CONFIG)/libgrpc_unsecure.$(SHARED_EXT): $(LIBGRPC_UNSECURE_OBJS) $(ZLIB_DEP) $(LIBDIR)/$(CONFIG)/libgpr.$(SHARED_EXT) + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` +ifeq ($(SYSTEM),Darwin) + $(Q) $(LD) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -install_name libgrpc_unsecure.$(SHARED_EXT) -dynamiclib -o $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.$(SHARED_EXT) $(LIBGRPC_UNSECURE_OBJS) $(LDLIBS) -lgpr +else + $(Q) $(LD) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,-soname,libgrpc_unsecure.so.0 -o $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.$(SHARED_EXT) $(LIBGRPC_UNSECURE_OBJS) $(LDLIBS) -lgpr + $(Q) ln -sf libgrpc_unsecure.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.so.0 + $(Q) ln -sf libgrpc_unsecure.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.so +endif +endif + +ifneq ($(NO_DEPS),true) +-include $(LIBGRPC_UNSECURE_OBJS:.o=.dep) +endif + + +LIBGRPC_ZOOKEEPER_SRC = \ + src/core/client_config/resolvers/zookeeper_resolver.c \ + +PUBLIC_HEADERS_C += \ + include/grpc/grpc_zookeeper.h \ + +LIBGRPC_ZOOKEEPER_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC_ZOOKEEPER_SRC)))) + +$(LIBDIR)/$(CONFIG)/libgrpc_zookeeper.a: $(ZLIB_DEP) $(LIBGRPC_ZOOKEEPER_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libgrpc_zookeeper.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libgrpc_zookeeper.a $(LIBGRPC_ZOOKEEPER_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libgrpc_zookeeper.a +endif + + + +ifeq ($(SYSTEM),MINGW32) +$(LIBDIR)/$(CONFIG)/grpc_zookeeper.$(SHARED_EXT): $(LIBGRPC_ZOOKEEPER_OBJS) $(ZLIB_DEP) $(LIBDIR)/$(CONFIG)/gpr.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/grpc.$(SHARED_EXT) + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,--output-def=$(LIBDIR)/$(CONFIG)/grpc_zookeeper.def -Wl,--out-implib=$(LIBDIR)/$(CONFIG)/libgrpc_zookeeper-imp.a -o $(LIBDIR)/$(CONFIG)/grpc_zookeeper.$(SHARED_EXT) $(LIBGRPC_ZOOKEEPER_OBJS) $(LDLIBS) -lgpr-imp -lgrpc-imp +else +$(LIBDIR)/$(CONFIG)/libgrpc_zookeeper.$(SHARED_EXT): $(LIBGRPC_ZOOKEEPER_OBJS) $(ZLIB_DEP) $(LIBDIR)/$(CONFIG)/libgpr.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc.$(SHARED_EXT) + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` +ifeq ($(SYSTEM),Darwin) + $(Q) $(LD) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -install_name libgrpc_zookeeper.$(SHARED_EXT) -dynamiclib -o $(LIBDIR)/$(CONFIG)/libgrpc_zookeeper.$(SHARED_EXT) $(LIBGRPC_ZOOKEEPER_OBJS) $(LDLIBS) -lgpr -lgrpc -lzookeeper_mt +else + $(Q) $(LD) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,-soname,libgrpc_zookeeper.so.0 -o $(LIBDIR)/$(CONFIG)/libgrpc_zookeeper.$(SHARED_EXT) $(LIBGRPC_ZOOKEEPER_OBJS) $(LDLIBS) -lgpr -lgrpc -lzookeeper_mt + $(Q) ln -sf libgrpc_zookeeper.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc_zookeeper.so.0 + $(Q) ln -sf libgrpc_zookeeper.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc_zookeeper.so +endif +endif + +ifneq ($(NO_DEPS),true) +-include $(LIBGRPC_ZOOKEEPER_OBJS:.o=.dep) +endif + + +LIBRECONNECT_SERVER_SRC = \ + test/core/util/reconnect_server.c \ + + +LIBRECONNECT_SERVER_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBRECONNECT_SERVER_SRC)))) + +ifeq ($(NO_SECURE),true) + +# You can't build secure libraries if you don't have OpenSSL. + +$(LIBDIR)/$(CONFIG)/libreconnect_server.a: openssl_dep_error + + +else + + +$(LIBDIR)/$(CONFIG)/libreconnect_server.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBRECONNECT_SERVER_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libreconnect_server.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libreconnect_server.a $(LIBRECONNECT_SERVER_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libreconnect_server.a +endif + + + + +endif + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(LIBRECONNECT_SERVER_OBJS:.o=.dep) +endif +endif + + +LIBGRPC++_SRC = \ + src/cpp/client/secure_channel_arguments.cc \ + src/cpp/client/secure_credentials.cc \ + src/cpp/common/auth_property_iterator.cc \ + src/cpp/common/secure_auth_context.cc \ + src/cpp/common/secure_create_auth_context.cc \ + src/cpp/server/secure_server_credentials.cc \ + src/cpp/client/channel.cc \ + src/cpp/client/channel_arguments.cc \ + src/cpp/client/client_context.cc \ + src/cpp/client/create_channel.cc \ + src/cpp/client/create_channel_internal.cc \ + src/cpp/client/credentials.cc \ + src/cpp/client/generic_stub.cc \ + src/cpp/client/insecure_credentials.cc \ + src/cpp/common/call.cc \ + src/cpp/common/completion_queue.cc \ + src/cpp/common/rpc_method.cc \ + src/cpp/proto/proto_utils.cc \ + src/cpp/server/async_generic_service.cc \ + src/cpp/server/create_default_thread_pool.cc \ + src/cpp/server/dynamic_thread_pool.cc \ + src/cpp/server/fixed_size_thread_pool.cc \ + src/cpp/server/insecure_server_credentials.cc \ + src/cpp/server/server.cc \ + src/cpp/server/server_builder.cc \ + src/cpp/server/server_context.cc \ + src/cpp/server/server_credentials.cc \ + src/cpp/util/byte_buffer.cc \ + src/cpp/util/slice.cc \ + src/cpp/util/status.cc \ + src/cpp/util/string_ref.cc \ + src/cpp/util/time.cc \ + +PUBLIC_HEADERS_CXX += \ + include/grpc++/channel.h \ + include/grpc++/client_context.h \ + include/grpc++/completion_queue.h \ + include/grpc++/create_channel.h \ + include/grpc++/generic/async_generic_service.h \ + include/grpc++/generic/generic_stub.h \ + 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/proto_utils.h \ + include/grpc++/impl/rpc_method.h \ + include/grpc++/impl/rpc_service_method.h \ + include/grpc++/impl/serialization_traits.h \ + include/grpc++/impl/service_type.h \ + include/grpc++/impl/sync.h \ + include/grpc++/impl/sync_cxx11.h \ + include/grpc++/impl/sync_no_cxx11.h \ + include/grpc++/impl/thd.h \ + include/grpc++/impl/thd_cxx11.h \ + include/grpc++/impl/thd_no_cxx11.h \ + include/grpc++/security/auth_context.h \ + include/grpc++/security/auth_metadata_processor.h \ + include/grpc++/security/credentials.h \ + include/grpc++/security/server_credentials.h \ + include/grpc++/server.h \ + include/grpc++/server_builder.h \ + include/grpc++/server_context.h \ + include/grpc++/support/async_stream.h \ + include/grpc++/support/async_unary_call.h \ + include/grpc++/support/byte_buffer.h \ + include/grpc++/support/channel_arguments.h \ + include/grpc++/support/config.h \ + include/grpc++/support/config_protobuf.h \ + include/grpc++/support/slice.h \ + include/grpc++/support/status.h \ + include/grpc++/support/status_code_enum.h \ + include/grpc++/support/string_ref.h \ + include/grpc++/support/stub_options.h \ + include/grpc++/support/sync_stream.h \ + include/grpc++/support/time.h \ + +LIBGRPC++_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC++_SRC)))) + +ifeq ($(NO_SECURE),true) + +# You can't build secure libraries if you don't have OpenSSL. + +$(LIBDIR)/$(CONFIG)/libgrpc++.a: openssl_dep_error + +ifeq ($(SYSTEM),MINGW32) +$(LIBDIR)/$(CONFIG)/grpc++.$(SHARED_EXT): openssl_dep_error +else +$(LIBDIR)/$(CONFIG)/libgrpc++.$(SHARED_EXT): openssl_dep_error +endif + +else + +ifeq ($(NO_PROTOBUF),true) + +# You can't build a C++ library if you don't have protobuf - a bit overreached, but still okay. + +$(LIBDIR)/$(CONFIG)/libgrpc++.a: protobuf_dep_error + +ifeq ($(SYSTEM),MINGW32) +$(LIBDIR)/$(CONFIG)/grpc++.$(SHARED_EXT): protobuf_dep_error +else +$(LIBDIR)/$(CONFIG)/libgrpc++.$(SHARED_EXT): protobuf_dep_error +endif + +else + +$(LIBDIR)/$(CONFIG)/libgrpc++.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(PROTOBUF_DEP) $(LIBGRPC++_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libgrpc++.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBGRPC++_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libgrpc++.a +endif + + + +ifeq ($(SYSTEM),MINGW32) +$(LIBDIR)/$(CONFIG)/grpc++.$(SHARED_EXT): $(LIBGRPC++_OBJS) $(ZLIB_DEP) $(PROTOBUF_DEP) $(LIBDIR)/$(CONFIG)/gpr.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/grpc.$(SHARED_EXT) $(OPENSSL_DEP) + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,--output-def=$(LIBDIR)/$(CONFIG)/grpc++.def -Wl,--out-implib=$(LIBDIR)/$(CONFIG)/libgrpc++-imp.a -o $(LIBDIR)/$(CONFIG)/grpc++.$(SHARED_EXT) $(LIBGRPC++_OBJS) $(LDLIBS) $(LDLIBSXX) $(LDLIBS_PROTOBUF) -lgpr-imp -lgrpc-imp +else +$(LIBDIR)/$(CONFIG)/libgrpc++.$(SHARED_EXT): $(LIBGRPC++_OBJS) $(ZLIB_DEP) $(PROTOBUF_DEP) $(LIBDIR)/$(CONFIG)/libgpr.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc.$(SHARED_EXT) $(OPENSSL_DEP) + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` +ifeq ($(SYSTEM),Darwin) + $(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -install_name libgrpc++.$(SHARED_EXT) -dynamiclib -o $(LIBDIR)/$(CONFIG)/libgrpc++.$(SHARED_EXT) $(LIBGRPC++_OBJS) $(LDLIBS) $(LDLIBSXX) $(LDLIBS_PROTOBUF) -lgpr -lgrpc +else + $(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,-soname,libgrpc++.so.0 -o $(LIBDIR)/$(CONFIG)/libgrpc++.$(SHARED_EXT) $(LIBGRPC++_OBJS) $(LDLIBS) $(LDLIBSXX) $(LDLIBS_PROTOBUF) -lgpr -lgrpc + $(Q) ln -sf libgrpc++.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc++.so.0 + $(Q) ln -sf libgrpc++.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc++.so +endif +endif + +endif + +endif + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(LIBGRPC++_OBJS:.o=.dep) +endif +endif + + +LIBGRPC++_TEST_CONFIG_SRC = \ + test/cpp/util/test_config.cc \ + + +LIBGRPC++_TEST_CONFIG_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC++_TEST_CONFIG_SRC)))) + +ifeq ($(NO_SECURE),true) + +# You can't build secure libraries if you don't have OpenSSL. + +$(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a: openssl_dep_error + + +else + +ifeq ($(NO_PROTOBUF),true) + +# You can't build a C++ library if you don't have protobuf - a bit overreached, but still okay. + +$(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a: protobuf_dep_error + + +else + +$(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(PROTOBUF_DEP) $(LIBGRPC++_TEST_CONFIG_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBGRPC++_TEST_CONFIG_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a +endif + + + + +endif + +endif + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(LIBGRPC++_TEST_CONFIG_OBJS:.o=.dep) +endif +endif + + +LIBGRPC++_TEST_UTIL_SRC = \ + $(GENDIR)/test/cpp/util/messages.pb.cc $(GENDIR)/test/cpp/util/messages.grpc.pb.cc \ + $(GENDIR)/test/cpp/util/echo.pb.cc $(GENDIR)/test/cpp/util/echo.grpc.pb.cc \ + $(GENDIR)/test/cpp/util/echo_duplicate.pb.cc $(GENDIR)/test/cpp/util/echo_duplicate.grpc.pb.cc \ + test/cpp/util/cli_call.cc \ + test/cpp/util/create_test_channel.cc \ + test/cpp/util/string_ref_helper.cc \ + test/cpp/util/subprocess.cc \ + + +LIBGRPC++_TEST_UTIL_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC++_TEST_UTIL_SRC)))) + +ifeq ($(NO_SECURE),true) + +# You can't build secure libraries if you don't have OpenSSL. + +$(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a: openssl_dep_error + + +else + +ifeq ($(NO_PROTOBUF),true) + +# You can't build a C++ library if you don't have protobuf - a bit overreached, but still okay. + +$(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a: protobuf_dep_error + + +else + +$(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(PROTOBUF_DEP) $(LIBGRPC++_TEST_UTIL_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBGRPC++_TEST_UTIL_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a +endif + + + + +endif + +endif + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(LIBGRPC++_TEST_UTIL_OBJS:.o=.dep) +endif +endif +$(OBJDIR)/$(CONFIG)/test/cpp/util/cli_call.o: $(GENDIR)/test/cpp/util/messages.pb.cc $(GENDIR)/test/cpp/util/messages.grpc.pb.cc $(GENDIR)/test/cpp/util/echo.pb.cc $(GENDIR)/test/cpp/util/echo.grpc.pb.cc $(GENDIR)/test/cpp/util/echo_duplicate.pb.cc $(GENDIR)/test/cpp/util/echo_duplicate.grpc.pb.cc +$(OBJDIR)/$(CONFIG)/test/cpp/util/create_test_channel.o: $(GENDIR)/test/cpp/util/messages.pb.cc $(GENDIR)/test/cpp/util/messages.grpc.pb.cc $(GENDIR)/test/cpp/util/echo.pb.cc $(GENDIR)/test/cpp/util/echo.grpc.pb.cc $(GENDIR)/test/cpp/util/echo_duplicate.pb.cc $(GENDIR)/test/cpp/util/echo_duplicate.grpc.pb.cc +$(OBJDIR)/$(CONFIG)/test/cpp/util/string_ref_helper.o: $(GENDIR)/test/cpp/util/messages.pb.cc $(GENDIR)/test/cpp/util/messages.grpc.pb.cc $(GENDIR)/test/cpp/util/echo.pb.cc $(GENDIR)/test/cpp/util/echo.grpc.pb.cc $(GENDIR)/test/cpp/util/echo_duplicate.pb.cc $(GENDIR)/test/cpp/util/echo_duplicate.grpc.pb.cc +$(OBJDIR)/$(CONFIG)/test/cpp/util/subprocess.o: $(GENDIR)/test/cpp/util/messages.pb.cc $(GENDIR)/test/cpp/util/messages.grpc.pb.cc $(GENDIR)/test/cpp/util/echo.pb.cc $(GENDIR)/test/cpp/util/echo.grpc.pb.cc $(GENDIR)/test/cpp/util/echo_duplicate.pb.cc $(GENDIR)/test/cpp/util/echo_duplicate.grpc.pb.cc + + +LIBGRPC++_UNSECURE_SRC = \ + src/cpp/common/insecure_create_auth_context.cc \ + src/cpp/client/channel.cc \ + src/cpp/client/channel_arguments.cc \ + src/cpp/client/client_context.cc \ + src/cpp/client/create_channel.cc \ + src/cpp/client/create_channel_internal.cc \ + src/cpp/client/credentials.cc \ + src/cpp/client/generic_stub.cc \ + src/cpp/client/insecure_credentials.cc \ + src/cpp/common/call.cc \ + src/cpp/common/completion_queue.cc \ + src/cpp/common/rpc_method.cc \ + src/cpp/proto/proto_utils.cc \ + src/cpp/server/async_generic_service.cc \ + src/cpp/server/create_default_thread_pool.cc \ + src/cpp/server/dynamic_thread_pool.cc \ + src/cpp/server/fixed_size_thread_pool.cc \ + src/cpp/server/insecure_server_credentials.cc \ + src/cpp/server/server.cc \ + src/cpp/server/server_builder.cc \ + src/cpp/server/server_context.cc \ + src/cpp/server/server_credentials.cc \ + src/cpp/util/byte_buffer.cc \ + src/cpp/util/slice.cc \ + src/cpp/util/status.cc \ + src/cpp/util/string_ref.cc \ + src/cpp/util/time.cc \ + +PUBLIC_HEADERS_CXX += \ + include/grpc++/channel.h \ + include/grpc++/client_context.h \ + include/grpc++/completion_queue.h \ + include/grpc++/create_channel.h \ + include/grpc++/generic/async_generic_service.h \ + include/grpc++/generic/generic_stub.h \ + 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/proto_utils.h \ + include/grpc++/impl/rpc_method.h \ + include/grpc++/impl/rpc_service_method.h \ + include/grpc++/impl/serialization_traits.h \ + include/grpc++/impl/service_type.h \ + include/grpc++/impl/sync.h \ + include/grpc++/impl/sync_cxx11.h \ + include/grpc++/impl/sync_no_cxx11.h \ + include/grpc++/impl/thd.h \ + include/grpc++/impl/thd_cxx11.h \ + include/grpc++/impl/thd_no_cxx11.h \ + include/grpc++/security/auth_context.h \ + include/grpc++/security/auth_metadata_processor.h \ + include/grpc++/security/credentials.h \ + include/grpc++/security/server_credentials.h \ + include/grpc++/server.h \ + include/grpc++/server_builder.h \ + include/grpc++/server_context.h \ + include/grpc++/support/async_stream.h \ + include/grpc++/support/async_unary_call.h \ + include/grpc++/support/byte_buffer.h \ + include/grpc++/support/channel_arguments.h \ + include/grpc++/support/config.h \ + include/grpc++/support/config_protobuf.h \ + include/grpc++/support/slice.h \ + include/grpc++/support/status.h \ + include/grpc++/support/status_code_enum.h \ + include/grpc++/support/string_ref.h \ + include/grpc++/support/stub_options.h \ + include/grpc++/support/sync_stream.h \ + include/grpc++/support/time.h \ + +LIBGRPC++_UNSECURE_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC++_UNSECURE_SRC)))) + +ifeq ($(NO_PROTOBUF),true) + +# You can't build a C++ library if you don't have protobuf - a bit overreached, but still okay. + +$(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.a: protobuf_dep_error + +ifeq ($(SYSTEM),MINGW32) +$(LIBDIR)/$(CONFIG)/grpc++_unsecure.$(SHARED_EXT): protobuf_dep_error +else +$(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.$(SHARED_EXT): protobuf_dep_error +endif + +else + +$(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.a: $(ZLIB_DEP) $(PROTOBUF_DEP) $(LIBGRPC++_UNSECURE_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.a $(LIBGRPC++_UNSECURE_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.a +endif + + + +ifeq ($(SYSTEM),MINGW32) +$(LIBDIR)/$(CONFIG)/grpc++_unsecure.$(SHARED_EXT): $(LIBGRPC++_UNSECURE_OBJS) $(ZLIB_DEP) $(PROTOBUF_DEP) $(LIBDIR)/$(CONFIG)/gpr.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/grpc_unsecure.$(SHARED_EXT) + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,--output-def=$(LIBDIR)/$(CONFIG)/grpc++_unsecure.def -Wl,--out-implib=$(LIBDIR)/$(CONFIG)/libgrpc++_unsecure-imp.a -o $(LIBDIR)/$(CONFIG)/grpc++_unsecure.$(SHARED_EXT) $(LIBGRPC++_UNSECURE_OBJS) $(LDLIBS) $(LDLIBSXX) $(LDLIBS_PROTOBUF) -lgpr-imp -lgrpc_unsecure-imp +else +$(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.$(SHARED_EXT): $(LIBGRPC++_UNSECURE_OBJS) $(ZLIB_DEP) $(PROTOBUF_DEP) $(LIBDIR)/$(CONFIG)/libgpr.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.$(SHARED_EXT) + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` +ifeq ($(SYSTEM),Darwin) + $(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -install_name libgrpc++_unsecure.$(SHARED_EXT) -dynamiclib -o $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.$(SHARED_EXT) $(LIBGRPC++_UNSECURE_OBJS) $(LDLIBS) $(LDLIBSXX) $(LDLIBS_PROTOBUF) -lgpr -lgrpc_unsecure +else + $(Q) $(LDXX) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,-soname,libgrpc++_unsecure.so.0 -o $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.$(SHARED_EXT) $(LIBGRPC++_UNSECURE_OBJS) $(LDLIBS) $(LDLIBSXX) $(LDLIBS_PROTOBUF) -lgpr -lgrpc_unsecure + $(Q) ln -sf libgrpc++_unsecure.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.so.0 + $(Q) ln -sf libgrpc++_unsecure.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.so +endif +endif + +endif + +ifneq ($(NO_DEPS),true) +-include $(LIBGRPC++_UNSECURE_OBJS:.o=.dep) +endif + + +LIBGRPC_PLUGIN_SUPPORT_SRC = \ + src/compiler/cpp_generator.cc \ + src/compiler/csharp_generator.cc \ + src/compiler/objective_c_generator.cc \ + src/compiler/python_generator.cc \ + src/compiler/ruby_generator.cc \ + + +LIBGRPC_PLUGIN_SUPPORT_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC_PLUGIN_SUPPORT_SRC)))) + +ifeq ($(NO_PROTOBUF),true) + +# You can't build a C++ library if you don't have protobuf - a bit overreached, but still okay. + +$(LIBDIR)/$(CONFIG)/libgrpc_plugin_support.a: protobuf_dep_error + + +else + +$(LIBDIR)/$(CONFIG)/libgrpc_plugin_support.a: $(ZLIB_DEP) $(PROTOBUF_DEP) $(LIBGRPC_PLUGIN_SUPPORT_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libgrpc_plugin_support.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libgrpc_plugin_support.a $(LIBGRPC_PLUGIN_SUPPORT_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libgrpc_plugin_support.a +endif + + + + +endif + +ifneq ($(NO_DEPS),true) +-include $(LIBGRPC_PLUGIN_SUPPORT_OBJS:.o=.dep) +endif + + +LIBINTEROP_CLIENT_HELPER_SRC = \ + $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc \ + test/cpp/interop/client_helper.cc \ + + +LIBINTEROP_CLIENT_HELPER_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBINTEROP_CLIENT_HELPER_SRC)))) + +ifeq ($(NO_SECURE),true) + +# You can't build secure libraries if you don't have OpenSSL. + +$(LIBDIR)/$(CONFIG)/libinterop_client_helper.a: openssl_dep_error + + +else + +ifeq ($(NO_PROTOBUF),true) + +# You can't build a C++ library if you don't have protobuf - a bit overreached, but still okay. + +$(LIBDIR)/$(CONFIG)/libinterop_client_helper.a: protobuf_dep_error + + +else + +$(LIBDIR)/$(CONFIG)/libinterop_client_helper.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(PROTOBUF_DEP) $(LIBINTEROP_CLIENT_HELPER_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libinterop_client_helper.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libinterop_client_helper.a $(LIBINTEROP_CLIENT_HELPER_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libinterop_client_helper.a +endif + + + + +endif + +endif + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(LIBINTEROP_CLIENT_HELPER_OBJS:.o=.dep) +endif +endif +$(OBJDIR)/$(CONFIG)/test/cpp/interop/client_helper.o: $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc + + +LIBINTEROP_CLIENT_MAIN_SRC = \ + $(GENDIR)/test/proto/empty.pb.cc $(GENDIR)/test/proto/empty.grpc.pb.cc \ + $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc \ + $(GENDIR)/test/proto/test.pb.cc $(GENDIR)/test/proto/test.grpc.pb.cc \ + test/cpp/interop/client.cc \ + test/cpp/interop/interop_client.cc \ + + +LIBINTEROP_CLIENT_MAIN_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBINTEROP_CLIENT_MAIN_SRC)))) + +ifeq ($(NO_SECURE),true) + +# You can't build secure libraries if you don't have OpenSSL. + +$(LIBDIR)/$(CONFIG)/libinterop_client_main.a: openssl_dep_error + + +else + +ifeq ($(NO_PROTOBUF),true) + +# You can't build a C++ library if you don't have protobuf - a bit overreached, but still okay. + +$(LIBDIR)/$(CONFIG)/libinterop_client_main.a: protobuf_dep_error + + +else + +$(LIBDIR)/$(CONFIG)/libinterop_client_main.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(PROTOBUF_DEP) $(LIBINTEROP_CLIENT_MAIN_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libinterop_client_main.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libinterop_client_main.a $(LIBINTEROP_CLIENT_MAIN_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libinterop_client_main.a +endif + + + + +endif + +endif + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(LIBINTEROP_CLIENT_MAIN_OBJS:.o=.dep) +endif +endif +$(OBJDIR)/$(CONFIG)/test/cpp/interop/client.o: $(GENDIR)/test/proto/empty.pb.cc $(GENDIR)/test/proto/empty.grpc.pb.cc $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/test.pb.cc $(GENDIR)/test/proto/test.grpc.pb.cc +$(OBJDIR)/$(CONFIG)/test/cpp/interop/interop_client.o: $(GENDIR)/test/proto/empty.pb.cc $(GENDIR)/test/proto/empty.grpc.pb.cc $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/test.pb.cc $(GENDIR)/test/proto/test.grpc.pb.cc + + +LIBINTEROP_SERVER_HELPER_SRC = \ + test/cpp/interop/server_helper.cc \ + + +LIBINTEROP_SERVER_HELPER_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBINTEROP_SERVER_HELPER_SRC)))) + +ifeq ($(NO_SECURE),true) + +# You can't build secure libraries if you don't have OpenSSL. + +$(LIBDIR)/$(CONFIG)/libinterop_server_helper.a: openssl_dep_error + + +else + +ifeq ($(NO_PROTOBUF),true) + +# You can't build a C++ library if you don't have protobuf - a bit overreached, but still okay. + +$(LIBDIR)/$(CONFIG)/libinterop_server_helper.a: protobuf_dep_error + + +else + +$(LIBDIR)/$(CONFIG)/libinterop_server_helper.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(PROTOBUF_DEP) $(LIBINTEROP_SERVER_HELPER_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libinterop_server_helper.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libinterop_server_helper.a $(LIBINTEROP_SERVER_HELPER_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libinterop_server_helper.a +endif + + + + +endif + +endif + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(LIBINTEROP_SERVER_HELPER_OBJS:.o=.dep) +endif +endif + + +LIBINTEROP_SERVER_MAIN_SRC = \ + $(GENDIR)/test/proto/empty.pb.cc $(GENDIR)/test/proto/empty.grpc.pb.cc \ + $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc \ + $(GENDIR)/test/proto/test.pb.cc $(GENDIR)/test/proto/test.grpc.pb.cc \ + test/cpp/interop/server.cc \ + + +LIBINTEROP_SERVER_MAIN_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBINTEROP_SERVER_MAIN_SRC)))) + +ifeq ($(NO_SECURE),true) + +# You can't build secure libraries if you don't have OpenSSL. + +$(LIBDIR)/$(CONFIG)/libinterop_server_main.a: openssl_dep_error + + +else + +ifeq ($(NO_PROTOBUF),true) + +# You can't build a C++ library if you don't have protobuf - a bit overreached, but still okay. + +$(LIBDIR)/$(CONFIG)/libinterop_server_main.a: protobuf_dep_error + + +else + +$(LIBDIR)/$(CONFIG)/libinterop_server_main.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(PROTOBUF_DEP) $(LIBINTEROP_SERVER_MAIN_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libinterop_server_main.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libinterop_server_main.a $(LIBINTEROP_SERVER_MAIN_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libinterop_server_main.a +endif + + + + +endif + +endif + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(LIBINTEROP_SERVER_MAIN_OBJS:.o=.dep) +endif +endif +$(OBJDIR)/$(CONFIG)/test/cpp/interop/server.o: $(GENDIR)/test/proto/empty.pb.cc $(GENDIR)/test/proto/empty.grpc.pb.cc $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/test.pb.cc $(GENDIR)/test/proto/test.grpc.pb.cc + + +LIBQPS_SRC = \ + $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc \ + $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc \ + test/cpp/qps/client_async.cc \ + test/cpp/qps/client_sync.cc \ + test/cpp/qps/driver.cc \ + test/cpp/qps/perf_db_client.cc \ + test/cpp/qps/qps_worker.cc \ + test/cpp/qps/report.cc \ + test/cpp/qps/server_async.cc \ + test/cpp/qps/server_sync.cc \ + test/cpp/qps/timer.cc \ + test/cpp/util/benchmark_config.cc \ + + +LIBQPS_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBQPS_SRC)))) + +ifeq ($(NO_SECURE),true) + +# You can't build secure libraries if you don't have OpenSSL. + +$(LIBDIR)/$(CONFIG)/libqps.a: openssl_dep_error + + +else + +ifeq ($(NO_PROTOBUF),true) + +# You can't build a C++ library if you don't have protobuf - a bit overreached, but still okay. + +$(LIBDIR)/$(CONFIG)/libqps.a: protobuf_dep_error + + +else + +$(LIBDIR)/$(CONFIG)/libqps.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(PROTOBUF_DEP) $(LIBQPS_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libqps.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libqps.a $(LIBQPS_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libqps.a +endif + + + + +endif + +endif + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(LIBQPS_OBJS:.o=.dep) +endif +endif +$(OBJDIR)/$(CONFIG)/test/cpp/qps/client_async.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc +$(OBJDIR)/$(CONFIG)/test/cpp/qps/client_sync.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc +$(OBJDIR)/$(CONFIG)/test/cpp/qps/driver.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc +$(OBJDIR)/$(CONFIG)/test/cpp/qps/perf_db_client.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc +$(OBJDIR)/$(CONFIG)/test/cpp/qps/qps_worker.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc +$(OBJDIR)/$(CONFIG)/test/cpp/qps/report.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc +$(OBJDIR)/$(CONFIG)/test/cpp/qps/server_async.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc +$(OBJDIR)/$(CONFIG)/test/cpp/qps/server_sync.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc +$(OBJDIR)/$(CONFIG)/test/cpp/qps/timer.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc +$(OBJDIR)/$(CONFIG)/test/cpp/util/benchmark_config.o: $(GENDIR)/test/cpp/qps/qpstest.pb.cc $(GENDIR)/test/cpp/qps/qpstest.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc + + +LIBGRPC_CSHARP_EXT_SRC = \ + src/csharp/ext/grpc_csharp_ext.c \ + + +LIBGRPC_CSHARP_EXT_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC_CSHARP_EXT_SRC)))) + +ifeq ($(NO_SECURE),true) + +# You can't build secure libraries if you don't have OpenSSL. + +$(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext.a: openssl_dep_error + +ifeq ($(SYSTEM),MINGW32) +$(LIBDIR)/$(CONFIG)/grpc_csharp_ext.$(SHARED_EXT): openssl_dep_error +else +$(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext.$(SHARED_EXT): openssl_dep_error +endif + +else + + +$(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBGRPC_CSHARP_EXT_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext.a $(LIBGRPC_CSHARP_EXT_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext.a +endif + + + +ifeq ($(SYSTEM),MINGW32) +$(LIBDIR)/$(CONFIG)/grpc_csharp_ext.$(SHARED_EXT): $(LIBGRPC_CSHARP_EXT_OBJS) $(ZLIB_DEP) $(LIBDIR)/$(CONFIG)/gpr.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/grpc.$(SHARED_EXT) $(OPENSSL_DEP) + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,--output-def=$(LIBDIR)/$(CONFIG)/grpc_csharp_ext.def -Wl,--out-implib=$(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext-imp.a -o $(LIBDIR)/$(CONFIG)/grpc_csharp_ext.$(SHARED_EXT) $(LIBGRPC_CSHARP_EXT_OBJS) $(LDLIBS) -lgpr-imp -lgrpc-imp +else +$(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext.$(SHARED_EXT): $(LIBGRPC_CSHARP_EXT_OBJS) $(ZLIB_DEP) $(LIBDIR)/$(CONFIG)/libgpr.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc.$(SHARED_EXT) $(OPENSSL_DEP) + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` +ifeq ($(SYSTEM),Darwin) + $(Q) $(LD) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -install_name libgrpc_csharp_ext.$(SHARED_EXT) -dynamiclib -o $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext.$(SHARED_EXT) $(LIBGRPC_CSHARP_EXT_OBJS) $(LDLIBS) -lgpr -lgrpc +else + $(Q) $(LD) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,-soname,libgrpc_csharp_ext.so.0 -o $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext.$(SHARED_EXT) $(LIBGRPC_CSHARP_EXT_OBJS) $(LDLIBS) -lgpr -lgrpc + $(Q) ln -sf libgrpc_csharp_ext.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext.so.0 + $(Q) ln -sf libgrpc_csharp_ext.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext.so +endif +endif + +endif + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(LIBGRPC_CSHARP_EXT_OBJS:.o=.dep) +endif +endif + + +LIBEND2END_FIXTURE_H2_COMPRESS_SRC = \ + test/core/end2end/fixtures/h2_compress.c \ + + +LIBEND2END_FIXTURE_H2_COMPRESS_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_FIXTURE_H2_COMPRESS_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a: $(ZLIB_DEP) $(LIBEND2END_FIXTURE_H2_COMPRESS_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBEND2END_FIXTURE_H2_COMPRESS_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_FIXTURE_H2_COMPRESS_OBJS:.o=.dep) +endif + + +LIBEND2END_FIXTURE_H2_FAKESEC_SRC = \ + test/core/end2end/fixtures/h2_fakesec.c \ + + +LIBEND2END_FIXTURE_H2_FAKESEC_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_FIXTURE_H2_FAKESEC_SRC)))) + +ifeq ($(NO_SECURE),true) + +# You can't build secure libraries if you don't have OpenSSL. + +$(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a: openssl_dep_error + + +else + + +$(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBEND2END_FIXTURE_H2_FAKESEC_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBEND2END_FIXTURE_H2_FAKESEC_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a +endif + + + + +endif + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_FIXTURE_H2_FAKESEC_OBJS:.o=.dep) +endif +endif + + +LIBEND2END_FIXTURE_H2_FULL_SRC = \ + test/core/end2end/fixtures/h2_full.c \ + + +LIBEND2END_FIXTURE_H2_FULL_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_FIXTURE_H2_FULL_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a: $(ZLIB_DEP) $(LIBEND2END_FIXTURE_H2_FULL_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBEND2END_FIXTURE_H2_FULL_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_FIXTURE_H2_FULL_OBJS:.o=.dep) +endif + + +LIBEND2END_FIXTURE_H2_FULL+POLL_SRC = \ + test/core/end2end/fixtures/h2_full+poll.c \ + + +LIBEND2END_FIXTURE_H2_FULL+POLL_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_FIXTURE_H2_FULL+POLL_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a: $(ZLIB_DEP) $(LIBEND2END_FIXTURE_H2_FULL+POLL_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBEND2END_FIXTURE_H2_FULL+POLL_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_FIXTURE_H2_FULL+POLL_OBJS:.o=.dep) +endif + + +LIBEND2END_FIXTURE_H2_OAUTH2_SRC = \ + test/core/end2end/fixtures/h2_oauth2.c \ + + +LIBEND2END_FIXTURE_H2_OAUTH2_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_FIXTURE_H2_OAUTH2_SRC)))) + +ifeq ($(NO_SECURE),true) + +# You can't build secure libraries if you don't have OpenSSL. + +$(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a: openssl_dep_error + + +else + + +$(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBEND2END_FIXTURE_H2_OAUTH2_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBEND2END_FIXTURE_H2_OAUTH2_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a +endif + + + + +endif + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_FIXTURE_H2_OAUTH2_OBJS:.o=.dep) +endif +endif + + +LIBEND2END_FIXTURE_H2_PROXY_SRC = \ + test/core/end2end/fixtures/h2_proxy.c \ + + +LIBEND2END_FIXTURE_H2_PROXY_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_FIXTURE_H2_PROXY_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a: $(ZLIB_DEP) $(LIBEND2END_FIXTURE_H2_PROXY_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBEND2END_FIXTURE_H2_PROXY_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_FIXTURE_H2_PROXY_OBJS:.o=.dep) +endif + + +LIBEND2END_FIXTURE_H2_SOCKPAIR_SRC = \ + test/core/end2end/fixtures/h2_sockpair.c \ + + +LIBEND2END_FIXTURE_H2_SOCKPAIR_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_FIXTURE_H2_SOCKPAIR_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a: $(ZLIB_DEP) $(LIBEND2END_FIXTURE_H2_SOCKPAIR_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBEND2END_FIXTURE_H2_SOCKPAIR_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_FIXTURE_H2_SOCKPAIR_OBJS:.o=.dep) +endif + + +LIBEND2END_FIXTURE_H2_SOCKPAIR+TRACE_SRC = \ + test/core/end2end/fixtures/h2_sockpair+trace.c \ + + +LIBEND2END_FIXTURE_H2_SOCKPAIR+TRACE_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_FIXTURE_H2_SOCKPAIR+TRACE_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a: $(ZLIB_DEP) $(LIBEND2END_FIXTURE_H2_SOCKPAIR+TRACE_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBEND2END_FIXTURE_H2_SOCKPAIR+TRACE_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_FIXTURE_H2_SOCKPAIR+TRACE_OBJS:.o=.dep) +endif + + +LIBEND2END_FIXTURE_H2_SOCKPAIR_1BYTE_SRC = \ + test/core/end2end/fixtures/h2_sockpair_1byte.c \ + + +LIBEND2END_FIXTURE_H2_SOCKPAIR_1BYTE_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_FIXTURE_H2_SOCKPAIR_1BYTE_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a: $(ZLIB_DEP) $(LIBEND2END_FIXTURE_H2_SOCKPAIR_1BYTE_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBEND2END_FIXTURE_H2_SOCKPAIR_1BYTE_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_FIXTURE_H2_SOCKPAIR_1BYTE_OBJS:.o=.dep) +endif + + +LIBEND2END_FIXTURE_H2_SSL_SRC = \ + test/core/end2end/fixtures/h2_ssl.c \ + + +LIBEND2END_FIXTURE_H2_SSL_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_FIXTURE_H2_SSL_SRC)))) + +ifeq ($(NO_SECURE),true) + +# You can't build secure libraries if you don't have OpenSSL. + +$(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a: openssl_dep_error + + +else + + +$(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBEND2END_FIXTURE_H2_SSL_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBEND2END_FIXTURE_H2_SSL_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a +endif + + + + +endif + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_FIXTURE_H2_SSL_OBJS:.o=.dep) +endif +endif + + +LIBEND2END_FIXTURE_H2_SSL+POLL_SRC = \ + test/core/end2end/fixtures/h2_ssl+poll.c \ + + +LIBEND2END_FIXTURE_H2_SSL+POLL_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_FIXTURE_H2_SSL+POLL_SRC)))) + +ifeq ($(NO_SECURE),true) + +# You can't build secure libraries if you don't have OpenSSL. + +$(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a: openssl_dep_error + + +else + + +$(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBEND2END_FIXTURE_H2_SSL+POLL_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBEND2END_FIXTURE_H2_SSL+POLL_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a +endif + + + + +endif + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_FIXTURE_H2_SSL+POLL_OBJS:.o=.dep) +endif +endif + + +LIBEND2END_FIXTURE_H2_SSL_PROXY_SRC = \ + test/core/end2end/fixtures/h2_ssl_proxy.c \ + + +LIBEND2END_FIXTURE_H2_SSL_PROXY_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_FIXTURE_H2_SSL_PROXY_SRC)))) + +ifeq ($(NO_SECURE),true) + +# You can't build secure libraries if you don't have OpenSSL. + +$(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a: openssl_dep_error + + +else + + +$(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBEND2END_FIXTURE_H2_SSL_PROXY_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBEND2END_FIXTURE_H2_SSL_PROXY_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a +endif + + + + +endif + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_FIXTURE_H2_SSL_PROXY_OBJS:.o=.dep) +endif +endif + + +LIBEND2END_FIXTURE_H2_UDS_SRC = \ + test/core/end2end/fixtures/h2_uds.c \ + + +LIBEND2END_FIXTURE_H2_UDS_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_FIXTURE_H2_UDS_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a: $(ZLIB_DEP) $(LIBEND2END_FIXTURE_H2_UDS_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBEND2END_FIXTURE_H2_UDS_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_FIXTURE_H2_UDS_OBJS:.o=.dep) +endif + + +LIBEND2END_FIXTURE_H2_UDS+POLL_SRC = \ + test/core/end2end/fixtures/h2_uds+poll.c \ + + +LIBEND2END_FIXTURE_H2_UDS+POLL_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_FIXTURE_H2_UDS+POLL_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a: $(ZLIB_DEP) $(LIBEND2END_FIXTURE_H2_UDS+POLL_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBEND2END_FIXTURE_H2_UDS+POLL_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_FIXTURE_H2_UDS+POLL_OBJS:.o=.dep) +endif + + +LIBEND2END_TEST_BAD_HOSTNAME_SRC = \ + test/core/end2end/tests/bad_hostname.c \ + + +LIBEND2END_TEST_BAD_HOSTNAME_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_TEST_BAD_HOSTNAME_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a: $(ZLIB_DEP) $(LIBEND2END_TEST_BAD_HOSTNAME_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBEND2END_TEST_BAD_HOSTNAME_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_TEST_BAD_HOSTNAME_OBJS:.o=.dep) +endif + + +LIBEND2END_TEST_BINARY_METADATA_SRC = \ + test/core/end2end/tests/binary_metadata.c \ + + +LIBEND2END_TEST_BINARY_METADATA_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_TEST_BINARY_METADATA_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a: $(ZLIB_DEP) $(LIBEND2END_TEST_BINARY_METADATA_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBEND2END_TEST_BINARY_METADATA_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_TEST_BINARY_METADATA_OBJS:.o=.dep) +endif + + +LIBEND2END_TEST_CALL_CREDS_SRC = \ + test/core/end2end/tests/call_creds.c \ + + +LIBEND2END_TEST_CALL_CREDS_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_TEST_CALL_CREDS_SRC)))) + +ifeq ($(NO_SECURE),true) + +# You can't build secure libraries if you don't have OpenSSL. + +$(LIBDIR)/$(CONFIG)/libend2end_test_call_creds.a: openssl_dep_error + + +else + + +$(LIBDIR)/$(CONFIG)/libend2end_test_call_creds.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBEND2END_TEST_CALL_CREDS_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_test_call_creds.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_test_call_creds.a $(LIBEND2END_TEST_CALL_CREDS_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_test_call_creds.a +endif + + + + +endif + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_TEST_CALL_CREDS_OBJS:.o=.dep) +endif +endif + + +LIBEND2END_TEST_CANCEL_AFTER_ACCEPT_SRC = \ + test/core/end2end/tests/cancel_after_accept.c \ + + +LIBEND2END_TEST_CANCEL_AFTER_ACCEPT_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_TEST_CANCEL_AFTER_ACCEPT_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a: $(ZLIB_DEP) $(LIBEND2END_TEST_CANCEL_AFTER_ACCEPT_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBEND2END_TEST_CANCEL_AFTER_ACCEPT_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_TEST_CANCEL_AFTER_ACCEPT_OBJS:.o=.dep) +endif + + +LIBEND2END_TEST_CANCEL_AFTER_CLIENT_DONE_SRC = \ + test/core/end2end/tests/cancel_after_client_done.c \ + + +LIBEND2END_TEST_CANCEL_AFTER_CLIENT_DONE_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_TEST_CANCEL_AFTER_CLIENT_DONE_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a: $(ZLIB_DEP) $(LIBEND2END_TEST_CANCEL_AFTER_CLIENT_DONE_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBEND2END_TEST_CANCEL_AFTER_CLIENT_DONE_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_TEST_CANCEL_AFTER_CLIENT_DONE_OBJS:.o=.dep) +endif + + +LIBEND2END_TEST_CANCEL_AFTER_INVOKE_SRC = \ + test/core/end2end/tests/cancel_after_invoke.c \ + + +LIBEND2END_TEST_CANCEL_AFTER_INVOKE_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_TEST_CANCEL_AFTER_INVOKE_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a: $(ZLIB_DEP) $(LIBEND2END_TEST_CANCEL_AFTER_INVOKE_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBEND2END_TEST_CANCEL_AFTER_INVOKE_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_TEST_CANCEL_AFTER_INVOKE_OBJS:.o=.dep) +endif + + +LIBEND2END_TEST_CANCEL_BEFORE_INVOKE_SRC = \ + test/core/end2end/tests/cancel_before_invoke.c \ + + +LIBEND2END_TEST_CANCEL_BEFORE_INVOKE_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_TEST_CANCEL_BEFORE_INVOKE_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a: $(ZLIB_DEP) $(LIBEND2END_TEST_CANCEL_BEFORE_INVOKE_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBEND2END_TEST_CANCEL_BEFORE_INVOKE_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_TEST_CANCEL_BEFORE_INVOKE_OBJS:.o=.dep) +endif + + +LIBEND2END_TEST_CANCEL_IN_A_VACUUM_SRC = \ + test/core/end2end/tests/cancel_in_a_vacuum.c \ + + +LIBEND2END_TEST_CANCEL_IN_A_VACUUM_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_TEST_CANCEL_IN_A_VACUUM_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a: $(ZLIB_DEP) $(LIBEND2END_TEST_CANCEL_IN_A_VACUUM_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBEND2END_TEST_CANCEL_IN_A_VACUUM_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_TEST_CANCEL_IN_A_VACUUM_OBJS:.o=.dep) +endif + + +LIBEND2END_TEST_CENSUS_SIMPLE_REQUEST_SRC = \ + test/core/end2end/tests/census_simple_request.c \ + + +LIBEND2END_TEST_CENSUS_SIMPLE_REQUEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_TEST_CENSUS_SIMPLE_REQUEST_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a: $(ZLIB_DEP) $(LIBEND2END_TEST_CENSUS_SIMPLE_REQUEST_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBEND2END_TEST_CENSUS_SIMPLE_REQUEST_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_TEST_CENSUS_SIMPLE_REQUEST_OBJS:.o=.dep) +endif + + +LIBEND2END_TEST_CHANNEL_CONNECTIVITY_SRC = \ + test/core/end2end/tests/channel_connectivity.c \ + + +LIBEND2END_TEST_CHANNEL_CONNECTIVITY_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_TEST_CHANNEL_CONNECTIVITY_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_test_channel_connectivity.a: $(ZLIB_DEP) $(LIBEND2END_TEST_CHANNEL_CONNECTIVITY_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_test_channel_connectivity.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_test_channel_connectivity.a $(LIBEND2END_TEST_CHANNEL_CONNECTIVITY_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_test_channel_connectivity.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_TEST_CHANNEL_CONNECTIVITY_OBJS:.o=.dep) +endif + + +LIBEND2END_TEST_COMPRESSED_PAYLOAD_SRC = \ + test/core/end2end/tests/compressed_payload.c \ + + +LIBEND2END_TEST_COMPRESSED_PAYLOAD_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_TEST_COMPRESSED_PAYLOAD_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a: $(ZLIB_DEP) $(LIBEND2END_TEST_COMPRESSED_PAYLOAD_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBEND2END_TEST_COMPRESSED_PAYLOAD_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_TEST_COMPRESSED_PAYLOAD_OBJS:.o=.dep) +endif + + +LIBEND2END_TEST_DEFAULT_HOST_SRC = \ + test/core/end2end/tests/default_host.c \ + + +LIBEND2END_TEST_DEFAULT_HOST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_TEST_DEFAULT_HOST_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_test_default_host.a: $(ZLIB_DEP) $(LIBEND2END_TEST_DEFAULT_HOST_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_test_default_host.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_test_default_host.a $(LIBEND2END_TEST_DEFAULT_HOST_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_test_default_host.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_TEST_DEFAULT_HOST_OBJS:.o=.dep) +endif + + +LIBEND2END_TEST_DISAPPEARING_SERVER_SRC = \ + test/core/end2end/tests/disappearing_server.c \ + + +LIBEND2END_TEST_DISAPPEARING_SERVER_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_TEST_DISAPPEARING_SERVER_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a: $(ZLIB_DEP) $(LIBEND2END_TEST_DISAPPEARING_SERVER_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBEND2END_TEST_DISAPPEARING_SERVER_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_TEST_DISAPPEARING_SERVER_OBJS:.o=.dep) +endif + + +LIBEND2END_TEST_EMPTY_BATCH_SRC = \ + test/core/end2end/tests/empty_batch.c \ + + +LIBEND2END_TEST_EMPTY_BATCH_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_TEST_EMPTY_BATCH_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a: $(ZLIB_DEP) $(LIBEND2END_TEST_EMPTY_BATCH_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBEND2END_TEST_EMPTY_BATCH_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_TEST_EMPTY_BATCH_OBJS:.o=.dep) +endif + + +LIBEND2END_TEST_GRACEFUL_SERVER_SHUTDOWN_SRC = \ + test/core/end2end/tests/graceful_server_shutdown.c \ + + +LIBEND2END_TEST_GRACEFUL_SERVER_SHUTDOWN_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_TEST_GRACEFUL_SERVER_SHUTDOWN_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a: $(ZLIB_DEP) $(LIBEND2END_TEST_GRACEFUL_SERVER_SHUTDOWN_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBEND2END_TEST_GRACEFUL_SERVER_SHUTDOWN_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_TEST_GRACEFUL_SERVER_SHUTDOWN_OBJS:.o=.dep) +endif + + +LIBEND2END_TEST_HIGH_INITIAL_SEQNO_SRC = \ + test/core/end2end/tests/high_initial_seqno.c \ + + +LIBEND2END_TEST_HIGH_INITIAL_SEQNO_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_TEST_HIGH_INITIAL_SEQNO_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a: $(ZLIB_DEP) $(LIBEND2END_TEST_HIGH_INITIAL_SEQNO_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBEND2END_TEST_HIGH_INITIAL_SEQNO_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_TEST_HIGH_INITIAL_SEQNO_OBJS:.o=.dep) +endif + + +LIBEND2END_TEST_INVOKE_LARGE_REQUEST_SRC = \ + test/core/end2end/tests/invoke_large_request.c \ + + +LIBEND2END_TEST_INVOKE_LARGE_REQUEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_TEST_INVOKE_LARGE_REQUEST_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a: $(ZLIB_DEP) $(LIBEND2END_TEST_INVOKE_LARGE_REQUEST_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBEND2END_TEST_INVOKE_LARGE_REQUEST_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_TEST_INVOKE_LARGE_REQUEST_OBJS:.o=.dep) +endif + + +LIBEND2END_TEST_LARGE_METADATA_SRC = \ + test/core/end2end/tests/large_metadata.c \ + + +LIBEND2END_TEST_LARGE_METADATA_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_TEST_LARGE_METADATA_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a: $(ZLIB_DEP) $(LIBEND2END_TEST_LARGE_METADATA_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBEND2END_TEST_LARGE_METADATA_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_TEST_LARGE_METADATA_OBJS:.o=.dep) +endif + + +LIBEND2END_TEST_MAX_CONCURRENT_STREAMS_SRC = \ + test/core/end2end/tests/max_concurrent_streams.c \ + + +LIBEND2END_TEST_MAX_CONCURRENT_STREAMS_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_TEST_MAX_CONCURRENT_STREAMS_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a: $(ZLIB_DEP) $(LIBEND2END_TEST_MAX_CONCURRENT_STREAMS_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBEND2END_TEST_MAX_CONCURRENT_STREAMS_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_TEST_MAX_CONCURRENT_STREAMS_OBJS:.o=.dep) +endif + + +LIBEND2END_TEST_MAX_MESSAGE_LENGTH_SRC = \ + test/core/end2end/tests/max_message_length.c \ + + +LIBEND2END_TEST_MAX_MESSAGE_LENGTH_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_TEST_MAX_MESSAGE_LENGTH_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a: $(ZLIB_DEP) $(LIBEND2END_TEST_MAX_MESSAGE_LENGTH_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBEND2END_TEST_MAX_MESSAGE_LENGTH_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_TEST_MAX_MESSAGE_LENGTH_OBJS:.o=.dep) +endif + + +LIBEND2END_TEST_METADATA_SRC = \ + test/core/end2end/tests/metadata.c \ + + +LIBEND2END_TEST_METADATA_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_TEST_METADATA_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a: $(ZLIB_DEP) $(LIBEND2END_TEST_METADATA_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBEND2END_TEST_METADATA_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_TEST_METADATA_OBJS:.o=.dep) +endif + + +LIBEND2END_TEST_NO_OP_SRC = \ + test/core/end2end/tests/no_op.c \ + + +LIBEND2END_TEST_NO_OP_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_TEST_NO_OP_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a: $(ZLIB_DEP) $(LIBEND2END_TEST_NO_OP_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBEND2END_TEST_NO_OP_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_TEST_NO_OP_OBJS:.o=.dep) +endif + + +LIBEND2END_TEST_PAYLOAD_SRC = \ + test/core/end2end/tests/payload.c \ + + +LIBEND2END_TEST_PAYLOAD_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_TEST_PAYLOAD_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_test_payload.a: $(ZLIB_DEP) $(LIBEND2END_TEST_PAYLOAD_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBEND2END_TEST_PAYLOAD_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_TEST_PAYLOAD_OBJS:.o=.dep) +endif + + +LIBEND2END_TEST_PING_PONG_STREAMING_SRC = \ + test/core/end2end/tests/ping_pong_streaming.c \ + + +LIBEND2END_TEST_PING_PONG_STREAMING_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_TEST_PING_PONG_STREAMING_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a: $(ZLIB_DEP) $(LIBEND2END_TEST_PING_PONG_STREAMING_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBEND2END_TEST_PING_PONG_STREAMING_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_TEST_PING_PONG_STREAMING_OBJS:.o=.dep) +endif + + +LIBEND2END_TEST_REGISTERED_CALL_SRC = \ + test/core/end2end/tests/registered_call.c \ + + +LIBEND2END_TEST_REGISTERED_CALL_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_TEST_REGISTERED_CALL_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a: $(ZLIB_DEP) $(LIBEND2END_TEST_REGISTERED_CALL_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBEND2END_TEST_REGISTERED_CALL_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_TEST_REGISTERED_CALL_OBJS:.o=.dep) +endif + + +LIBEND2END_TEST_REQUEST_WITH_FLAGS_SRC = \ + test/core/end2end/tests/request_with_flags.c \ + + +LIBEND2END_TEST_REQUEST_WITH_FLAGS_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_TEST_REQUEST_WITH_FLAGS_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a: $(ZLIB_DEP) $(LIBEND2END_TEST_REQUEST_WITH_FLAGS_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBEND2END_TEST_REQUEST_WITH_FLAGS_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_TEST_REQUEST_WITH_FLAGS_OBJS:.o=.dep) +endif + + +LIBEND2END_TEST_REQUEST_WITH_PAYLOAD_SRC = \ + test/core/end2end/tests/request_with_payload.c \ + + +LIBEND2END_TEST_REQUEST_WITH_PAYLOAD_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_TEST_REQUEST_WITH_PAYLOAD_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a: $(ZLIB_DEP) $(LIBEND2END_TEST_REQUEST_WITH_PAYLOAD_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBEND2END_TEST_REQUEST_WITH_PAYLOAD_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_TEST_REQUEST_WITH_PAYLOAD_OBJS:.o=.dep) +endif + + +LIBEND2END_TEST_SERVER_FINISHES_REQUEST_SRC = \ + test/core/end2end/tests/server_finishes_request.c \ + + +LIBEND2END_TEST_SERVER_FINISHES_REQUEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_TEST_SERVER_FINISHES_REQUEST_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a: $(ZLIB_DEP) $(LIBEND2END_TEST_SERVER_FINISHES_REQUEST_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBEND2END_TEST_SERVER_FINISHES_REQUEST_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_TEST_SERVER_FINISHES_REQUEST_OBJS:.o=.dep) +endif + + +LIBEND2END_TEST_SHUTDOWN_FINISHES_CALLS_SRC = \ + test/core/end2end/tests/shutdown_finishes_calls.c \ + + +LIBEND2END_TEST_SHUTDOWN_FINISHES_CALLS_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_TEST_SHUTDOWN_FINISHES_CALLS_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a: $(ZLIB_DEP) $(LIBEND2END_TEST_SHUTDOWN_FINISHES_CALLS_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBEND2END_TEST_SHUTDOWN_FINISHES_CALLS_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_TEST_SHUTDOWN_FINISHES_CALLS_OBJS:.o=.dep) +endif + + +LIBEND2END_TEST_SHUTDOWN_FINISHES_TAGS_SRC = \ + test/core/end2end/tests/shutdown_finishes_tags.c \ + + +LIBEND2END_TEST_SHUTDOWN_FINISHES_TAGS_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_TEST_SHUTDOWN_FINISHES_TAGS_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a: $(ZLIB_DEP) $(LIBEND2END_TEST_SHUTDOWN_FINISHES_TAGS_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBEND2END_TEST_SHUTDOWN_FINISHES_TAGS_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_TEST_SHUTDOWN_FINISHES_TAGS_OBJS:.o=.dep) +endif + + +LIBEND2END_TEST_SIMPLE_DELAYED_REQUEST_SRC = \ + test/core/end2end/tests/simple_delayed_request.c \ + + +LIBEND2END_TEST_SIMPLE_DELAYED_REQUEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_TEST_SIMPLE_DELAYED_REQUEST_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a: $(ZLIB_DEP) $(LIBEND2END_TEST_SIMPLE_DELAYED_REQUEST_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBEND2END_TEST_SIMPLE_DELAYED_REQUEST_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_TEST_SIMPLE_DELAYED_REQUEST_OBJS:.o=.dep) +endif + + +LIBEND2END_TEST_SIMPLE_REQUEST_SRC = \ + test/core/end2end/tests/simple_request.c \ + + +LIBEND2END_TEST_SIMPLE_REQUEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_TEST_SIMPLE_REQUEST_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a: $(ZLIB_DEP) $(LIBEND2END_TEST_SIMPLE_REQUEST_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBEND2END_TEST_SIMPLE_REQUEST_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_TEST_SIMPLE_REQUEST_OBJS:.o=.dep) +endif + + +LIBEND2END_TEST_TRAILING_METADATA_SRC = \ + test/core/end2end/tests/trailing_metadata.c \ + + +LIBEND2END_TEST_TRAILING_METADATA_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_TEST_TRAILING_METADATA_SRC)))) + +$(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a: $(ZLIB_DEP) $(LIBEND2END_TEST_TRAILING_METADATA_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBEND2END_TEST_TRAILING_METADATA_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a +endif + + + + +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_TEST_TRAILING_METADATA_OBJS:.o=.dep) +endif + + +LIBEND2END_CERTS_SRC = \ + test/core/end2end/data/test_root_cert.c \ + test/core/end2end/data/server1_cert.c \ + test/core/end2end/data/server1_key.c \ + + +LIBEND2END_CERTS_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBEND2END_CERTS_SRC)))) + +ifeq ($(NO_SECURE),true) + +# You can't build secure libraries if you don't have OpenSSL. + +$(LIBDIR)/$(CONFIG)/libend2end_certs.a: openssl_dep_error + + +else + + +$(LIBDIR)/$(CONFIG)/libend2end_certs.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBEND2END_CERTS_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_certs.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBEND2END_CERTS_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libend2end_certs.a +endif + + + + +endif + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(LIBEND2END_CERTS_OBJS:.o=.dep) +endif +endif + + +LIBBAD_CLIENT_TEST_SRC = \ + test/core/bad_client/bad_client.c \ + + +LIBBAD_CLIENT_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBBAD_CLIENT_TEST_SRC)))) + +ifeq ($(NO_SECURE),true) + +# You can't build secure libraries if you don't have OpenSSL. + +$(LIBDIR)/$(CONFIG)/libbad_client_test.a: openssl_dep_error + + +else + + +$(LIBDIR)/$(CONFIG)/libbad_client_test.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBBAD_CLIENT_TEST_OBJS) + $(E) "[AR] Creating $@" + $(Q) mkdir -p `dirname $@` + $(Q) rm -f $(LIBDIR)/$(CONFIG)/libbad_client_test.a + $(Q) $(AR) rcs $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBBAD_CLIENT_TEST_OBJS) +ifeq ($(SYSTEM),Darwin) + $(Q) ranlib $(LIBDIR)/$(CONFIG)/libbad_client_test.a +endif + + + + +endif + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(LIBBAD_CLIENT_TEST_OBJS:.o=.dep) +endif +endif + + + +# All of the test targets, and protoc plugins + + +ALARM_HEAP_TEST_SRC = \ + test/core/iomgr/alarm_heap_test.c \ + +ALARM_HEAP_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(ALARM_HEAP_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/alarm_heap_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/alarm_heap_test: $(ALARM_HEAP_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(ALARM_HEAP_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/alarm_heap_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/iomgr/alarm_heap_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_alarm_heap_test: $(ALARM_HEAP_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(ALARM_HEAP_TEST_OBJS:.o=.dep) +endif +endif + + +ALARM_LIST_TEST_SRC = \ + test/core/iomgr/alarm_list_test.c \ + +ALARM_LIST_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(ALARM_LIST_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/alarm_list_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/alarm_list_test: $(ALARM_LIST_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(ALARM_LIST_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/alarm_list_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/iomgr/alarm_list_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_alarm_list_test: $(ALARM_LIST_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(ALARM_LIST_TEST_OBJS:.o=.dep) +endif +endif + + +ALARM_TEST_SRC = \ + test/core/iomgr/alarm_test.c \ + +ALARM_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(ALARM_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/alarm_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/alarm_test: $(ALARM_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(ALARM_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/alarm_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/iomgr/alarm_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_alarm_test: $(ALARM_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(ALARM_TEST_OBJS:.o=.dep) +endif +endif + + +ALPN_TEST_SRC = \ + test/core/transport/chttp2/alpn_test.c \ + +ALPN_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(ALPN_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/alpn_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/alpn_test: $(ALPN_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(ALPN_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/alpn_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/transport/chttp2/alpn_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_alpn_test: $(ALPN_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(ALPN_TEST_OBJS:.o=.dep) +endif +endif + + +BIN_ENCODER_TEST_SRC = \ + test/core/transport/chttp2/bin_encoder_test.c \ + +BIN_ENCODER_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(BIN_ENCODER_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/bin_encoder_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/bin_encoder_test: $(BIN_ENCODER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(BIN_ENCODER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/bin_encoder_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/transport/chttp2/bin_encoder_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_bin_encoder_test: $(BIN_ENCODER_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(BIN_ENCODER_TEST_OBJS:.o=.dep) +endif +endif + + +CHTTP2_STATUS_CONVERSION_TEST_SRC = \ + test/core/transport/chttp2/status_conversion_test.c \ + +CHTTP2_STATUS_CONVERSION_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CHTTP2_STATUS_CONVERSION_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/chttp2_status_conversion_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/chttp2_status_conversion_test: $(CHTTP2_STATUS_CONVERSION_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(CHTTP2_STATUS_CONVERSION_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/chttp2_status_conversion_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/transport/chttp2/status_conversion_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_chttp2_status_conversion_test: $(CHTTP2_STATUS_CONVERSION_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(CHTTP2_STATUS_CONVERSION_TEST_OBJS:.o=.dep) +endif +endif + + +CHTTP2_STREAM_ENCODER_TEST_SRC = \ + test/core/transport/chttp2/stream_encoder_test.c \ + +CHTTP2_STREAM_ENCODER_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CHTTP2_STREAM_ENCODER_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/chttp2_stream_encoder_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/chttp2_stream_encoder_test: $(CHTTP2_STREAM_ENCODER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(CHTTP2_STREAM_ENCODER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/chttp2_stream_encoder_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/transport/chttp2/stream_encoder_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_chttp2_stream_encoder_test: $(CHTTP2_STREAM_ENCODER_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(CHTTP2_STREAM_ENCODER_TEST_OBJS:.o=.dep) +endif +endif + + +CHTTP2_STREAM_MAP_TEST_SRC = \ + test/core/transport/chttp2/stream_map_test.c \ + +CHTTP2_STREAM_MAP_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CHTTP2_STREAM_MAP_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/chttp2_stream_map_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/chttp2_stream_map_test: $(CHTTP2_STREAM_MAP_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(CHTTP2_STREAM_MAP_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/chttp2_stream_map_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/transport/chttp2/stream_map_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_chttp2_stream_map_test: $(CHTTP2_STREAM_MAP_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(CHTTP2_STREAM_MAP_TEST_OBJS:.o=.dep) +endif +endif + + +COMPRESSION_TEST_SRC = \ + test/core/compression/compression_test.c \ + +COMPRESSION_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(COMPRESSION_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/compression_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/compression_test: $(COMPRESSION_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(COMPRESSION_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/compression_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/compression/compression_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_compression_test: $(COMPRESSION_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(COMPRESSION_TEST_OBJS:.o=.dep) +endif +endif + + +DUALSTACK_SOCKET_TEST_SRC = \ + test/core/end2end/dualstack_socket_test.c \ + +DUALSTACK_SOCKET_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(DUALSTACK_SOCKET_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/dualstack_socket_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/dualstack_socket_test: $(DUALSTACK_SOCKET_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(DUALSTACK_SOCKET_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/dualstack_socket_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/end2end/dualstack_socket_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_dualstack_socket_test: $(DUALSTACK_SOCKET_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(DUALSTACK_SOCKET_TEST_OBJS:.o=.dep) +endif +endif + + +ENDPOINT_PAIR_TEST_SRC = \ + test/core/iomgr/endpoint_pair_test.c \ + +ENDPOINT_PAIR_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(ENDPOINT_PAIR_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/endpoint_pair_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/endpoint_pair_test: $(ENDPOINT_PAIR_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(ENDPOINT_PAIR_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/endpoint_pair_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/iomgr/endpoint_pair_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_endpoint_pair_test: $(ENDPOINT_PAIR_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(ENDPOINT_PAIR_TEST_OBJS:.o=.dep) +endif +endif + + +FD_CONSERVATION_POSIX_TEST_SRC = \ + test/core/iomgr/fd_conservation_posix_test.c \ + +FD_CONSERVATION_POSIX_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(FD_CONSERVATION_POSIX_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/fd_conservation_posix_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/fd_conservation_posix_test: $(FD_CONSERVATION_POSIX_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(FD_CONSERVATION_POSIX_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/fd_conservation_posix_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/iomgr/fd_conservation_posix_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_fd_conservation_posix_test: $(FD_CONSERVATION_POSIX_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(FD_CONSERVATION_POSIX_TEST_OBJS:.o=.dep) +endif +endif + + +FD_POSIX_TEST_SRC = \ + test/core/iomgr/fd_posix_test.c \ + +FD_POSIX_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(FD_POSIX_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/fd_posix_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/fd_posix_test: $(FD_POSIX_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(FD_POSIX_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/fd_posix_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/iomgr/fd_posix_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_fd_posix_test: $(FD_POSIX_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(FD_POSIX_TEST_OBJS:.o=.dep) +endif +endif + + +FLING_CLIENT_SRC = \ + test/core/fling/client.c \ + +FLING_CLIENT_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(FLING_CLIENT_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/fling_client: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/fling_client: $(FLING_CLIENT_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(FLING_CLIENT_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/fling_client + +endif + +$(OBJDIR)/$(CONFIG)/test/core/fling/client.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_fling_client: $(FLING_CLIENT_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(FLING_CLIENT_OBJS:.o=.dep) +endif +endif + + +FLING_SERVER_SRC = \ + test/core/fling/server.c \ + +FLING_SERVER_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(FLING_SERVER_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/fling_server: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/fling_server: $(FLING_SERVER_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(FLING_SERVER_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/fling_server + +endif + +$(OBJDIR)/$(CONFIG)/test/core/fling/server.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_fling_server: $(FLING_SERVER_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(FLING_SERVER_OBJS:.o=.dep) +endif +endif + + +FLING_STREAM_TEST_SRC = \ + test/core/fling/fling_stream_test.c \ + +FLING_STREAM_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(FLING_STREAM_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/fling_stream_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/fling_stream_test: $(FLING_STREAM_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(FLING_STREAM_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/fling_stream_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/fling/fling_stream_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_fling_stream_test: $(FLING_STREAM_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(FLING_STREAM_TEST_OBJS:.o=.dep) +endif +endif + + +FLING_TEST_SRC = \ + test/core/fling/fling_test.c \ + +FLING_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(FLING_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/fling_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/fling_test: $(FLING_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(FLING_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/fling_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/fling/fling_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_fling_test: $(FLING_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(FLING_TEST_OBJS:.o=.dep) +endif +endif + + +GEN_HPACK_TABLES_SRC = \ + tools/codegen/core/gen_hpack_tables.c \ + +GEN_HPACK_TABLES_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GEN_HPACK_TABLES_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/gen_hpack_tables: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/gen_hpack_tables: $(GEN_HPACK_TABLES_OBJS) $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GEN_HPACK_TABLES_OBJS) $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/gen_hpack_tables + +endif + +$(OBJDIR)/$(CONFIG)/tools/codegen/core/gen_hpack_tables.o: $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a +deps_gen_hpack_tables: $(GEN_HPACK_TABLES_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GEN_HPACK_TABLES_OBJS:.o=.dep) +endif +endif + + +GEN_LEGAL_METADATA_CHARACTERS_SRC = \ + tools/codegen/core/gen_legal_metadata_characters.c \ + +GEN_LEGAL_METADATA_CHARACTERS_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GEN_LEGAL_METADATA_CHARACTERS_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/gen_legal_metadata_characters: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/gen_legal_metadata_characters: $(GEN_LEGAL_METADATA_CHARACTERS_OBJS) + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GEN_LEGAL_METADATA_CHARACTERS_OBJS) $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/gen_legal_metadata_characters + +endif + +$(OBJDIR)/$(CONFIG)/tools/codegen/core/gen_legal_metadata_characters.o: +deps_gen_legal_metadata_characters: $(GEN_LEGAL_METADATA_CHARACTERS_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GEN_LEGAL_METADATA_CHARACTERS_OBJS:.o=.dep) +endif +endif + + +GPR_CMDLINE_TEST_SRC = \ + test/core/support/cmdline_test.c \ + +GPR_CMDLINE_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GPR_CMDLINE_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/gpr_cmdline_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/gpr_cmdline_test: $(GPR_CMDLINE_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GPR_CMDLINE_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/gpr_cmdline_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/support/cmdline_test.o: $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_gpr_cmdline_test: $(GPR_CMDLINE_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GPR_CMDLINE_TEST_OBJS:.o=.dep) +endif +endif + + +GPR_ENV_TEST_SRC = \ + test/core/support/env_test.c \ + +GPR_ENV_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GPR_ENV_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/gpr_env_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/gpr_env_test: $(GPR_ENV_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GPR_ENV_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/gpr_env_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/support/env_test.o: $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_gpr_env_test: $(GPR_ENV_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GPR_ENV_TEST_OBJS:.o=.dep) +endif +endif + + +GPR_FILE_TEST_SRC = \ + test/core/support/file_test.c \ + +GPR_FILE_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GPR_FILE_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/gpr_file_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/gpr_file_test: $(GPR_FILE_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GPR_FILE_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/gpr_file_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/support/file_test.o: $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_gpr_file_test: $(GPR_FILE_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GPR_FILE_TEST_OBJS:.o=.dep) +endif +endif + + +GPR_HISTOGRAM_TEST_SRC = \ + test/core/support/histogram_test.c \ + +GPR_HISTOGRAM_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GPR_HISTOGRAM_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/gpr_histogram_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/gpr_histogram_test: $(GPR_HISTOGRAM_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GPR_HISTOGRAM_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/gpr_histogram_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/support/histogram_test.o: $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_gpr_histogram_test: $(GPR_HISTOGRAM_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GPR_HISTOGRAM_TEST_OBJS:.o=.dep) +endif +endif + + +GPR_HOST_PORT_TEST_SRC = \ + test/core/support/host_port_test.c \ + +GPR_HOST_PORT_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GPR_HOST_PORT_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/gpr_host_port_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/gpr_host_port_test: $(GPR_HOST_PORT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GPR_HOST_PORT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/gpr_host_port_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/support/host_port_test.o: $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_gpr_host_port_test: $(GPR_HOST_PORT_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GPR_HOST_PORT_TEST_OBJS:.o=.dep) +endif +endif + + +GPR_LOG_TEST_SRC = \ + test/core/support/log_test.c \ + +GPR_LOG_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GPR_LOG_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/gpr_log_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/gpr_log_test: $(GPR_LOG_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GPR_LOG_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/gpr_log_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/support/log_test.o: $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_gpr_log_test: $(GPR_LOG_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GPR_LOG_TEST_OBJS:.o=.dep) +endif +endif + + +GPR_SLICE_BUFFER_TEST_SRC = \ + test/core/support/slice_buffer_test.c \ + +GPR_SLICE_BUFFER_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GPR_SLICE_BUFFER_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/gpr_slice_buffer_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/gpr_slice_buffer_test: $(GPR_SLICE_BUFFER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GPR_SLICE_BUFFER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/gpr_slice_buffer_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/support/slice_buffer_test.o: $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_gpr_slice_buffer_test: $(GPR_SLICE_BUFFER_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GPR_SLICE_BUFFER_TEST_OBJS:.o=.dep) +endif +endif + + +GPR_SLICE_TEST_SRC = \ + test/core/support/slice_test.c \ + +GPR_SLICE_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GPR_SLICE_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/gpr_slice_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/gpr_slice_test: $(GPR_SLICE_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GPR_SLICE_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/gpr_slice_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/support/slice_test.o: $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_gpr_slice_test: $(GPR_SLICE_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GPR_SLICE_TEST_OBJS:.o=.dep) +endif +endif + + +GPR_STACK_LOCKFREE_TEST_SRC = \ + test/core/support/stack_lockfree_test.c \ + +GPR_STACK_LOCKFREE_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GPR_STACK_LOCKFREE_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/gpr_stack_lockfree_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/gpr_stack_lockfree_test: $(GPR_STACK_LOCKFREE_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GPR_STACK_LOCKFREE_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/gpr_stack_lockfree_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/support/stack_lockfree_test.o: $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_gpr_stack_lockfree_test: $(GPR_STACK_LOCKFREE_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GPR_STACK_LOCKFREE_TEST_OBJS:.o=.dep) +endif +endif + + +GPR_STRING_TEST_SRC = \ + test/core/support/string_test.c \ + +GPR_STRING_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GPR_STRING_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/gpr_string_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/gpr_string_test: $(GPR_STRING_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GPR_STRING_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/gpr_string_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/support/string_test.o: $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_gpr_string_test: $(GPR_STRING_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GPR_STRING_TEST_OBJS:.o=.dep) +endif +endif + + +GPR_SYNC_TEST_SRC = \ + test/core/support/sync_test.c \ + +GPR_SYNC_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GPR_SYNC_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/gpr_sync_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/gpr_sync_test: $(GPR_SYNC_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GPR_SYNC_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/gpr_sync_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/support/sync_test.o: $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_gpr_sync_test: $(GPR_SYNC_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GPR_SYNC_TEST_OBJS:.o=.dep) +endif +endif + + +GPR_THD_TEST_SRC = \ + test/core/support/thd_test.c \ + +GPR_THD_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GPR_THD_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/gpr_thd_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/gpr_thd_test: $(GPR_THD_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GPR_THD_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/gpr_thd_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/support/thd_test.o: $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_gpr_thd_test: $(GPR_THD_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GPR_THD_TEST_OBJS:.o=.dep) +endif +endif + + +GPR_TIME_TEST_SRC = \ + test/core/support/time_test.c \ + +GPR_TIME_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GPR_TIME_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/gpr_time_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/gpr_time_test: $(GPR_TIME_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GPR_TIME_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/gpr_time_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/support/time_test.o: $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_gpr_time_test: $(GPR_TIME_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GPR_TIME_TEST_OBJS:.o=.dep) +endif +endif + + +GPR_TLS_TEST_SRC = \ + test/core/support/tls_test.c \ + +GPR_TLS_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GPR_TLS_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/gpr_tls_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/gpr_tls_test: $(GPR_TLS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GPR_TLS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/gpr_tls_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/support/tls_test.o: $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_gpr_tls_test: $(GPR_TLS_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GPR_TLS_TEST_OBJS:.o=.dep) +endif +endif + + +GPR_USEFUL_TEST_SRC = \ + test/core/support/useful_test.c \ + +GPR_USEFUL_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GPR_USEFUL_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/gpr_useful_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/gpr_useful_test: $(GPR_USEFUL_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GPR_USEFUL_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/gpr_useful_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/support/useful_test.o: $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_gpr_useful_test: $(GPR_USEFUL_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GPR_USEFUL_TEST_OBJS:.o=.dep) +endif +endif + + +GRPC_AUTH_CONTEXT_TEST_SRC = \ + test/core/security/auth_context_test.c \ + +GRPC_AUTH_CONTEXT_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GRPC_AUTH_CONTEXT_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/grpc_auth_context_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/grpc_auth_context_test: $(GRPC_AUTH_CONTEXT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GRPC_AUTH_CONTEXT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/grpc_auth_context_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/security/auth_context_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_grpc_auth_context_test: $(GRPC_AUTH_CONTEXT_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GRPC_AUTH_CONTEXT_TEST_OBJS:.o=.dep) +endif +endif + + +GRPC_BASE64_TEST_SRC = \ + test/core/security/base64_test.c \ + +GRPC_BASE64_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GRPC_BASE64_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/grpc_base64_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/grpc_base64_test: $(GRPC_BASE64_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GRPC_BASE64_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/grpc_base64_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/security/base64_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_grpc_base64_test: $(GRPC_BASE64_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GRPC_BASE64_TEST_OBJS:.o=.dep) +endif +endif + + +GRPC_BYTE_BUFFER_READER_TEST_SRC = \ + test/core/surface/byte_buffer_reader_test.c \ + +GRPC_BYTE_BUFFER_READER_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GRPC_BYTE_BUFFER_READER_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/grpc_byte_buffer_reader_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/grpc_byte_buffer_reader_test: $(GRPC_BYTE_BUFFER_READER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GRPC_BYTE_BUFFER_READER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/grpc_byte_buffer_reader_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/surface/byte_buffer_reader_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_grpc_byte_buffer_reader_test: $(GRPC_BYTE_BUFFER_READER_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GRPC_BYTE_BUFFER_READER_TEST_OBJS:.o=.dep) +endif +endif + + +GRPC_CHANNEL_ARGS_TEST_SRC = \ + test/core/channel/channel_args_test.c \ + +GRPC_CHANNEL_ARGS_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GRPC_CHANNEL_ARGS_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/grpc_channel_args_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/grpc_channel_args_test: $(GRPC_CHANNEL_ARGS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GRPC_CHANNEL_ARGS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/grpc_channel_args_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/channel/channel_args_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_grpc_channel_args_test: $(GRPC_CHANNEL_ARGS_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GRPC_CHANNEL_ARGS_TEST_OBJS:.o=.dep) +endif +endif + + +GRPC_CHANNEL_STACK_TEST_SRC = \ + test/core/channel/channel_stack_test.c \ + +GRPC_CHANNEL_STACK_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GRPC_CHANNEL_STACK_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/grpc_channel_stack_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/grpc_channel_stack_test: $(GRPC_CHANNEL_STACK_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GRPC_CHANNEL_STACK_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/grpc_channel_stack_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/channel/channel_stack_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_grpc_channel_stack_test: $(GRPC_CHANNEL_STACK_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GRPC_CHANNEL_STACK_TEST_OBJS:.o=.dep) +endif +endif + + +GRPC_COMPLETION_QUEUE_TEST_SRC = \ + test/core/surface/completion_queue_test.c \ + +GRPC_COMPLETION_QUEUE_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GRPC_COMPLETION_QUEUE_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/grpc_completion_queue_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/grpc_completion_queue_test: $(GRPC_COMPLETION_QUEUE_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GRPC_COMPLETION_QUEUE_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/grpc_completion_queue_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/surface/completion_queue_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_grpc_completion_queue_test: $(GRPC_COMPLETION_QUEUE_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GRPC_COMPLETION_QUEUE_TEST_OBJS:.o=.dep) +endif +endif + + +GRPC_CREATE_JWT_SRC = \ + test/core/security/create_jwt.c \ + +GRPC_CREATE_JWT_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GRPC_CREATE_JWT_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/grpc_create_jwt: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/grpc_create_jwt: $(GRPC_CREATE_JWT_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GRPC_CREATE_JWT_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/grpc_create_jwt + +endif + +$(OBJDIR)/$(CONFIG)/test/core/security/create_jwt.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_grpc_create_jwt: $(GRPC_CREATE_JWT_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GRPC_CREATE_JWT_OBJS:.o=.dep) +endif +endif + + +GRPC_CREDENTIALS_TEST_SRC = \ + test/core/security/credentials_test.c \ + +GRPC_CREDENTIALS_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GRPC_CREDENTIALS_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/grpc_credentials_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/grpc_credentials_test: $(GRPC_CREDENTIALS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GRPC_CREDENTIALS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/grpc_credentials_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/security/credentials_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_grpc_credentials_test: $(GRPC_CREDENTIALS_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GRPC_CREDENTIALS_TEST_OBJS:.o=.dep) +endif +endif + + +GRPC_FETCH_OAUTH2_SRC = \ + test/core/security/fetch_oauth2.c \ + +GRPC_FETCH_OAUTH2_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GRPC_FETCH_OAUTH2_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/grpc_fetch_oauth2: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/grpc_fetch_oauth2: $(GRPC_FETCH_OAUTH2_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GRPC_FETCH_OAUTH2_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/grpc_fetch_oauth2 + +endif + +$(OBJDIR)/$(CONFIG)/test/core/security/fetch_oauth2.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_grpc_fetch_oauth2: $(GRPC_FETCH_OAUTH2_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GRPC_FETCH_OAUTH2_OBJS:.o=.dep) +endif +endif + + +GRPC_JSON_TOKEN_TEST_SRC = \ + test/core/security/json_token_test.c \ + +GRPC_JSON_TOKEN_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GRPC_JSON_TOKEN_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/grpc_json_token_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/grpc_json_token_test: $(GRPC_JSON_TOKEN_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GRPC_JSON_TOKEN_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/grpc_json_token_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/security/json_token_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_grpc_json_token_test: $(GRPC_JSON_TOKEN_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GRPC_JSON_TOKEN_TEST_OBJS:.o=.dep) +endif +endif + + +GRPC_JWT_VERIFIER_TEST_SRC = \ + test/core/security/jwt_verifier_test.c \ + +GRPC_JWT_VERIFIER_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GRPC_JWT_VERIFIER_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/grpc_jwt_verifier_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/grpc_jwt_verifier_test: $(GRPC_JWT_VERIFIER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GRPC_JWT_VERIFIER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/grpc_jwt_verifier_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/security/jwt_verifier_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_grpc_jwt_verifier_test: $(GRPC_JWT_VERIFIER_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GRPC_JWT_VERIFIER_TEST_OBJS:.o=.dep) +endif +endif + + +GRPC_PRINT_GOOGLE_DEFAULT_CREDS_TOKEN_SRC = \ + test/core/security/print_google_default_creds_token.c \ + +GRPC_PRINT_GOOGLE_DEFAULT_CREDS_TOKEN_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GRPC_PRINT_GOOGLE_DEFAULT_CREDS_TOKEN_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/grpc_print_google_default_creds_token: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/grpc_print_google_default_creds_token: $(GRPC_PRINT_GOOGLE_DEFAULT_CREDS_TOKEN_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GRPC_PRINT_GOOGLE_DEFAULT_CREDS_TOKEN_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/grpc_print_google_default_creds_token + +endif + +$(OBJDIR)/$(CONFIG)/test/core/security/print_google_default_creds_token.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_grpc_print_google_default_creds_token: $(GRPC_PRINT_GOOGLE_DEFAULT_CREDS_TOKEN_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GRPC_PRINT_GOOGLE_DEFAULT_CREDS_TOKEN_OBJS:.o=.dep) +endif +endif + + +GRPC_SECURITY_CONNECTOR_TEST_SRC = \ + test/core/security/security_connector_test.c \ + +GRPC_SECURITY_CONNECTOR_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GRPC_SECURITY_CONNECTOR_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/grpc_security_connector_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/grpc_security_connector_test: $(GRPC_SECURITY_CONNECTOR_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GRPC_SECURITY_CONNECTOR_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/grpc_security_connector_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/security/security_connector_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_grpc_security_connector_test: $(GRPC_SECURITY_CONNECTOR_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GRPC_SECURITY_CONNECTOR_TEST_OBJS:.o=.dep) +endif +endif + + +GRPC_STREAM_OP_TEST_SRC = \ + test/core/transport/stream_op_test.c \ + +GRPC_STREAM_OP_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GRPC_STREAM_OP_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/grpc_stream_op_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/grpc_stream_op_test: $(GRPC_STREAM_OP_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GRPC_STREAM_OP_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/grpc_stream_op_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/transport/stream_op_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_grpc_stream_op_test: $(GRPC_STREAM_OP_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GRPC_STREAM_OP_TEST_OBJS:.o=.dep) +endif +endif + + +GRPC_VERIFY_JWT_SRC = \ + test/core/security/verify_jwt.c \ + +GRPC_VERIFY_JWT_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GRPC_VERIFY_JWT_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/grpc_verify_jwt: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/grpc_verify_jwt: $(GRPC_VERIFY_JWT_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GRPC_VERIFY_JWT_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/grpc_verify_jwt + +endif + +$(OBJDIR)/$(CONFIG)/test/core/security/verify_jwt.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_grpc_verify_jwt: $(GRPC_VERIFY_JWT_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GRPC_VERIFY_JWT_OBJS:.o=.dep) +endif +endif + + +HPACK_PARSER_TEST_SRC = \ + test/core/transport/chttp2/hpack_parser_test.c \ + +HPACK_PARSER_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(HPACK_PARSER_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/hpack_parser_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/hpack_parser_test: $(HPACK_PARSER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(HPACK_PARSER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/hpack_parser_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/transport/chttp2/hpack_parser_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_hpack_parser_test: $(HPACK_PARSER_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(HPACK_PARSER_TEST_OBJS:.o=.dep) +endif +endif + + +HPACK_TABLE_TEST_SRC = \ + test/core/transport/chttp2/hpack_table_test.c \ + +HPACK_TABLE_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(HPACK_TABLE_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/hpack_table_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/hpack_table_test: $(HPACK_TABLE_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(HPACK_TABLE_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/hpack_table_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/transport/chttp2/hpack_table_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_hpack_table_test: $(HPACK_TABLE_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(HPACK_TABLE_TEST_OBJS:.o=.dep) +endif +endif + + +HTTPCLI_FORMAT_REQUEST_TEST_SRC = \ + test/core/httpcli/format_request_test.c \ + +HTTPCLI_FORMAT_REQUEST_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(HTTPCLI_FORMAT_REQUEST_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/httpcli_format_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/httpcli_format_request_test: $(HTTPCLI_FORMAT_REQUEST_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(HTTPCLI_FORMAT_REQUEST_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/httpcli_format_request_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/httpcli/format_request_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_httpcli_format_request_test: $(HTTPCLI_FORMAT_REQUEST_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(HTTPCLI_FORMAT_REQUEST_TEST_OBJS:.o=.dep) +endif +endif + + +HTTPCLI_PARSER_TEST_SRC = \ + test/core/httpcli/parser_test.c \ + +HTTPCLI_PARSER_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(HTTPCLI_PARSER_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/httpcli_parser_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/httpcli_parser_test: $(HTTPCLI_PARSER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(HTTPCLI_PARSER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/httpcli_parser_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/httpcli/parser_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_httpcli_parser_test: $(HTTPCLI_PARSER_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(HTTPCLI_PARSER_TEST_OBJS:.o=.dep) +endif +endif + + +HTTPCLI_TEST_SRC = \ + test/core/httpcli/httpcli_test.c \ + +HTTPCLI_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(HTTPCLI_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/httpcli_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/httpcli_test: $(HTTPCLI_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(HTTPCLI_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/httpcli_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/httpcli/httpcli_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_httpcli_test: $(HTTPCLI_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(HTTPCLI_TEST_OBJS:.o=.dep) +endif +endif + + +JSON_REWRITE_SRC = \ + test/core/json/json_rewrite.c \ + +JSON_REWRITE_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(JSON_REWRITE_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/json_rewrite: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/json_rewrite: $(JSON_REWRITE_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(JSON_REWRITE_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/json_rewrite + +endif + +$(OBJDIR)/$(CONFIG)/test/core/json/json_rewrite.o: $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_json_rewrite: $(JSON_REWRITE_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(JSON_REWRITE_OBJS:.o=.dep) +endif +endif + + +JSON_REWRITE_TEST_SRC = \ + test/core/json/json_rewrite_test.c \ + +JSON_REWRITE_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(JSON_REWRITE_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/json_rewrite_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/json_rewrite_test: $(JSON_REWRITE_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(JSON_REWRITE_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/json_rewrite_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/json/json_rewrite_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_json_rewrite_test: $(JSON_REWRITE_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(JSON_REWRITE_TEST_OBJS:.o=.dep) +endif +endif + + +JSON_TEST_SRC = \ + test/core/json/json_test.c \ + +JSON_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(JSON_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/json_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/json_test: $(JSON_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(JSON_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/json_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/json/json_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_json_test: $(JSON_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(JSON_TEST_OBJS:.o=.dep) +endif +endif + + +LAME_CLIENT_TEST_SRC = \ + test/core/surface/lame_client_test.c \ + +LAME_CLIENT_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LAME_CLIENT_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/lame_client_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/lame_client_test: $(LAME_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LAME_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/lame_client_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/surface/lame_client_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_lame_client_test: $(LAME_CLIENT_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(LAME_CLIENT_TEST_OBJS:.o=.dep) +endif +endif + + +LOW_LEVEL_PING_PONG_BENCHMARK_SRC = \ + test/core/network_benchmarks/low_level_ping_pong.c \ + +LOW_LEVEL_PING_PONG_BENCHMARK_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LOW_LEVEL_PING_PONG_BENCHMARK_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/low_level_ping_pong_benchmark: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/low_level_ping_pong_benchmark: $(LOW_LEVEL_PING_PONG_BENCHMARK_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LOW_LEVEL_PING_PONG_BENCHMARK_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/low_level_ping_pong_benchmark + +endif + +$(OBJDIR)/$(CONFIG)/test/core/network_benchmarks/low_level_ping_pong.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_low_level_ping_pong_benchmark: $(LOW_LEVEL_PING_PONG_BENCHMARK_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(LOW_LEVEL_PING_PONG_BENCHMARK_OBJS:.o=.dep) +endif +endif + + +MESSAGE_COMPRESS_TEST_SRC = \ + test/core/compression/message_compress_test.c \ + +MESSAGE_COMPRESS_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(MESSAGE_COMPRESS_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/message_compress_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/message_compress_test: $(MESSAGE_COMPRESS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(MESSAGE_COMPRESS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/message_compress_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/compression/message_compress_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_message_compress_test: $(MESSAGE_COMPRESS_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(MESSAGE_COMPRESS_TEST_OBJS:.o=.dep) +endif +endif + + +MULTI_INIT_TEST_SRC = \ + test/core/surface/multi_init_test.c \ + +MULTI_INIT_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(MULTI_INIT_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/multi_init_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/multi_init_test: $(MULTI_INIT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(MULTI_INIT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/multi_init_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/surface/multi_init_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_multi_init_test: $(MULTI_INIT_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(MULTI_INIT_TEST_OBJS:.o=.dep) +endif +endif + + +MULTIPLE_SERVER_QUEUES_TEST_SRC = \ + test/core/end2end/multiple_server_queues_test.c \ + +MULTIPLE_SERVER_QUEUES_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(MULTIPLE_SERVER_QUEUES_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/multiple_server_queues_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/multiple_server_queues_test: $(MULTIPLE_SERVER_QUEUES_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(MULTIPLE_SERVER_QUEUES_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/multiple_server_queues_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/end2end/multiple_server_queues_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_multiple_server_queues_test: $(MULTIPLE_SERVER_QUEUES_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(MULTIPLE_SERVER_QUEUES_TEST_OBJS:.o=.dep) +endif +endif + + +MURMUR_HASH_TEST_SRC = \ + test/core/support/murmur_hash_test.c \ + +MURMUR_HASH_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(MURMUR_HASH_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/murmur_hash_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/murmur_hash_test: $(MURMUR_HASH_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(MURMUR_HASH_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/murmur_hash_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/support/murmur_hash_test.o: $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_murmur_hash_test: $(MURMUR_HASH_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(MURMUR_HASH_TEST_OBJS:.o=.dep) +endif +endif + + +NO_SERVER_TEST_SRC = \ + test/core/end2end/no_server_test.c \ + +NO_SERVER_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(NO_SERVER_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/no_server_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/no_server_test: $(NO_SERVER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(NO_SERVER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/no_server_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/end2end/no_server_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_no_server_test: $(NO_SERVER_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(NO_SERVER_TEST_OBJS:.o=.dep) +endif +endif + + +RESOLVE_ADDRESS_TEST_SRC = \ + test/core/iomgr/resolve_address_test.c \ + +RESOLVE_ADDRESS_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(RESOLVE_ADDRESS_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/resolve_address_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/resolve_address_test: $(RESOLVE_ADDRESS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(RESOLVE_ADDRESS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/resolve_address_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/iomgr/resolve_address_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_resolve_address_test: $(RESOLVE_ADDRESS_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(RESOLVE_ADDRESS_TEST_OBJS:.o=.dep) +endif +endif + + +SECURE_ENDPOINT_TEST_SRC = \ + test/core/security/secure_endpoint_test.c \ + +SECURE_ENDPOINT_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(SECURE_ENDPOINT_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/secure_endpoint_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/secure_endpoint_test: $(SECURE_ENDPOINT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(SECURE_ENDPOINT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/secure_endpoint_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/security/secure_endpoint_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_secure_endpoint_test: $(SECURE_ENDPOINT_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(SECURE_ENDPOINT_TEST_OBJS:.o=.dep) +endif +endif + + +SOCKADDR_UTILS_TEST_SRC = \ + test/core/iomgr/sockaddr_utils_test.c \ + +SOCKADDR_UTILS_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(SOCKADDR_UTILS_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/sockaddr_utils_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/sockaddr_utils_test: $(SOCKADDR_UTILS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(SOCKADDR_UTILS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/sockaddr_utils_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/iomgr/sockaddr_utils_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_sockaddr_utils_test: $(SOCKADDR_UTILS_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(SOCKADDR_UTILS_TEST_OBJS:.o=.dep) +endif +endif + + +TCP_CLIENT_POSIX_TEST_SRC = \ + test/core/iomgr/tcp_client_posix_test.c \ + +TCP_CLIENT_POSIX_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(TCP_CLIENT_POSIX_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/tcp_client_posix_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/tcp_client_posix_test: $(TCP_CLIENT_POSIX_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(TCP_CLIENT_POSIX_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/tcp_client_posix_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/iomgr/tcp_client_posix_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_tcp_client_posix_test: $(TCP_CLIENT_POSIX_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(TCP_CLIENT_POSIX_TEST_OBJS:.o=.dep) +endif +endif + + +TCP_POSIX_TEST_SRC = \ + test/core/iomgr/tcp_posix_test.c \ + +TCP_POSIX_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(TCP_POSIX_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/tcp_posix_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/tcp_posix_test: $(TCP_POSIX_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(TCP_POSIX_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/tcp_posix_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/iomgr/tcp_posix_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_tcp_posix_test: $(TCP_POSIX_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(TCP_POSIX_TEST_OBJS:.o=.dep) +endif +endif + + +TCP_SERVER_POSIX_TEST_SRC = \ + test/core/iomgr/tcp_server_posix_test.c \ + +TCP_SERVER_POSIX_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(TCP_SERVER_POSIX_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/tcp_server_posix_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/tcp_server_posix_test: $(TCP_SERVER_POSIX_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(TCP_SERVER_POSIX_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/tcp_server_posix_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/iomgr/tcp_server_posix_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_tcp_server_posix_test: $(TCP_SERVER_POSIX_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(TCP_SERVER_POSIX_TEST_OBJS:.o=.dep) +endif +endif + + +TIME_AVERAGED_STATS_TEST_SRC = \ + test/core/iomgr/time_averaged_stats_test.c \ + +TIME_AVERAGED_STATS_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(TIME_AVERAGED_STATS_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/time_averaged_stats_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/time_averaged_stats_test: $(TIME_AVERAGED_STATS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(TIME_AVERAGED_STATS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/time_averaged_stats_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/iomgr/time_averaged_stats_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_time_averaged_stats_test: $(TIME_AVERAGED_STATS_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(TIME_AVERAGED_STATS_TEST_OBJS:.o=.dep) +endif +endif + + +TIMEOUT_ENCODING_TEST_SRC = \ + test/core/transport/chttp2/timeout_encoding_test.c \ + +TIMEOUT_ENCODING_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(TIMEOUT_ENCODING_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/timeout_encoding_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/timeout_encoding_test: $(TIMEOUT_ENCODING_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(TIMEOUT_ENCODING_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/timeout_encoding_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/transport/chttp2/timeout_encoding_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_timeout_encoding_test: $(TIMEOUT_ENCODING_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(TIMEOUT_ENCODING_TEST_OBJS:.o=.dep) +endif +endif + + +TIMERS_TEST_SRC = \ + test/core/profiling/timers_test.c \ + +TIMERS_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(TIMERS_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/timers_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/timers_test: $(TIMERS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(TIMERS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/timers_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/profiling/timers_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_timers_test: $(TIMERS_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(TIMERS_TEST_OBJS:.o=.dep) +endif +endif + + +TRANSPORT_METADATA_TEST_SRC = \ + test/core/transport/metadata_test.c \ + +TRANSPORT_METADATA_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(TRANSPORT_METADATA_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/transport_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/transport_metadata_test: $(TRANSPORT_METADATA_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(TRANSPORT_METADATA_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/transport_metadata_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/transport/metadata_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_transport_metadata_test: $(TRANSPORT_METADATA_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(TRANSPORT_METADATA_TEST_OBJS:.o=.dep) +endif +endif + + +TRANSPORT_SECURITY_TEST_SRC = \ + test/core/tsi/transport_security_test.c \ + +TRANSPORT_SECURITY_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(TRANSPORT_SECURITY_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/transport_security_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/transport_security_test: $(TRANSPORT_SECURITY_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(TRANSPORT_SECURITY_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/transport_security_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/tsi/transport_security_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_transport_security_test: $(TRANSPORT_SECURITY_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(TRANSPORT_SECURITY_TEST_OBJS:.o=.dep) +endif +endif + + +UDP_SERVER_TEST_SRC = \ + test/core/iomgr/udp_server_test.c \ + +UDP_SERVER_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(UDP_SERVER_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/udp_server_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/udp_server_test: $(UDP_SERVER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(UDP_SERVER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/udp_server_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/iomgr/udp_server_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_udp_server_test: $(UDP_SERVER_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(UDP_SERVER_TEST_OBJS:.o=.dep) +endif +endif + + +URI_PARSER_TEST_SRC = \ + test/core/client_config/uri_parser_test.c \ + +URI_PARSER_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(URI_PARSER_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/uri_parser_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/uri_parser_test: $(URI_PARSER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(URI_PARSER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/uri_parser_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/client_config/uri_parser_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_uri_parser_test: $(URI_PARSER_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(URI_PARSER_TEST_OBJS:.o=.dep) +endif +endif + + +ASYNC_END2END_TEST_SRC = \ + test/cpp/end2end/async_end2end_test.cc \ + +ASYNC_END2END_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(ASYNC_END2END_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/async_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)/async_end2end_test: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/async_end2end_test: $(PROTOBUF_DEP) $(ASYNC_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) $(ASYNC_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)/async_end2end_test + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/end2end/async_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_async_end2end_test: $(ASYNC_END2END_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(ASYNC_END2END_TEST_OBJS:.o=.dep) +endif +endif + + +ASYNC_STREAMING_PING_PONG_TEST_SRC = \ + test/cpp/qps/async_streaming_ping_pong_test.cc \ + +ASYNC_STREAMING_PING_PONG_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(ASYNC_STREAMING_PING_PONG_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/async_streaming_ping_pong_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)/async_streaming_ping_pong_test: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/async_streaming_ping_pong_test: $(PROTOBUF_DEP) $(ASYNC_STREAMING_PING_PONG_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libqps.a $(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) $(ASYNC_STREAMING_PING_PONG_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libqps.a $(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)/async_streaming_ping_pong_test + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/qps/async_streaming_ping_pong_test.o: $(LIBDIR)/$(CONFIG)/libqps.a $(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_async_streaming_ping_pong_test: $(ASYNC_STREAMING_PING_PONG_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(ASYNC_STREAMING_PING_PONG_TEST_OBJS:.o=.dep) +endif +endif + + +ASYNC_UNARY_PING_PONG_TEST_SRC = \ + test/cpp/qps/async_unary_ping_pong_test.cc \ + +ASYNC_UNARY_PING_PONG_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(ASYNC_UNARY_PING_PONG_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/async_unary_ping_pong_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)/async_unary_ping_pong_test: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/async_unary_ping_pong_test: $(PROTOBUF_DEP) $(ASYNC_UNARY_PING_PONG_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libqps.a $(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) $(ASYNC_UNARY_PING_PONG_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libqps.a $(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)/async_unary_ping_pong_test + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/qps/async_unary_ping_pong_test.o: $(LIBDIR)/$(CONFIG)/libqps.a $(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_async_unary_ping_pong_test: $(ASYNC_UNARY_PING_PONG_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(ASYNC_UNARY_PING_PONG_TEST_OBJS:.o=.dep) +endif +endif + + +AUTH_PROPERTY_ITERATOR_TEST_SRC = \ + test/cpp/common/auth_property_iterator_test.cc \ + +AUTH_PROPERTY_ITERATOR_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(AUTH_PROPERTY_ITERATOR_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/auth_property_iterator_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)/auth_property_iterator_test: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/auth_property_iterator_test: $(PROTOBUF_DEP) $(AUTH_PROPERTY_ITERATOR_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) $(AUTH_PROPERTY_ITERATOR_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)/auth_property_iterator_test + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/common/auth_property_iterator_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_auth_property_iterator_test: $(AUTH_PROPERTY_ITERATOR_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(AUTH_PROPERTY_ITERATOR_TEST_OBJS:.o=.dep) +endif +endif + + +CHANNEL_ARGUMENTS_TEST_SRC = \ + test/cpp/client/channel_arguments_test.cc \ + +CHANNEL_ARGUMENTS_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CHANNEL_ARGUMENTS_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/channel_arguments_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)/channel_arguments_test: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/channel_arguments_test: $(PROTOBUF_DEP) $(CHANNEL_ARGUMENTS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LDXX) $(LDFLAGS) $(CHANNEL_ARGUMENTS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/channel_arguments_test + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/client/channel_arguments_test.o: $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_channel_arguments_test: $(CHANNEL_ARGUMENTS_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(CHANNEL_ARGUMENTS_TEST_OBJS:.o=.dep) +endif +endif + + +CLI_CALL_TEST_SRC = \ + test/cpp/util/cli_call_test.cc \ + +CLI_CALL_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CLI_CALL_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/cli_call_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)/cli_call_test: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/cli_call_test: $(PROTOBUF_DEP) $(CLI_CALL_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) $(CLI_CALL_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)/cli_call_test + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/util/cli_call_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_cli_call_test: $(CLI_CALL_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(CLI_CALL_TEST_OBJS:.o=.dep) +endif +endif + + +CLIENT_CRASH_TEST_SRC = \ + test/cpp/end2end/client_crash_test.cc \ + +CLIENT_CRASH_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CLIENT_CRASH_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/client_crash_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)/client_crash_test: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/client_crash_test: $(PROTOBUF_DEP) $(CLIENT_CRASH_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) $(CLIENT_CRASH_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)/client_crash_test + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/end2end/client_crash_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_client_crash_test: $(CLIENT_CRASH_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(CLIENT_CRASH_TEST_OBJS:.o=.dep) +endif +endif + + +CLIENT_CRASH_TEST_SERVER_SRC = \ + test/cpp/end2end/client_crash_test_server.cc \ + +CLIENT_CRASH_TEST_SERVER_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CLIENT_CRASH_TEST_SERVER_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/client_crash_test_server: 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)/client_crash_test_server: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/client_crash_test_server: $(PROTOBUF_DEP) $(CLIENT_CRASH_TEST_SERVER_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) $(CLIENT_CRASH_TEST_SERVER_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)/client_crash_test_server + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/end2end/client_crash_test_server.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_client_crash_test_server: $(CLIENT_CRASH_TEST_SERVER_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(CLIENT_CRASH_TEST_SERVER_OBJS:.o=.dep) +endif +endif + + +CREDENTIALS_TEST_SRC = \ + test/cpp/client/credentials_test.cc \ + +CREDENTIALS_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CREDENTIALS_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/credentials_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)/credentials_test: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/credentials_test: $(PROTOBUF_DEP) $(CREDENTIALS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LDXX) $(LDFLAGS) $(CREDENTIALS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/credentials_test + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/client/credentials_test.o: $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_credentials_test: $(CREDENTIALS_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(CREDENTIALS_TEST_OBJS:.o=.dep) +endif +endif + + +CXX_BYTE_BUFFER_TEST_SRC = \ + test/cpp/util/byte_buffer_test.cc \ + +CXX_BYTE_BUFFER_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CXX_BYTE_BUFFER_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/cxx_byte_buffer_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)/cxx_byte_buffer_test: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/cxx_byte_buffer_test: $(PROTOBUF_DEP) $(CXX_BYTE_BUFFER_TEST_OBJS) $(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) $(CXX_BYTE_BUFFER_TEST_OBJS) $(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)/cxx_byte_buffer_test + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/util/byte_buffer_test.o: $(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_cxx_byte_buffer_test: $(CXX_BYTE_BUFFER_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(CXX_BYTE_BUFFER_TEST_OBJS:.o=.dep) +endif +endif + + +CXX_SLICE_TEST_SRC = \ + test/cpp/util/slice_test.cc \ + +CXX_SLICE_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CXX_SLICE_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/cxx_slice_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)/cxx_slice_test: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/cxx_slice_test: $(PROTOBUF_DEP) $(CXX_SLICE_TEST_OBJS) $(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) $(CXX_SLICE_TEST_OBJS) $(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)/cxx_slice_test + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/util/slice_test.o: $(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_cxx_slice_test: $(CXX_SLICE_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(CXX_SLICE_TEST_OBJS:.o=.dep) +endif +endif + + +CXX_STRING_REF_TEST_SRC = \ + test/cpp/util/string_ref_test.cc \ + +CXX_STRING_REF_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CXX_STRING_REF_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/cxx_string_ref_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)/cxx_string_ref_test: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/cxx_string_ref_test: $(PROTOBUF_DEP) $(CXX_STRING_REF_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LDXX) $(LDFLAGS) $(CXX_STRING_REF_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/cxx_string_ref_test + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/util/string_ref_test.o: $(LIBDIR)/$(CONFIG)/libgrpc++.a +deps_cxx_string_ref_test: $(CXX_STRING_REF_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(CXX_STRING_REF_TEST_OBJS:.o=.dep) +endif +endif + + +CXX_TIME_TEST_SRC = \ + test/cpp/util/time_test.cc \ + +CXX_TIME_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CXX_TIME_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/cxx_time_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)/cxx_time_test: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/cxx_time_test: $(PROTOBUF_DEP) $(CXX_TIME_TEST_OBJS) $(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) $(CXX_TIME_TEST_OBJS) $(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)/cxx_time_test + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/util/time_test.o: $(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_cxx_time_test: $(CXX_TIME_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(CXX_TIME_TEST_OBJS:.o=.dep) +endif +endif + + +END2END_TEST_SRC = \ + test/cpp/end2end/end2end_test.cc \ + +END2END_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(END2END_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/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)/end2end_test: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/end2end_test: $(PROTOBUF_DEP) $(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) $(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)/end2end_test + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/end2end/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_end2end_test: $(END2END_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(END2END_TEST_OBJS:.o=.dep) +endif +endif + + +GENERIC_END2END_TEST_SRC = \ + test/cpp/end2end/generic_end2end_test.cc \ + +GENERIC_END2END_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GENERIC_END2END_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/generic_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)/generic_end2end_test: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/generic_end2end_test: $(PROTOBUF_DEP) $(GENERIC_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) $(GENERIC_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)/generic_end2end_test + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/end2end/generic_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_generic_end2end_test: $(GENERIC_END2END_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GENERIC_END2END_TEST_OBJS:.o=.dep) +endif +endif + + +GRPC_CLI_SRC = \ + test/cpp/util/grpc_cli.cc \ + +GRPC_CLI_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GRPC_CLI_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/grpc_cli: 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)/grpc_cli: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/grpc_cli: $(PROTOBUF_DEP) $(GRPC_CLI_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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LDXX) $(LDFLAGS) $(GRPC_CLI_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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/grpc_cli + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/util/grpc_cli.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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a +deps_grpc_cli: $(GRPC_CLI_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GRPC_CLI_OBJS:.o=.dep) +endif +endif + + +GRPC_CPP_PLUGIN_SRC = \ + src/compiler/cpp_plugin.cc \ + +GRPC_CPP_PLUGIN_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GRPC_CPP_PLUGIN_SRC)))) + +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)/grpc_cpp_plugin: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/grpc_cpp_plugin: $(PROTOBUF_DEP) $(GRPC_CPP_PLUGIN_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_plugin_support.a + $(E) "[HOSTLD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(HOST_LDXX) $(HOST_LDFLAGS) $(GRPC_CPP_PLUGIN_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_plugin_support.a $(HOST_LDLIBSXX) $(HOST_LDLIBS_PROTOC) $(HOST_LDLIBS) $(HOST_LDLIBS_PROTOC) -o $(BINDIR)/$(CONFIG)/grpc_cpp_plugin + +endif + +$(OBJDIR)/$(CONFIG)/src/compiler/cpp_plugin.o: $(LIBDIR)/$(CONFIG)/libgrpc_plugin_support.a +deps_grpc_cpp_plugin: $(GRPC_CPP_PLUGIN_OBJS:.o=.dep) + +ifneq ($(NO_DEPS),true) +-include $(GRPC_CPP_PLUGIN_OBJS:.o=.dep) +endif + + +GRPC_CSHARP_PLUGIN_SRC = \ + src/compiler/csharp_plugin.cc \ + +GRPC_CSHARP_PLUGIN_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GRPC_CSHARP_PLUGIN_SRC)))) + +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)/grpc_csharp_plugin: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/grpc_csharp_plugin: $(PROTOBUF_DEP) $(GRPC_CSHARP_PLUGIN_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_plugin_support.a + $(E) "[HOSTLD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(HOST_LDXX) $(HOST_LDFLAGS) $(GRPC_CSHARP_PLUGIN_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_plugin_support.a $(HOST_LDLIBSXX) $(HOST_LDLIBS_PROTOC) $(HOST_LDLIBS) $(HOST_LDLIBS_PROTOC) -o $(BINDIR)/$(CONFIG)/grpc_csharp_plugin + +endif + +$(OBJDIR)/$(CONFIG)/src/compiler/csharp_plugin.o: $(LIBDIR)/$(CONFIG)/libgrpc_plugin_support.a +deps_grpc_csharp_plugin: $(GRPC_CSHARP_PLUGIN_OBJS:.o=.dep) + +ifneq ($(NO_DEPS),true) +-include $(GRPC_CSHARP_PLUGIN_OBJS:.o=.dep) +endif + + +GRPC_OBJECTIVE_C_PLUGIN_SRC = \ + src/compiler/objective_c_plugin.cc \ + +GRPC_OBJECTIVE_C_PLUGIN_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GRPC_OBJECTIVE_C_PLUGIN_SRC)))) + +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)/grpc_objective_c_plugin: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/grpc_objective_c_plugin: $(PROTOBUF_DEP) $(GRPC_OBJECTIVE_C_PLUGIN_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_plugin_support.a + $(E) "[HOSTLD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(HOST_LDXX) $(HOST_LDFLAGS) $(GRPC_OBJECTIVE_C_PLUGIN_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_plugin_support.a $(HOST_LDLIBSXX) $(HOST_LDLIBS_PROTOC) $(HOST_LDLIBS) $(HOST_LDLIBS_PROTOC) -o $(BINDIR)/$(CONFIG)/grpc_objective_c_plugin + +endif + +$(OBJDIR)/$(CONFIG)/src/compiler/objective_c_plugin.o: $(LIBDIR)/$(CONFIG)/libgrpc_plugin_support.a +deps_grpc_objective_c_plugin: $(GRPC_OBJECTIVE_C_PLUGIN_OBJS:.o=.dep) + +ifneq ($(NO_DEPS),true) +-include $(GRPC_OBJECTIVE_C_PLUGIN_OBJS:.o=.dep) +endif + + +GRPC_PYTHON_PLUGIN_SRC = \ + src/compiler/python_plugin.cc \ + +GRPC_PYTHON_PLUGIN_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GRPC_PYTHON_PLUGIN_SRC)))) + +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)/grpc_python_plugin: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/grpc_python_plugin: $(PROTOBUF_DEP) $(GRPC_PYTHON_PLUGIN_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_plugin_support.a + $(E) "[HOSTLD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(HOST_LDXX) $(HOST_LDFLAGS) $(GRPC_PYTHON_PLUGIN_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_plugin_support.a $(HOST_LDLIBSXX) $(HOST_LDLIBS_PROTOC) $(HOST_LDLIBS) $(HOST_LDLIBS_PROTOC) -o $(BINDIR)/$(CONFIG)/grpc_python_plugin + +endif + +$(OBJDIR)/$(CONFIG)/src/compiler/python_plugin.o: $(LIBDIR)/$(CONFIG)/libgrpc_plugin_support.a +deps_grpc_python_plugin: $(GRPC_PYTHON_PLUGIN_OBJS:.o=.dep) + +ifneq ($(NO_DEPS),true) +-include $(GRPC_PYTHON_PLUGIN_OBJS:.o=.dep) +endif + + +GRPC_RUBY_PLUGIN_SRC = \ + src/compiler/ruby_plugin.cc \ + +GRPC_RUBY_PLUGIN_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GRPC_RUBY_PLUGIN_SRC)))) + +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)/grpc_ruby_plugin: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/grpc_ruby_plugin: $(PROTOBUF_DEP) $(GRPC_RUBY_PLUGIN_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_plugin_support.a + $(E) "[HOSTLD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(HOST_LDXX) $(HOST_LDFLAGS) $(GRPC_RUBY_PLUGIN_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_plugin_support.a $(HOST_LDLIBSXX) $(HOST_LDLIBS_PROTOC) $(HOST_LDLIBS) $(HOST_LDLIBS_PROTOC) -o $(BINDIR)/$(CONFIG)/grpc_ruby_plugin + +endif + +$(OBJDIR)/$(CONFIG)/src/compiler/ruby_plugin.o: $(LIBDIR)/$(CONFIG)/libgrpc_plugin_support.a +deps_grpc_ruby_plugin: $(GRPC_RUBY_PLUGIN_OBJS:.o=.dep) + +ifneq ($(NO_DEPS),true) +-include $(GRPC_RUBY_PLUGIN_OBJS:.o=.dep) +endif + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/interop_client: 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)/interop_client: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/interop_client: $(LIBDIR)/$(CONFIG)/libinterop_client_main.a $(LIBDIR)/$(CONFIG)/libinterop_client_helper.a $(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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LDXX) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libinterop_client_main.a $(LIBDIR)/$(CONFIG)/libinterop_client_helper.a $(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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/interop_client + +endif + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/interop_server: 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)/interop_server: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/interop_server: $(LIBDIR)/$(CONFIG)/libinterop_server_main.a $(LIBDIR)/$(CONFIG)/libinterop_server_helper.a $(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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LDXX) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libinterop_server_main.a $(LIBDIR)/$(CONFIG)/libinterop_server_helper.a $(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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/interop_server + +endif + +endif + + + + +INTEROP_TEST_SRC = \ + test/cpp/interop/interop_test.cc \ + +INTEROP_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(INTEROP_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/interop_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)/interop_test: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/interop_test: $(PROTOBUF_DEP) $(INTEROP_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.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) $(INTEROP_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.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)/interop_test + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/interop/interop_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_interop_test: $(INTEROP_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(INTEROP_TEST_OBJS:.o=.dep) +endif +endif + + +MOCK_TEST_SRC = \ + test/cpp/end2end/mock_test.cc \ + +MOCK_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(MOCK_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/mock_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)/mock_test: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/mock_test: $(PROTOBUF_DEP) $(MOCK_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) $(MOCK_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)/mock_test + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/end2end/mock_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_mock_test: $(MOCK_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(MOCK_TEST_OBJS:.o=.dep) +endif +endif + + +QPS_DRIVER_SRC = \ + test/cpp/qps/qps_driver.cc \ + +QPS_DRIVER_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(QPS_DRIVER_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/qps_driver: 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)/qps_driver: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/qps_driver: $(PROTOBUF_DEP) $(QPS_DRIVER_OBJS) $(LIBDIR)/$(CONFIG)/libqps.a $(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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LDXX) $(LDFLAGS) $(QPS_DRIVER_OBJS) $(LIBDIR)/$(CONFIG)/libqps.a $(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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/qps_driver + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/qps/qps_driver.o: $(LIBDIR)/$(CONFIG)/libqps.a $(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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a +deps_qps_driver: $(QPS_DRIVER_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(QPS_DRIVER_OBJS:.o=.dep) +endif +endif + + +QPS_INTERARRIVAL_TEST_SRC = \ + test/cpp/qps/qps_interarrival_test.cc \ + +QPS_INTERARRIVAL_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(QPS_INTERARRIVAL_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/qps_interarrival_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)/qps_interarrival_test: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/qps_interarrival_test: $(PROTOBUF_DEP) $(QPS_INTERARRIVAL_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libqps.a $(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) $(QPS_INTERARRIVAL_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libqps.a $(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)/qps_interarrival_test + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/qps/qps_interarrival_test.o: $(LIBDIR)/$(CONFIG)/libqps.a $(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_qps_interarrival_test: $(QPS_INTERARRIVAL_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(QPS_INTERARRIVAL_TEST_OBJS:.o=.dep) +endif +endif + + +QPS_OPENLOOP_TEST_SRC = \ + test/cpp/qps/qps_openloop_test.cc \ + +QPS_OPENLOOP_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(QPS_OPENLOOP_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/qps_openloop_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)/qps_openloop_test: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/qps_openloop_test: $(PROTOBUF_DEP) $(QPS_OPENLOOP_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libqps.a $(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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LDXX) $(LDFLAGS) $(QPS_OPENLOOP_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libqps.a $(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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/qps_openloop_test + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/qps/qps_openloop_test.o: $(LIBDIR)/$(CONFIG)/libqps.a $(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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a +deps_qps_openloop_test: $(QPS_OPENLOOP_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(QPS_OPENLOOP_TEST_OBJS:.o=.dep) +endif +endif + + +QPS_TEST_SRC = \ + test/cpp/qps/qps_test.cc \ + +QPS_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(QPS_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/qps_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)/qps_test: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/qps_test: $(PROTOBUF_DEP) $(QPS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libqps.a $(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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LDXX) $(LDFLAGS) $(QPS_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libqps.a $(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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/qps_test + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/qps/qps_test.o: $(LIBDIR)/$(CONFIG)/libqps.a $(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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a +deps_qps_test: $(QPS_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(QPS_TEST_OBJS:.o=.dep) +endif +endif + + +QPS_WORKER_SRC = \ + test/cpp/qps/worker.cc \ + +QPS_WORKER_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(QPS_WORKER_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/qps_worker: 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)/qps_worker: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/qps_worker: $(PROTOBUF_DEP) $(QPS_WORKER_OBJS) $(LIBDIR)/$(CONFIG)/libqps.a $(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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LDXX) $(LDFLAGS) $(QPS_WORKER_OBJS) $(LIBDIR)/$(CONFIG)/libqps.a $(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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/qps_worker + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/qps/worker.o: $(LIBDIR)/$(CONFIG)/libqps.a $(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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a +deps_qps_worker: $(QPS_WORKER_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(QPS_WORKER_OBJS:.o=.dep) +endif +endif + + +RECONNECT_INTEROP_CLIENT_SRC = \ + $(GENDIR)/test/proto/empty.pb.cc $(GENDIR)/test/proto/empty.grpc.pb.cc \ + $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc \ + $(GENDIR)/test/proto/test.pb.cc $(GENDIR)/test/proto/test.grpc.pb.cc \ + test/cpp/interop/reconnect_interop_client.cc \ + +RECONNECT_INTEROP_CLIENT_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(RECONNECT_INTEROP_CLIENT_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/reconnect_interop_client: 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)/reconnect_interop_client: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/reconnect_interop_client: $(PROTOBUF_DEP) $(RECONNECT_INTEROP_CLIENT_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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LDXX) $(LDFLAGS) $(RECONNECT_INTEROP_CLIENT_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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/reconnect_interop_client + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/proto/empty.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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a +$(OBJDIR)/$(CONFIG)/test/proto/messages.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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a +$(OBJDIR)/$(CONFIG)/test/proto/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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a +$(OBJDIR)/$(CONFIG)/test/cpp/interop/reconnect_interop_client.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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a +deps_reconnect_interop_client: $(RECONNECT_INTEROP_CLIENT_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(RECONNECT_INTEROP_CLIENT_OBJS:.o=.dep) +endif +endif + + +RECONNECT_INTEROP_SERVER_SRC = \ + $(GENDIR)/test/proto/empty.pb.cc $(GENDIR)/test/proto/empty.grpc.pb.cc \ + $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc \ + $(GENDIR)/test/proto/test.pb.cc $(GENDIR)/test/proto/test.grpc.pb.cc \ + test/cpp/interop/reconnect_interop_server.cc \ + +RECONNECT_INTEROP_SERVER_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(RECONNECT_INTEROP_SERVER_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/reconnect_interop_server: 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)/reconnect_interop_server: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/reconnect_interop_server: $(PROTOBUF_DEP) $(RECONNECT_INTEROP_SERVER_OBJS) $(LIBDIR)/$(CONFIG)/libreconnect_server.a $(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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LDXX) $(LDFLAGS) $(RECONNECT_INTEROP_SERVER_OBJS) $(LIBDIR)/$(CONFIG)/libreconnect_server.a $(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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/reconnect_interop_server + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/proto/empty.o: $(LIBDIR)/$(CONFIG)/libreconnect_server.a $(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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a +$(OBJDIR)/$(CONFIG)/test/proto/messages.o: $(LIBDIR)/$(CONFIG)/libreconnect_server.a $(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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a +$(OBJDIR)/$(CONFIG)/test/proto/test.o: $(LIBDIR)/$(CONFIG)/libreconnect_server.a $(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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a +$(OBJDIR)/$(CONFIG)/test/cpp/interop/reconnect_interop_server.o: $(LIBDIR)/$(CONFIG)/libreconnect_server.a $(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 $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a +deps_reconnect_interop_server: $(RECONNECT_INTEROP_SERVER_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(RECONNECT_INTEROP_SERVER_OBJS:.o=.dep) +endif +endif + + +SECURE_AUTH_CONTEXT_TEST_SRC = \ + test/cpp/common/secure_auth_context_test.cc \ + +SECURE_AUTH_CONTEXT_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(SECURE_AUTH_CONTEXT_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/secure_auth_context_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)/secure_auth_context_test: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/secure_auth_context_test: $(PROTOBUF_DEP) $(SECURE_AUTH_CONTEXT_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) $(SECURE_AUTH_CONTEXT_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)/secure_auth_context_test + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/common/secure_auth_context_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_secure_auth_context_test: $(SECURE_AUTH_CONTEXT_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(SECURE_AUTH_CONTEXT_TEST_OBJS:.o=.dep) +endif +endif + + +SERVER_CRASH_TEST_SRC = \ + test/cpp/end2end/server_crash_test.cc \ + +SERVER_CRASH_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(SERVER_CRASH_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/server_crash_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)/server_crash_test: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/server_crash_test: $(PROTOBUF_DEP) $(SERVER_CRASH_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) $(SERVER_CRASH_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)/server_crash_test + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/end2end/server_crash_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_server_crash_test: $(SERVER_CRASH_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(SERVER_CRASH_TEST_OBJS:.o=.dep) +endif +endif + + +SERVER_CRASH_TEST_CLIENT_SRC = \ + test/cpp/end2end/server_crash_test_client.cc \ + +SERVER_CRASH_TEST_CLIENT_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(SERVER_CRASH_TEST_CLIENT_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/server_crash_test_client: 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)/server_crash_test_client: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/server_crash_test_client: $(PROTOBUF_DEP) $(SERVER_CRASH_TEST_CLIENT_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) $(SERVER_CRASH_TEST_CLIENT_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)/server_crash_test_client + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/end2end/server_crash_test_client.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_server_crash_test_client: $(SERVER_CRASH_TEST_CLIENT_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(SERVER_CRASH_TEST_CLIENT_OBJS:.o=.dep) +endif +endif + + +SHUTDOWN_TEST_SRC = \ + test/cpp/end2end/shutdown_test.cc \ + +SHUTDOWN_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(SHUTDOWN_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/shutdown_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)/shutdown_test: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/shutdown_test: $(PROTOBUF_DEP) $(SHUTDOWN_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) $(SHUTDOWN_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)/shutdown_test + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/end2end/shutdown_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_shutdown_test: $(SHUTDOWN_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(SHUTDOWN_TEST_OBJS:.o=.dep) +endif +endif + + +STATUS_TEST_SRC = \ + test/cpp/util/status_test.cc \ + +STATUS_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(STATUS_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/status_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)/status_test: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/status_test: $(PROTOBUF_DEP) $(STATUS_TEST_OBJS) $(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) $(STATUS_TEST_OBJS) $(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)/status_test + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/util/status_test.o: $(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_status_test: $(STATUS_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(STATUS_TEST_OBJS:.o=.dep) +endif +endif + + +STREAMING_THROUGHPUT_TEST_SRC = \ + test/cpp/end2end/streaming_throughput_test.cc \ + +STREAMING_THROUGHPUT_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(STREAMING_THROUGHPUT_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/streaming_throughput_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)/streaming_throughput_test: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/streaming_throughput_test: $(PROTOBUF_DEP) $(STREAMING_THROUGHPUT_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) $(STREAMING_THROUGHPUT_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)/streaming_throughput_test + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/end2end/streaming_throughput_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_streaming_throughput_test: $(STREAMING_THROUGHPUT_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(STREAMING_THROUGHPUT_TEST_OBJS:.o=.dep) +endif +endif + + +SYNC_STREAMING_PING_PONG_TEST_SRC = \ + test/cpp/qps/sync_streaming_ping_pong_test.cc \ + +SYNC_STREAMING_PING_PONG_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(SYNC_STREAMING_PING_PONG_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/sync_streaming_ping_pong_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)/sync_streaming_ping_pong_test: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/sync_streaming_ping_pong_test: $(PROTOBUF_DEP) $(SYNC_STREAMING_PING_PONG_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libqps.a $(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) $(SYNC_STREAMING_PING_PONG_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libqps.a $(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)/sync_streaming_ping_pong_test + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/qps/sync_streaming_ping_pong_test.o: $(LIBDIR)/$(CONFIG)/libqps.a $(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_sync_streaming_ping_pong_test: $(SYNC_STREAMING_PING_PONG_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(SYNC_STREAMING_PING_PONG_TEST_OBJS:.o=.dep) +endif +endif + + +SYNC_UNARY_PING_PONG_TEST_SRC = \ + test/cpp/qps/sync_unary_ping_pong_test.cc \ + +SYNC_UNARY_PING_PONG_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(SYNC_UNARY_PING_PONG_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/sync_unary_ping_pong_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)/sync_unary_ping_pong_test: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/sync_unary_ping_pong_test: $(PROTOBUF_DEP) $(SYNC_UNARY_PING_PONG_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libqps.a $(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) $(SYNC_UNARY_PING_PONG_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libqps.a $(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)/sync_unary_ping_pong_test + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/qps/sync_unary_ping_pong_test.o: $(LIBDIR)/$(CONFIG)/libqps.a $(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_sync_unary_ping_pong_test: $(SYNC_UNARY_PING_PONG_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(SYNC_UNARY_PING_PONG_TEST_OBJS:.o=.dep) +endif +endif + + +THREAD_STRESS_TEST_SRC = \ + test/cpp/end2end/thread_stress_test.cc \ + +THREAD_STRESS_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(THREAD_STRESS_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/thread_stress_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)/thread_stress_test: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/thread_stress_test: $(PROTOBUF_DEP) $(THREAD_STRESS_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) $(THREAD_STRESS_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)/thread_stress_test + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/end2end/thread_stress_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_thread_stress_test: $(THREAD_STRESS_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(THREAD_STRESS_TEST_OBJS:.o=.dep) +endif +endif + + +ZOOKEEPER_TEST_SRC = \ + test/cpp/end2end/zookeeper_test.cc \ + +ZOOKEEPER_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(ZOOKEEPER_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/zookeeper_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)/zookeeper_test: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/zookeeper_test: $(PROTOBUF_DEP) $(ZOOKEEPER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc_zookeeper.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) $(ZOOKEEPER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc_zookeeper.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a -lzookeeper_mt $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/zookeeper_test + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/cpp/end2end/zookeeper_test.o: $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc_zookeeper.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_zookeeper_test: $(ZOOKEEPER_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(ZOOKEEPER_TEST_OBJS:.o=.dep) +endif +endif + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_compress_bad_hostname_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_compress_bad_hostname_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_compress_bad_hostname_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_compress_binary_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_compress_binary_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_compress_binary_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_compress_call_creds_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_compress_call_creds_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_call_creds.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_call_creds.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_compress_call_creds_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_compress_cancel_after_accept_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_compress_cancel_after_accept_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_compress_cancel_after_accept_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_compress_cancel_after_client_done_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_compress_cancel_after_client_done_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_compress_cancel_after_client_done_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_compress_cancel_after_invoke_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_compress_cancel_after_invoke_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_compress_cancel_after_invoke_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_compress_cancel_before_invoke_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_compress_cancel_before_invoke_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_compress_cancel_before_invoke_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_compress_cancel_in_a_vacuum_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_compress_cancel_in_a_vacuum_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_compress_cancel_in_a_vacuum_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_compress_census_simple_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_compress_census_simple_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_compress_census_simple_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_compress_channel_connectivity_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_compress_channel_connectivity_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_channel_connectivity.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_channel_connectivity.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_compress_channel_connectivity_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_compress_compressed_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_compress_compressed_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_compress_compressed_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_compress_default_host_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_compress_default_host_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_default_host.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_default_host.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_compress_default_host_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_compress_disappearing_server_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_compress_disappearing_server_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_compress_disappearing_server_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_compress_empty_batch_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_compress_empty_batch_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_compress_empty_batch_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_compress_graceful_server_shutdown_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_compress_graceful_server_shutdown_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_compress_graceful_server_shutdown_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_compress_high_initial_seqno_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_compress_high_initial_seqno_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_compress_high_initial_seqno_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_compress_invoke_large_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_compress_invoke_large_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_compress_invoke_large_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_compress_large_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_compress_large_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_compress_large_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_compress_max_concurrent_streams_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_compress_max_concurrent_streams_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_compress_max_concurrent_streams_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_compress_max_message_length_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_compress_max_message_length_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_compress_max_message_length_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_compress_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_compress_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_compress_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_compress_no_op_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_compress_no_op_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_compress_no_op_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_compress_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_compress_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_compress_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_compress_ping_pong_streaming_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_compress_ping_pong_streaming_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_compress_ping_pong_streaming_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_compress_registered_call_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_compress_registered_call_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_compress_registered_call_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_compress_request_with_flags_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_compress_request_with_flags_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_compress_request_with_flags_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_compress_request_with_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_compress_request_with_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_compress_request_with_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_compress_server_finishes_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_compress_server_finishes_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_compress_server_finishes_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_compress_shutdown_finishes_calls_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_compress_shutdown_finishes_calls_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_compress_shutdown_finishes_calls_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_compress_shutdown_finishes_tags_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_compress_shutdown_finishes_tags_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_compress_shutdown_finishes_tags_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_compress_simple_delayed_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_compress_simple_delayed_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_compress_simple_delayed_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_compress_simple_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_compress_simple_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_compress_simple_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_compress_trailing_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_compress_trailing_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_compress_trailing_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_fakesec_bad_hostname_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_fakesec_bad_hostname_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_fakesec_bad_hostname_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_fakesec_binary_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_fakesec_binary_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_fakesec_binary_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_fakesec_call_creds_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_fakesec_call_creds_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_call_creds.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_call_creds.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_fakesec_call_creds_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_fakesec_cancel_after_accept_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_fakesec_cancel_after_accept_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_fakesec_cancel_after_accept_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_fakesec_cancel_after_client_done_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_fakesec_cancel_after_client_done_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_fakesec_cancel_after_client_done_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_fakesec_cancel_after_invoke_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_fakesec_cancel_after_invoke_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_fakesec_cancel_after_invoke_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_fakesec_cancel_before_invoke_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_fakesec_cancel_before_invoke_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_fakesec_cancel_before_invoke_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_fakesec_cancel_in_a_vacuum_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_fakesec_cancel_in_a_vacuum_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_fakesec_cancel_in_a_vacuum_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_fakesec_census_simple_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_fakesec_census_simple_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_fakesec_census_simple_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_fakesec_channel_connectivity_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_fakesec_channel_connectivity_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_channel_connectivity.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_channel_connectivity.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_fakesec_channel_connectivity_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_fakesec_compressed_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_fakesec_compressed_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_fakesec_compressed_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_fakesec_default_host_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_fakesec_default_host_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_default_host.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_default_host.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_fakesec_default_host_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_fakesec_disappearing_server_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_fakesec_disappearing_server_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_fakesec_disappearing_server_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_fakesec_empty_batch_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_fakesec_empty_batch_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_fakesec_empty_batch_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_fakesec_graceful_server_shutdown_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_fakesec_graceful_server_shutdown_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_fakesec_graceful_server_shutdown_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_fakesec_high_initial_seqno_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_fakesec_high_initial_seqno_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_fakesec_high_initial_seqno_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_fakesec_invoke_large_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_fakesec_invoke_large_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_fakesec_invoke_large_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_fakesec_large_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_fakesec_large_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_fakesec_large_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_fakesec_max_concurrent_streams_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_fakesec_max_concurrent_streams_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_fakesec_max_concurrent_streams_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_fakesec_max_message_length_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_fakesec_max_message_length_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_fakesec_max_message_length_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_fakesec_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_fakesec_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_fakesec_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_fakesec_no_op_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_fakesec_no_op_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_fakesec_no_op_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_fakesec_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_fakesec_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_fakesec_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_fakesec_ping_pong_streaming_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_fakesec_ping_pong_streaming_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_fakesec_ping_pong_streaming_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_fakesec_registered_call_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_fakesec_registered_call_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_fakesec_registered_call_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_fakesec_request_with_flags_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_fakesec_request_with_flags_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_fakesec_request_with_flags_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_fakesec_request_with_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_fakesec_request_with_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_fakesec_request_with_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_fakesec_server_finishes_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_fakesec_server_finishes_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_fakesec_server_finishes_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_fakesec_shutdown_finishes_calls_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_fakesec_shutdown_finishes_calls_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_fakesec_shutdown_finishes_calls_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_fakesec_shutdown_finishes_tags_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_fakesec_shutdown_finishes_tags_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_fakesec_shutdown_finishes_tags_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_fakesec_simple_delayed_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_fakesec_simple_delayed_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_fakesec_simple_delayed_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_fakesec_simple_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_fakesec_simple_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_fakesec_simple_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_fakesec_trailing_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_fakesec_trailing_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_fakesec.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_fakesec_trailing_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full_bad_hostname_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full_bad_hostname_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full_bad_hostname_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full_binary_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full_binary_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full_binary_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full_call_creds_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full_call_creds_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_call_creds.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_call_creds.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full_call_creds_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full_cancel_after_accept_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full_cancel_after_accept_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full_cancel_after_accept_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full_cancel_after_client_done_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full_cancel_after_client_done_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full_cancel_after_client_done_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full_cancel_after_invoke_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full_cancel_after_invoke_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full_cancel_after_invoke_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full_cancel_before_invoke_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full_cancel_before_invoke_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full_cancel_before_invoke_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full_cancel_in_a_vacuum_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full_cancel_in_a_vacuum_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full_cancel_in_a_vacuum_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full_census_simple_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full_census_simple_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full_census_simple_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full_channel_connectivity_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full_channel_connectivity_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_channel_connectivity.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_channel_connectivity.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full_channel_connectivity_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full_compressed_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full_compressed_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full_compressed_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full_default_host_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full_default_host_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_default_host.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_default_host.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full_default_host_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full_disappearing_server_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full_disappearing_server_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full_disappearing_server_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full_empty_batch_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full_empty_batch_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full_empty_batch_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full_graceful_server_shutdown_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full_graceful_server_shutdown_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full_graceful_server_shutdown_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full_high_initial_seqno_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full_high_initial_seqno_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full_high_initial_seqno_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full_invoke_large_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full_invoke_large_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full_invoke_large_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full_large_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full_large_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full_large_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full_max_concurrent_streams_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full_max_concurrent_streams_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full_max_concurrent_streams_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full_max_message_length_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full_max_message_length_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full_max_message_length_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full_no_op_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full_no_op_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full_no_op_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full_ping_pong_streaming_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full_ping_pong_streaming_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full_ping_pong_streaming_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full_registered_call_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full_registered_call_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full_registered_call_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full_request_with_flags_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full_request_with_flags_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full_request_with_flags_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full_request_with_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full_request_with_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full_request_with_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full_server_finishes_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full_server_finishes_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full_server_finishes_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full_shutdown_finishes_calls_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full_shutdown_finishes_calls_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full_shutdown_finishes_calls_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full_shutdown_finishes_tags_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full_shutdown_finishes_tags_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full_shutdown_finishes_tags_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full_simple_delayed_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full_simple_delayed_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full_simple_delayed_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full_simple_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full_simple_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full_simple_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full_trailing_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full_trailing_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full_trailing_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full+poll_bad_hostname_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full+poll_bad_hostname_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full+poll_bad_hostname_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full+poll_binary_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full+poll_binary_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full+poll_binary_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full+poll_call_creds_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full+poll_call_creds_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_call_creds.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_call_creds.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full+poll_call_creds_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_accept_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_accept_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_accept_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_client_done_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_client_done_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_client_done_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_invoke_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_invoke_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_invoke_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full+poll_cancel_before_invoke_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full+poll_cancel_before_invoke_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_before_invoke_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full+poll_cancel_in_a_vacuum_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full+poll_cancel_in_a_vacuum_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_in_a_vacuum_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full+poll_census_simple_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full+poll_census_simple_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full+poll_census_simple_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full+poll_channel_connectivity_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full+poll_channel_connectivity_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_channel_connectivity.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_channel_connectivity.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full+poll_channel_connectivity_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full+poll_compressed_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full+poll_compressed_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full+poll_compressed_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full+poll_default_host_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full+poll_default_host_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_default_host.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_default_host.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full+poll_default_host_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full+poll_disappearing_server_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full+poll_disappearing_server_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full+poll_disappearing_server_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full+poll_empty_batch_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full+poll_empty_batch_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full+poll_empty_batch_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full+poll_graceful_server_shutdown_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full+poll_graceful_server_shutdown_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full+poll_graceful_server_shutdown_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full+poll_high_initial_seqno_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full+poll_high_initial_seqno_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full+poll_high_initial_seqno_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full+poll_invoke_large_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full+poll_invoke_large_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full+poll_invoke_large_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full+poll_large_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full+poll_large_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full+poll_large_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full+poll_max_concurrent_streams_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full+poll_max_concurrent_streams_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full+poll_max_concurrent_streams_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full+poll_max_message_length_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full+poll_max_message_length_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full+poll_max_message_length_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full+poll_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full+poll_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full+poll_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full+poll_no_op_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full+poll_no_op_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full+poll_no_op_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full+poll_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full+poll_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full+poll_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full+poll_ping_pong_streaming_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full+poll_ping_pong_streaming_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full+poll_ping_pong_streaming_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full+poll_registered_call_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full+poll_registered_call_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full+poll_registered_call_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full+poll_request_with_flags_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full+poll_request_with_flags_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full+poll_request_with_flags_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full+poll_request_with_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full+poll_request_with_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full+poll_request_with_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full+poll_server_finishes_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full+poll_server_finishes_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full+poll_server_finishes_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full+poll_shutdown_finishes_calls_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full+poll_shutdown_finishes_calls_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full+poll_shutdown_finishes_calls_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full+poll_shutdown_finishes_tags_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full+poll_shutdown_finishes_tags_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full+poll_shutdown_finishes_tags_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full+poll_simple_delayed_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full+poll_simple_delayed_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full+poll_simple_delayed_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full+poll_simple_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full+poll_simple_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full+poll_simple_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_full+poll_trailing_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_full+poll_trailing_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_full+poll_trailing_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_oauth2_bad_hostname_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_oauth2_bad_hostname_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_oauth2_bad_hostname_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_oauth2_binary_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_oauth2_binary_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_oauth2_binary_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_oauth2_call_creds_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_oauth2_call_creds_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_call_creds.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_call_creds.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_oauth2_call_creds_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_oauth2_cancel_after_accept_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_oauth2_cancel_after_accept_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_oauth2_cancel_after_accept_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_oauth2_cancel_after_client_done_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_oauth2_cancel_after_client_done_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_oauth2_cancel_after_client_done_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_oauth2_cancel_after_invoke_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_oauth2_cancel_after_invoke_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_oauth2_cancel_after_invoke_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_oauth2_cancel_before_invoke_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_oauth2_cancel_before_invoke_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_oauth2_cancel_before_invoke_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_oauth2_cancel_in_a_vacuum_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_oauth2_cancel_in_a_vacuum_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_oauth2_cancel_in_a_vacuum_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_oauth2_census_simple_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_oauth2_census_simple_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_oauth2_census_simple_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_oauth2_channel_connectivity_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_oauth2_channel_connectivity_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_channel_connectivity.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_channel_connectivity.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_oauth2_channel_connectivity_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_oauth2_compressed_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_oauth2_compressed_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_oauth2_compressed_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_oauth2_default_host_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_oauth2_default_host_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_default_host.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_default_host.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_oauth2_default_host_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_oauth2_disappearing_server_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_oauth2_disappearing_server_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_oauth2_disappearing_server_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_oauth2_empty_batch_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_oauth2_empty_batch_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_oauth2_empty_batch_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_oauth2_graceful_server_shutdown_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_oauth2_graceful_server_shutdown_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_oauth2_graceful_server_shutdown_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_oauth2_high_initial_seqno_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_oauth2_high_initial_seqno_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_oauth2_high_initial_seqno_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_oauth2_invoke_large_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_oauth2_invoke_large_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_oauth2_invoke_large_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_oauth2_large_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_oauth2_large_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_oauth2_large_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_oauth2_max_concurrent_streams_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_oauth2_max_concurrent_streams_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_oauth2_max_concurrent_streams_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_oauth2_max_message_length_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_oauth2_max_message_length_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_oauth2_max_message_length_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_oauth2_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_oauth2_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_oauth2_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_oauth2_no_op_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_oauth2_no_op_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_oauth2_no_op_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_oauth2_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_oauth2_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_oauth2_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_oauth2_ping_pong_streaming_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_oauth2_ping_pong_streaming_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_oauth2_ping_pong_streaming_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_oauth2_registered_call_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_oauth2_registered_call_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_oauth2_registered_call_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_oauth2_request_with_flags_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_oauth2_request_with_flags_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_oauth2_request_with_flags_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_oauth2_request_with_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_oauth2_request_with_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_oauth2_request_with_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_oauth2_server_finishes_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_oauth2_server_finishes_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_oauth2_server_finishes_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_oauth2_shutdown_finishes_calls_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_oauth2_shutdown_finishes_calls_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_oauth2_shutdown_finishes_calls_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_oauth2_shutdown_finishes_tags_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_oauth2_shutdown_finishes_tags_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_oauth2_shutdown_finishes_tags_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_oauth2_simple_delayed_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_oauth2_simple_delayed_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_oauth2_simple_delayed_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_oauth2_simple_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_oauth2_simple_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_oauth2_simple_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_oauth2_trailing_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_oauth2_trailing_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_oauth2.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_oauth2_trailing_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_proxy_bad_hostname_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_proxy_bad_hostname_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_proxy_bad_hostname_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_proxy_binary_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_proxy_binary_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_proxy_binary_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_proxy_call_creds_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_proxy_call_creds_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_call_creds.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_call_creds.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_proxy_call_creds_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_accept_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_accept_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_accept_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_client_done_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_client_done_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_client_done_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_invoke_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_invoke_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_invoke_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_proxy_cancel_before_invoke_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_proxy_cancel_before_invoke_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_proxy_cancel_before_invoke_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_proxy_cancel_in_a_vacuum_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_proxy_cancel_in_a_vacuum_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_proxy_cancel_in_a_vacuum_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_proxy_census_simple_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_proxy_census_simple_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_proxy_census_simple_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_proxy_default_host_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_proxy_default_host_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_default_host.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_default_host.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_proxy_default_host_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_proxy_disappearing_server_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_proxy_disappearing_server_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_proxy_disappearing_server_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_proxy_empty_batch_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_proxy_empty_batch_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_proxy_empty_batch_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_proxy_graceful_server_shutdown_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_proxy_graceful_server_shutdown_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_proxy_graceful_server_shutdown_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_proxy_high_initial_seqno_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_proxy_high_initial_seqno_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_proxy_high_initial_seqno_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_proxy_invoke_large_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_proxy_invoke_large_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_proxy_invoke_large_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_proxy_large_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_proxy_large_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_proxy_large_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_proxy_max_message_length_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_proxy_max_message_length_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_proxy_max_message_length_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_proxy_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_proxy_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_proxy_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_proxy_no_op_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_proxy_no_op_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_proxy_no_op_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_proxy_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_proxy_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_proxy_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_proxy_ping_pong_streaming_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_proxy_ping_pong_streaming_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_proxy_ping_pong_streaming_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_proxy_registered_call_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_proxy_registered_call_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_proxy_registered_call_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_proxy_request_with_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_proxy_request_with_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_proxy_request_with_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_proxy_server_finishes_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_proxy_server_finishes_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_proxy_server_finishes_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_proxy_shutdown_finishes_calls_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_proxy_shutdown_finishes_calls_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_proxy_shutdown_finishes_calls_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_proxy_shutdown_finishes_tags_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_proxy_shutdown_finishes_tags_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_proxy_shutdown_finishes_tags_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_proxy_simple_delayed_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_proxy_simple_delayed_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_proxy_simple_delayed_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_proxy_simple_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_proxy_simple_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_proxy_simple_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_proxy_trailing_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_proxy_trailing_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_proxy_trailing_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_bad_hostname_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_bad_hostname_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_bad_hostname_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_binary_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_binary_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_binary_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_call_creds_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_call_creds_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_call_creds.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_call_creds.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_call_creds_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_accept_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_accept_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_accept_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_client_done_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_client_done_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_client_done_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_invoke_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_invoke_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_invoke_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_cancel_before_invoke_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_cancel_before_invoke_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_before_invoke_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_cancel_in_a_vacuum_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_cancel_in_a_vacuum_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_in_a_vacuum_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_census_simple_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_census_simple_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_census_simple_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_compressed_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_compressed_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_compressed_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_empty_batch_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_empty_batch_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_empty_batch_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_graceful_server_shutdown_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_graceful_server_shutdown_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_graceful_server_shutdown_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_high_initial_seqno_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_high_initial_seqno_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_high_initial_seqno_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_invoke_large_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_invoke_large_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_invoke_large_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_large_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_large_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_large_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_max_concurrent_streams_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_max_concurrent_streams_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_max_concurrent_streams_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_max_message_length_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_max_message_length_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_max_message_length_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_no_op_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_no_op_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_no_op_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_ping_pong_streaming_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_ping_pong_streaming_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_ping_pong_streaming_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_registered_call_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_registered_call_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_registered_call_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_request_with_flags_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_request_with_flags_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_request_with_flags_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_request_with_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_request_with_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_request_with_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_server_finishes_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_server_finishes_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_server_finishes_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_shutdown_finishes_calls_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_shutdown_finishes_calls_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_shutdown_finishes_calls_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_shutdown_finishes_tags_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_shutdown_finishes_tags_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_shutdown_finishes_tags_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_simple_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_simple_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_simple_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_trailing_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_trailing_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_trailing_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_bad_hostname_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_bad_hostname_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_bad_hostname_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_binary_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_binary_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_binary_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_call_creds_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_call_creds_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_call_creds.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_call_creds.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_call_creds_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_accept_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_accept_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_accept_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_client_done_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_client_done_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_client_done_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_invoke_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_invoke_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_invoke_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_before_invoke_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_before_invoke_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_before_invoke_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_in_a_vacuum_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_in_a_vacuum_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_in_a_vacuum_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_census_simple_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_census_simple_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_census_simple_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_compressed_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_compressed_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_compressed_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_empty_batch_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_empty_batch_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_empty_batch_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_graceful_server_shutdown_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_graceful_server_shutdown_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_graceful_server_shutdown_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_high_initial_seqno_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_high_initial_seqno_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_high_initial_seqno_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_invoke_large_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_invoke_large_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_invoke_large_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_large_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_large_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_large_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_max_concurrent_streams_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_max_concurrent_streams_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_max_concurrent_streams_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_max_message_length_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_max_message_length_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_max_message_length_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_no_op_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_no_op_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_no_op_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_ping_pong_streaming_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_ping_pong_streaming_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_ping_pong_streaming_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_registered_call_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_registered_call_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_registered_call_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_request_with_flags_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_request_with_flags_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_request_with_flags_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_request_with_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_request_with_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_request_with_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_server_finishes_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_server_finishes_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_server_finishes_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_shutdown_finishes_calls_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_shutdown_finishes_calls_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_shutdown_finishes_calls_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_shutdown_finishes_tags_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_shutdown_finishes_tags_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_shutdown_finishes_tags_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_simple_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_simple_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_simple_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_trailing_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_trailing_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_trailing_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_bad_hostname_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_bad_hostname_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_bad_hostname_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_binary_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_binary_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_binary_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_call_creds_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_call_creds_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_call_creds.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_call_creds.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_call_creds_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_accept_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_accept_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_accept_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_client_done_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_client_done_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_client_done_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_invoke_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_invoke_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_invoke_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_before_invoke_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_before_invoke_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_before_invoke_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_in_a_vacuum_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_in_a_vacuum_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_in_a_vacuum_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_census_simple_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_census_simple_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_census_simple_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_compressed_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_compressed_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_compressed_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_empty_batch_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_empty_batch_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_empty_batch_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_graceful_server_shutdown_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_graceful_server_shutdown_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_graceful_server_shutdown_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_high_initial_seqno_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_high_initial_seqno_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_high_initial_seqno_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_invoke_large_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_invoke_large_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_invoke_large_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_large_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_large_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_large_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_max_concurrent_streams_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_max_concurrent_streams_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_max_concurrent_streams_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_max_message_length_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_max_message_length_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_max_message_length_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_no_op_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_no_op_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_no_op_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_ping_pong_streaming_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_ping_pong_streaming_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_ping_pong_streaming_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_registered_call_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_registered_call_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_registered_call_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_request_with_flags_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_request_with_flags_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_request_with_flags_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_request_with_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_request_with_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_request_with_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_server_finishes_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_server_finishes_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_server_finishes_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_shutdown_finishes_calls_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_shutdown_finishes_calls_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_shutdown_finishes_calls_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_shutdown_finishes_tags_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_shutdown_finishes_tags_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_shutdown_finishes_tags_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_simple_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_simple_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_simple_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_trailing_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_trailing_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_trailing_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_bad_hostname_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_bad_hostname_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_bad_hostname_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_binary_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_binary_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_binary_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_call_creds_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_call_creds_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_call_creds.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_call_creds.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_call_creds_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_cancel_after_accept_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_cancel_after_accept_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_cancel_after_accept_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_cancel_after_client_done_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_cancel_after_client_done_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_cancel_after_client_done_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_cancel_after_invoke_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_cancel_after_invoke_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_cancel_after_invoke_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_cancel_before_invoke_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_cancel_before_invoke_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_cancel_before_invoke_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_cancel_in_a_vacuum_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_cancel_in_a_vacuum_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_cancel_in_a_vacuum_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_census_simple_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_census_simple_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_census_simple_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_channel_connectivity_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_channel_connectivity_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_channel_connectivity.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_channel_connectivity.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_channel_connectivity_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_compressed_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_compressed_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_compressed_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_default_host_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_default_host_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_default_host.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_default_host.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_default_host_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_disappearing_server_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_disappearing_server_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_disappearing_server_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_empty_batch_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_empty_batch_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_empty_batch_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_graceful_server_shutdown_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_graceful_server_shutdown_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_graceful_server_shutdown_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_high_initial_seqno_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_high_initial_seqno_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_high_initial_seqno_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_invoke_large_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_invoke_large_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_invoke_large_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_large_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_large_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_large_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_max_concurrent_streams_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_max_concurrent_streams_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_max_concurrent_streams_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_max_message_length_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_max_message_length_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_max_message_length_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_no_op_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_no_op_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_no_op_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_ping_pong_streaming_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_ping_pong_streaming_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_ping_pong_streaming_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_registered_call_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_registered_call_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_registered_call_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_request_with_flags_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_request_with_flags_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_request_with_flags_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_request_with_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_request_with_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_request_with_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_server_finishes_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_server_finishes_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_server_finishes_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_shutdown_finishes_calls_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_shutdown_finishes_calls_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_shutdown_finishes_calls_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_shutdown_finishes_tags_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_shutdown_finishes_tags_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_shutdown_finishes_tags_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_simple_delayed_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_simple_delayed_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_simple_delayed_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_simple_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_simple_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_simple_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_trailing_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_trailing_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_trailing_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_bad_hostname_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_bad_hostname_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl+poll_bad_hostname_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_binary_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_binary_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl+poll_binary_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_call_creds_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_call_creds_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_call_creds.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_call_creds.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl+poll_call_creds_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_cancel_after_accept_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_cancel_after_accept_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl+poll_cancel_after_accept_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_cancel_after_client_done_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_cancel_after_client_done_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl+poll_cancel_after_client_done_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_cancel_after_invoke_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_cancel_after_invoke_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl+poll_cancel_after_invoke_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_cancel_before_invoke_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_cancel_before_invoke_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl+poll_cancel_before_invoke_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_cancel_in_a_vacuum_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_cancel_in_a_vacuum_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl+poll_cancel_in_a_vacuum_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_census_simple_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_census_simple_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl+poll_census_simple_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_channel_connectivity_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_channel_connectivity_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_channel_connectivity.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_channel_connectivity.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl+poll_channel_connectivity_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_compressed_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_compressed_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl+poll_compressed_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_default_host_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_default_host_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_default_host.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_default_host.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl+poll_default_host_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_disappearing_server_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_disappearing_server_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl+poll_disappearing_server_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_empty_batch_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_empty_batch_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl+poll_empty_batch_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_graceful_server_shutdown_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_graceful_server_shutdown_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl+poll_graceful_server_shutdown_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_high_initial_seqno_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_high_initial_seqno_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl+poll_high_initial_seqno_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_invoke_large_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_invoke_large_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl+poll_invoke_large_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_large_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_large_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl+poll_large_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_max_concurrent_streams_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_max_concurrent_streams_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl+poll_max_concurrent_streams_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_max_message_length_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_max_message_length_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl+poll_max_message_length_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl+poll_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_no_op_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_no_op_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl+poll_no_op_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl+poll_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_ping_pong_streaming_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_ping_pong_streaming_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl+poll_ping_pong_streaming_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_registered_call_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_registered_call_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl+poll_registered_call_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_request_with_flags_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_request_with_flags_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl+poll_request_with_flags_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_request_with_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_request_with_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl+poll_request_with_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_server_finishes_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_server_finishes_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl+poll_server_finishes_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_shutdown_finishes_calls_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_shutdown_finishes_calls_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl+poll_shutdown_finishes_calls_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_shutdown_finishes_tags_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_shutdown_finishes_tags_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl+poll_shutdown_finishes_tags_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_simple_delayed_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_simple_delayed_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl+poll_simple_delayed_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_simple_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_simple_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl+poll_simple_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_trailing_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl+poll_trailing_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl+poll_trailing_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_bad_hostname_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_bad_hostname_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_proxy_bad_hostname_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_binary_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_binary_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_proxy_binary_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_call_creds_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_call_creds_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_call_creds.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_call_creds.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_proxy_call_creds_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_cancel_after_accept_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_cancel_after_accept_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_proxy_cancel_after_accept_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_cancel_after_client_done_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_cancel_after_client_done_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_proxy_cancel_after_client_done_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_cancel_after_invoke_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_cancel_after_invoke_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_proxy_cancel_after_invoke_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_cancel_before_invoke_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_cancel_before_invoke_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_proxy_cancel_before_invoke_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_cancel_in_a_vacuum_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_cancel_in_a_vacuum_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_proxy_cancel_in_a_vacuum_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_census_simple_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_census_simple_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_proxy_census_simple_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_default_host_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_default_host_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_default_host.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_default_host.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_proxy_default_host_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_disappearing_server_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_disappearing_server_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_proxy_disappearing_server_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_empty_batch_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_empty_batch_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_proxy_empty_batch_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_graceful_server_shutdown_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_graceful_server_shutdown_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_proxy_graceful_server_shutdown_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_high_initial_seqno_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_high_initial_seqno_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_proxy_high_initial_seqno_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_invoke_large_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_invoke_large_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_proxy_invoke_large_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_large_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_large_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_proxy_large_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_max_message_length_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_max_message_length_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_proxy_max_message_length_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_proxy_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_no_op_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_no_op_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_proxy_no_op_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_proxy_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_ping_pong_streaming_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_ping_pong_streaming_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_proxy_ping_pong_streaming_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_registered_call_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_registered_call_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_proxy_registered_call_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_request_with_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_request_with_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_proxy_request_with_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_server_finishes_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_server_finishes_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_proxy_server_finishes_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_shutdown_finishes_calls_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_shutdown_finishes_calls_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_proxy_shutdown_finishes_calls_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_shutdown_finishes_tags_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_shutdown_finishes_tags_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_proxy_shutdown_finishes_tags_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_simple_delayed_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_simple_delayed_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_proxy_simple_delayed_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_simple_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_simple_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_proxy_simple_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_trailing_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_proxy_trailing_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_ssl_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_ssl_proxy_trailing_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds_bad_hostname_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds_bad_hostname_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds_bad_hostname_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds_binary_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds_binary_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds_binary_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds_call_creds_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds_call_creds_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_call_creds.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_call_creds.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds_call_creds_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds_cancel_after_accept_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds_cancel_after_accept_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds_cancel_after_accept_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds_cancel_after_client_done_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds_cancel_after_client_done_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds_cancel_after_client_done_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds_cancel_after_invoke_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds_cancel_after_invoke_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds_cancel_after_invoke_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds_cancel_before_invoke_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds_cancel_before_invoke_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds_cancel_before_invoke_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds_cancel_in_a_vacuum_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds_cancel_in_a_vacuum_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds_cancel_in_a_vacuum_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds_census_simple_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds_census_simple_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds_census_simple_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds_channel_connectivity_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds_channel_connectivity_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_channel_connectivity.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_channel_connectivity.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds_channel_connectivity_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds_compressed_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds_compressed_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds_compressed_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds_disappearing_server_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds_disappearing_server_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds_disappearing_server_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds_empty_batch_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds_empty_batch_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds_empty_batch_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds_graceful_server_shutdown_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds_graceful_server_shutdown_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds_graceful_server_shutdown_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds_high_initial_seqno_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds_high_initial_seqno_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds_high_initial_seqno_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds_invoke_large_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds_invoke_large_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds_invoke_large_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds_large_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds_large_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds_large_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds_max_concurrent_streams_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds_max_concurrent_streams_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds_max_concurrent_streams_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds_max_message_length_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds_max_message_length_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds_max_message_length_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds_no_op_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds_no_op_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds_no_op_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds_ping_pong_streaming_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds_ping_pong_streaming_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds_ping_pong_streaming_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds_registered_call_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds_registered_call_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds_registered_call_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds_request_with_flags_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds_request_with_flags_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds_request_with_flags_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds_request_with_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds_request_with_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds_request_with_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds_server_finishes_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds_server_finishes_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds_server_finishes_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds_shutdown_finishes_calls_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds_shutdown_finishes_calls_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds_shutdown_finishes_calls_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds_shutdown_finishes_tags_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds_shutdown_finishes_tags_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds_shutdown_finishes_tags_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds_simple_delayed_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds_simple_delayed_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds_simple_delayed_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds_simple_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds_simple_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds_simple_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds_trailing_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds_trailing_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds_trailing_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds+poll_bad_hostname_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds+poll_bad_hostname_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_bad_hostname_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds+poll_binary_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds+poll_binary_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_binary_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds+poll_call_creds_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds+poll_call_creds_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_call_creds.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_call_creds.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_call_creds_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_accept_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_accept_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_accept_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_client_done_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_client_done_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_client_done_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_invoke_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_invoke_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_invoke_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_before_invoke_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_before_invoke_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_before_invoke_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_in_a_vacuum_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_in_a_vacuum_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_in_a_vacuum_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds+poll_census_simple_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds+poll_census_simple_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_census_simple_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds+poll_channel_connectivity_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds+poll_channel_connectivity_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_channel_connectivity.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_channel_connectivity.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_channel_connectivity_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds+poll_compressed_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds+poll_compressed_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_compressed_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds+poll_disappearing_server_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds+poll_disappearing_server_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_disappearing_server_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds+poll_empty_batch_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds+poll_empty_batch_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_empty_batch_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds+poll_graceful_server_shutdown_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds+poll_graceful_server_shutdown_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_graceful_server_shutdown_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds+poll_high_initial_seqno_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds+poll_high_initial_seqno_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_high_initial_seqno_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds+poll_invoke_large_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds+poll_invoke_large_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_invoke_large_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds+poll_large_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds+poll_large_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_large_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds+poll_max_concurrent_streams_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds+poll_max_concurrent_streams_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_max_concurrent_streams_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds+poll_max_message_length_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds+poll_max_message_length_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_max_message_length_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds+poll_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds+poll_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_metadata_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds+poll_no_op_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds+poll_no_op_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_no_op_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds+poll_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds+poll_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds+poll_ping_pong_streaming_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds+poll_ping_pong_streaming_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_ping_pong_streaming_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds+poll_registered_call_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds+poll_registered_call_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_registered_call_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds+poll_request_with_flags_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds+poll_request_with_flags_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_request_with_flags_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds+poll_request_with_payload_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds+poll_request_with_payload_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_request_with_payload_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds+poll_server_finishes_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds+poll_server_finishes_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_server_finishes_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds+poll_shutdown_finishes_calls_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds+poll_shutdown_finishes_calls_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_shutdown_finishes_calls_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds+poll_shutdown_finishes_tags_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds+poll_shutdown_finishes_tags_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_shutdown_finishes_tags_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds+poll_simple_delayed_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds+poll_simple_delayed_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_simple_delayed_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds+poll_simple_request_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds+poll_simple_request_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_simple_request_test + +endif + + + + +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_uds+poll_trailing_metadata_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_uds+poll_trailing_metadata_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libend2end_certs.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_trailing_metadata_test + +endif + + + + +$(BINDIR)/$(CONFIG)/h2_compress_bad_hostname_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_compress_bad_hostname_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_compress_binary_metadata_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_compress_binary_metadata_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_compress_cancel_after_accept_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_compress_cancel_after_accept_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_compress_cancel_after_client_done_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_compress_cancel_after_client_done_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_compress_cancel_after_invoke_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_compress_cancel_after_invoke_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_compress_cancel_before_invoke_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_compress_cancel_before_invoke_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_compress_cancel_in_a_vacuum_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_compress_cancel_in_a_vacuum_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_compress_census_simple_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_compress_census_simple_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_compress_channel_connectivity_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_channel_connectivity.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_channel_connectivity.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_compress_channel_connectivity_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_compress_compressed_payload_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_compress_compressed_payload_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_compress_default_host_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_default_host.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_default_host.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_compress_default_host_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_compress_disappearing_server_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_compress_disappearing_server_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_compress_empty_batch_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_compress_empty_batch_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_compress_graceful_server_shutdown_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_compress_graceful_server_shutdown_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_compress_high_initial_seqno_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_compress_high_initial_seqno_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_compress_invoke_large_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_compress_invoke_large_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_compress_large_metadata_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_compress_large_metadata_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_compress_max_concurrent_streams_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_compress_max_concurrent_streams_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_compress_max_message_length_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_compress_max_message_length_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_compress_metadata_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_compress_metadata_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_compress_no_op_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_compress_no_op_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_compress_payload_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_compress_payload_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_compress_ping_pong_streaming_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_compress_ping_pong_streaming_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_compress_registered_call_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_compress_registered_call_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_compress_request_with_flags_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_compress_request_with_flags_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_compress_request_with_payload_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_compress_request_with_payload_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_compress_server_finishes_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_compress_server_finishes_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_compress_shutdown_finishes_calls_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_compress_shutdown_finishes_calls_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_compress_shutdown_finishes_tags_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_compress_shutdown_finishes_tags_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_compress_simple_delayed_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_compress_simple_delayed_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_compress_simple_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_compress_simple_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_compress_trailing_metadata_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_compress.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_compress_trailing_metadata_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full_bad_hostname_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full_bad_hostname_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full_binary_metadata_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full_binary_metadata_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full_cancel_after_accept_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full_cancel_after_accept_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full_cancel_after_client_done_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full_cancel_after_client_done_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full_cancel_after_invoke_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full_cancel_after_invoke_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full_cancel_before_invoke_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full_cancel_before_invoke_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full_cancel_in_a_vacuum_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full_cancel_in_a_vacuum_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full_census_simple_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full_census_simple_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full_channel_connectivity_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_channel_connectivity.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_channel_connectivity.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full_channel_connectivity_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full_compressed_payload_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full_compressed_payload_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full_default_host_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_default_host.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_default_host.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full_default_host_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full_disappearing_server_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full_disappearing_server_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full_empty_batch_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full_empty_batch_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full_graceful_server_shutdown_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full_graceful_server_shutdown_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full_high_initial_seqno_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full_high_initial_seqno_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full_invoke_large_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full_invoke_large_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full_large_metadata_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full_large_metadata_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full_max_concurrent_streams_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full_max_concurrent_streams_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full_max_message_length_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full_max_message_length_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full_metadata_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full_metadata_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full_no_op_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full_no_op_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full_payload_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full_payload_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full_ping_pong_streaming_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full_ping_pong_streaming_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full_registered_call_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full_registered_call_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full_request_with_flags_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full_request_with_flags_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full_request_with_payload_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full_request_with_payload_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full_server_finishes_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full_server_finishes_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full_shutdown_finishes_calls_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full_shutdown_finishes_calls_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full_shutdown_finishes_tags_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full_shutdown_finishes_tags_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full_simple_delayed_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full_simple_delayed_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full_simple_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full_simple_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full_trailing_metadata_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full_trailing_metadata_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full+poll_bad_hostname_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full+poll_bad_hostname_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full+poll_binary_metadata_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full+poll_binary_metadata_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_accept_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_accept_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_client_done_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_client_done_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_invoke_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_after_invoke_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full+poll_cancel_before_invoke_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_before_invoke_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full+poll_cancel_in_a_vacuum_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full+poll_cancel_in_a_vacuum_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full+poll_census_simple_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full+poll_census_simple_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full+poll_channel_connectivity_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_channel_connectivity.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_channel_connectivity.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full+poll_channel_connectivity_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full+poll_compressed_payload_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full+poll_compressed_payload_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full+poll_default_host_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_default_host.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_default_host.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full+poll_default_host_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full+poll_disappearing_server_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full+poll_disappearing_server_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full+poll_empty_batch_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full+poll_empty_batch_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full+poll_graceful_server_shutdown_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full+poll_graceful_server_shutdown_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full+poll_high_initial_seqno_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full+poll_high_initial_seqno_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full+poll_invoke_large_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full+poll_invoke_large_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full+poll_large_metadata_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full+poll_large_metadata_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full+poll_max_concurrent_streams_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full+poll_max_concurrent_streams_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full+poll_max_message_length_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full+poll_max_message_length_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full+poll_metadata_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full+poll_metadata_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full+poll_no_op_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full+poll_no_op_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full+poll_payload_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full+poll_payload_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full+poll_ping_pong_streaming_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full+poll_ping_pong_streaming_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full+poll_registered_call_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full+poll_registered_call_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full+poll_request_with_flags_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full+poll_request_with_flags_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full+poll_request_with_payload_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full+poll_request_with_payload_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full+poll_server_finishes_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full+poll_server_finishes_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full+poll_shutdown_finishes_calls_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full+poll_shutdown_finishes_calls_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full+poll_shutdown_finishes_tags_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full+poll_shutdown_finishes_tags_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full+poll_simple_delayed_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full+poll_simple_delayed_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full+poll_simple_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full+poll_simple_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_full+poll_trailing_metadata_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_full+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_full+poll_trailing_metadata_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_proxy_bad_hostname_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_proxy_bad_hostname_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_proxy_binary_metadata_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_proxy_binary_metadata_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_accept_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_accept_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_client_done_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_client_done_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_invoke_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_proxy_cancel_after_invoke_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_proxy_cancel_before_invoke_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_proxy_cancel_before_invoke_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_proxy_cancel_in_a_vacuum_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_proxy_cancel_in_a_vacuum_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_proxy_census_simple_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_proxy_census_simple_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_proxy_default_host_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_default_host.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_default_host.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_proxy_default_host_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_proxy_disappearing_server_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_proxy_disappearing_server_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_proxy_empty_batch_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_proxy_empty_batch_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_proxy_graceful_server_shutdown_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_proxy_graceful_server_shutdown_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_proxy_high_initial_seqno_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_proxy_high_initial_seqno_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_proxy_invoke_large_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_proxy_invoke_large_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_proxy_large_metadata_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_proxy_large_metadata_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_proxy_max_message_length_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_proxy_max_message_length_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_proxy_metadata_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_proxy_metadata_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_proxy_no_op_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_proxy_no_op_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_proxy_payload_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_proxy_payload_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_proxy_ping_pong_streaming_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_proxy_ping_pong_streaming_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_proxy_registered_call_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_proxy_registered_call_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_proxy_request_with_payload_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_proxy_request_with_payload_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_proxy_server_finishes_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_proxy_server_finishes_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_proxy_shutdown_finishes_calls_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_proxy_shutdown_finishes_calls_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_proxy_shutdown_finishes_tags_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_proxy_shutdown_finishes_tags_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_proxy_simple_delayed_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_proxy_simple_delayed_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_proxy_simple_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_proxy_simple_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_proxy_trailing_metadata_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_proxy.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_proxy_trailing_metadata_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_bad_hostname_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_bad_hostname_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_binary_metadata_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_binary_metadata_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_accept_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_accept_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_client_done_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_client_done_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_invoke_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_after_invoke_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_cancel_before_invoke_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_before_invoke_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_cancel_in_a_vacuum_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_cancel_in_a_vacuum_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_census_simple_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_census_simple_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_compressed_payload_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_compressed_payload_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_empty_batch_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_empty_batch_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_graceful_server_shutdown_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_graceful_server_shutdown_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_high_initial_seqno_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_high_initial_seqno_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_invoke_large_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_invoke_large_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_large_metadata_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_large_metadata_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_max_concurrent_streams_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_max_concurrent_streams_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_max_message_length_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_max_message_length_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_metadata_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_metadata_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_no_op_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_no_op_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_payload_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_payload_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_ping_pong_streaming_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_ping_pong_streaming_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_registered_call_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_registered_call_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_request_with_flags_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_request_with_flags_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_request_with_payload_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_request_with_payload_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_server_finishes_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_server_finishes_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_shutdown_finishes_calls_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_shutdown_finishes_calls_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_shutdown_finishes_tags_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_shutdown_finishes_tags_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_simple_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_simple_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_trailing_metadata_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_trailing_metadata_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_bad_hostname_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_bad_hostname_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_binary_metadata_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_binary_metadata_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_accept_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_accept_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_client_done_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_client_done_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_invoke_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_after_invoke_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_before_invoke_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_before_invoke_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_in_a_vacuum_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_cancel_in_a_vacuum_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_census_simple_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_census_simple_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_compressed_payload_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_compressed_payload_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_empty_batch_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_empty_batch_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_graceful_server_shutdown_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_graceful_server_shutdown_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_high_initial_seqno_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_high_initial_seqno_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_invoke_large_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_invoke_large_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_large_metadata_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_large_metadata_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_max_concurrent_streams_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_max_concurrent_streams_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_max_message_length_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_max_message_length_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_metadata_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_metadata_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_no_op_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_no_op_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_payload_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_payload_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_ping_pong_streaming_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_ping_pong_streaming_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_registered_call_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_registered_call_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_request_with_flags_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_request_with_flags_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_request_with_payload_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_request_with_payload_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_server_finishes_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_server_finishes_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_shutdown_finishes_calls_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_shutdown_finishes_calls_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_shutdown_finishes_tags_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_shutdown_finishes_tags_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_simple_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_simple_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair+trace_trailing_metadata_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair+trace.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair+trace_trailing_metadata_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_bad_hostname_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_bad_hostname_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_binary_metadata_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_binary_metadata_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_accept_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_accept_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_client_done_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_client_done_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_invoke_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_after_invoke_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_before_invoke_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_before_invoke_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_in_a_vacuum_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_cancel_in_a_vacuum_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_census_simple_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_census_simple_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_compressed_payload_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_compressed_payload_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_empty_batch_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_empty_batch_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_graceful_server_shutdown_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_graceful_server_shutdown_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_high_initial_seqno_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_high_initial_seqno_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_invoke_large_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_invoke_large_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_large_metadata_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_large_metadata_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_max_concurrent_streams_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_max_concurrent_streams_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_max_message_length_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_max_message_length_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_metadata_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_metadata_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_no_op_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_no_op_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_payload_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_payload_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_ping_pong_streaming_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_ping_pong_streaming_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_registered_call_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_registered_call_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_request_with_flags_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_request_with_flags_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_request_with_payload_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_request_with_payload_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_server_finishes_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_server_finishes_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_shutdown_finishes_calls_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_shutdown_finishes_calls_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_shutdown_finishes_tags_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_shutdown_finishes_tags_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_simple_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_simple_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_sockpair_1byte_trailing_metadata_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_sockpair_1byte.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_sockpair_1byte_trailing_metadata_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds_bad_hostname_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds_bad_hostname_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds_binary_metadata_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds_binary_metadata_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds_cancel_after_accept_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds_cancel_after_accept_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds_cancel_after_client_done_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds_cancel_after_client_done_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds_cancel_after_invoke_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds_cancel_after_invoke_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds_cancel_before_invoke_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds_cancel_before_invoke_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds_cancel_in_a_vacuum_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds_cancel_in_a_vacuum_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds_census_simple_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds_census_simple_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds_channel_connectivity_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_channel_connectivity.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_channel_connectivity.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds_channel_connectivity_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds_compressed_payload_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds_compressed_payload_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds_disappearing_server_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds_disappearing_server_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds_empty_batch_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds_empty_batch_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds_graceful_server_shutdown_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds_graceful_server_shutdown_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds_high_initial_seqno_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds_high_initial_seqno_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds_invoke_large_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds_invoke_large_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds_large_metadata_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds_large_metadata_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds_max_concurrent_streams_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds_max_concurrent_streams_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds_max_message_length_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds_max_message_length_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds_metadata_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds_metadata_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds_no_op_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds_no_op_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds_payload_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds_payload_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds_ping_pong_streaming_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds_ping_pong_streaming_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds_registered_call_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds_registered_call_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds_request_with_flags_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds_request_with_flags_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds_request_with_payload_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds_request_with_payload_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds_server_finishes_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds_server_finishes_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds_shutdown_finishes_calls_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds_shutdown_finishes_calls_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds_shutdown_finishes_tags_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds_shutdown_finishes_tags_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds_simple_delayed_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds_simple_delayed_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds_simple_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds_simple_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds_trailing_metadata_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds_trailing_metadata_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds+poll_bad_hostname_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_bad_hostname.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_bad_hostname_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds+poll_binary_metadata_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_binary_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_binary_metadata_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_accept_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_accept.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_accept_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_client_done_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_client_done.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_client_done_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_invoke_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_after_invoke.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_after_invoke_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_before_invoke_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_before_invoke.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_before_invoke_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_in_a_vacuum_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_cancel_in_a_vacuum_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds+poll_census_simple_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_census_simple_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_census_simple_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds+poll_channel_connectivity_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_channel_connectivity.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_channel_connectivity.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_channel_connectivity_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds+poll_compressed_payload_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_compressed_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_compressed_payload_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds+poll_disappearing_server_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_disappearing_server.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_disappearing_server_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds+poll_empty_batch_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_empty_batch.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_empty_batch_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds+poll_graceful_server_shutdown_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_graceful_server_shutdown.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_graceful_server_shutdown_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds+poll_high_initial_seqno_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_high_initial_seqno.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_high_initial_seqno_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds+poll_invoke_large_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_invoke_large_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_invoke_large_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds+poll_large_metadata_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_large_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_large_metadata_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds+poll_max_concurrent_streams_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_concurrent_streams.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_max_concurrent_streams_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds+poll_max_message_length_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_max_message_length.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_max_message_length_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds+poll_metadata_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_metadata_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds+poll_no_op_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_no_op.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_no_op_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds+poll_payload_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_payload_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds+poll_ping_pong_streaming_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_ping_pong_streaming.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_ping_pong_streaming_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds+poll_registered_call_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_registered_call.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_registered_call_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds+poll_request_with_flags_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_flags.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_request_with_flags_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds+poll_request_with_payload_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_request_with_payload.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_request_with_payload_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds+poll_server_finishes_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_server_finishes_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_server_finishes_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds+poll_shutdown_finishes_calls_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_calls.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_shutdown_finishes_calls_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds+poll_shutdown_finishes_tags_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_shutdown_finishes_tags.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_shutdown_finishes_tags_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds+poll_simple_delayed_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_delayed_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_simple_delayed_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds+poll_simple_request_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_simple_request.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_simple_request_nosec_test + + + + +$(BINDIR)/$(CONFIG)/h2_uds+poll_trailing_metadata_nosec_test: $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(LIBDIR)/$(CONFIG)/libend2end_fixture_h2_uds+poll.a $(LIBDIR)/$(CONFIG)/libend2end_test_trailing_metadata.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/h2_uds+poll_trailing_metadata_nosec_test + + + + +CONNECTION_PREFIX_BAD_CLIENT_TEST_SRC = \ + test/core/bad_client/tests/connection_prefix.c \ + +CONNECTION_PREFIX_BAD_CLIENT_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CONNECTION_PREFIX_BAD_CLIENT_TEST_SRC)))) +$(BINDIR)/$(CONFIG)/connection_prefix_bad_client_test: $(CONNECTION_PREFIX_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(CONNECTION_PREFIX_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/connection_prefix_bad_client_test + +$(OBJDIR)/$(CONFIG)/test/core/bad_client/tests/connection_prefix.o: $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_connection_prefix_bad_client_test: $(CONNECTION_PREFIX_BAD_CLIENT_TEST_OBJS:.o=.dep) + +ifneq ($(NO_DEPS),true) +-include $(CONNECTION_PREFIX_BAD_CLIENT_TEST_OBJS:.o=.dep) +endif + + +INITIAL_SETTINGS_FRAME_BAD_CLIENT_TEST_SRC = \ + test/core/bad_client/tests/initial_settings_frame.c \ + +INITIAL_SETTINGS_FRAME_BAD_CLIENT_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(INITIAL_SETTINGS_FRAME_BAD_CLIENT_TEST_SRC)))) +$(BINDIR)/$(CONFIG)/initial_settings_frame_bad_client_test: $(INITIAL_SETTINGS_FRAME_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(INITIAL_SETTINGS_FRAME_BAD_CLIENT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) -o $(BINDIR)/$(CONFIG)/initial_settings_frame_bad_client_test + +$(OBJDIR)/$(CONFIG)/test/core/bad_client/tests/initial_settings_frame.o: $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_initial_settings_frame_bad_client_test: $(INITIAL_SETTINGS_FRAME_BAD_CLIENT_TEST_OBJS:.o=.dep) + +ifneq ($(NO_DEPS),true) +-include $(INITIAL_SETTINGS_FRAME_BAD_CLIENT_TEST_OBJS:.o=.dep) +endif + + + + + + +ifneq ($(OPENSSL_DEP),) +# This is to ensure the embedded OpenSSL is built beforehand, properly +# installing headers to their final destination on the drive. We need this +# otherwise parallel compilation will fail if a source is compiled first. +src/core/httpcli/httpcli_security_connector.c: $(OPENSSL_DEP) +src/core/security/base64.c: $(OPENSSL_DEP) +src/core/security/client_auth_filter.c: $(OPENSSL_DEP) +src/core/security/credentials.c: $(OPENSSL_DEP) +src/core/security/credentials_metadata.c: $(OPENSSL_DEP) +src/core/security/credentials_posix.c: $(OPENSSL_DEP) +src/core/security/credentials_win32.c: $(OPENSSL_DEP) +src/core/security/google_default_credentials.c: $(OPENSSL_DEP) +src/core/security/json_token.c: $(OPENSSL_DEP) +src/core/security/jwt_verifier.c: $(OPENSSL_DEP) +src/core/security/secure_endpoint.c: $(OPENSSL_DEP) +src/core/security/secure_transport_setup.c: $(OPENSSL_DEP) +src/core/security/security_connector.c: $(OPENSSL_DEP) +src/core/security/security_context.c: $(OPENSSL_DEP) +src/core/security/server_auth_filter.c: $(OPENSSL_DEP) +src/core/security/server_secure_chttp2.c: $(OPENSSL_DEP) +src/core/surface/init_secure.c: $(OPENSSL_DEP) +src/core/surface/secure_channel_create.c: $(OPENSSL_DEP) +src/core/tsi/fake_transport_security.c: $(OPENSSL_DEP) +src/core/tsi/ssl_transport_security.c: $(OPENSSL_DEP) +src/core/tsi/transport_security.c: $(OPENSSL_DEP) +src/cpp/client/secure_channel_arguments.cc: $(OPENSSL_DEP) +src/cpp/client/secure_credentials.cc: $(OPENSSL_DEP) +src/cpp/common/auth_property_iterator.cc: $(OPENSSL_DEP) +src/cpp/common/secure_auth_context.cc: $(OPENSSL_DEP) +src/cpp/common/secure_create_auth_context.cc: $(OPENSSL_DEP) +src/cpp/server/secure_server_credentials.cc: $(OPENSSL_DEP) +src/csharp/ext/grpc_csharp_ext.c: $(OPENSSL_DEP) +test/core/bad_client/bad_client.c: $(OPENSSL_DEP) +test/core/end2end/data/server1_cert.c: $(OPENSSL_DEP) +test/core/end2end/data/server1_key.c: $(OPENSSL_DEP) +test/core/end2end/data/test_root_cert.c: $(OPENSSL_DEP) +test/core/end2end/fixtures/h2_fakesec.c: $(OPENSSL_DEP) +test/core/end2end/fixtures/h2_oauth2.c: $(OPENSSL_DEP) +test/core/end2end/fixtures/h2_ssl+poll.c: $(OPENSSL_DEP) +test/core/end2end/fixtures/h2_ssl.c: $(OPENSSL_DEP) +test/core/end2end/fixtures/h2_ssl_proxy.c: $(OPENSSL_DEP) +test/core/end2end/tests/call_creds.c: $(OPENSSL_DEP) +test/core/util/reconnect_server.c: $(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) +test/cpp/interop/server.cc: $(OPENSSL_DEP) +test/cpp/interop/server_helper.cc: $(OPENSSL_DEP) +test/cpp/qps/client_async.cc: $(OPENSSL_DEP) +test/cpp/qps/client_sync.cc: $(OPENSSL_DEP) +test/cpp/qps/driver.cc: $(OPENSSL_DEP) +test/cpp/qps/perf_db_client.cc: $(OPENSSL_DEP) +test/cpp/qps/qps_worker.cc: $(OPENSSL_DEP) +test/cpp/qps/report.cc: $(OPENSSL_DEP) +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/cli_call.cc: $(OPENSSL_DEP) +test/cpp/util/create_test_channel.cc: $(OPENSSL_DEP) +test/cpp/util/string_ref_helper.cc: $(OPENSSL_DEP) +test/cpp/util/subprocess.cc: $(OPENSSL_DEP) +test/cpp/util/test_config.cc: $(OPENSSL_DEP) +endif + +.PHONY: all strip tools dep_error openssl_dep_error openssl_dep_message git_update stop buildtests buildtests_c buildtests_cxx test test_c test_cxx install install_c install_cxx install-headers install-headers_c install-headers_cxx install-shared install-shared_c install-shared_cxx install-static install-static_c install-static_cxx strip strip-shared strip-static strip_c strip-shared_c strip-static_c strip_cxx strip-shared_cxx strip-static_cxx dep_c dep_cxx bins_dep_c bins_dep_cxx clean diff --git a/PATENTS b/PATENTS new file mode 100644 index 00000000..619f9dbf --- /dev/null +++ b/PATENTS @@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the GRPC project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of GRPC, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of GRPC. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of GRPC or any code incorporated within this +implementation of GRPC constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of GRPC +shall terminate as of the date such litigation is filed. diff --git a/README.md b/README.md new file mode 100644 index 00000000..ab1004f9 --- /dev/null +++ b/README.md @@ -0,0 +1,109 @@ +[![Build Status](https://grpc-testing.appspot.com/job/gRPC_master/badge/icon)](https://grpc-testing.appspot.com/job/gRPC_master) +[![Coverage Status](https://img.shields.io/coveralls/grpc/grpc.svg)](https://coveralls.io/r/grpc/grpc?branch=master) + +[gRPC - An RPC library and framework](http://github.com/grpc/grpc) +=================================== + +Copyright 2015 Google Inc. + +#Documentation + +You can find more detailed documentation and examples in the [doc](doc) and [examples](examples) directories respectively. + +#Installation + +See grpc/INSTALL for installation instructions for various platforms. + +#Repository Structure + +This repository contains source code for gRPC libraries for multiple languages written on top +of shared C core library [src/core] (src/core). + + * C++ source code: [src/cpp] (src/cpp) + * Ruby source code: [src/ruby] (src/ruby) + * NodeJS source code: [src/node] (src/node) + * Python source code: [src/python] (src/python) + * PHP source code: [src/php] (src/php) + * C# source code: [src/csharp] (src/csharp) + * Objective-C source code: [src/objective-c] (src/objective-c) + +Java source code is in [grpc-java] (http://github.com/grpc/grpc-java) repository. +Go source code is in [grpc-go] (http://github.com/grpc/grpc-go) repository. + +#Current Status of libraries + +Libraries in different languages are in different state of development. We are seeking contributions for all of these libraries. + + * shared C core library [src/core] (src/core) : Early adopter ready - Alpha. + * C++ Library: [src/cpp] (src/cpp) : Early adopter ready - Alpha. + * Ruby Library: [src/ruby] (src/ruby) : Early adopter ready - Alpha. + * NodeJS Library: [src/node] (src/node) : Early adopter ready - Alpha. + * Python Library: [src/python] (src/python) : Early adopter ready - Alpha. + * C# Library: [src/csharp] (src/csharp) : Early adopter ready - Alpha. + * Objective-C Library: [src/objective-c] (src/objective-c): Early adopter ready - Alpha. + * PHP Library: [src/php] (src/php) : Pre-Alpha. + +#Overview + + +Remote Procedure Calls (RPCs) provide a useful abstraction for building +distributed applications and services. The libraries in this repository +provide a concrete implementation of the gRPC protocol, layered over HTTP/2. +These libraries enable communication between clients and servers using any +combination of the supported languages. + + +##Interface + + +Developers using gRPC typically start with the description of an RPC service +(a collection of methods), and generate client and server side interfaces +which they use on the client-side and implement on the server side. + +By default, gRPC uses [Protocol Buffers](https://github.com/google/protobuf) as the +Interface Definition Language (IDL) for describing both the service interface +and the structure of the payload messages. It is possible to use other +alternatives if desired. + +###Surface API +Starting from an interface definition in a .proto file, gRPC provides +Protocol Compiler plugins that generate Client- and Server-side APIs. +gRPC users typically call into these APIs on the Client side and implement +the corresponding API on the server side. + +#### Synchronous vs. asynchronous +Synchronous RPC calls, that block until a response arrives from the server, are +the closest approximation to the abstraction of a procedure call that RPC +aspires to. + +On the other hand, networks are inherently asynchronous and in many scenarios, +it is desirable to have the ability to start RPCs without blocking the current +thread. + +The gRPC programming surface in most languages comes in both synchronous and +asynchronous flavors. + + +## Streaming + +gRPC supports streaming semantics, where either the client or the server (or both) +send a stream of messages on a single RPC call. The most general case is +Bidirectional Streaming where a single gRPC call establishes a stream where both +the client and the server can send a stream of messages to each other. The streamed +messages are delivered in the order they were sent. + + +#Protocol + +The [gRPC protocol](doc/PROTOCOL-HTTP2.md) specifies the abstract requirements for communication between +clients and servers. A concrete embedding over HTTP/2 completes the picture by +fleshing out the details of each of the required operations. + +## Abstract gRPC protocol +A gRPC RPC comprises of a bidirectional stream of messages, initiated by the client. In the client-to-server direction, this stream begins with a mandatory `Call Header`, followed by optional `Initial-Metadata`, followed by zero or more `Payload Messages`. The server-to-client direction contains an optional `Initial-Metadata`, followed by zero or more `Payload Messages` terminated with a mandatory `Status` and optional `Status-Metadata` (a.k.a.,`Trailing-Metadata`). + +## Implementation over HTTP/2 +The abstract protocol defined above is implemented over [HTTP/2](https://http2.github.io/). gRPC bidirectional streams are mapped to HTTP/2 streams. The contents of `Call Header` and `Initial Metadata` are sent as HTTP/2 headers and subject to HPACK compression. `Payload Messages` are serialized into a byte stream of length prefixed gRPC frames which are then fragmented into HTTP/2 frames at the sender and reassembled at the receiver. `Status` and `Trailing-Metadata` are sent as HTTP/2 trailing headers (a.k.a., trailers). + +## Flow Control +gRPC inherits the flow control mechanisms in HTTP/2 and uses them to enable fine-grained control of the amount of memory used for buffering in-flight messages. diff --git a/build.yaml b/build.yaml new file mode 100644 index 00000000..5eb7b22d --- /dev/null +++ b/build.yaml @@ -0,0 +1,1008 @@ +'#1': This file describes the list of targets and dependencies. +'#2': It is used among other things to generate all of our project files. +'#3': Please refer to the templates directory for more information. +settings: + '#': The public version number of the library. + version: {major: 0, minor: 11, micro: 0, build: 0} +filegroups: +- name: census + public_headers: [include/grpc/census.h] + headers: [src/core/census/aggregation.h, src/core/census/context.h, src/core/census/rpc_metric_id.h] + src: [src/core/census/context.c, src/core/census/initialize.c, src/core/census/operation.c, + src/core/census/tracing.c] +- name: grpc++_base + public_headers: [include/grpc++/channel.h, include/grpc++/client_context.h, include/grpc++/completion_queue.h, + include/grpc++/create_channel.h, include/grpc++/generic/async_generic_service.h, + include/grpc++/generic/generic_stub.h, 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/proto_utils.h, + include/grpc++/impl/rpc_method.h, include/grpc++/impl/rpc_service_method.h, include/grpc++/impl/serialization_traits.h, + include/grpc++/impl/service_type.h, include/grpc++/impl/sync.h, include/grpc++/impl/sync_cxx11.h, + include/grpc++/impl/sync_no_cxx11.h, include/grpc++/impl/thd.h, include/grpc++/impl/thd_cxx11.h, + include/grpc++/impl/thd_no_cxx11.h, include/grpc++/security/auth_context.h, include/grpc++/security/auth_metadata_processor.h, + include/grpc++/security/credentials.h, include/grpc++/security/server_credentials.h, + include/grpc++/server.h, include/grpc++/server_builder.h, include/grpc++/server_context.h, + include/grpc++/support/async_stream.h, include/grpc++/support/async_unary_call.h, + include/grpc++/support/byte_buffer.h, include/grpc++/support/channel_arguments.h, + include/grpc++/support/config.h, include/grpc++/support/config_protobuf.h, include/grpc++/support/slice.h, + include/grpc++/support/status.h, include/grpc++/support/status_code_enum.h, include/grpc++/support/string_ref.h, + include/grpc++/support/stub_options.h, include/grpc++/support/sync_stream.h, include/grpc++/support/time.h] + headers: [src/cpp/client/create_channel_internal.h, src/cpp/common/create_auth_context.h, + src/cpp/server/dynamic_thread_pool.h, src/cpp/server/fixed_size_thread_pool.h, + src/cpp/server/thread_pool_interface.h] + src: [src/cpp/client/channel.cc, src/cpp/client/channel_arguments.cc, src/cpp/client/client_context.cc, + src/cpp/client/create_channel.cc, src/cpp/client/create_channel_internal.cc, src/cpp/client/credentials.cc, + src/cpp/client/generic_stub.cc, src/cpp/client/insecure_credentials.cc, src/cpp/common/call.cc, + src/cpp/common/completion_queue.cc, src/cpp/common/rpc_method.cc, src/cpp/proto/proto_utils.cc, + src/cpp/server/async_generic_service.cc, src/cpp/server/create_default_thread_pool.cc, + src/cpp/server/dynamic_thread_pool.cc, src/cpp/server/fixed_size_thread_pool.cc, + src/cpp/server/insecure_server_credentials.cc, src/cpp/server/server.cc, src/cpp/server/server_builder.cc, + src/cpp/server/server_context.cc, src/cpp/server/server_credentials.cc, src/cpp/util/byte_buffer.cc, + src/cpp/util/slice.cc, src/cpp/util/status.cc, src/cpp/util/string_ref.cc, src/cpp/util/time.cc] +- name: grpc_base + public_headers: [include/grpc/byte_buffer.h, include/grpc/byte_buffer_reader.h, + include/grpc/compression.h, include/grpc/grpc.h, include/grpc/status.h] + headers: [src/core/census/grpc_filter.h, src/core/channel/channel_args.h, src/core/channel/channel_stack.h, + src/core/channel/client_channel.h, src/core/channel/compress_filter.h, src/core/channel/connected_channel.h, + src/core/channel/context.h, src/core/channel/http_client_filter.h, src/core/channel/http_server_filter.h, + src/core/channel/noop_filter.h, src/core/client_config/client_config.h, src/core/client_config/connector.h, + src/core/client_config/lb_policies/pick_first.h, src/core/client_config/lb_policy.h, + src/core/client_config/resolver.h, src/core/client_config/resolver_factory.h, + src/core/client_config/resolver_registry.h, src/core/client_config/resolvers/dns_resolver.h, + src/core/client_config/resolvers/sockaddr_resolver.h, src/core/client_config/subchannel.h, + src/core/client_config/subchannel_factory.h, src/core/client_config/subchannel_factory_decorators/add_channel_arg.h, + src/core/client_config/subchannel_factory_decorators/merge_channel_args.h, src/core/client_config/uri_parser.h, + src/core/compression/message_compress.h, src/core/debug/trace.h, src/core/httpcli/format_request.h, + src/core/httpcli/httpcli.h, src/core/httpcli/parser.h, src/core/iomgr/alarm.h, + src/core/iomgr/alarm_heap.h, src/core/iomgr/alarm_internal.h, src/core/iomgr/endpoint.h, + src/core/iomgr/endpoint_pair.h, src/core/iomgr/fd_posix.h, src/core/iomgr/iocp_windows.h, + src/core/iomgr/iomgr.h, src/core/iomgr/iomgr_internal.h, src/core/iomgr/iomgr_posix.h, + src/core/iomgr/pollset.h, src/core/iomgr/pollset_posix.h, src/core/iomgr/pollset_set.h, + src/core/iomgr/pollset_set_posix.h, src/core/iomgr/pollset_set_windows.h, src/core/iomgr/pollset_windows.h, + src/core/iomgr/resolve_address.h, src/core/iomgr/sockaddr.h, src/core/iomgr/sockaddr_posix.h, + src/core/iomgr/sockaddr_utils.h, src/core/iomgr/sockaddr_win32.h, src/core/iomgr/socket_utils_posix.h, + src/core/iomgr/socket_windows.h, src/core/iomgr/tcp_client.h, src/core/iomgr/tcp_posix.h, + src/core/iomgr/tcp_server.h, src/core/iomgr/tcp_windows.h, src/core/iomgr/time_averaged_stats.h, + src/core/iomgr/udp_server.h, src/core/iomgr/wakeup_fd_pipe.h, src/core/iomgr/wakeup_fd_posix.h, + src/core/json/json.h, src/core/json/json_common.h, src/core/json/json_reader.h, + src/core/json/json_writer.h, src/core/profiling/timers.h, src/core/statistics/census_interface.h, + src/core/statistics/census_rpc_stats.h, src/core/surface/byte_buffer_queue.h, + src/core/surface/call.h, src/core/surface/channel.h, src/core/surface/completion_queue.h, + src/core/surface/event_string.h, src/core/surface/init.h, src/core/surface/server.h, + src/core/surface/surface_trace.h, src/core/transport/chttp2/alpn.h, src/core/transport/chttp2/bin_encoder.h, + src/core/transport/chttp2/frame.h, src/core/transport/chttp2/frame_data.h, src/core/transport/chttp2/frame_goaway.h, + src/core/transport/chttp2/frame_ping.h, src/core/transport/chttp2/frame_rst_stream.h, + src/core/transport/chttp2/frame_settings.h, src/core/transport/chttp2/frame_window_update.h, + src/core/transport/chttp2/hpack_parser.h, src/core/transport/chttp2/hpack_table.h, + src/core/transport/chttp2/http2_errors.h, src/core/transport/chttp2/huffsyms.h, + src/core/transport/chttp2/incoming_metadata.h, src/core/transport/chttp2/internal.h, + src/core/transport/chttp2/status_conversion.h, src/core/transport/chttp2/stream_encoder.h, + src/core/transport/chttp2/stream_map.h, src/core/transport/chttp2/timeout_encoding.h, + src/core/transport/chttp2/varint.h, src/core/transport/chttp2_transport.h, src/core/transport/connectivity_state.h, + src/core/transport/metadata.h, src/core/transport/stream_op.h, src/core/transport/transport.h, + src/core/transport/transport_impl.h] + src: [src/core/census/grpc_context.c, src/core/census/grpc_filter.c, src/core/channel/channel_args.c, + src/core/channel/channel_stack.c, src/core/channel/client_channel.c, src/core/channel/compress_filter.c, + src/core/channel/connected_channel.c, src/core/channel/http_client_filter.c, src/core/channel/http_server_filter.c, + src/core/channel/noop_filter.c, src/core/client_config/client_config.c, src/core/client_config/connector.c, + src/core/client_config/lb_policies/pick_first.c, src/core/client_config/lb_policy.c, + src/core/client_config/resolver.c, src/core/client_config/resolver_factory.c, + src/core/client_config/resolver_registry.c, src/core/client_config/resolvers/dns_resolver.c, + src/core/client_config/resolvers/sockaddr_resolver.c, src/core/client_config/subchannel.c, + src/core/client_config/subchannel_factory.c, src/core/client_config/subchannel_factory_decorators/add_channel_arg.c, + src/core/client_config/subchannel_factory_decorators/merge_channel_args.c, src/core/client_config/uri_parser.c, + src/core/compression/algorithm.c, src/core/compression/message_compress.c, src/core/debug/trace.c, + src/core/httpcli/format_request.c, src/core/httpcli/httpcli.c, src/core/httpcli/parser.c, + src/core/iomgr/alarm.c, src/core/iomgr/alarm_heap.c, src/core/iomgr/endpoint.c, + src/core/iomgr/endpoint_pair_posix.c, src/core/iomgr/endpoint_pair_windows.c, + src/core/iomgr/fd_posix.c, src/core/iomgr/iocp_windows.c, src/core/iomgr/iomgr.c, + src/core/iomgr/iomgr_posix.c, src/core/iomgr/iomgr_windows.c, src/core/iomgr/pollset_multipoller_with_epoll.c, + src/core/iomgr/pollset_multipoller_with_poll_posix.c, src/core/iomgr/pollset_posix.c, + src/core/iomgr/pollset_set_posix.c, src/core/iomgr/pollset_set_windows.c, src/core/iomgr/pollset_windows.c, + src/core/iomgr/resolve_address_posix.c, src/core/iomgr/resolve_address_windows.c, + src/core/iomgr/sockaddr_utils.c, src/core/iomgr/socket_utils_common_posix.c, src/core/iomgr/socket_utils_linux.c, + src/core/iomgr/socket_utils_posix.c, src/core/iomgr/socket_windows.c, src/core/iomgr/tcp_client_posix.c, + src/core/iomgr/tcp_client_windows.c, src/core/iomgr/tcp_posix.c, src/core/iomgr/tcp_server_posix.c, + src/core/iomgr/tcp_server_windows.c, src/core/iomgr/tcp_windows.c, src/core/iomgr/time_averaged_stats.c, + src/core/iomgr/udp_server.c, src/core/iomgr/wakeup_fd_eventfd.c, src/core/iomgr/wakeup_fd_nospecial.c, + src/core/iomgr/wakeup_fd_pipe.c, src/core/iomgr/wakeup_fd_posix.c, src/core/json/json.c, + src/core/json/json_reader.c, src/core/json/json_string.c, src/core/json/json_writer.c, + src/core/profiling/basic_timers.c, src/core/profiling/stap_timers.c, src/core/surface/byte_buffer.c, + src/core/surface/byte_buffer_queue.c, src/core/surface/byte_buffer_reader.c, src/core/surface/call.c, + src/core/surface/call_details.c, src/core/surface/call_log_batch.c, src/core/surface/channel.c, + src/core/surface/channel_connectivity.c, src/core/surface/channel_create.c, src/core/surface/completion_queue.c, + src/core/surface/event_string.c, src/core/surface/init.c, src/core/surface/lame_client.c, + src/core/surface/metadata_array.c, src/core/surface/server.c, src/core/surface/server_chttp2.c, + src/core/surface/server_create.c, src/core/surface/surface_trace.c, src/core/surface/version.c, + src/core/transport/chttp2/alpn.c, src/core/transport/chttp2/bin_encoder.c, src/core/transport/chttp2/frame_data.c, + src/core/transport/chttp2/frame_goaway.c, src/core/transport/chttp2/frame_ping.c, + src/core/transport/chttp2/frame_rst_stream.c, src/core/transport/chttp2/frame_settings.c, + src/core/transport/chttp2/frame_window_update.c, src/core/transport/chttp2/hpack_parser.c, + src/core/transport/chttp2/hpack_table.c, src/core/transport/chttp2/huffsyms.c, + src/core/transport/chttp2/incoming_metadata.c, src/core/transport/chttp2/parsing.c, + src/core/transport/chttp2/status_conversion.c, src/core/transport/chttp2/stream_encoder.c, + src/core/transport/chttp2/stream_lists.c, src/core/transport/chttp2/stream_map.c, + src/core/transport/chttp2/timeout_encoding.c, src/core/transport/chttp2/varint.c, + src/core/transport/chttp2/writing.c, src/core/transport/chttp2_transport.c, src/core/transport/connectivity_state.c, + src/core/transport/metadata.c, src/core/transport/stream_op.c, src/core/transport/transport.c, + src/core/transport/transport_op_string.c] +- name: grpc_test_util_base + headers: [test/core/end2end/cq_verifier.h, test/core/end2end/fixtures/proxy.h, test/core/iomgr/endpoint_tests.h, + test/core/security/oauth2_utils.h, test/core/util/grpc_profiler.h, test/core/util/parse_hexstring.h, + test/core/util/port.h, test/core/util/slice_splitter.h] + src: [test/core/end2end/cq_verifier.c, test/core/end2end/fixtures/proxy.c, test/core/iomgr/endpoint_tests.c, + test/core/security/oauth2_utils.c, test/core/util/grpc_profiler.c, test/core/util/parse_hexstring.c, + test/core/util/port_posix.c, test/core/util/port_windows.c, test/core/util/slice_splitter.c] +libs: +- name: gpr + build: all + language: c + public_headers: [include/grpc/support/alloc.h, include/grpc/support/atm.h, include/grpc/support/atm_gcc_atomic.h, + include/grpc/support/atm_gcc_sync.h, include/grpc/support/atm_win32.h, include/grpc/support/cmdline.h, + include/grpc/support/cpu.h, include/grpc/support/histogram.h, include/grpc/support/host_port.h, + include/grpc/support/log.h, include/grpc/support/log_win32.h, include/grpc/support/port_platform.h, + include/grpc/support/slice.h, include/grpc/support/slice_buffer.h, include/grpc/support/string_util.h, + include/grpc/support/subprocess.h, include/grpc/support/sync.h, include/grpc/support/sync_generic.h, + include/grpc/support/sync_posix.h, include/grpc/support/sync_win32.h, include/grpc/support/thd.h, + include/grpc/support/time.h, include/grpc/support/tls.h, include/grpc/support/tls_gcc.h, + include/grpc/support/tls_msvc.h, include/grpc/support/tls_pthread.h, include/grpc/support/useful.h] + headers: [src/core/support/env.h, src/core/support/file.h, src/core/support/murmur_hash.h, + src/core/support/stack_lockfree.h, src/core/support/string.h, src/core/support/string_win32.h, + src/core/support/thd_internal.h, src/core/support/time_precise.h] + src: [src/core/support/alloc.c, src/core/support/cmdline.c, src/core/support/cpu_iphone.c, + src/core/support/cpu_linux.c, src/core/support/cpu_posix.c, src/core/support/cpu_windows.c, + src/core/support/env_linux.c, src/core/support/env_posix.c, src/core/support/env_win32.c, + src/core/support/file.c, src/core/support/file_posix.c, src/core/support/file_win32.c, + src/core/support/histogram.c, src/core/support/host_port.c, src/core/support/log.c, + src/core/support/log_android.c, src/core/support/log_linux.c, src/core/support/log_posix.c, + src/core/support/log_win32.c, src/core/support/murmur_hash.c, src/core/support/slice.c, + src/core/support/slice_buffer.c, src/core/support/stack_lockfree.c, src/core/support/string.c, + src/core/support/string_posix.c, src/core/support/string_win32.c, src/core/support/subprocess_posix.c, + src/core/support/sync.c, src/core/support/sync_posix.c, src/core/support/sync_win32.c, + src/core/support/thd.c, src/core/support/thd_posix.c, src/core/support/thd_win32.c, + src/core/support/time.c, src/core/support/time_posix.c, src/core/support/time_win32.c, + src/core/support/tls_pthread.c] + secure: false + vs_project_guid: '{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}' +- name: gpr_test_util + build: private + language: c + headers: [test/core/util/test_config.h] + src: [test/core/util/test_config.c] + deps: [gpr] + secure: false + vs_project_guid: '{EAB0A629-17A9-44DB-B5FF-E91A721FE037}' +- name: grpc + build: all + language: c + public_headers: [include/grpc/grpc_security.h] + headers: [src/core/security/auth_filters.h, src/core/security/base64.h, src/core/security/credentials.h, + src/core/security/json_token.h, src/core/security/jwt_verifier.h, src/core/security/secure_endpoint.h, + src/core/security/secure_transport_setup.h, src/core/security/security_connector.h, + src/core/security/security_context.h, src/core/tsi/fake_transport_security.h, + src/core/tsi/ssl_transport_security.h, src/core/tsi/transport_security.h, src/core/tsi/transport_security_interface.h] + src: [src/core/httpcli/httpcli_security_connector.c, src/core/security/base64.c, + src/core/security/client_auth_filter.c, src/core/security/credentials.c, src/core/security/credentials_metadata.c, + src/core/security/credentials_posix.c, src/core/security/credentials_win32.c, + src/core/security/google_default_credentials.c, src/core/security/json_token.c, + src/core/security/jwt_verifier.c, src/core/security/secure_endpoint.c, src/core/security/secure_transport_setup.c, + src/core/security/security_connector.c, src/core/security/security_context.c, + src/core/security/server_auth_filter.c, src/core/security/server_secure_chttp2.c, + src/core/surface/init_secure.c, src/core/surface/secure_channel_create.c, src/core/tsi/fake_transport_security.c, + src/core/tsi/ssl_transport_security.c, src/core/tsi/transport_security.c] + deps: [gpr] + baselib: true + dll: true + filegroups: [grpc_base, census] + secure: true + vs_packages: [grpc.dependencies.openssl, grpc.dependencies.zlib] + vs_project_guid: '{29D16885-7228-4C31-81ED-5F9187C7F2A9}' +- name: grpc_test_util + build: private + language: c + headers: [test/core/end2end/data/ssl_test_data.h] + src: [test/core/end2end/data/server1_cert.c, test/core/end2end/data/server1_key.c, + test/core/end2end/data/test_root_cert.c] + deps: [gpr, gpr_test_util, grpc] + filegroups: [grpc_test_util_base] + vs_project_guid: '{17BCAFC0-5FDC-4C94-AEB9-95F3E220614B}' +- name: grpc_test_util_unsecure + build: private + language: c + deps: [gpr, gpr_test_util, grpc] + filegroups: [grpc_test_util_base] + secure: false + vs_project_guid: '{0A7E7F92-FDEA-40F1-A9EC-3BA484F98BBF}' +- name: grpc_unsecure + build: all + language: c + src: [src/core/surface/init_unsecure.c] + deps: [gpr] + baselib: true + dll: true + filegroups: [grpc_base, census] + secure: false + vs_project_guid: '{46CEDFFF-9692-456A-AA24-38B5D6BCF4C5}' +- name: grpc_zookeeper + build: all + language: c + public_headers: [include/grpc/grpc_zookeeper.h] + headers: [src/core/client_config/resolvers/zookeeper_resolver.h] + src: [src/core/client_config/resolvers/zookeeper_resolver.c] + deps: [gpr, grpc] + external_deps: [zookeeper] + platforms: [linux] + secure: false +- name: reconnect_server + build: private + language: c + headers: [test/core/util/reconnect_server.h] + src: [test/core/util/reconnect_server.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: grpc++ + build: all + language: c++ + headers: [src/cpp/client/secure_credentials.h, src/cpp/common/secure_auth_context.h, + src/cpp/server/secure_server_credentials.h] + src: [src/cpp/client/secure_channel_arguments.cc, src/cpp/client/secure_credentials.cc, + src/cpp/common/auth_property_iterator.cc, src/cpp/common/secure_auth_context.cc, + src/cpp/common/secure_create_auth_context.cc, src/cpp/server/secure_server_credentials.cc] + deps: [gpr, grpc] + baselib: true + dll: true + filegroups: [grpc++_base] + secure: check + vs_project_guid: '{C187A093-A0FE-489D-A40A-6E33DE0F9FEB}' +- name: grpc++_test_config + build: private + language: c++ + headers: [test/cpp/util/test_config.h] + src: [test/cpp/util/test_config.cc] +- name: grpc++_test_util + build: private + language: c++ + headers: [test/cpp/util/cli_call.h, test/cpp/util/create_test_channel.h, test/cpp/util/string_ref_helper.h, + test/cpp/util/subprocess.h] + src: [test/cpp/util/messages.proto, test/cpp/util/echo.proto, test/cpp/util/echo_duplicate.proto, + test/cpp/util/cli_call.cc, test/cpp/util/create_test_channel.cc, test/cpp/util/string_ref_helper.cc, + test/cpp/util/subprocess.cc] + deps: [grpc++, grpc_test_util] +- name: grpc++_unsecure + build: all + language: c++ + src: [src/cpp/common/insecure_create_auth_context.cc] + deps: [gpr, grpc_unsecure] + baselib: true + dll: true + filegroups: [grpc++_base] + secure: false + vs_project_guid: '{6EE56155-DF7C-4F6E-BFC4-F6F776BEB211}' +- name: grpc_plugin_support + build: protoc + language: c++ + headers: [include/grpc++/support/config.h, include/grpc++/support/config_protobuf.h, + src/compiler/config.h, src/compiler/cpp_generator.h, src/compiler/cpp_generator_helpers.h, + src/compiler/csharp_generator.h, src/compiler/csharp_generator_helpers.h, src/compiler/generator_helpers.h, + src/compiler/objective_c_generator.h, src/compiler/objective_c_generator_helpers.h, + src/compiler/python_generator.h, src/compiler/ruby_generator.h, src/compiler/ruby_generator_helpers-inl.h, + src/compiler/ruby_generator_map-inl.h, src/compiler/ruby_generator_string-inl.h] + src: [src/compiler/cpp_generator.cc, src/compiler/csharp_generator.cc, src/compiler/objective_c_generator.cc, + src/compiler/python_generator.cc, src/compiler/ruby_generator.cc] + deps: [] + secure: false + vs_project_guid: '{B6E81D84-2ACB-41B8-8781-493A944C7817}' +- name: interop_client_helper + build: private + language: c++ + headers: [test/cpp/interop/client_helper.h] + src: [test/proto/messages.proto, test/cpp/interop/client_helper.cc] + deps: [grpc++_test_util, grpc_test_util, grpc++, grpc, gpr] +- name: interop_client_main + build: private + language: c++ + headers: [test/cpp/interop/interop_client.h] + src: [test/proto/empty.proto, test/proto/messages.proto, test/proto/test.proto, + test/cpp/interop/client.cc, test/cpp/interop/interop_client.cc] + deps: [interop_client_helper, grpc++_test_util, grpc_test_util, grpc++, grpc, gpr_test_util, + gpr, grpc++_test_config] +- name: interop_server_helper + build: private + language: c++ + headers: [test/cpp/interop/server_helper.h] + src: [test/cpp/interop/server_helper.cc] + deps: [grpc_test_util, grpc++, grpc, gpr] +- name: interop_server_main + build: private + language: c++ + src: [test/proto/empty.proto, test/proto/messages.proto, test/proto/test.proto, + test/cpp/interop/server.cc] + deps: [interop_server_helper, grpc++_test_util, grpc_test_util, grpc++, grpc, gpr_test_util, + gpr, grpc++_test_config] +- name: qps + build: private + language: c++ + headers: [test/cpp/qps/client.h, test/cpp/qps/driver.h, test/cpp/qps/histogram.h, + test/cpp/qps/interarrival.h, test/cpp/qps/perf_db_client.h, test/cpp/qps/qps_worker.h, + test/cpp/qps/report.h, test/cpp/qps/server.h, test/cpp/qps/stats.h, test/cpp/qps/timer.h, + test/cpp/util/benchmark_config.h] + src: [test/cpp/qps/qpstest.proto, test/cpp/qps/perf_db.proto, test/cpp/qps/client_async.cc, + test/cpp/qps/client_sync.cc, test/cpp/qps/driver.cc, test/cpp/qps/perf_db_client.cc, + test/cpp/qps/qps_worker.cc, test/cpp/qps/report.cc, test/cpp/qps/server_async.cc, + test/cpp/qps/server_sync.cc, test/cpp/qps/timer.cc, test/cpp/util/benchmark_config.cc] + deps: [grpc_test_util, grpc++_test_util, grpc++] +- name: grpc_csharp_ext + build: all + language: csharp + src: [src/csharp/ext/grpc_csharp_ext.c] + deps: [gpr, grpc] + dll: only + vs_config_type: DynamicLibrary + vs_packages: [grpc.dependencies.openssl, grpc.dependencies.zlib] + vs_project_guid: '{D64C6D63-4458-4A88-AB38-35678384A7E4}' + vs_props: [zlib, openssl, winsock, global] +targets: +- name: alarm_heap_test + build: test + language: c + src: [test/core/iomgr/alarm_heap_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: alarm_list_test + build: test + language: c + src: [test/core/iomgr/alarm_list_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: alarm_test + build: test + language: c + src: [test/core/iomgr/alarm_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: alpn_test + build: test + language: c + src: [test/core/transport/chttp2/alpn_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: bin_encoder_test + build: test + language: c + src: [test/core/transport/chttp2/bin_encoder_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: chttp2_status_conversion_test + build: test + language: c + src: [test/core/transport/chttp2/status_conversion_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: chttp2_stream_encoder_test + build: test + language: c + src: [test/core/transport/chttp2/stream_encoder_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: chttp2_stream_map_test + build: test + language: c + src: [test/core/transport/chttp2/stream_map_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: compression_test + build: test + language: c + src: [test/core/compression/compression_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: dualstack_socket_test + build: test + language: c + src: [test/core/end2end/dualstack_socket_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] + platforms: [mac, linux, posix] +- name: endpoint_pair_test + build: test + language: c + src: [test/core/iomgr/endpoint_pair_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: fd_conservation_posix_test + build: test + language: c + src: [test/core/iomgr/fd_conservation_posix_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] + platforms: [mac, linux, posix] +- name: fd_posix_test + build: test + language: c + src: [test/core/iomgr/fd_posix_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] + platforms: [mac, linux, posix] +- name: fling_client + build: test + run: false + language: c + src: [test/core/fling/client.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: fling_server + build: test + run: false + language: c + src: [test/core/fling/server.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: fling_stream_test + build: test + language: c + src: [test/core/fling/fling_stream_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] + platforms: [mac, linux, posix] +- name: fling_test + build: test + language: c + src: [test/core/fling/fling_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] + platforms: [mac, linux, posix] +- name: gen_hpack_tables + build: tool + language: c + src: [tools/codegen/core/gen_hpack_tables.c] + deps: [gpr, grpc] +- name: gen_legal_metadata_characters + build: tool + language: c + src: [tools/codegen/core/gen_legal_metadata_characters.c] + deps: [] +- name: gpr_cmdline_test + build: test + language: c + src: [test/core/support/cmdline_test.c] + deps: [gpr_test_util, gpr] +- name: gpr_env_test + build: test + language: c + src: [test/core/support/env_test.c] + deps: [gpr_test_util, gpr] +- name: gpr_file_test + build: test + language: c + src: [test/core/support/file_test.c] + deps: [gpr_test_util, gpr] +- name: gpr_histogram_test + build: test + language: c + src: [test/core/support/histogram_test.c] + deps: [gpr_test_util, gpr] +- name: gpr_host_port_test + build: test + language: c + src: [test/core/support/host_port_test.c] + deps: [gpr_test_util, gpr] +- name: gpr_log_test + build: test + language: c + src: [test/core/support/log_test.c] + deps: [gpr_test_util, gpr] +- name: gpr_slice_buffer_test + build: test + language: c + src: [test/core/support/slice_buffer_test.c] + deps: [gpr_test_util, gpr] +- name: gpr_slice_test + build: test + language: c + src: [test/core/support/slice_test.c] + deps: [gpr_test_util, gpr] +- name: gpr_stack_lockfree_test + build: test + language: c + src: [test/core/support/stack_lockfree_test.c] + deps: [gpr_test_util, gpr] +- name: gpr_string_test + build: test + language: c + src: [test/core/support/string_test.c] + deps: [gpr_test_util, gpr] +- name: gpr_sync_test + build: test + language: c + src: [test/core/support/sync_test.c] + deps: [gpr_test_util, gpr] +- name: gpr_thd_test + build: test + language: c + src: [test/core/support/thd_test.c] + deps: [gpr_test_util, gpr] +- name: gpr_time_test + build: test + language: c + src: [test/core/support/time_test.c] + deps: [gpr_test_util, gpr] +- name: gpr_tls_test + build: test + language: c + src: [test/core/support/tls_test.c] + deps: [gpr_test_util, gpr] +- name: gpr_useful_test + build: test + language: c + src: [test/core/support/useful_test.c] + deps: [gpr_test_util, gpr] +- name: grpc_auth_context_test + build: test + language: c + src: [test/core/security/auth_context_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: grpc_base64_test + build: test + language: c + src: [test/core/security/base64_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: grpc_byte_buffer_reader_test + build: test + language: c + src: [test/core/surface/byte_buffer_reader_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: grpc_channel_args_test + build: test + language: c + src: [test/core/channel/channel_args_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: grpc_channel_stack_test + build: test + language: c + src: [test/core/channel/channel_stack_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: grpc_completion_queue_test + build: test + language: c + src: [test/core/surface/completion_queue_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: grpc_create_jwt + build: tool + language: c + src: [test/core/security/create_jwt.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: grpc_credentials_test + build: test + language: c + src: [test/core/security/credentials_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: grpc_fetch_oauth2 + build: tool + language: c + src: [test/core/security/fetch_oauth2.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: grpc_json_token_test + build: test + language: c + src: [test/core/security/json_token_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] + platforms: [linux, posix, mac] +- name: grpc_jwt_verifier_test + build: test + language: c + src: [test/core/security/jwt_verifier_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: grpc_print_google_default_creds_token + build: tool + language: c + src: [test/core/security/print_google_default_creds_token.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: grpc_security_connector_test + build: test + language: c + src: [test/core/security/security_connector_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: grpc_stream_op_test + build: test + language: c + src: [test/core/transport/stream_op_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: grpc_verify_jwt + build: tool + language: c + src: [test/core/security/verify_jwt.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: hpack_parser_test + build: test + language: c + src: [test/core/transport/chttp2/hpack_parser_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: hpack_table_test + build: test + language: c + src: [test/core/transport/chttp2/hpack_table_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: httpcli_format_request_test + build: test + language: c + src: [test/core/httpcli/format_request_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: httpcli_parser_test + build: test + language: c + src: [test/core/httpcli/parser_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: httpcli_test + build: test + language: c + src: [test/core/httpcli/httpcli_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] + platforms: [mac, linux, posix] +- name: json_rewrite + build: test + run: false + language: c + src: [test/core/json/json_rewrite.c] + deps: [grpc, gpr] +- name: json_rewrite_test + build: test + language: c + src: [test/core/json/json_rewrite_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: json_test + build: test + language: c + src: [test/core/json/json_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: lame_client_test + build: test + language: c + src: [test/core/surface/lame_client_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: low_level_ping_pong_benchmark + build: benchmark + language: c + src: [test/core/network_benchmarks/low_level_ping_pong.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] + platforms: [mac, linux, posix] +- name: message_compress_test + build: test + language: c + src: [test/core/compression/message_compress_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: multi_init_test + build: test + language: c + src: [test/core/surface/multi_init_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: multiple_server_queues_test + build: test + language: c + src: [test/core/end2end/multiple_server_queues_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: murmur_hash_test + build: test + language: c + src: [test/core/support/murmur_hash_test.c] + deps: [gpr_test_util, gpr] +- name: no_server_test + build: test + language: c + src: [test/core/end2end/no_server_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: resolve_address_test + build: test + language: c + src: [test/core/iomgr/resolve_address_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: secure_endpoint_test + build: test + language: c + src: [test/core/security/secure_endpoint_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: sockaddr_utils_test + build: test + language: c + src: [test/core/iomgr/sockaddr_utils_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: tcp_client_posix_test + build: test + language: c + src: [test/core/iomgr/tcp_client_posix_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] + platforms: [mac, linux, posix] +- name: tcp_posix_test + build: test + language: c + src: [test/core/iomgr/tcp_posix_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] + platforms: [mac, linux, posix] +- name: tcp_server_posix_test + build: test + language: c + src: [test/core/iomgr/tcp_server_posix_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] + platforms: [mac, linux, posix] +- name: time_averaged_stats_test + build: test + language: c + src: [test/core/iomgr/time_averaged_stats_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: timeout_encoding_test + build: test + language: c + src: [test/core/transport/chttp2/timeout_encoding_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: timers_test + build: test + language: c + src: [test/core/profiling/timers_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: transport_metadata_test + build: test + language: c + src: [test/core/transport/metadata_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: transport_security_test + build: test + language: c + src: [test/core/tsi/transport_security_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] + platforms: [linux, posix, mac] +- name: udp_server_test + build: test + language: c + src: [test/core/iomgr/udp_server_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] + platforms: [posix] +- name: uri_parser_test + build: test + language: c + src: [test/core/client_config/uri_parser_test.c] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] +- name: async_end2end_test + build: test + language: c++ + src: [test/cpp/end2end/async_end2end_test.cc] + deps: [grpc++_test_util, grpc_test_util, grpc++, grpc, gpr_test_util, gpr] +- name: async_streaming_ping_pong_test + build: test + language: c++ + src: [test/cpp/qps/async_streaming_ping_pong_test.cc] + deps: [qps, grpc++_test_util, grpc_test_util, grpc++, grpc, gpr_test_util, gpr] + platforms: [mac, linux, posix] +- name: async_unary_ping_pong_test + build: test + language: c++ + src: [test/cpp/qps/async_unary_ping_pong_test.cc] + deps: [qps, grpc++_test_util, grpc_test_util, grpc++, grpc, gpr_test_util, gpr] + platforms: [mac, linux, posix] +- name: auth_property_iterator_test + build: test + language: c++ + src: [test/cpp/common/auth_property_iterator_test.cc] + deps: [grpc++_test_util, grpc_test_util, grpc++, grpc, gpr_test_util, gpr] +- name: channel_arguments_test + build: test + language: c++ + src: [test/cpp/client/channel_arguments_test.cc] + deps: [grpc++, grpc, gpr] +- name: cli_call_test + build: test + language: c++ + src: [test/cpp/util/cli_call_test.cc] + deps: [grpc++_test_util, grpc_test_util, grpc++, grpc, gpr_test_util, gpr] +- name: client_crash_test + build: test + language: c++ + src: [test/cpp/end2end/client_crash_test.cc] + deps: [grpc++_test_util, grpc_test_util, grpc++, grpc, gpr_test_util, gpr] + platforms: [mac, linux, posix] +- name: client_crash_test_server + build: test + run: false + language: c++ + src: [test/cpp/end2end/client_crash_test_server.cc] + deps: [grpc++_test_util, grpc_test_util, grpc++, grpc, gpr_test_util, gpr] +- name: credentials_test + build: test + language: c++ + src: [test/cpp/client/credentials_test.cc] + deps: [grpc++, grpc, gpr] +- name: cxx_byte_buffer_test + build: test + language: c++ + src: [test/cpp/util/byte_buffer_test.cc] + deps: [grpc_test_util, grpc++, grpc, gpr_test_util, gpr] +- name: cxx_slice_test + build: test + language: c++ + src: [test/cpp/util/slice_test.cc] + deps: [grpc_test_util, grpc++, grpc, gpr_test_util, gpr] +- name: cxx_string_ref_test + build: test + language: c++ + src: [test/cpp/util/string_ref_test.cc] + deps: [grpc++] +- name: cxx_time_test + build: test + language: c++ + src: [test/cpp/util/time_test.cc] + deps: [grpc_test_util, grpc++, grpc, gpr_test_util, gpr] +- name: end2end_test + build: test + language: c++ + src: [test/cpp/end2end/end2end_test.cc] + deps: [grpc++_test_util, grpc_test_util, grpc++, grpc, gpr_test_util, gpr] +- name: generic_end2end_test + build: test + language: c++ + src: [test/cpp/end2end/generic_end2end_test.cc] + deps: [grpc++_test_util, grpc_test_util, grpc++, grpc, gpr_test_util, gpr] +- name: grpc_cli + build: test + run: false + language: c++ + src: [test/cpp/util/grpc_cli.cc] + deps: [grpc++_test_util, grpc_test_util, grpc++, grpc, gpr_test_util, gpr, grpc++_test_config] +- name: grpc_cpp_plugin + build: protoc + language: c++ + src: [src/compiler/cpp_plugin.cc] + deps: [grpc_plugin_support] + secure: false + vs_config_type: Application + vs_project_guid: '{7E51A25F-AC59-488F-906C-C60FAAE706AA}' +- name: grpc_csharp_plugin + build: protoc + language: c++ + src: [src/compiler/csharp_plugin.cc] + deps: [grpc_plugin_support] + secure: false + vs_config_type: Application + vs_project_guid: '{3C813052-A49A-4662-B90A-1ADBEC7EE453}' +- name: grpc_objective_c_plugin + build: protoc + language: c++ + src: [src/compiler/objective_c_plugin.cc] + deps: [grpc_plugin_support] + secure: false + vs_config_type: Application + vs_project_guid: '{19564640-CEE6-4921-ABA5-676ED79A36F6}' +- name: grpc_python_plugin + build: protoc + language: c++ + src: [src/compiler/python_plugin.cc] + deps: [grpc_plugin_support] + secure: false + vs_config_type: Application + vs_project_guid: '{DF52D501-A6CF-4E6F-BA38-6EBE2E8DAFB2}' +- name: grpc_ruby_plugin + build: protoc + language: c++ + src: [src/compiler/ruby_plugin.cc] + deps: [grpc_plugin_support] + secure: false + vs_config_type: Application + vs_project_guid: '{069E9D05-B78B-4751-9252-D21EBAE7DE8E}' +- name: interop_client + build: test + run: false + language: c++ + src: [] + deps: [interop_client_main, interop_client_helper, grpc++_test_util, grpc_test_util, + grpc++, grpc, gpr_test_util, gpr, grpc++_test_config] + platforms: [mac, linux, posix] +- name: interop_server + build: test + run: false + language: c++ + src: [] + deps: [interop_server_main, interop_server_helper, grpc++_test_util, grpc_test_util, + grpc++, grpc, gpr_test_util, gpr, grpc++_test_config] + platforms: [mac, linux, posix] +- name: interop_test + build: test + language: c++ + src: [test/cpp/interop/interop_test.cc] + deps: [grpc_test_util, grpc, gpr_test_util, gpr] + platforms: [mac, linux, posix] +- name: mock_test + build: test + language: c++ + src: [test/cpp/end2end/mock_test.cc] + deps: [grpc++_test_util, grpc_test_util, grpc++, grpc, gpr_test_util, gpr] +- name: qps_driver + build: benchmark + language: c++ + src: [test/cpp/qps/qps_driver.cc] + deps: [qps, grpc++_test_util, grpc_test_util, grpc++, grpc, gpr_test_util, gpr, + grpc++_test_config] +- name: qps_interarrival_test + build: test + run: false + language: c++ + src: [test/cpp/qps/qps_interarrival_test.cc] + deps: [qps, grpc++_test_util, grpc_test_util, grpc++, grpc, gpr_test_util, gpr] + platforms: [mac, linux, posix] +- name: qps_openloop_test + build: test + language: c++ + src: [test/cpp/qps/qps_openloop_test.cc] + deps: [qps, grpc++_test_util, grpc_test_util, grpc++, grpc, gpr_test_util, gpr, + grpc++_test_config] + platforms: [mac, linux, posix] +- name: qps_test + build: test + language: c++ + src: [test/cpp/qps/qps_test.cc] + deps: [qps, grpc++_test_util, grpc_test_util, grpc++, grpc, gpr_test_util, gpr, + grpc++_test_config] + exclude_configs: [tsan] + platforms: [mac, linux, posix] +- name: qps_worker + build: benchmark + language: c++ + headers: [test/cpp/qps/client.h, test/cpp/qps/server.h] + src: [test/cpp/qps/worker.cc] + deps: [qps, grpc++_test_util, grpc_test_util, grpc++, grpc, gpr_test_util, gpr, + grpc++_test_config] +- name: reconnect_interop_client + build: test + run: false + language: c++ + src: [test/proto/empty.proto, test/proto/messages.proto, test/proto/test.proto, + test/cpp/interop/reconnect_interop_client.cc] + deps: [grpc++_test_util, grpc_test_util, grpc++, grpc, gpr_test_util, gpr, grpc++_test_config] +- name: reconnect_interop_server + build: test + run: false + language: c++ + src: [test/proto/empty.proto, test/proto/messages.proto, test/proto/test.proto, + test/cpp/interop/reconnect_interop_server.cc] + deps: [reconnect_server, grpc++_test_util, grpc_test_util, grpc++, grpc, gpr_test_util, + gpr, grpc++_test_config] +- name: secure_auth_context_test + build: test + language: c++ + src: [test/cpp/common/secure_auth_context_test.cc] + deps: [grpc++_test_util, grpc_test_util, grpc++, grpc, gpr_test_util, gpr] +- name: server_crash_test + build: test + language: c++ + src: [test/cpp/end2end/server_crash_test.cc] + deps: [grpc++_test_util, grpc_test_util, grpc++, grpc, gpr_test_util, gpr] + platforms: [mac, linux, posix] +- name: server_crash_test_client + build: test + run: false + language: c++ + src: [test/cpp/end2end/server_crash_test_client.cc] + deps: [grpc++_test_util, grpc_test_util, grpc++, grpc, gpr_test_util, gpr] +- name: shutdown_test + build: test + language: c++ + src: [test/cpp/end2end/shutdown_test.cc] + deps: [grpc++_test_util, grpc_test_util, grpc++, grpc, gpr_test_util, gpr] +- name: status_test + build: test + language: c++ + src: [test/cpp/util/status_test.cc] + deps: [grpc_test_util, grpc++, grpc, gpr_test_util, gpr] +- name: streaming_throughput_test + build: test + language: c++ + src: [test/cpp/end2end/streaming_throughput_test.cc] + deps: [grpc++_test_util, grpc_test_util, grpc++, grpc, gpr_test_util, gpr] + platforms: [mac, linux, posix] +- name: sync_streaming_ping_pong_test + build: test + language: c++ + src: [test/cpp/qps/sync_streaming_ping_pong_test.cc] + deps: [qps, grpc++_test_util, grpc_test_util, grpc++, grpc, gpr_test_util, gpr] + platforms: [mac, linux, posix] +- name: sync_unary_ping_pong_test + build: test + language: c++ + src: [test/cpp/qps/sync_unary_ping_pong_test.cc] + deps: [qps, grpc++_test_util, grpc_test_util, grpc++, grpc, gpr_test_util, gpr] + platforms: [mac, linux, posix] +- name: thread_stress_test + build: test + language: c++ + src: [test/cpp/end2end/thread_stress_test.cc] + deps: [grpc++_test_util, grpc_test_util, grpc++, grpc, gpr_test_util, gpr] +- name: zookeeper_test + build: test + language: c++ + src: [test/cpp/end2end/zookeeper_test.cc] + deps: [grpc++_test_util, grpc_test_util, grpc++, grpc_zookeeper, grpc, gpr_test_util, + gpr] + external_deps: [zookeeper] + platforms: [linux] +vspackages: +- {linkage: static, name: grpc.dependencies.zlib, props: false, redist: true, version: 1.2.8.9} +- {name: grpc.dependencies.openssl, props: true, redist: true, version: 1.0.2.3} +- {name: gflags, props: false, redist: false, version: 2.1.2.1} +- {name: gtest, props: false, redist: false, version: 1.7.0.1} diff --git a/composer.json b/composer.json new file mode 100644 index 00000000..61f81e02 --- /dev/null +++ b/composer.json @@ -0,0 +1,18 @@ +{ + "name": "grpc/grpc", + "type": "library", + "description": "gRPC library for PHP", + "version": "0.6.0", + "keywords": ["rpc"], + "homepage": "http://grpc.io", + "license": "BSD-3-Clause", + "require": { + "php": ">=5.5.0", + "google/auth": "dev-master" + }, + "autoload": { + "psr-4": { + "Grpc\\": "src/php/lib/Grpc/" + } + } +} diff --git a/doc/PROTOCOL-HTTP2.md b/doc/PROTOCOL-HTTP2.md new file mode 100644 index 00000000..d3fbb60d --- /dev/null +++ b/doc/PROTOCOL-HTTP2.md @@ -0,0 +1,192 @@ +# gRPC over HTTP2 + +## Introduction +This document serves as a detailed description for an implementation of gRPC carried over HTTP2 draft 17 framing. It assumes familiarity with the HTTP2 specification. + +## Protocol +Production rules are using ABNF syntax. + +### Outline + +The following is the general sequence of message atoms in a GRPC request & response message stream + +* Request → Request-Headers *Delimited-Message EOS +* Response → (Response-Headers *Delimited-Message Trailers) / Trailers-Only + + +### Requests + +* Request → Request-Headers *Delimited-Message EOS + +Request-Headers are delivered as HTTP2 headers in HEADERS + CONTINUATION frames. + +* **Request-Headers** → Call-Definition *Custom-Metadata +* **Call-Definition** → Method Scheme Path TE [Authority] [Timeout] [Content-Type] [Message-Type] [Message-Encoding] [Message-Accept-Encoding] [User-Agent] +* **Method** → “:method POST” +* **Scheme** → “:scheme ” (“http” / “https”) +* **Path** → “:path” {_path identifying method within exposed API_} +* **Authority** → “:authority” {_virtual host name of authority_} +* **TE** → “te” “trailers” # Used to detect incompatible proxies +* **Timeout** → “grpc-timeout” TimeoutValue TimeoutUnit +* **TimeoutValue** → {_positive integer as ASCII string of at most 8 digits_} +* **TimeoutUnit** → Hour / Minute / Second / Millisecond / Microsecond / Nanosecond +* **Hour** → “H” +* **Minute** → “M” +* **Second** → “S” +* **Millisecond** → “m” +* **Microsecond** → “u” +* **Nanosecond** → “n” +* **Content-Type** → “content-type” “application/grpc” [(“+proto” / “+json” / {_custom_})] +* **Content-Coding** → “gzip” / “deflate” / “snappy” / {_custom_} +* **Message-Encoding** → “grpc-encoding” Content-Coding +* **Message-Accept-Encoding** → “grpc-accept-encoding” Content-Coding *("," Content-Coding) +* **User-Agent** → “user-agent” {_structured user-agent string_} +* **Message-Type** → “grpc-message-type” {_type name for message schema_} +* **Custom-Metadata** → Binary-Header / ASCII-Header +* **Binary-Header** → {lowercase ASCII header name ending in “-bin” } {_base64 encoded value_} +* **ASCII-Header** → {lowercase ASCII header name} {_value_} + + +HTTP2 requires that reserved headers, ones starting with “:” appear before all other headers. Additionally implementations should send **Timeout** immediately after the reserved headers and they should send the **Call-Definition** headers before sending **Custom-Metadata**. + +If **Timeout** is omitted a server should assume an infinite timeout. Client implementations are free to send a default minimum timeout based on their deployment requirements. + +**Custom-Metadata** is an arbitrary set of key-value pairs defined by the application layer. Aside from transport limits on the total length of HTTP2 HEADERS the only other constraint is that header names starting with “grpc-” are reserved for future use. + +Note that HTTP2 does not allow arbitrary octet sequences for header values so binary header values must be encoded using Base64 as per https://tools.ietf.org/html/rfc4648#section-4. Implementations MUST accept padded and un-padded values and should emit un-padded values. Applications define binary headers by having their names end with “-bin”. Runtime libraries use this suffix to detect binary headers and properly apply base64 encoding & decoding as headers are sent and received. + +The repeated sequence of **Delimited-Message** items is delivered in DATA frames + +* **Delimited-Message** → Compressed-Flag Message-Length Message +* **Compressed-Flag** → 0 / 1 # encoded as 1 byte unsigned integer +* **Message-Length** → {_length of Message_} # encoded as 4 byte unsigned integer +* **Message** → *{binary octet} + +A **Compressed-Flag** value of 1 indicates that the binary octet sequence of **Message** is compressed using the mechanism declared by the **Message-Encoding** header. A value of 0 indicates that no encoding of **Message** bytes has occurred. Compression contexts are NOT maintained over message boundaries, implementations must create a new context for each message in the stream. If the **Message-Encoding** header is omitted then the **Compressed-Flag** must be 0. + +For requests, **EOS** (end-of-stream) is indicated by the presence of the END_STREAM flag on the last received DATA frame. In scenarios where the **Request** stream needs to be closed but no data remains to be sent implementations MUST send an empty DATA frame with this flag set. + +###Responses + +* **Response** → (Response-Headers *Delimited-Message Trailers) / Trailers-Only +* **Response-Headers** → HTTP-Status [Message-Encoding] [Message-Accept-Encoding] Content-Type *Custom-Metadata +* **Trailers-Only** → HTTP-Status Content-Type Trailers +* **Trailers** → Status [Status-Message] *Custom-Metadata +* **HTTP-Status** → “:status 200” +* **Status** → “grpc-status” +* **Status-Message** → “grpc-message” + +**Response-Headers** & **Trailers-Only** are each delivered in a single HTTP2 HEADERS frame block. Most responses are expected to have both headers and trailers but **Trailers-Only** is permitted for calls that produce an immediate error. Status must be sent in **Trailers** even if the status code is OK. + +For responses end-of-stream is indicated by the presence of the END_STREAM flag on the last received HEADERS frame that carries **Trailers**. + +Implementations should expect broken deployments to send non-200 HTTP status codes in responses as well as a variety of non-GRPC content-types and to omit **Status** & **Status-Message**. Implementations must synthesize a **Status** & **Status-Message** to propagate to the application layer when this occurs. + +####Example + +Sample unary-call showing HTTP2 framing sequence + +**Request** + +``` +HEADERS (flags = END_HEADERS) +:method = POST +:scheme = http +:path = /google.pubsub.v2.PublisherService/CreateTopic +:authority = pubsub.googleapis.com +grpc-timeout = 1S +content-type = application/grpc+proto +grpc-encoding = gzip +authorization = Bearer y235.wef315yfh138vh31hv93hv8h3v + +DATA (flags = END_STREAM) + +``` +**Response** +``` +HEADERS (flags = END_HEADERS) +:status = 200 +grpc-encoding = gzip + +DATA + + +HEADERS (flags = END_STREAM, END_HEADERS) +grpc-status = 0 # OK +trace-proto-bin = jher831yy13JHy3hc +``` +####User Agents + +While the protocol does not require a user-agent to function it is recommended that clients provide a structured user-agent string that provides a basic description of the calling library, version & platform to facilitate issue diagnosis in heterogeneous environments. The following structure is recommended to library developers +``` +User-Agent → “grpc-” Language ?(“-” Variant) “/” Version ?( “ (“ *(AdditionalProperty “;”) “)” ) +``` +E.g. + +``` +grpc-java/1.2.3 +grpc-ruby/1.2.3 +grpc-ruby-jruby/1.3.4 +grpc-java-android/0.9.1 (gingerbread/1.2.4; nexus5; tmobile) +``` +####HTTP2 Transport Mapping + +#####Stream Identification +All GRPC calls need to specify an internal ID. We will use HTTP2 stream-ids as call identifiers in this scheme. NOTE: These id’s are contextual to an open HTTP2 session and will not be unique within a given process that is handling more than one HTTP2 session nor can they be used as GUIDs. + +#####Data Frames +DATA frame boundaries have no relation to **Delimited-Message** boundaries and implementations should make no assumptions about their alignment. + +#####Errors + +When an application or runtime error occurs during an RPC a **Status** and **Status-Message** are delivered in **Trailers**. + +In some cases it is possible that the framing of the message stream has become corrupt and the RPC runtime will choose to use an **RST_STREAM** frame to indicate this state to its peer. RPC runtime implementations should interpret RST_STREAM as immediate full-closure of the stream and should propagate an error up to the calling application layer. + +The following mapping from RST_STREAM error codes to GRPC error codes is applied. + +HTTP2 Code|GRPC Code +----------|----------- +NO_ERROR(0)|INTERNAL - An explicit GRPC status of OK should have been sent but this might be used to aggressively lameduck in some scenarios. +PROTOCOL_ERROR(1)|INTERNAL +INTERNAL_ERROR(2)|INTERNAL +FLOW_CONTROL_ERROR(3)|INTERNAL +SETTINGS_TIMEOUT(4)|INTERNAL +STREAM_CLOSED|No mapping as there is no open stream to propagate to. Implementations should log. +FRAME_SIZE_ERROR|INTERNAL +REFUSED_STREAM|UNAVAILABLE - Indicates that no processing occurred and the request can be retried, possibly elsewhere. +CANCEL(8)|Mapped to call cancellation when sent by a client.Mapped to CANCELLED when sent by a server. Note that servers should only use this mechanism when they need to cancel a call but the payload byte sequence is incomplete. +COMPRESSION_ERROR|INTERNAL +CONNECT_ERROR|INTERNAL +ENHANCE_YOUR_CALM|RESOURCE_EXHAUSTED ...with additional error detail provided by runtime to indicate that the exhausted resource is bandwidth. +INADEQUATE_SECURITY| PERMISSION_DENIED … with additional detail indicating that permission was denied as protocol is not secure enough for call. + + +#####Security + +The HTTP2 specification mandates the use of TLS 1.2 or higher when TLS is used with HTTP2. It also places some additional constraints on the allowed ciphers in deployments to avoid known-problems as well as requiring SNI support. It is also expected that HTTP2 will be used in conjunction with proprietary transport security mechanisms about which the specification can make no meaningful recommendations. + +#####Connection Management +######GOAWAY Frame +Sent by servers to clients to indicate that they will no longer accept any new streams on the associated connections. This frame includes the id of the last successfully accepted stream by the server. Clients should consider any stream initiated after the last successfully accepted stream as UNAVAILABLE and retry the call elsewhere. Clients are free to continue working with the already accepted streams until they complete or the connection is terminated. + +Servers should send GOAWAY before terminating a connection to reliably inform clients which work has been accepted by the server and is being executed. + +######PING Frame +Both clients and servers can send a PING frame that the peer must respond to by precisely echoing what they received. This is used to assert that the connection is still live as well as providing a means to estimate end-to-end latency. If a server initiated PING does not receive a response within the deadline expected by the runtime all outstanding calls on the server will be closed with a CANCELLED status. An expired client initiated PING will cause all calls to be closed with an UNAVAILABLE status. Note that the frequency of PINGs is highly dependent on the network environment, implementations are free to adjust PING frequency based on network and application requirements. + +######Connection failure +If a detectable connection failure occurs on the client all calls will be closed with an UNAVAILABLE status. For servers open calls will be closed with a CANCELLED status. + + +### Appendix A - GRPC for Protobuf + +The service interfaces declared by protobuf are easily mapped onto GRPC by code generation extensions to protoc. The following defines the mapping to be used + + +* **Path** → / Service-Name / {_method name_} +* **Service-Name** → ?( {_proto package name_} "." ) {_service name_} +* **Message-Type** → {_fully qualified proto message name_} +* **Content-Type** → "application/grpc+proto" + + diff --git a/doc/connection-backoff-interop-test-description.md b/doc/connection-backoff-interop-test-description.md new file mode 100644 index 00000000..64405431 --- /dev/null +++ b/doc/connection-backoff-interop-test-description.md @@ -0,0 +1,77 @@ +Connection Backoff Interop Test Descriptions +=============================================== + +This test is to verify the client is reconnecting the server with correct +backoffs as specified in +[the spec](http://github.com/grpc/grpc/blob/master/doc/connection-backoff.md). +The test server has a port (control_port) running a rpc service for controlling +the server and another port (retry_port) to close any incoming tcp connections. +The test has the following flow: + +1. The server starts listening on control_port. +2. The client calls Start rpc on server control_port. +3. The server starts listening on retry_port. +4. The client connects to server retry_port and retries with backoff for 540s, +which translates to about 13 retries. +5. The client calls Stop rpc on server control port. +6. The client checks the response to see whether the server thinks the backoffs +are conforming the spec or do its own check on the backoffs in the response. + +Client and server use +[test.proto](https://github.com/grpc/grpc/blob/master/test/proto/test.proto). +Each language should implement its own client. The C++ server is shared among +languages. + +Client +------ + +Clients should accept these arguments: +* --server_control_port=PORT + * The server port to connect to for rpc. For example, "8080" +* --server_retry_port=PORT + * The server port to connect to for testing backoffs. For example, "8081" + +The client must connect to the control port without TLS. The client must connect +to the retry port with TLS. The client should either assert on the server +returned backoff status or check the returned backoffs on its own. + +Procedure of client: + +1. Calls Start on server control port with a large deadline or no deadline, +waits for its finish and checks it succeeded. +2. Initiates a channel connection to server retry port, which should perform +reconnections with proper backoffs. A convienent way to achieve this is to +call Start with a deadline of 540s. The rpc should fail with deadline exceeded. +3. Calls Stop on server control port and checks it succeeded. +4. Checks the response to see whether the server thinks the backoffs passed the + test. +5. Optionally, the client can do its own check on the returned backoffs. + + +Server +------ + +A C++ server can be used for the test. Other languages do NOT need to implement +a server. To minimize the network delay, the server binary should run on the +same machine or on a nearby machine (in terms of network distance) with the +client binary. + +A server implements the ReconnectService to its state. It also opens a +tcp server on the retry_port, which just shuts down all incoming tcp +connections to simulate connection failures. The server will keep a record of +all the reconnection timestamps and return the connection backoffs in the +response in milliseconds. The server also checks the backoffs to see whether +they conform the spec and returns whether the client passes the test. + +If the server receives a Start call when another client is being tested, it +finishes the call when the other client is done. If some other host connects +to the server retry_port when a client is being tested, the server will log an +error but likely would think the client fails the test. + +The server accepts these arguments: + +* --control_port=PORT + * The port to listen on for control rpcs. For example, "8080" +* --retry_port=PORT + * The tcp server port. For example, "8081" + diff --git a/doc/connection-backoff.md b/doc/connection-backoff.md new file mode 100644 index 00000000..251a60f3 --- /dev/null +++ b/doc/connection-backoff.md @@ -0,0 +1,55 @@ +GRPC Connection Backoff Protocol +================================ + +When we do a connection to a backend which fails, it is typically desirable to +not retry immediately (to avoid flooding the network or the server with +requests) and instead do some form of exponential backoff. + +We have several parameters: + 1. INITIAL_BACKOFF (how long to wait after the first failure before retrying) + 2. MULTIPLIER (factor with which to multiply backoff after a failed retry) + 3. MAX_BACKOFF (upper bound on backoff) + 4. MIN_CONNECT_TIMEOUT (minimum time we're willing to give a connection to + complete) + +## Proposed Backoff Algorithm + +Exponentially back off the start time of connection attempts up to a limit of +MAX_BACKOFF, with jitter. + +``` +ConnectWithBackoff() + current_backoff = INITIAL_BACKOFF + current_deadline = now() + INITIAL_BACKOFF + while (TryConnect(Max(current_deadline, now() + MIN_CONNECT_TIMEOUT)) + != SUCCESS) + SleepUntil(current_deadline) + current_backoff = Min(current_backoff * MULTIPLIER, MAX_BACKOFF) + current_deadline = now() + current_backoff + + UniformRandom(-JITTER * current_backoff, JITTER * current_backoff) + +``` + +With specific parameters of +MIN_CONNECT_TIMEOUT = 20 seconds +INITIAL_BACKOFF = 1 second +MULTIPLIER = 1.6 +MAX_BACKOFF = 120 seconds +JITTER = 0.2 + +Implementations with pressing concerns (such as minimizing the number of wakeups +on a mobile phone) may wish to use a different algorithm, and in particular +different jitter logic. + +Alternate implementations must ensure that connection backoffs started at the +same time disperse, and must not attempt connections substantially more often +than the above algorithm. + +## Reset Backoff + +The back off should be reset to INITIAL_BACKOFF at some time point, so that the +reconnecting behavior is consistent no matter the connection is a newly started +one or a previously disconnected one. + +We choose to reset the Backoff when the SETTINGS frame is received, at that time +point, we know for sure that this connection was accepted by the server. diff --git a/doc/connectivity-semantics-and-api.md b/doc/connectivity-semantics-and-api.md new file mode 100644 index 00000000..54279003 --- /dev/null +++ b/doc/connectivity-semantics-and-api.md @@ -0,0 +1,147 @@ +gRPC Connectivity Semantics and API +=================================== + +This document describes the connectivity semantics for gRPC channels and the +corresponding impact on RPCs. We then discuss an API. + +States of Connectivity +---------------------- + +gRPC Channels provide the abstraction over which clients can communicate with +servers.The client-side channel object can be constructed using little more +than a DNS name. Channels encapsulate a range of functionality including name +resolution, establishing a TCP connection (with retries and backoff) and TLS +handshakes. Channels can also handle errors on established connections and +reconnect, or in the case of HTTP/2 GO_AWAY, re-resolve the name and reconnect. + +To hide the details of all this activity from the user of the gRPC API (i.e., +application code) while exposing meaningful information about the state of a +channel, we use a state machine with four states, defined below: + +CONNECTING: The channel is trying to establish a connection and is waiting to +make progress on one of the steps involved in name resolution, TCP connection +establishment or TLS handshake. This may be used as the initial state for channels upon +creation. + +READY: The channel has successfully established a connection all the way +through TLS handshake (or equivalent) and all subsequent attempt to communicate +have succeeded (or are pending without any known failure ). + +TRANSIENT_FAILURE: There has been some transient failure (such as a TCP 3-way +handshake timing out or a socket error). Channels in this state will eventually +switch to the CONNECTING state and try to establish a connection again. Since +retries are done with exponential backoff, channels that fail to connect will +start out spending very little time in this state but as the attempts fail +repeatedly, the channel will spend increasingly large amounts of time in this +state. For many non-fatal failures (e.g., TCP connection attempts timing out +because the server is not yet available), the channel may spend increasingly +large amounts of time in this state. + +IDLE: This is the state where the channel is not even trying to create a +connection because of a lack of new or pending RPCs. New RPCs MAY be created +in this state. Any attempt to start an RPC on the channel will push the channel +out of this state to connecting. When there has been no RPC activity on a channel +for a specified IDLE_TIMEOUT, i.e., no new or pending (active) RPCs for this +period, channels that are READY or CONNECTING switch to IDLE. Additionaly, +channels that receive a GOAWAY when there are no active or pending RPCs should +also switch to IDLE to avoid connection overload at servers that are attempting +to shed connections. We will use a default IDLE_TIMEOUT of 300 seconds (5 minutes). + +SHUTDOWN: This channel has started shutting down. Any new RPCs should fail +immediately. Pending RPCs may continue running till the application cancels them. +Channels may enter this state either because the application explicitly requested +a shutdown or if a non-recoverable error has happened during attempts to connect +communicate . (As of 6/12/2015, there are no known errors (while connecting or +communicating) that are classified as non-recoverable) +Channels that enter this state never leave this state. + +The following table lists the legal transitions from one state to another and +corresponding reasons. Empty cells denote disallowed transitions. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
From/ToCONNECTINGREADYTRANSIENT_FAILUREIDLESHUTDOWN
CONNECTINGIncremental progress during connection establishmentAll steps needed to establish a connection succeededAny failure in any of the steps needed to establish connectionNo RPC activity on channel for IDLE_TIMEOUTShutdown triggered by application.
READYIncremental successful communication on established channel.Any failure encountered while expecting successful communication on + established channel.No RPC activity on channel for IDLE_TIMEOUT
OR
upon receiving a GOAWAY while there are no pending RPCs.
Shutdown triggered by application.
TRANSIENT_FAILUREWait time required to implement (exponential) backoff is over.Shutdown triggered by application.
IDLEAny new RPC activity on the channelShutdown triggered by application.
FATAL_FAILURE
+ + +Channel State API +----------------- + +All gRPC libraries will expose a channel-level API method to poll the current +state of a channel. In C++, this method is called GetCurrentState and returns +an enum for one of the four legal states. + +All libraries should also expose an API that enables the application (user of +the gRPC API) to be notified when the channel state changes. Since state +changes can be rapid and race with any such notification, the notification +should just inform the user that some state change has happened, leaving it to +the user to poll the channel for the current state. + +The synchronous version of this API is: + +```cpp +bool WaitForStateChange(gpr_timespec deadline, ChannelState source_state); +``` + +which returns true when the state changes to something other than the +source_state and false if the deadline expires. Asynchronous and futures based +APIs should have a corresponding method that allows the application to be +notified when the state of a channel changes. + +Note that a notification is delivered every time there is a transition from any +state to any *other* state. On the other hand the rules for legal state +transition, require a transition from CONNECTING to TRANSIENT_FAILURE and back +to CONNECTING for every recoverable failure, even if the corresponding +exponential backoff requires no wait before retry. The combined effect is that +the application may receive state change notifications that appear spurious. +e.g., an application waiting for state changes on a channel that is CONNECTING +may receive a state change notification but find the channel in the same +CONNECTING state on polling for current state because the channel may have +spent infinitesimally small amount of time in the TRANSIENT_FAILURE state. diff --git a/doc/grpc-auth-support.md b/doc/grpc-auth-support.md new file mode 100644 index 00000000..800fbedd --- /dev/null +++ b/doc/grpc-auth-support.md @@ -0,0 +1,289 @@ +#gRPC Authentication support + +gRPC is designed to plug-in a number of authentication mechanisms. This document +provides a quick overview of the various auth mechanisms supported, discusses +the API with some examples, and concludes with a discussion of extensibility. +More documentation and examples are coming soon! + +## Supported auth mechanisms + +###SSL/TLS +gRPC has SSL/TLS integration and promotes the use of SSL/TLS to authenticate the +server, and encrypt all the data exchanged between the client and the server. +Optional mechanisms are available for clients to provide certificates to +accomplish mutual authentication. + +###OAuth 2.0 +gRPC provides a generic mechanism (described below) to attach metadata to +requests and responses. This mechanism can be used to attach OAuth 2.0 Access +Tokens to RPCs being made at a client. Additional support for acquiring Access +Tokens while accessing Google APIs through gRPC is provided for certain auth +flows, demonstrated through code examples below. + +## API +To reduce complexity and minimize API clutter, gRPC works with a unified concept +of a Credentials object. Users construct gRPC credentials using corresponding +bootstrap credentials (e.g., SSL client certs or Service Account Keys), and use +the credentials while creating a gRPC channel to any server. Depending on the +type of credential supplied, the channel uses the credentials during the initial +SSL/TLS handshake with the server, or uses the credential to generate and +attach Access Tokens to each request being made on the channel. + +###SSL/TLS for server authentication and encryption +This is the simplest authentication scenario, where a client just wants to +authenticate the server and encrypt all data. + +```cpp +SslCredentialsOptions ssl_opts; // Options to override SSL params, empty by default +// Create the credentials object by providing service account key in constructor +std::unique_ptr creds = CredentialsFactory::SslCredentials(ssl_opts); +// Create a channel using the credentials created in the previous step +std::shared_ptr channel = CreateChannel(server_name, creds, channel_args); +// Create a stub on the channel +std::unique_ptr stub(Greeter::NewStub(channel)); +// Make actual RPC calls on the stub. +grpc::Status s = stub->sayHello(&context, *request, response); +``` + +For advanced use cases such as modifying the root CA or using client certs, +the corresponding options can be set in the SslCredentialsOptions parameter +passed to the factory method. + + +###Authenticating with Google + +gRPC applications can use a simple API to create a credential that works in various deployment scenarios. + +```cpp +std::unique_ptr creds = CredentialsFactory::GoogleDefaultCredentials(); +// Create a channel, stub and make RPC calls (same as in the previous example) +std::shared_ptr channel = CreateChannel(server_name, creds, channel_args); +std::unique_ptr stub(Greeter::NewStub(channel)); +grpc::Status s = stub->sayHello(&context, *request, response); +``` + +This credential works for applications using Service Accounts as well as for +applications running in [Google Compute Engine (GCE)](https://cloud.google.com/compute/). In the former case, the +service account’s private keys are loaded from the file named in the environment +variable `GOOGLE_APPLICATION_CREDENTIALS`. The +keys are used to generate bearer tokens that are attached to each outgoing RPC +on the corresponding channel. + +For applications running in GCE, a default service account and corresponding +OAuth scopes can be configured during VM setup. At run-time, this credential +handles communication with the authentication systems to obtain OAuth2 access +tokens and attaches them to each outgoing RPC on the corresponding channel. +Extending gRPC to support other authentication mechanisms +The gRPC protocol is designed with a general mechanism for sending metadata +associated with RPC. Clients can send metadata at the beginning of an RPC and +servers can send back metadata at the beginning and end of the RPC. This +provides a natural mechanism to support OAuth2 and other authentication +mechanisms that need attach bearer tokens to individual request. + +In the simplest case, there is a single line of code required on the client +to add a specific token as metadata to an RPC and a corresponding access on +the server to retrieve this piece of metadata. The generation of the token +on the client side and its verification at the server can be done separately. + +A deeper integration can be achieved by plugging in a gRPC credentials implementation for any custom authentication mechanism that needs to attach per-request tokens. gRPC internals also allow switching out SSL/TLS with other encryption mechanisms. + +## Examples + +These authentication mechanisms will be available in all gRPC's supported languages. +The following sections demonstrate how authentication and authorization features described above appear in each language: more languages are coming soon. + +###SSL/TLS for server authentication and encryption (Ruby) +```ruby +# Base case - No encryption +stub = Helloworld::Greeter::Stub.new('localhost:50051') +... + +# With server authentication SSL/TLS +creds = GRPC::Core::Credentials.new(load_certs) # load_certs typically loads a CA roots file +stub = Helloworld::Greeter::Stub.new('localhost:50051', creds: creds) +``` + +###SSL/TLS for server authentication and encryption (C#) +```csharp +// Base case - No encryption +var channel = new Channel("localhost:50051"); +var client = new Greeter.GreeterClient(channel); +... + +// With server authentication SSL/TLS +var credentials = new SslCredentials(File.ReadAllText("ca.pem")); // Load a CA file +var channel = new Channel("localhost:50051", credentials); +var client = new Greeter.GreeterClient(channel); +``` + +###SSL/TLS for server authentication and encryption (Objective-C) + +The default for Objective-C is to use SSL/TLS, as that's the most common use case when accessing +remote APIs. + +```objective-c +// Base case - With server authentication SSL/TLS +HLWGreeter *client = [[HLWGreeter alloc] initWithHost:@"localhost:50051"]; +// Same as using @"https://localhost:50051". +... + +// No encryption +HLWGreeter *client = [[HLWGreeter alloc] initWithHost:@"http://localhost:50051"]; +// Specifying the HTTP scheme explicitly forces no encryption. +``` + +###SSL/TLS for server authentication and encryption (Python) +```python +# Base case - No encryption +stub = early_adopter_create_GreeterService_stub('localhost', 50051) +... + +# With server authentication SSL/TLS +stub = early_adopter_create_GreeterService_stub( + 'localhost', 50051, secure=True, root_certificates=open('ca.pem').read()) +... +``` +n.b.: the beta API will look different + +###Authenticating with Google (Ruby) +```ruby +# Base case - No encryption/authorization +stub = Helloworld::Greeter::Stub.new('localhost:50051') +... + +# Authenticating with Google +require 'googleauth' # from http://www.rubydoc.info/gems/googleauth/0.1.0 +... +creds = GRPC::Core::Credentials.new(load_certs) # load_certs typically loads a CA roots file +scope = 'https://www.googleapis.com/auth/grpc-testing' +authorization = Google::Auth.get_application_default(scope) +stub = Helloworld::Greeter::Stub.new('localhost:50051', + creds: creds, + update_metadata: authorization.updater_proc) +``` + +###Authenticating with Google (Node.js) + +```node +// Base case - No encryption/authorization +var stub = new helloworld.Greeter('localhost:50051'); +... +// Authenticating with Google +var GoogleAuth = require('google-auth-library'); // from https://www.npmjs.com/package/google-auth-library +... +var creds = grpc.Credentials.createSsl(load_certs); // load_certs typically loads a CA roots file +var scope = 'https://www.googleapis.com/auth/grpc-testing'; +(new GoogleAuth()).getApplicationDefault(function(err, auth) { + if (auth.createScopeRequired()) { + auth = auth.createScoped(scope); + } + var stub = new helloworld.Greeter('localhost:50051', + {credentials: creds}, + grpc.getGoogleAuthDelegate(auth)); +}); +``` + +###Authenticating with Google (C#) +```csharp +// Base case - No encryption/authorization +var channel = new Channel("localhost:50051"); +var client = new Greeter.GreeterClient(channel); +... + +// Authenticating with Google +using Grpc.Auth; // from Grpc.Auth NuGet package +... +var credentials = new SslCredentials(File.ReadAllText("ca.pem")); // Load a CA file +var channel = new Channel("localhost:50051", credentials); + +string scope = "https://www.googleapis.com/auth/grpc-testing"; +var authorization = GoogleCredential.GetApplicationDefault(); +if (authorization.IsCreateScopedRequired) +{ + authorization = credential.CreateScoped(new[] { scope }); +} +var client = new Greeter.GreeterClient(channel, + new StubConfiguration(OAuth2InterceptorFactory.Create(credential))); +``` + +###Authenticating with Google (PHP) +```php +// Base case - No encryption/authorization +$client = new helloworld\GreeterClient( + new Grpc\BaseStub('localhost:50051', [])); +... + +// Authenticating with Google +// the environment variable "GOOGLE_APPLICATION_CREDENTIALS" needs to be set +$scope = "https://www.googleapis.com/auth/grpc-testing"; +$auth = Google\Auth\ApplicationDefaultCredentials::getCredentials($scope); +$opts = [ + 'credentials' => Grpc\Credentials::createSsl(file_get_contents('ca.pem')); + 'update_metadata' => $auth->getUpdateMetadataFunc(), +]; + +$client = new helloworld\GreeterClient( + new Grpc\BaseStub('localhost:50051', $opts)); + +``` + +###Authenticating with Google (Objective-C) + +This example uses the [Google iOS Sign-In library](https://developers.google.com/identity/sign-in/ios/), +but it's easily extrapolated to any other OAuth2 library. + +```objective-c +// Base case - No authentication +[client sayHelloWithRequest:request handler:^(HLWHelloReply *response, NSError *error) { + ... +}]; + +... + +// Authenticating with Google + +// When signing the user in, ask her for the relevant scopes. +GIDSignIn.sharedInstance.scopes = @[@"https://www.googleapis.com/auth/grpc-testing"]; + +... + +#import + +// Create a not-yet-started RPC. We want to set the request headers on this object before starting +// it. +ProtoRPC *call = + [client RPCToSayHelloWithRequest:request handler:^(HLWHelloReply *response, NSError *error) { + ... + }]; + +// Set the access token to be used. +NSString *accessToken = GIDSignIn.sharedInstance.currentUser.authentication.accessToken; +call.requestMetadata[@"Authorization"] = [@"Bearer " stringByAppendingString:accessToken]}]; + +// Start the RPC. +[call start]; +``` + +You can see a working example app, with a more detailed explanation, [here](examples/objective-c/auth_sample). + +### Authenticating with Google (Python) +```python +# Base case - No encryption +stub = early_adopter_create_GreeterService_stub('localhost', 50051) +... + +# With server authentication SSL/TLS +import oauth2client.client +credentials = oauth2client.GoogleCredentials.get_application_default() +scope = 'https://www.googleapis.com/auth/grpc-testing' +scoped_credentials = credentials.create_scoped([scope]) +access_token = scoped_credentials.get_access_token().access_token +metadata_transformer = ( + lambda x: [('Authorization', 'Bearer {}'.format(access_token))]) + +stub = early_adopter_create_GreeterService_stub( + 'localhost', 50051, secure=True, root_certificates=open('ca.pem').read(), + metadata_transformer=metadata_transformer) +... +``` +n.b.: the beta API will look different diff --git a/doc/health-checking.md b/doc/health-checking.md new file mode 100644 index 00000000..0b3f9c6a --- /dev/null +++ b/doc/health-checking.md @@ -0,0 +1,70 @@ +GRPC Health Checking Protocol +================================ + +Health checks are used to probe whether the server is able to handle rpcs. The +client-to-server health checking can happen from point to point or via some +control system. A server may choose to reply “unhealthy” because it +is not ready to take requests, it is shutting down or some other reason. +The client can act accordingly if the response is not received within some time +window or the response says unhealthy in it. + + +A GRPC service is used as the health checking mechanism for both simple +client-to-server scenario and other control systems such as load-balancing. +Being a high +level service provides some benefits. Firstly, since it is a GRPC service +itself, doing a health check is in the same format as a normal rpc. Secondly, +it has rich semantics such as per-service health status. Thirdly, as a GRPC +service, it is able reuse all the existing billing, quota infrastructure, etc, +and thus the server has full control over the access of the health checking +service. + +## Service Definition + +The server should export a service defined in the following proto: + +``` +syntax = "proto3"; + +package grpc.health.v1alpha; + +message HealthCheckRequest { + string service = 1; +} + +message HealthCheckResponse { + enum ServingStatus { + UNKNOWN = 0; + SERVING = 1; + NOT_SERVING = 2; + } + ServingStatus status = 1; +} + +service Health { + rpc Check(HealthCheckRequest) returns (HealthCheckResponse); +} +``` + +A client can query the server’s health status by calling the `Check` method, and +a deadline should be set on the rpc. The client can optionally set the service +name it wants to query for health status. The suggested format of service name +is `package_names.ServiceName`, such as `grpc.health.v1alpha.Health`. + +The server should register all the services manually and set +the individual status, including an empty service name and its status. For each +request received, if the service name can be found in the registry, +a response must be sent back with an `OK` status and the status field should be +set to `SERVING` or `NOT_SERVING` accordingly. If the service name is not +registered, the server returns a `NOT_FOUND` GRPC status. + +The server should use an empty string as the key for server’s +overall health status, so that a client not interested in a specific service can +query the server's status with an empty request. The server can just do exact +matching of the service name without support of any kind of wildcard matching. +However, the service owner has the freedom to implement more complicated +matching semantics that both the client and server agree upon. + +A client can declare the server as unhealthy if the rpc is not finished after +some amount of time. The client should be able to handle the case where server +does not have the Health service. diff --git a/doc/interop-test-descriptions.md b/doc/interop-test-descriptions.md new file mode 100644 index 00000000..98cd5ab4 --- /dev/null +++ b/doc/interop-test-descriptions.md @@ -0,0 +1,1087 @@ +Interoperability Test Case Descriptions +======================================= + +Client and server use +[test.proto](https://github.com/grpc/grpc/blob/master/test/proto/test.proto) +and the [gRPC over HTTP/2 v2 +protocol](doc/PROTOCOL-HTTP2.md). + +Client +------ + +Clients implement test cases that test certain functionally. Each client is +provided the test case it is expected to run as a command-line parameter. Names +should be lowercase and without spaces. + +Clients should accept these arguments: +* --server_host=HOSTNAME + * The server host to connect to. For example, "localhost" or "127.0.0.1" +* --server_host_override=HOSTNAME + * The server host to claim to be connecting to, for use in TLS and HTTP/2 + :authority header. If unspecified, the value of --server_host will be + used +* --server_port=PORT + * The server port to connect to. For example, "8080" +* --test_case=TESTCASE + * The name of the test case to execute. For example, "empty_unary" +* --use_tls=BOOLEAN + * Whether to use a plaintext or encrypted connection +* --use_test_ca=BOOLEAN + * Whether to replace platform root CAs with + [ca.pem](https://github.com/grpc/grpc/blob/master/src/core/tsi/test_creds/ca.pem) + as the CA root +* --default_service_account=ACCOUNT_EMAIL + * Email of the GCE default service account. Only applicable + for compute_engine_creds test. +* --oauth_scope=SCOPE + * OAuth scope. For example, "https://www.googleapis.com/auth/xapi.zoo" +* --service_account_key_file=PATH + * The path to the service account JSON key file generated from GCE developer + console. + +Clients must support TLS with ALPN. Clients must not disable certificate +checking. + +### empty_unary + +This test verifies that implementations support zero-size messages. Ideally, +client implementations would verify that the request and response were zero +bytes serialized, but this is generally prohibitive to perform, so is not +required. + +Server features: +* [EmptyCall][] + +Procedure: + 1. Client calls EmptyCall with the default Empty message + +Client asserts: +* call was successful +* response is non-null + +*It may be possible to use UnaryCall instead of EmptyCall, but it is harder to +ensure that the proto serialized to zero bytes.* + +### large_unary + +This test verifies unary calls succeed in sending messages, and touches on flow +control (even if compression is enabled on the channel). + +Server features: +* [UnaryCall][] +* [Compressable Payload][] + +Procedure: + 1. Client calls UnaryCall with: + + ``` + { + response_type: COMPRESSABLE + response_size: 314159 + payload:{ + body: 271828 bytes of zeros + } + } + ``` + +Client asserts: +* call was successful +* response payload type is COMPRESSABLE +* response payload body is 314159 bytes in size +* clients are free to assert that the response payload body contents are zero + and comparing the entire response message against a golden response + +### large_compressed_unary + +This test verifies compressed unary calls succeed in sending messages. It +sends one unary request for every combination of compression algorithm and +payload type. + +In all scenarios, whether compression was actually performed is determined by +the compression bit in the response's message flags. The response's compression +value indicates which algorithm was used if said compression bit is set. + + +Server features: +* [UnaryCall][] +* [Compressable Payload][] +* [Uncompressable Payload][] +* [Random Payload][] + +Procedure: + 1. Client calls UnaryCall with: + + ``` + { + response_compression: + response_type: COMPRESSABLE + response_size: 314159 + payload:{ + body: 271828 bytes of zeros + } + } + ``` + Client asserts: + * call was successful + * response payload type is COMPRESSABLE + * response compression is consistent with the requested one. + * if `response_compression == NONE`, the response MUST NOT have the + compressed message flag set. + * if `response_compression != NONE`, the response MUST have the compressed + message flag set. + * response payload body is 314159 bytes in size + * clients are free to assert that the response payload body contents are + zero and comparing the entire response message against a golden response + + + 2. Client calls UnaryCall with: + ``` + { + response_compression: + response_type: UNCOMPRESSABLE + response_size: 314159 + payload:{ + body: 271828 bytes of zeros + } + } + ``` + Client asserts: + * call was successful + * response payload type is UNCOMPRESSABLE + * response compression is consistent with the requested one. + * the response MUST NOT have the compressed message flag set. + * response payload body is 314159 bytes in size + * clients are free to assert that the response payload body contents are + identical to the golden uncompressable data at `test/cpp/interop/rnd.dat`. + + + 3. Client calls UnaryCall with: + ``` + { + response_compression: + response_type: RANDOM + response_size: 314159 + payload:{ + body: 271828 bytes of zeros + } + } + ``` + Client asserts: + * call was successful + * response payload type is either COMPRESSABLE or UNCOMPRESSABLE + * the behavior is consistent with the randomly chosen incoming payload type, + as described in their respective sections. + +### client_streaming + +This test verifies that client-only streaming succeeds. + +Server features: +* [StreamingInputCall][] +* [Compressable Payload][] + +Procedure: + 1. Client calls StreamingInputCall + 2. Client sends: + + ``` + { + payload:{ + body: 27182 bytes of zeros + } + } + ``` + + 3. Client then sends: + + ``` + { + payload:{ + body: 8 bytes of zeros + } + } + ``` + + 4. Client then sends: + + ``` + { + payload:{ + body: 1828 bytes of zeros + } + } + ``` + + 5. Client then sends: + + ``` + { + payload:{ + body: 45904 bytes of zeros + } + } + ``` + + 6. Client half-closes + +Client asserts: +* call was successful +* response aggregated_payload_size is 74922 + +### server_streaming + +This test verifies that server-only streaming succeeds. + +Server features: +* [StreamingOutputCall][] +* [Compressable Payload][] + +Procedure: + 1. Client calls StreamingOutputCall with: + + ``` + { + response_type:COMPRESSABLE + response_parameters:{ + size: 31415 + } + response_parameters:{ + size: 9 + } + response_parameters:{ + size: 2653 + } + response_parameters:{ + size: 58979 + } + } + ``` + +Client asserts: +* call was successful +* exactly four responses +* response payloads are COMPRESSABLE +* response payload bodies are sized (in order): 31415, 9, 2653, 58979 +* clients are free to assert that the response payload body contents are zero + and comparing the entire response messages against golden responses + +### server_compressed_streaming + +This test verifies that server-only compressed streaming succeeds. + +Server features: +* [StreamingOutputCall][] +* [Compressable Payload][] +* [Uncompressable Payload][] +* [Random Payload][] + + +Procedure: + 1. Client calls StreamingOutputCall with: + + ``` + { + response_compression: + response_type:COMPRESSABLE + response_parameters:{ + size: 31415 + } + response_parameters:{ + size: 9 + } + response_parameters:{ + size: 2653 + } + response_parameters:{ + size: 58979 + } + } + ``` + + Client asserts: + * call was successful + * exactly four responses + * response payloads are COMPRESSABLE + * response compression is consistent with the requested one. + * if `response_compression == NONE`, the response MUST NOT have the + compressed message flag set. + * if `response_compression != NONE`, the response MUST have the compressed + message flag set. + * response payload bodies are sized (in order): 31415, 9, 2653, 58979 + * clients are free to assert that the response payload body contents are + zero and comparing the entire response messages against golden responses + + + 2. Client calls StreamingOutputCall with: + + ``` + { + response_compression: + response_type:UNCOMPRESSABLE + response_parameters:{ + size: 31415 + } + response_parameters:{ + size: 9 + } + response_parameters:{ + size: 2653 + } + response_parameters:{ + size: 58979 + } + } + ``` + + Client asserts: + * call was successful + * exactly four responses + * response payloads are UNCOMPRESSABLE + * response compressions are consistent with the requested one. + * the responses MUST NOT have the compressed message flag set. + * response payload bodies are sized (in order): 31415, 9, 2653, 58979 + * clients are free to assert that the body of the responses are identical to + the golden uncompressable data at `test/cpp/interop/rnd.dat`. + + + 3. Client calls StreamingOutputCall with: + + ``` + { + response_compression: + response_type:RANDOM + response_parameters:{ + size: 31415 + } + response_parameters:{ + size: 9 + } + response_parameters:{ + size: 2653 + } + response_parameters:{ + size: 58979 + } + } + ``` + + Client asserts: + * call was successful + * response payload type is either COMPRESSABLE or UNCOMPRESSABLE + * the behavior is consistent with the randomly chosen incoming payload type, + as described in their respective sections. + +### ping_pong + +This test verifies that full duplex bidi is supported. + +Server features: +* [FullDuplexCall][] +* [Compressable Payload][] + +Procedure: + 1. Client calls FullDuplexCall with: + + ``` + { + response_type: COMPRESSABLE + response_parameters:{ + size: 31415 + } + payload:{ + body: 27182 bytes of zeros + } + } + ``` + + 2. After getting a reply, it sends: + + ``` + { + response_type: COMPRESSABLE + response_parameters:{ + size: 9 + } + payload:{ + body: 8 bytes of zeros + } + } + ``` + + 3. After getting a reply, it sends: + + ``` + { + response_type: COMPRESSABLE + response_parameters:{ + size: 2653 + } + payload:{ + body: 1828 bytes of zeros + } + } + ``` + + 4. After getting a reply, it sends: + + ``` + { + response_type: COMPRESSABLE + response_parameters:{ + size: 58979 + } + payload:{ + body: 45904 bytes of zeros + } + } + ``` + + 5. After getting a reply, client half-closes + +Client asserts: +* call was successful +* exactly four responses +* response payloads are COMPRESSABLE +* response payload bodies are sized (in order): 31415, 9, 2653, 58979 +* clients are free to assert that the response payload body contents are zero + and comparing the entire response messages against golden responses + +### empty_stream + +This test verifies that streams support having zero-messages in both +directions. + +Server features: +* [FullDuplexCall][] + +Procedure: + 1. Client calls FullDuplexCall and then half-closes + +Client asserts: +* call was successful +* exactly zero responses + +### compute_engine_creds + +This test is only for cloud-to-prod path. + +This test verifies unary calls succeed in sending messages while using Service +Credentials from GCE metadata server. The client instance needs to be created +with desired oauth scope. + +The test uses `--default_service_account` with GCE service account email and +`--oauth_scope` with the OAuth scope to use. For testing against +grpc-test.sandbox.google.com, "https://www.googleapis.com/auth/xapi.zoo" should +be passed in as `--oauth_scope`. + +Server features: +* [UnaryCall][] +* [Compressable Payload][] +* [Echo Authenticated Username][] +* [Echo OAuth Scope][] + +Procedure: + 1. Client configures channel to use GCECredentials + 2. Client calls UnaryCall on the channel with: + + ``` + { + response_type: COMPRESSABLE + response_size: 314159 + payload:{ + body: 271828 bytes of zeros + } + fill_username: true + fill_oauth_scope: true + } + ``` + +Client asserts: +* call was successful +* received SimpleResponse.username equals the value of `--default_service_account` flag +* received SimpleResponse.oauth_scope is in `--oauth_scope` +* response payload body is 314159 bytes in size +* clients are free to assert that the response payload body contents are zero + and comparing the entire response message against a golden response + +### service_account_creds + +This test is only for cloud-to-prod path. + +This test verifies unary calls succeed in sending messages while using JWT +signing keys (redeemed for OAuth2 access tokens by the auth implementation) + +The test uses `--service_account_key_file` with the path to a json key file +downloaded from https://console.developers.google.com, and `--oauth_scope` +to the oauth scope. For testing against grpc-test.sandbox.google.com, +"https://www.googleapis.com/auth/xapi.zoo" should be passed in +as `--oauth_scope`. + +Server features: +* [UnaryCall][] +* [Compressable Payload][] +* [Echo Authenticated Username][] +* [Echo OAuth Scope][] + +Procedure: + 1. Client configures the channel to use ServiceAccountCredentials + 2. Client calls UnaryCall with: + + ``` + { + response_type: COMPRESSABLE + response_size: 314159 + payload:{ + body: 271828 bytes of zeros + } + fill_username: true + fill_oauth_scope: true + } + ``` + +Client asserts: +* call was successful +* received SimpleResponse.username is in the json key file read from + `--service_account_key_file` +* received SimpleResponse.oauth_scope is in `--oauth_scope` +* response payload body is 314159 bytes in size +* clients are free to assert that the response payload body contents are zero + and comparing the entire response message against a golden response + +### jwt_token_creds + +This test is only for cloud-to-prod path. + +This test verifies unary calls succeed in sending messages while using JWT +token (created by the project's key file) + +Test caller should set flag `--service_account_key_file` with the +path to json key file downloaded from +https://console.developers.google.com. + +Server features: +* [UnaryCall][] +* [Compressable Payload][] +* [Echo Authenticated Username][] +* [Echo OAuth Scope][] + +Procedure: + 1. Client configures the channel to use JWTTokenCredentials + 2. Client calls UnaryCall with: + + ``` + { + response_type: COMPRESSABLE + response_size: 314159 + payload:{ + body: 271828 bytes of zeros + } + fill_username: true + } + ``` + +Client asserts: +* call was successful +* received SimpleResponse.username is in the json key file read from + `--service_account_key_file` +* response payload body is 314159 bytes in size +* clients are free to assert that the response payload body contents are zero + and comparing the entire response message against a golden response + +### oauth2_auth_token + +Similar to the other auth tests, this test is only for cloud-to-prod path. + +This test verifies unary calls succeed in sending messages using an OAuth2 token +that is obtained out of band. For the purpose of the test, the OAuth2 token is +actually obtained from the service account credentials via the +language-specific authorization library. + +The difference between this test and the other auth tests is that rather than +configuring the test client with ServiceAccountCredentials directly, the test +first uses the authorization library to obtain an authorization token. + +The test +- uses the flag `--service_account_key_file` with the path to a json key file +downloaded from https://console.developers.google.com. Alternately, if using a +usable auth implementation, it may specify the file location in the environment +variable GOOGLE_APPLICATION_CREDENTIALS +- uses the flag `--oauth_scope` for the oauth scope. For testing against +grpc-test.sandbox.google.com, "https://www.googleapis.com/auth/xapi.zoo" should +be passed as the `--oauth_scope`. + +Server features: +* [UnaryCall][] +* [Compressable Payload][] +* [Echo Authenticated Username][] +* [Echo OAuth Scope][] + +Procedure: + 1. Client uses the auth library to obtain an authorization token + 2. Client configures the channel to use AccessTokenCredentials with the access token obtained in step 1 + 3. Client calls UnaryCall with the following message + + ``` + { + fill_username: true + fill_oauth_scope: true + } + ``` + +Client asserts: +* call was successful +* received SimpleResponse.username is in the json key file used by the auth +library to obtain the authorization token +* received SimpleResponse.oauth_scope is in `--oauth_scope` + +### per_rpc_creds + +Similar to the other auth tests, this test is only for cloud-to-prod path. + +This test verifies unary calls succeed in sending messages using an OAuth2 token +that is obtained out of band. For the purpose of the test, the OAuth2 token is +actually obtained from the service account credentials via the +language-specific authorization library. + +The test +- uses the flag `--service_account_key_file` with the path to a json key file +downloaded from https://console.developers.google.com. Alternately, if using a +usable auth implementation, it may specify the file location in the environment +variable GOOGLE_APPLICATION_CREDENTIALS +- uses the flag `--oauth_scope` for the oauth scope. For testing against +grpc-test.sandbox.google.com, "https://www.googleapis.com/auth/xapi.zoo" should +be passed as the `--oauth_scope`. + +Server features: +* [UnaryCall][] +* [Compressable Payload][] +* [Echo Authenticated Username][] +* [Echo OAuth Scope][] + +Procedure: + 1. Client uses the auth library to obtain an authorization token + 2. Client configures the channel with just SSL credentials + 3. Client calls UnaryCall, setting per-call credentials to + AccessTokenCredentials with the access token obtained in step 1. The request + is the following message + + ``` + { + fill_username: true + fill_oauth_scope: true + } + ``` + +Client asserts: +* call was successful +* received SimpleResponse.username is in the json key file used by the auth +library to obtain the authorization token +* received SimpleResponse.oauth_scope is in `--oauth_scope` + + +### custom_metadata + +This test verifies that custom metadata in either binary or ascii format can be +sent as initial-metadata by the client and as both initial- and trailing-metadata +by the server. + +Server features: +* [UnaryCall][] +* [FullDuplexCall][] +* [Compressable Payload][] +* [Echo Metadata][] + +Procedure: + 1. The client attaches custom metadata with the following keys and values: + + ``` + key: "x-grpc-test-echo-initial", value: "test_initial_metadata_value" + key: "x-grpc-test-echo-trailing-bin", value: 0xababab + ``` + + to a UnaryCall with request: + + ``` + { + response_type: COMPRESSABLE + response_size: 314159 + payload:{ + body: 271828 bytes of zeros + } + } + ``` + + 2. The client attaches custom metadata with the following keys and values: + + ``` + key: "x-grpc-test-echo-initial", value: "test_initial_metadata_value" + key: "x-grpc-test-echo-trailing-bin", value: 0xababab + ``` + + to a FullDuplexCall with request: + + ``` + { + response_type: COMPRESSABLE + response_size: 314159 + payload:{ + body: 271828 bytes of zeros + } + } + ``` + + and then half-closes + +Client asserts: +* call was successful +* metadata with key `"x-grpc-test-echo-initial"` and value + `"test_initial_metadata_value"`is received in the initial metadata for calls + in Procedure steps 1 and 2. +* metadata with key `"x-grpc-test-echo-trailing-bin"` and value `0xababab` is + received in the trailing metadata for calls in Procedure steps 1 and 2. + + + +### status_code_and_message + +This test verifies unary calls succeed in sending messages, and propagate back +status code and message sent along with the messages. + +Server features: +* [UnaryCall][] +* [FullDuplexCall][] +* [Echo Status][] + +Procedure: + 1. Client calls UnaryCall with: + + ``` + { + response_status:{ + code: 2 + message: "test status message" + } + } + ``` + + 2. Client calls FullDuplexCall with: + + ``` + { + response_status:{ + code: 2 + message: "test status message" + } + } + ``` + + and then half-closes + + +Client asserts: +* received status code is the same as the sent code for both Procedure steps 1 + and 2 +* received status message is the same as the sent message for both Procedure + steps 1 and 2 + +### unimplemented_method + +Status: Ready for implementation. Blocking beta. + +This test verifies calling unimplemented RPC method returns the UNIMPLEMENTED status code. + +Server features: +N/A + +Procedure: +* Client calls `grpc.testing.UnimplementedService/UnimplementedCall` with an + empty request (defined as `grpc.testing.Empty`): + + ``` + { + } + ``` + +Client asserts: +* received status code is 12 (UNIMPLEMENTED) +* received status message is empty or null/unset + +### cancel_after_begin + +This test verifies that a request can be cancelled after metadata has been sent +but before payloads are sent. + +Server features: +* [StreamingInputCall][] + +Procedure: + 1. Client starts StreamingInputCall + 2. Client immediately cancels request + +Client asserts: +* Call completed with status CANCELLED + +### cancel_after_first_response + +This test verifies that a request can be cancelled after receiving a message +from the server. + +Server features: +* [FullDuplexCall][] +* [Compressable Payload][] + +Procedure: + 1. Client starts FullDuplexCall with + + ``` + { + response_type: COMPRESSABLE + response_parameters:{ + size: 31415 + } + payload:{ + body: 27182 bytes of zeros + } + } + ``` + + 2. After receiving a response, client cancels request + +Client asserts: +* Call completed with status CANCELLED + +### timeout_on_sleeping_server + +This test verifies that an RPC request whose lifetime exceeds its configured +timeout value will end with the DeadlineExceeded status. + +Server features: +* [FullDuplexCall][] + +Procedure: + 1. Client calls FullDuplexCall with the following request and sets its timeout + to 1ms + + ``` + { + payload:{ + body: 27182 bytes of zeros + } + } + ``` + + 2. Client waits + +Client asserts: +* Call completed with status DEADLINE_EXCEEDED. + +### concurrent_large_unary + +Status: TODO + +Client performs 1000 large_unary tests in parallel on the same channel. + +### Flow control. Pushback at client for large messages (TODO: fix name) + +Status: TODO + +This test verifies that a client sending faster than a server can drain sees +pushback (i.e., attempts to send succeed only after appropriate delays). + +### TODO Tests + +#### High priority: + +Propagation of status code and message (yangg) + +Multiple thousand simultaneous calls on same Channel (ctiller) + +Metadata: client headers, server headers + trailers, binary+ascii + +#### Normal priority: + +Cancel before start (ctiller) + +Cancel after sent first message (ctiller) + +Cancel after received headers (ctiller) + +Timeout but completed before expire (zhaoq) + +Multiple thousand simultaneous calls timeout on same Channel (ctiller) + +#### Lower priority: + +Flow control. Pushback at client for large messages (abhishek) + +Flow control. Pushback at server for large messages (abhishek) + +Going over max concurrent streams doesn't fail (client controls itself) +(abhishek) + +RPC method not implemented (yangg) + +Multiple thousand simultaneous calls on different Channels (ctiller) + +Failed TLS hostname verification (ejona?) + +Large amount of headers to cause CONTINUATIONs; 63K of 'X's, all in one header. + +#### To priorize: + +Start streaming RPC but don't send any requests, server responds + +### Postponed Tests + +Resilience to buggy servers: These tests would verify that a client application +isn't affected negatively by the responses put on the wire by a buggy server +(e.g. the client library won't make the application crash). + +Reconnect after transport failure + +Reconnect backoff + +Fuzz testing + + +Server +------ + +Servers implement various named features for clients to test with. Server +features are orthogonal. If a server implements a feature, it is always +available for clients. Names are simple descriptions for developer +communication and tracking. + +Servers should accept these arguments: + +* --port=PORT + + * The port to listen on. For example, "8080" + +* --use_tls=BOOLEAN + + * Whether to use a plaintext or encrypted connection + +Servers must support TLS with ALPN. They should use +[server1.pem](https://github.com/grpc/grpc/blob/master/src/core/tsi/test_creds/server1.pem) +for their certificate. + +### EmptyCall +[EmptyCall]: #emptycall + +Server implements EmptyCall which immediately returns the empty message. + +### UnaryCall +[UnaryCall]: #unarycall + +Server implements UnaryCall which immediately returns a SimpleResponse with a +payload body of size SimpleRequest.response_size bytes and type as appropriate +for the SimpleRequest.response_type. If the server does not support the +response_type, then it should fail the RPC with INVALID_ARGUMENT. + +### StreamingInputCall +[StreamingInputCall]: #streaminginputcall + +Server implements StreamingInputCall which upon half close immediately returns +a StreamingInputCallResponse where aggregated_payload_size is the sum of all +request payload bodies received. + +### StreamingOutputCall +[StreamingOutputCall]: #streamingoutputcall + +Server implements StreamingOutputCall by replying, in order, with one +StreamingOutputCallResponses for each ResponseParameters in +StreamingOutputCallRequest. Each StreamingOutputCallResponses should have a +payload body of size ResponseParameters.size bytes, as specified by its +respective ResponseParameters. After sending all responses, it closes with OK. + +### FullDuplexCall +[FullDuplexCall]: #fullduplexcall + +Server implements FullDuplexCall by replying, in order, with one +StreamingOutputCallResponses for each ResponseParameters in each +StreamingOutputCallRequest. Each StreamingOutputCallResponses should have a +payload body of size ResponseParameters.size bytes, as specified by its +respective ResponseParameters. After receiving half close and sending all +responses, it closes with OK. + +### Compressable Payload +[Compressable Payload]: #compressable-payload + +When the client requests COMPRESSABLE payload, the response includes a payload +of the size requested containing all zeros and the payload type is +COMPRESSABLE. + +### Uncompressable Payload +[Uncompressable Payload]: #uncompressable-payload + +When the client requests UNCOMPRESSABLE payload, the response includes a payload +of the size requested containing uncompressable data and the payload type is +UNCOMPRESSABLE. A 512 kB dump from /dev/urandom is the current golden data, +stored at `test/cpp/interop/rnd.dat` + +### Random Payload +[Random Payload]: #random-payload + +When the client requests RANDOM payload, the response includes either a randomly +chosen COMPRESSABLE or UNCOMPRESSABLE payload. The data and the payload type +will be consistent with this choice. + +### Echo Status +[Echo Status]: #echo-status +When the client sends a response_status in the request payload, the server closes +the stream with the status code and messsage contained within said response_status. +The server will not process any further messages on the stream sent by the client. +This can be used by clients to verify correct handling of different status codes and +associated status messages end-to-end. + +### Echo Metadata +[Echo Metadata]: #echo-metadata +When the client sends metadata with the key `"x-grpc-test-echo-initial"` with its +request, the server sends back exactly this key and the corresponding value back to +the client as part of initial metadata. When the client sends metadata with the key +`"x-grpc-test-echo-trailing-bin"` with its request, the server sends back exactly this +key and the corresponding value back to the client as trailing metadata. + +### Observe ResponseParameters.interval_us +[Observe ResponseParameters.interval_us]: #observe-responseparametersinterval_us + +In StreamingOutputCall and FullDuplexCall, server delays sending a +StreamingOutputCallResponse by the ResponseParameters's interval_us for that +particular response, relative to the last response sent. That is, interval_us +acts like a sleep *before* sending the response and accumulates from one +response to the next. + +Interaction with flow control is unspecified. + +### Echo Auth Information + +Status: Pending + +#### Echo Authenticated Username +[Echo Authenticated Username]: #echo-authenticated-username + +If a SimpleRequest has fill_username=true and that request was successfully +authenticated, then the SimpleResponse should have username filled with the +canonical form of the authenticated source. The canonical form is dependent on +the authentication method, but is likely to be a base 10 integer identifier or +an email address. + +#### Echo OAuth scope +[Echo OAuth Scope]: #echo-oauth-scope + +If a SimpleRequest has fill_oauth_scope=true and that request was successfully +authenticated via OAuth, then the SimpleResponse should have oauth_scope filled +with the scope of the method being invoked. + +Although a general server-side feature, most test servers won't implement this +feature. The TLS server grpc-test.sandbox.google.com:443 supports this feature. +It requires at least the OAuth scope +`https://www.googleapis.com/auth/xapi.zoo` for authentication to succeed. + +Discussion: + +Ideally, this would be communicated via metadata and not in the +request/response, but we want to use this test in code paths that don't yet +fully communicate metadata. + diff --git a/doc/naming.md b/doc/naming.md new file mode 100644 index 00000000..5ad7e662 --- /dev/null +++ b/doc/naming.md @@ -0,0 +1,52 @@ +#gRPC Naming and Discovery Support + +## Overview + +gRPC supports DNS as the default name-system. A number of alternative name-systems are used in various deployments. We propose an API that is general enough to support a range of name-systems and the corresponding syntax for names. The gRPC client library in various languages will provide a plugin mechanism so resolvers for different name-systems can be plugged in. + +## Detailed Proposal + + A fully qualified, self contained name used for gRPC channel construction uses the syntax: + +``` +scheme://authority/endpoint_name +``` + +Here, scheme indicates the name-system to be used. Example schemes to be supported include: + +* `dns` + +* `zookeeper` + +* `etcd` + +Authority indicates some scheme-specific bootstrap information, e.g., for DNS, the authority may include the IP[:port] of the DNS server to use. Often, a DNS name may used as the authority, since the ability to resolve DNS names is already built into all gRPC client libraries. + +Finally, the endpoint_name indicates a concrete name to be looked up in a given name-system identified by the scheme and the authority. The syntax of endpoint name is dictated by the scheme in use. + +### Plugins + +The gRPC client library will switch on the scheme to pick the right resolver plugin and pass it the fully qualified name string. + +Resolvers should be able to contact the authority and get a resolution that they return back to the gRPC client library. The returned contents include a list of IP:port, an optional config and optional auth config data to be used for channel authentication. The plugin API allows the resolvers to continuously watch an endpoint_name and return updated resolutions as needed. + +## Zookeeper + +Apache [ZooKeeper](https://zookeeper.apache.org/) is a popular solution for building name-systems. Curator is a service discovery system built on to of ZooKeeper. We propose to organize names hierarchically as `/path/service/instance` similar to Apache Curator. + +A fully-qualified ZooKeeper name used to construct a gRPC channel will look as follows: + +``` +zookeeper://host:port/path/service/instance +``` +Here `zookeeper` is the scheme identifying the name-system. `host:port` identifies an authoritative name-server for this scheme (i.e., a Zookeeper server). The host can be an IP address or a DNS name. +Finally `/path/service/instance` is the Zookeeper name to be resolved. + +## Service Registration + + +Service providers can register their services in Zookeeper by using a Zookeeper client. + +Each service is a zookeeper node, and each instance is a child node of the corresponding service. For example, a MySQL service may have multiple instances, `/mysql/1`, `/mysql/2`, `/mysql/3`. The name of the service or instance, as well as an optional path is specified by the service provider. + +The data in service nodes is empty. Each instance node stores its address in the format of `host:port`, where host can be either hostname or IP address. diff --git a/doc/server-reflection.md b/doc/server-reflection.md new file mode 100644 index 00000000..cceee164 --- /dev/null +++ b/doc/server-reflection.md @@ -0,0 +1,183 @@ +GRPC Server Reflection Protocol +=============================== + +This document describes server reflection as an optional extension for servers +to assist clients in runtime construction of requests without having stub +information precompiled into the client. + +The primary usecase for server reflection is to write (typically) command line +debugging tools for talking to a grpc server. In particular, such a tool will +take in a method and a payload (in human readable text format) send it to the +server (typically in binary proto wire format), and then take the response and +decode it to text to present to the user. + +This broadly involves two problems: determining what formats (which protobuf +messages) a server’s method uses, and determining how to convert messages +between human readable format and the (likely binary) wire format. + +## Method reflection + +We want to be able to answer the following queries: + 1. What methods does a server export? + 2. For a particular method, how do we call it? +Specifically, what are the names of the methods, are those methods unary or +streaming, and what are the types of the argument and result? + +``` +#TODO(dklempner): link to an actual .proto later. +package grpc.reflection.v1alpha; + +message ListApisRequest { +} + +message ListApisResponse { + repeated google.protobuf.Api apis = 1; +} + +message GetMethodRequest { + string method = 1; +} +message GetMethodResponse { + google.protobuf.Method method = 1; +} + +service ServerReflection { + rpc ListApis (ListApisRequest) returns (ListApisResponse); + rpc GetMethod (GetMethodRequest) returns (GetMethodResponse); +} +``` + +Note that a server is under no obligation to return a complete list of all +methods it supports. For example, a reverse proxy may support server reflection +for methods implemented directly on the proxy but not enumerate all methods +supported by its backends. + + +### Open questions on method reflection + * Consider how to extend this protocol to support non-protobuf methods. + +## Argument reflection +The second half of the problem is converting between the human readable +input/output of a debugging tool and the binary format understood by the +method. + +This is obviously dependent on protocol type. At one extreme, if both the +server and the debugging tool accept JSON, there may be no need for such a +conversion in the first place. At the opposite extreme, a server using a custom +binary format has no hope of being supported by a generic system. The +intermediate interesting common case is a server which speaks binary-proto and +a debugging client which speaks either ascii-proto or json-proto. + +One approach would be to require servers directly support human readable input. +In the future method reflection may be extended to document such support, +should it become widespread or standardized. + +## Protobuf descriptors + +A second would be for the server to export its +google::protobuf::DescriptorDatabase over the wire. This is very easy to +implement in C++, and Google implementations of a similar protocol already +exist in C++, Go, and Java. + +This protocol mostly returns FileDescriptorProtos, which are a proto encoding +of a parsed .proto file. It supports four queries: + 1. The FileDescriptorProto for a given file name + 2. The FileDescriptorProto for the file with a given symbol + 3. The FileDescriptorProto for the file with a given extension + 4. The list of known extension tag numbers of a given type + +These directly correspond to the methods of +google::protobuf::DescriptorDatabase. Note that this protocol includes support +for extensions, which have been removed from proto3 but are still in widespread +use in Google’s codebase. + +Because most usecases will require also requesting the transitive dependencies +of requested files, the queries will also return all transitive dependencies of +the returned file. Should interesting usecases for non-transitive queries turn +up later, we can easily extend the protocol to support them. + +### Reverse proxy traversal + +One potential issue with naive reverse proxies is that, while any individual +server will have a consistent and valid picture of the proto DB which is +sufficient to handle incoming requests, incompatibilities will arise if the +backend servers have a mix of builds. For example, if a given message is moved +from foo.proto to bar.proto, and the client requests foo.proto from an old +server and bar.proto from a new server, the resulting database will have a +double definition. + +To solve this problem, the protocol is structured as a bidirectional stream, +ensuring all related requests go to a single server. This has the additional +benefit that overlapping recursive requests don’t require sending a lot of +redundant information, because there is a single stream to maintain context +between queries. + +``` +package grpc.reflection.v1alpha; +message DescriptorDatabaseRequest { + string host = 1; + oneof message_request { + string files_for_file_name = 3; + string files_for_symbol_name = 4; + FileContainingExtensionRequest file_containing_extension = 5; + string list_all_extensions_of_type = 6; + } +} + +message FileContainingExtensionRequest { + string base_message = 1; + int64 extension_id = 2; +} + +message DescriptorDatabaseResponse { + string valid_host = 1; + DescriptorDatabaseRequest original_request = 2; + oneof message_response { + // These are proto2 type google.protobuf.FileDescriptorProto, but + // we avoid taking a dependency on descriptor.proto, which uses + // proto2 only features, by making them opaque + // bytes instead + repeated bytes fd_proto = 4; + ListAllExtensionsResponse extensions_response = 5; + // Notably includes error code 5, NOT FOUND + int32 error_code = 6; + } +} + +message ListAllExtensionsResponse { + string base_type_name; + repeated int64 extension_number; +} + +service ProtoDescriptorDatabase { + rpc DescriptorDatabaseInfo(stream DescriptorDatabaseRequest) returns (stream DescriptorDatabaseResponse); +} +``` + +Any given request must either result in an error code or an answer, usually in +the form of a series of FileDescriptorProtos with the requested file itself +and all previously unsent transitive imports of that file. Servers may track +which FileDescriptorProtos have been sent on a given stream, for a given value +of valid_host, and avoid sending them repeatedly for overlapping requests. + +| message_request message | Result | +| files_for_file_name | transitive closure of file name | +| files_for_symbol_name | transitive closure file containing symbol | +| file_containing_extension | transitive closure of file containing a given extension number of a given symbol | +| list_all_extensions_of_type | ListAllExtensionsResponse containing all known extension numbers of a given type | + +At some point it would make sense to additionally also support any.proto’s +format. Note that known any.proto messages can be queried by symbol using this +protocol even without any such support, by parsing the url and extracting the +symbol name from it. + +## Language specific implementation thoughts +All of the information needed to implement Proto reflection is available to the +code generator, but I’m not certain we actually generate this in every +language. If the proto implementation in the language doesn’t have something +like google::protobuf::DescriptorPool the grpc implementation for that language +will need to index those FileDescriptorProtos by file and symbol and imports. + +One issue is that some grpc implementations are very loosely coupled with +protobufs; in such implementations it probably makes sense to split apart these +reflection APIs so as not to take an additional proto dependency. diff --git a/etc/roots.pem b/etc/roots.pem new file mode 100644 index 00000000..7b4d5f10 --- /dev/null +++ b/etc/roots.pem @@ -0,0 +1,5114 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# Issuer: O=Equifax OU=Equifax Secure Certificate Authority +# Subject: O=Equifax OU=Equifax Secure Certificate Authority +# Label: "Equifax Secure CA" +# Serial: 903804111 +# MD5 Fingerprint: 67:cb:9d:c0:13:24:8a:82:9b:b2:17:1e:d1:1b:ec:d4 +# SHA1 Fingerprint: d2:32:09:ad:23:d3:14:23:21:74:e4:0d:7f:9d:62:13:97:86:63:3a +# SHA256 Fingerprint: 08:29:7a:40:47:db:a2:36:80:c7:31:db:6e:31:76:53:ca:78:48:e1:be:bd:3a:0b:01:79:a7:07:f9:2c:f1:78 +-----BEGIN CERTIFICATE----- +MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV +UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy +dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1 +MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx +dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B +AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f +BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A +cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC +AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ +MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm +aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw +ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj +IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF +MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA +A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y +7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh +1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4 +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign Root CA O=GlobalSign nv-sa OU=Root CA +# Subject: CN=GlobalSign Root CA O=GlobalSign nv-sa OU=Root CA +# Label: "GlobalSign Root CA" +# Serial: 4835703278459707669005204 +# MD5 Fingerprint: 3e:45:52:15:09:51:92:e1:b7:5d:37:9f:b1:87:29:8a +# SHA1 Fingerprint: b1:bc:96:8b:d4:f4:9d:62:2a:a8:9a:81:f2:15:01:52:a4:1d:82:9c +# SHA256 Fingerprint: eb:d4:10:40:e4:bb:3e:c7:42:c9:e3:81:d3:1e:f2:a4:1a:48:b6:68:5c:96:e7:ce:f3:c1:df:6c:d4:33:1c:99 +-----BEGIN CERTIFICATE----- +MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv +b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw +MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i +YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT +aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ +jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp +xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp +1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG +snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ +U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8 +9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B +AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz +yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE +38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP +AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad +DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME +HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R2 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R2 +# Label: "GlobalSign Root CA - R2" +# Serial: 4835703278459682885658125 +# MD5 Fingerprint: 94:14:77:7e:3e:5e:fd:8f:30:bd:41:b0:cf:e7:d0:30 +# SHA1 Fingerprint: 75:e0:ab:b6:13:85:12:27:1c:04:f8:5f:dd:de:38:e4:b7:24:2e:fe +# SHA256 Fingerprint: ca:42:dd:41:74:5f:d0:b8:1e:b9:02:36:2c:f9:d8:bf:71:9d:a1:bd:1b:1e:fc:94:6f:5b:4c:99:f4:2c:1b:9e +-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G +A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp +Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1 +MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG +A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL +v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8 +eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq +tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd +C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa +zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB +mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH +V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n +bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG +3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs +J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO +291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS +ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd +AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 +TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== +-----END CERTIFICATE----- + +# Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only +# Subject: CN=VeriSign Class 3 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only +# Label: "Verisign Class 3 Public Primary Certification Authority - G3" +# Serial: 206684696279472310254277870180966723415 +# MD5 Fingerprint: cd:68:b6:a7:c7:c4:ce:75:e0:1d:4f:57:44:61:92:09 +# SHA1 Fingerprint: 13:2d:0d:45:53:4b:69:97:cd:b2:d5:c3:39:e2:55:76:60:9b:5c:c6 +# SHA256 Fingerprint: eb:04:cf:5e:b1:f3:9a:fa:76:2f:2b:b1:20:f2:96:cb:a5:20:c1:b9:7d:b1:58:95:65:b8:1c:b9:a1:7b:72:44 +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl +cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu +LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT +aWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD +VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT +aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ +bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu +IENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMu6nFL8eB8aHm8b +N3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1EUGO+i2t +KmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGu +kxUccLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBm +CC+Vk7+qRy+oRpfwEuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJ +Xwzw3sJ2zq/3avL6QaaiMxTJ5Xpj055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWu +imi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAERSWwauSCPc/L8my/uRan2Te +2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5fj267Cz3qWhMe +DGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC +/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565p +F4ErWjfJXir0xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGt +TxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ== +-----END CERTIFICATE----- + +# Issuer: CN=VeriSign Class 4 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only +# Subject: CN=VeriSign Class 4 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only +# Label: "Verisign Class 4 Public Primary Certification Authority - G3" +# Serial: 314531972711909413743075096039378935511 +# MD5 Fingerprint: db:c8:f2:27:2e:b1:ea:6a:29:23:5d:fe:56:3e:33:df +# SHA1 Fingerprint: c8:ec:8c:87:92:69:cb:4b:ab:39:e9:8d:7e:57:67:f3:14:95:73:9d +# SHA256 Fingerprint: e3:89:36:0d:0f:db:ae:b3:d2:50:58:4b:47:30:31:4e:22:2f:39:c1:56:a0:20:14:4e:8d:96:05:61:79:15:06 +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQDsoKeLbnVqAc/EfMwvlF7XMA0GCSqGSIb3DQEBBQUAMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl +cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu +LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT +aWduIENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD +VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT +aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ +bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu +IENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK3LpRFpxlmr8Y+1 +GQ9Wzsy1HyDkniYlS+BzZYlZ3tCD5PUPtbut8XzoIfzk6AzufEUiGXaStBO3IFsJ ++mGuqPKljYXCKtbeZjbSmwL0qJJgfJxptI8kHtCGUvYynEFYHiK9zUVilQhu0Gbd +U6LM8BDcVHOLBKFGMzNcF0C5nk3T875Vg+ixiY5afJqWIpA7iCXy0lOIAgwLePLm +NxdLMEYH5IBtptiWLugs+BGzOA1mppvqySNb247i8xOOGlktqgLw7KSHZtzBP/XY +ufTsgsbSPZUd5cBPhMnZo0QoBmrXRazwa2rvTl/4EYIeOGM0ZlDUPpNz+jDDZq3/ +ky2X7wMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAj/ola09b5KROJ1WrIhVZPMq1 +CtRK26vdoV9TxaBXOcLORyu+OshWv8LZJxA6sQU8wHcxuzrTBXttmhwwjIDLk5Mq +g6sFUYICABFna/OIYUdfA5PVWw3g8dShMjWFsjrbsIKr0csKvE+MW8VLADsfKoKm +fjaF3H48ZwC15DtS4KjrXRX5xm3wrR0OhbepmnMUWluPQSjA1egtTaRezarZ7c7c +2NU8Qh0XwRJdRTjDOPP8hS6DRkiy1yBfkjaP53kPmF6Z6PDQpLv1U70qzlmwr25/ +bLvSHgCwIe34QWKCudiyxLtGUPMxxY8BqHTr9Xgn2uf3ZkPznoM+IKrDNWCRzg== +-----END CERTIFICATE----- + +# Issuer: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited +# Subject: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited +# Label: "Entrust.net Premium 2048 Secure Server CA" +# Serial: 946069240 +# MD5 Fingerprint: ee:29:31:bc:32:7e:9a:e6:e8:b5:f7:51:b4:34:71:90 +# SHA1 Fingerprint: 50:30:06:09:1d:97:d4:f5:ae:39:f7:cb:e7:92:7d:7d:65:2d:34:31 +# SHA256 Fingerprint: 6d:c4:71:72:e0:1c:bc:b0:bf:62:58:0d:89:5f:e2:b8:ac:9a:d4:f8:73:80:1e:0c:10:b9:c8:37:d2:1e:b1:77 +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML +RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp +bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5 +IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQxNzUwNTFaFw0yOTA3 +MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3 +LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxp +YWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEG +A1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQq +K0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQe +sYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuX +MlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVT +XTzWnLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/ +HoZdenoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH +4QIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJKoZIhvcNAQEFBQADggEBADub +j1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPyT/4xmf3IDExo +U8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf +zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5b +u/8j72gZyxKTJ1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+ +bYQLCIt+jerXmCHG8+c8eS9enNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/Er +fF6adulZkMV8gzURZVE= +-----END CERTIFICATE----- + +# Issuer: CN=Baltimore CyberTrust Root O=Baltimore OU=CyberTrust +# Subject: CN=Baltimore CyberTrust Root O=Baltimore OU=CyberTrust +# Label: "Baltimore CyberTrust Root" +# Serial: 33554617 +# MD5 Fingerprint: ac:b6:94:a5:9c:17:e0:d7:91:52:9b:b1:97:06:a6:e4 +# SHA1 Fingerprint: d4:de:20:d0:5e:66:fc:53:fe:1a:50:88:2c:78:db:28:52:ca:e4:74 +# SHA256 Fingerprint: 16:af:57:a9:f6:76:b0:ab:12:60:95:aa:5e:ba:de:f2:2a:b3:11:19:d6:44:ac:95:cd:4b:93:db:f3:f2:6a:eb +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ +RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD +VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX +DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y +ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy +VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr +mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr +IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK +mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu +XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy +dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye +jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1 +BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 +DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92 +9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx +jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0 +Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz +ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS +R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE----- + +# Issuer: CN=Equifax Secure Global eBusiness CA-1 O=Equifax Secure Inc. +# Subject: CN=Equifax Secure Global eBusiness CA-1 O=Equifax Secure Inc. +# Label: "Equifax Secure Global eBusiness CA" +# Serial: 1 +# MD5 Fingerprint: 8f:5d:77:06:27:c4:98:3c:5b:93:78:e7:d7:7d:9b:cc +# SHA1 Fingerprint: 7e:78:4a:10:1c:82:65:cc:2d:e1:f1:6d:47:b4:40:ca:d9:0a:19:45 +# SHA256 Fingerprint: 5f:0b:62:ea:b5:e3:53:ea:65:21:65:16:58:fb:b6:53:59:f4:43:28:0a:4a:fb:d1:04:d7:7d:10:f9:f0:4c:07 +-----BEGIN CERTIFICATE----- +MIICkDCCAfmgAwIBAgIBATANBgkqhkiG9w0BAQQFADBaMQswCQYDVQQGEwJVUzEc +MBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEtMCsGA1UEAxMkRXF1aWZheCBT +ZWN1cmUgR2xvYmFsIGVCdXNpbmVzcyBDQS0xMB4XDTk5MDYyMTA0MDAwMFoXDTIw +MDYyMTA0MDAwMFowWjELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0VxdWlmYXggU2Vj +dXJlIEluYy4xLTArBgNVBAMTJEVxdWlmYXggU2VjdXJlIEdsb2JhbCBlQnVzaW5l +c3MgQ0EtMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuucXkAJlsTRVPEnC +UdXfp9E3j9HngXNBUmCbnaEXJnitx7HoJpQytd4zjTov2/KaelpzmKNc6fuKcxtc +58O/gGzNqfTWK8D3+ZmqY6KxRwIP1ORROhI8bIpaVIRw28HFkM9yRcuoWcDNM50/ +o5brhTMhHD4ePmBudpxnhcXIw2ECAwEAAaNmMGQwEQYJYIZIAYb4QgEBBAQDAgAH +MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUvqigdHJQa0S3ySPY+6j/s1dr +aGwwHQYDVR0OBBYEFL6ooHRyUGtEt8kj2Puo/7NXa2hsMA0GCSqGSIb3DQEBBAUA +A4GBADDiAVGqx+pf2rnQZQ8w1j7aDRRJbpGTJxQx78T3LUX47Me/okENI7SS+RkA +Z70Br83gcfxaz2TE4JaY0KNA4gGK7ycH8WUBikQtBmV1UsCGECAhX2xrD2yuCRyv +8qIYNMR1pHMc8Y3c7635s3a0kr/clRAevsvIO1qEYBlWlKlV +-----END CERTIFICATE----- + +# Issuer: CN=AddTrust Class 1 CA Root O=AddTrust AB OU=AddTrust TTP Network +# Subject: CN=AddTrust Class 1 CA Root O=AddTrust AB OU=AddTrust TTP Network +# Label: "AddTrust Low-Value Services Root" +# Serial: 1 +# MD5 Fingerprint: 1e:42:95:02:33:92:6b:b9:5f:c0:7f:da:d6:b2:4b:fc +# SHA1 Fingerprint: cc:ab:0e:a0:4c:23:01:d6:69:7b:dd:37:9f:cd:12:eb:24:e3:94:9d +# SHA256 Fingerprint: 8c:72:09:27:9a:c0:4e:27:5e:16:d0:7f:d3:b7:75:e8:01:54:b5:96:80:46:e3:1f:52:dd:25:76:63:24:e9:a7 +-----BEGIN CERTIFICATE----- +MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJTRTEU +MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3 +b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwHhcNMDAwNTMw +MTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYD +VQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwzexODcEyPNwTXH+9ZOEQpnXvUGW2ul +CDtbKRY654eyNAbFvAWlA3yCyykQruGIgb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6n +tGO0/7Gcrjyvd7ZWxbWroulpOj0OM3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyl +dI+Yrsj5wAYi56xz36Uu+1LcsRVlIPo1Zmne3yzxbrww2ywkEtvrNTVokMsAsJch +PXQhI2U0K7t4WaPW4XY5mqRJjox0r26kmqPZm9I4XJuiGMx1I4S+6+JNM3GOGvDC ++Mcdoq0Dlyz4zyXG9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8wHQYDVR0O +BBYEFJWxtPCUtr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8E +BTADAQH/MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBl +MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFk +ZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENB +IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxtZBsfzQ3duQH6lmM0MkhHma6X +7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0PhiVYrqW9yTkkz +43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9tTEv2dB8Xfjea4MY +eDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL/bscVjby/rK25Xa71SJl +pz/+0WatC7xrmYbvP33zGDLKe8bjq2RGlfgmadlVg3sslgf/WSxEo8bl6ancoWOA +WiFeIc9TVPC6b4nbqKqVz4vjccweGyBECMB6tkD9xOQ14R0WHNC8K47Wcdk= +-----END CERTIFICATE----- + +# Issuer: CN=AddTrust External CA Root O=AddTrust AB OU=AddTrust External TTP Network +# Subject: CN=AddTrust External CA Root O=AddTrust AB OU=AddTrust External TTP Network +# Label: "AddTrust External Root" +# Serial: 1 +# MD5 Fingerprint: 1d:35:54:04:85:78:b0:3f:42:42:4d:bf:20:73:0a:3f +# SHA1 Fingerprint: 02:fa:f3:e2:91:43:54:68:60:78:57:69:4d:f5:e4:5b:68:85:18:68 +# SHA256 Fingerprint: 68:7f:a4:51:38:22:78:ff:f0:c8:b1:1f:8d:43:d5:76:67:1c:6e:b2:bc:ea:b4:13:fb:83:d9:65:d0:6d:2f:f2 +-----BEGIN CERTIFICATE----- +MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU +MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs +IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290 +MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux +FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h +bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v +dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt +H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9 +uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX +mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX +a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN +E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0 +WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD +VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0 +Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU +cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx +IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN +AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH +YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5 +6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC +Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX +c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a +mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= +-----END CERTIFICATE----- + +# Issuer: CN=AddTrust Public CA Root O=AddTrust AB OU=AddTrust TTP Network +# Subject: CN=AddTrust Public CA Root O=AddTrust AB OU=AddTrust TTP Network +# Label: "AddTrust Public Services Root" +# Serial: 1 +# MD5 Fingerprint: c1:62:3e:23:c5:82:73:9c:03:59:4b:2b:e9:77:49:7f +# SHA1 Fingerprint: 2a:b6:28:48:5e:78:fb:f3:ad:9e:79:10:dd:6b:df:99:72:2c:96:e5 +# SHA256 Fingerprint: 07:91:ca:07:49:b2:07:82:aa:d3:c7:d7:bd:0c:df:c9:48:58:35:84:3e:b2:d7:99:60:09:ce:43:ab:6c:69:27 +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJTRTEU +MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3 +b3JrMSAwHgYDVQQDExdBZGRUcnVzdCBQdWJsaWMgQ0EgUm9vdDAeFw0wMDA1MzAx +MDQxNTBaFw0yMDA1MzAxMDQxNTBaMGQxCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtB +ZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5ldHdvcmsxIDAeBgNV +BAMTF0FkZFRydXN0IFB1YmxpYyBDQSBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA6Rowj4OIFMEg2Dybjxt+A3S72mnTRqX4jsIMEZBRpS9mVEBV +6tsfSlbunyNu9DnLoblv8n75XYcmYZ4c+OLspoH4IcUkzBEMP9smcnrHAZcHF/nX +GCwwfQ56HmIexkvA/X1id9NEHif2P0tEs7c42TkfYNVRknMDtABp4/MUTu7R3AnP +dzRGULD4EfL+OHn3Bzn+UZKXC1sIXzSGAa2Il+tmzV7R/9x98oTaunet3IAIx6eH +1lWfl2royBFkuucZKT8Rs3iQhCBSWxHveNCD9tVIkNAwHM+A+WD+eeSI8t0A65RF +62WUaUC6wNW0uLp9BBGo6zEFlpROWCGOn9Bg/QIDAQABo4HRMIHOMB0GA1UdDgQW +BBSBPjfYkrAfd59ctKtzquf2NGAv+jALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUw +AwEB/zCBjgYDVR0jBIGGMIGDgBSBPjfYkrAfd59ctKtzquf2NGAv+qFopGYwZDEL +MAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRU +cnVzdCBUVFAgTmV0d29yazEgMB4GA1UEAxMXQWRkVHJ1c3QgUHVibGljIENBIFJv +b3SCAQEwDQYJKoZIhvcNAQEFBQADggEBAAP3FUr4JNojVhaTdt02KLmuG7jD8WS6 +IBh4lSknVwW8fCr0uVFV2ocC3g8WFzH4qnkuCRO7r7IgGRLlk/lL+YPoRNWyQSW/ +iHVv/xD8SlTQX/D67zZzfRs2RcYhbbQVuE7PnFylPVoAjgbjPGsye/Kf8Lb93/Ao +GEjwxrzQvzSAlsJKsW2Ox5BF3i9nrEUEo3rcVZLJR2bYGozH7ZxOmuASu7VqTITh +4SINhwBk/ox9Yjllpu9CtoAlEmEBqCQTcAARJl/6NVDFSMwGR+gn2HCNX2TmoUQm +XiLsks3/QppEIW1cxeMiHV9HEufOX1362KqxMy3ZdvJOOjMMK7MtkAY= +-----END CERTIFICATE----- + +# Issuer: CN=AddTrust Qualified CA Root O=AddTrust AB OU=AddTrust TTP Network +# Subject: CN=AddTrust Qualified CA Root O=AddTrust AB OU=AddTrust TTP Network +# Label: "AddTrust Qualified Certificates Root" +# Serial: 1 +# MD5 Fingerprint: 27:ec:39:47:cd:da:5a:af:e2:9a:01:65:21:a9:4c:bb +# SHA1 Fingerprint: 4d:23:78:ec:91:95:39:b5:00:7f:75:8f:03:3b:21:1e:c5:4d:8b:cf +# SHA256 Fingerprint: 80:95:21:08:05:db:4b:bc:35:5e:44:28:d8:fd:6e:c2:cd:e3:ab:5f:b9:7a:99:42:98:8e:b8:f4:dc:d0:60:16 +-----BEGIN CERTIFICATE----- +MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJTRTEU +MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3 +b3JrMSMwIQYDVQQDExpBZGRUcnVzdCBRdWFsaWZpZWQgQ0EgUm9vdDAeFw0wMDA1 +MzAxMDQ0NTBaFw0yMDA1MzAxMDQ0NTBaMGcxCzAJBgNVBAYTAlNFMRQwEgYDVQQK +EwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5ldHdvcmsxIzAh +BgNVBAMTGkFkZFRydXN0IFF1YWxpZmllZCBDQSBSb290MIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA5B6a/twJWoekn0e+EV+vhDTbYjx5eLfpMLXsDBwq +xBb/4Oxx64r1EW7tTw2R0hIYLUkVAcKkIhPHEWT/IhKauY5cLwjPcWqzZwFZ8V1G +87B4pfYOQnrjfxvM0PC3KP0q6p6zsLkEqv32x7SxuCqg+1jxGaBvcCV+PmlKfw8i +2O+tCBGaKZnhqkRFmhJePp1tUvznoD1oL/BLcHwTOK28FSXx1s6rosAx1i+f4P8U +WfyEk9mHfExUE+uf0S0R+Bg6Ot4l2ffTQO2kBhLEO+GRwVY18BTcZTYJbqukB8c1 +0cIDMzZbdSZtQvESa0NvS3GU+jQd7RNuyoB/mC9suWXY6QIDAQABo4HUMIHRMB0G +A1UdDgQWBBQ5lYtii1zJ1IC6WA+XPxUIQ8yYpzALBgNVHQ8EBAMCAQYwDwYDVR0T +AQH/BAUwAwEB/zCBkQYDVR0jBIGJMIGGgBQ5lYtii1zJ1IC6WA+XPxUIQ8yYp6Fr +pGkwZzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQL +ExRBZGRUcnVzdCBUVFAgTmV0d29yazEjMCEGA1UEAxMaQWRkVHJ1c3QgUXVhbGlm +aWVkIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBABmrder4i2VhlRO6aQTv +hsoToMeqT2QbPxj2qC0sVY8FtzDqQmodwCVRLae/DLPt7wh/bDxGGuoYQ992zPlm +hpwsaPXpF/gxsxjE1kh9I0xowX67ARRvxdlu3rsEQmr49lx95dr6h+sNNVJn0J6X +dgWTP5XHAeZpVTh/EGGZyeNfpso+gmNIquIISD6q8rKFYqa0p9m9N5xotS1WfbC3 +P6CxB9bpT9zeRXEwMn8bLgn5v1Kh7sKAPgZcLlVAwRv1cEWw3F369nJad9Jjzc9Y +iQBCYz95OdBEsIJuQRno3eDBiFrRHnGTHyQwdOUeqN48Jzd/g66ed8/wMLH/S5no +xqE= +-----END CERTIFICATE----- + +# Issuer: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc. +# Subject: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc. +# Label: "Entrust Root Certification Authority" +# Serial: 1164660820 +# MD5 Fingerprint: d6:a5:c3:ed:5d:dd:3e:00:c1:3d:87:92:1f:1d:3f:e4 +# SHA1 Fingerprint: b3:1e:b1:b7:40:e3:6c:84:02:da:dc:37:d4:4d:f5:d4:67:49:52:f9 +# SHA256 Fingerprint: 73:c1:76:43:4f:1b:c6:d5:ad:f4:5b:0e:76:e7:27:28:7c:8d:e5:76:16:c1:e6:e6:14:1a:2b:2c:bc:7d:8e:4c +-----BEGIN CERTIFICATE----- +MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0 +Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW +KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0MloXDTI2MTEyNzIw +NTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMTkw +NwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSBy +ZWZlcmVuY2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNV +BAMTJEVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBALaVtkNC+sZtKm9I35RMOVcF7sN5EUFo +Nu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYszA9u3g3s+IIRe7bJWKKf4 +4LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOwwCj0Yzfv9 +KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGI +rb68j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi +94DkZfs0Nw4pgHBNrziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOB +sDCBrTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAi +gA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1MzQyWjAfBgNVHSMEGDAWgBRo +kORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DHhmak8fdLQ/uE +vW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA +A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9t +O1KzKtvn1ISMY/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6Zua +AGAT/3B+XxFNSRuzFVJ7yVTav52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP +9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTSW3iDVuycNsMm4hH2Z0kdkquM++v/ +eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0tHuu2guQOHXvgR1m +0vdXcDazv/wor3ElhVsT/h5/WrQ8 +-----END CERTIFICATE----- + +# Issuer: O=RSA Security Inc OU=RSA Security 2048 V3 +# Subject: O=RSA Security Inc OU=RSA Security 2048 V3 +# Label: "RSA Security 2048 v3" +# Serial: 13297492616345471454730593562152402946 +# MD5 Fingerprint: 77:0d:19:b1:21:fd:00:42:9c:3e:0c:a5:dd:0b:02:8e +# SHA1 Fingerprint: 25:01:90:19:cf:fb:d9:99:1c:b7:68:25:74:8d:94:5f:30:93:95:42 +# SHA256 Fingerprint: af:8b:67:62:a1:e5:28:22:81:61:a9:5d:5c:55:9e:e2:66:27:8f:75:d7:9e:83:01:89:a5:03:50:6a:bd:6b:4c +-----BEGIN CERTIFICATE----- +MIIDYTCCAkmgAwIBAgIQCgEBAQAAAnwAAAAKAAAAAjANBgkqhkiG9w0BAQUFADA6 +MRkwFwYDVQQKExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0EgU2VjdXJp +dHkgMjA0OCBWMzAeFw0wMTAyMjIyMDM5MjNaFw0yNjAyMjIyMDM5MjNaMDoxGTAX +BgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAbBgNVBAsTFFJTQSBTZWN1cml0eSAy +MDQ4IFYzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt49VcdKA3Xtp +eafwGFAyPGJn9gqVB93mG/Oe2dJBVGutn3y+Gc37RqtBaB4Y6lXIL5F4iSj7Jylg +/9+PjDvJSZu1pJTOAeo+tWN7fyb9Gd3AIb2E0S1PRsNO3Ng3OTsor8udGuorryGl +wSMiuLgbWhOHV4PR8CDn6E8jQrAApX2J6elhc5SYcSa8LWrg903w8bYqODGBDSnh +AMFRD0xS+ARaqn1y07iHKrtjEAMqs6FPDVpeRrc9DvV07Jmf+T0kgYim3WBU6JU2 +PcYJk5qjEoAAVZkZR73QpXzDuvsf9/UP+Ky5tfQ3mBMY3oVbtwyCO4dvlTlYMNpu +AWgXIszACwIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAfBgNVHSMEGDAWgBQHw1EwpKrpRa41JPr/JCwz0LGdjDAdBgNVHQ4EFgQUB8NR +MKSq6UWuNST6/yQsM9CxnYwwDQYJKoZIhvcNAQEFBQADggEBAF8+hnZuuDU8TjYc +HnmYv/3VEhF5Ug7uMYm83X/50cYVIeiKAVQNOvtUudZj1LGqlk2iQk3UUx+LEN5/ +Zb5gEydxiKRz44Rj0aRV4VCT5hsOedBnvEbIvz8XDZXmxpBp3ue0L96VfdASPz0+ +f00/FGj1EVDVwfSQpQgdMWD/YIwjVAqv/qFuxdF6Kmh4zx6CCiC0H63lhbJqaHVO +rSU3lIW+vaHU6rcMSzyd6BIA8F+sDeGscGNz9395nzIlQnQFgCi/vcEkllgVsRch +6YlL2weIZ/QVrXA+L02FO8K32/6YaCOJ4XQP3vTFhGMpG8zLB8kApKnXwiJPZ9d3 +7CAFYd4= +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Global CA O=GeoTrust Inc. +# Subject: CN=GeoTrust Global CA O=GeoTrust Inc. +# Label: "GeoTrust Global CA" +# Serial: 144470 +# MD5 Fingerprint: f7:75:ab:29:fb:51:4e:b7:77:5e:ff:05:3c:99:8e:f5 +# SHA1 Fingerprint: de:28:f4:a4:ff:e5:b9:2f:a3:c5:03:d1:a3:49:a7:f9:96:2a:82:12 +# SHA256 Fingerprint: ff:85:6a:2d:25:1d:cd:88:d3:66:56:f4:50:12:67:98:cf:ab:aa:de:40:79:9c:72:2d:e4:d2:b5:db:36:a7:3a +-----BEGIN CERTIFICATE----- +MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg +R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9 +9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq +fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv +iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU +1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+ +bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW +MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA +ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l +uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn +Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS +tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF +PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un +hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV +5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw== +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Global CA 2 O=GeoTrust Inc. +# Subject: CN=GeoTrust Global CA 2 O=GeoTrust Inc. +# Label: "GeoTrust Global CA 2" +# Serial: 1 +# MD5 Fingerprint: 0e:40:a7:6c:de:03:5d:8f:d1:0f:e4:d1:8d:f9:6c:a9 +# SHA1 Fingerprint: a9:e9:78:08:14:37:58:88:f2:05:19:b0:6d:2b:0d:2b:60:16:90:7d +# SHA256 Fingerprint: ca:2d:82:a0:86:77:07:2f:8a:b6:76:4f:f0:35:67:6c:fe:3e:5e:32:5e:01:21:72:df:3f:92:09:6d:b7:9b:85 +-----BEGIN CERTIFICATE----- +MIIDZjCCAk6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBEMQswCQYDVQQGEwJVUzEW +MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFs +IENBIDIwHhcNMDQwMzA0MDUwMDAwWhcNMTkwMzA0MDUwMDAwWjBEMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3Qg +R2xvYmFsIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDvPE1A +PRDfO1MA4Wf+lGAVPoWI8YkNkMgoI5kF6CsgncbzYEbYwbLVjDHZ3CB5JIG/NTL8 +Y2nbsSpr7iFY8gjpeMtvy/wWUsiRxP89c96xPqfCfWbB9X5SJBri1WeR0IIQ13hL +TytCOb1kLUCgsBDTOEhGiKEMuzozKmKY+wCdE1l/bztyqu6mD4b5BWHqZ38MN5aL +5mkWRxHCJ1kDs6ZgwiFAVvqgx306E+PsV8ez1q6diYD3Aecs9pYrEw15LNnA5IZ7 +S4wMcoKK+xfNAGw6EzywhIdLFnopsk/bHdQL82Y3vdj2V7teJHq4PIu5+pIaGoSe +2HSPqht/XvT+RSIhAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE +FHE4NvICMVNHK266ZUapEBVYIAUJMB8GA1UdIwQYMBaAFHE4NvICMVNHK266ZUap +EBVYIAUJMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAQEAA/e1K6td +EPx7srJerJsOflN4WT5CBP51o62sgU7XAotexC3IUnbHLB/8gTKY0UvGkpMzNTEv +/NgdRN3ggX+d6YvhZJFiCzkIjKx0nVnZellSlxG5FntvRdOW2TF9AjYPnDtuzywN +A0ZF66D0f0hExghAzN4bcLUprbqLOzRldRtxIR0sFAqwlpW41uryZfspuk/qkZN0 +abby/+Ea0AzRdoXLiiW9l14sbxWZJue2Kf8i7MkCx1YAzUm5s2x7UwQa4qjJqhIF +I8LO57sEAszAR6LkxCkvW0VXiVHuPOtSCP8HNR6fNWpHSlaY0VqFH4z1Ir+rzoPz +4iIprn2DQKi6bA== +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Universal CA O=GeoTrust Inc. +# Subject: CN=GeoTrust Universal CA O=GeoTrust Inc. +# Label: "GeoTrust Universal CA" +# Serial: 1 +# MD5 Fingerprint: 92:65:58:8b:a2:1a:31:72:73:68:5c:b4:a5:7a:07:48 +# SHA1 Fingerprint: e6:21:f3:35:43:79:05:9a:4b:68:30:9d:8a:2f:74:22:15:87:ec:79 +# SHA256 Fingerprint: a0:45:9b:9f:63:b2:25:59:f5:fa:5d:4c:6d:b3:f9:f7:2f:f1:93:42:03:35:78:f0:73:bf:1d:1b:46:cb:b9:12 +-----BEGIN CERTIFICATE----- +MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEW +MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVy +c2FsIENBMB4XDTA0MDMwNDA1MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UE +BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xHjAcBgNVBAMTFUdlb1RydXN0 +IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKYV +VaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9tJPi8 +cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTT +QjOgNB0eRXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFh +F7em6fgemdtzbvQKoiFs7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2v +c7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d8Lsrlh/eezJS/R27tQahsiFepdaVaH/w +mZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7VqnJNk22CDtucvc+081xd +VHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3CgaRr0BHdCX +teGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZ +f9hBZ3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfRe +Bi9Fi1jUIxaS5BZuKGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+ +nhutxx9z3SxPGWX9f5NAEC7S8O08ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB +/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0XG0D08DYj3rWMB8GA1UdIwQY +MBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG +9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc +aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fX +IwjhmF7DWgh2qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzyn +ANXH/KttgCJwpQzgXQQpAvvLoJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0z +uzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsKxr2EoyNB3tZ3b4XUhRxQ4K5RirqN +Pnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxFKyDuSN/n3QmOGKja +QI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2DFKW +koRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9 +ER/frslKxfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQt +DF4JbAiXfKM9fJP/P6EUp8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/Sfuvm +bJxPgWp6ZKy7PtXny3YuxadIwVyQD8vIP/rmMuGNG2+k5o7Y+SlIis5z/iw= +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Universal CA 2 O=GeoTrust Inc. +# Subject: CN=GeoTrust Universal CA 2 O=GeoTrust Inc. +# Label: "GeoTrust Universal CA 2" +# Serial: 1 +# MD5 Fingerprint: 34:fc:b8:d0:36:db:9e:14:b3:c2:f2:db:8f:e4:94:c7 +# SHA1 Fingerprint: 37:9a:19:7b:41:85:45:35:0c:a6:03:69:f3:3c:2e:af:47:4f:20:79 +# SHA256 Fingerprint: a0:23:4f:3b:c8:52:7c:a5:62:8e:ec:81:ad:5d:69:89:5d:a5:68:0d:c9:1d:1c:b8:47:7f:33:f8:78:b9:5b:0b +-----BEGIN CERTIFICATE----- +MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEW +MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVy +c2FsIENBIDIwHhcNMDQwMzA0MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYD +VQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1 +c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0DE81 +WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUG +FF+3Qs17j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdq +XbboW0W63MOhBW9Wjo8QJqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxL +se4YuU6W3Nx2/zu+z18DwPw76L5GG//aQMJS9/7jOvdqdzXQ2o3rXhhqMcceujwb +KNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2WP0+GfPtDCapkzj4T8Fd +IgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP20gaXT73 +y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRt +hAAnZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgoc +QIgfksILAAX/8sgCSqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4 +Lt1ZrtmhN79UNdxzMk+MBB4zsslG8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAfBgNV +HSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8EBAMCAYYwDQYJ +KoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z +dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQ +L1EuxBRa3ugZ4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgr +Fg5fNuH8KrUwJM/gYwx7WBr+mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSo +ag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpqA1Ihn0CoZ1Dy81of398j9tx4TuaY +T1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpgY+RdM4kX2TGq2tbz +GDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiPpm8m +1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJV +OCiNUW7dFGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH +6aLcr34YEoP9VhdBLtUpgn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwX +QMAJKOSLakhT2+zNVVXxxvjpoixMptEmX36vWkzaH6byHCx+rgIW0lbQL1dTR+iS +-----END CERTIFICATE----- + +# Issuer: CN=Visa eCommerce Root O=VISA OU=Visa International Service Association +# Subject: CN=Visa eCommerce Root O=VISA OU=Visa International Service Association +# Label: "Visa eCommerce Root" +# Serial: 25952180776285836048024890241505565794 +# MD5 Fingerprint: fc:11:b8:d8:08:93:30:00:6d:23:f9:7e:eb:52:1e:02 +# SHA1 Fingerprint: 70:17:9b:86:8c:00:a4:fa:60:91:52:22:3f:9f:3e:32:bd:e0:05:62 +# SHA256 Fingerprint: 69:fa:c9:bd:55:fb:0a:c7:8d:53:bb:ee:5c:f1:d5:97:98:9f:d0:aa:ab:20:a2:51:51:bd:f1:73:3e:e7:d1:22 +-----BEGIN CERTIFICATE----- +MIIDojCCAoqgAwIBAgIQE4Y1TR0/BvLB+WUF1ZAcYjANBgkqhkiG9w0BAQUFADBr +MQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRl +cm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNv +bW1lcmNlIFJvb3QwHhcNMDIwNjI2MDIxODM2WhcNMjIwNjI0MDAxNjEyWjBrMQsw +CQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRlcm5h +dGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNvbW1l +cmNlIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvV95WHm6h +2mCxlCfLF9sHP4CFT8icttD0b0/Pmdjh28JIXDqsOTPHH2qLJj0rNfVIsZHBAk4E +lpF7sDPwsRROEW+1QK8bRaVK7362rPKgH1g/EkZgPI2h4H3PVz4zHvtH8aoVlwdV +ZqW1LS7YgFmypw23RuwhY/81q6UCzyr0TP579ZRdhE2o8mCP2w4lPJ9zcc+U30rq +299yOIzzlr3xF7zSujtFWsan9sYXiwGd/BmoKoMWuDpI/k4+oKsGGelT84ATB+0t +vz8KPFUgOSwsAGl0lUq8ILKpeeUYiZGo3BxN77t+Nwtd/jmliFKMAGzsGHxBvfaL +dXe6YJ2E5/4tAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD +AgEGMB0GA1UdDgQWBBQVOIMPPyw/cDMezUb+B4wg4NfDtzANBgkqhkiG9w0BAQUF +AAOCAQEAX/FBfXxcCLkr4NWSR/pnXKUTwwMhmytMiUbPWU3J/qVAtmPN3XEolWcR +zCSs00Rsca4BIGsDoo8Ytyk6feUWYFN4PMCvFYP3j1IzJL1kk5fui/fbGKhtcbP3 +LBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pzzkWKsKZJ/0x9nXGIxHYdkFsd +7v3M9+79YKWxehZx0RbQfBI8bGmX265fOZpwLwU8GUYEmSA20GBuYQa7FkKMcPcw +++DbZqMAAb3mLNqRX6BGi01qnD093QVG/na/oAo85ADmJ7f/hC3euiInlhBx6yLt +398znM/jra6O1I7mT1GvFpLgXPYHDw== +-----END CERTIFICATE----- + +# Issuer: CN=Certum CA O=Unizeto Sp. z o.o. +# Subject: CN=Certum CA O=Unizeto Sp. z o.o. +# Label: "Certum Root CA" +# Serial: 65568 +# MD5 Fingerprint: 2c:8f:9f:66:1d:18:90:b1:47:26:9d:8e:86:82:8c:a9 +# SHA1 Fingerprint: 62:52:dc:40:f7:11:43:a2:2f:de:9e:f7:34:8e:06:42:51:b1:81:18 +# SHA256 Fingerprint: d8:e0:fe:bc:1d:b2:e3:8d:00:94:0f:37:d2:7d:41:34:4d:99:3e:73:4b:99:d5:65:6d:97:78:d4:d8:14:36:24 +-----BEGIN CERTIFICATE----- +MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBM +MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD +QTAeFw0wMjA2MTExMDQ2MzlaFw0yNzA2MTExMDQ2MzlaMD4xCzAJBgNVBAYTAlBM +MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD +QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6xwS7TT3zNJc4YPk/E +jG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYVM42sLQnFdvkrOYCJ5JdLkKWo +ePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/OxLjBos8Q82KxujZlakE403Daaj4GI +ULdtlkIJ89eVgw1BS7Bqa/j8D35in2fE7SZfECYPCE/wpFcozo+47UX2bu4lXapu +Ob7kky/ZR6By6/qmW6/KUz/iDsaWVhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUg +AKpoC6EahQGcxEZjgoi2IrHu/qpGWX7PNSzVttpd90gzFFS269lvzs2I1qsb2pY7 +HVkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEA +uI3O7+cUus/usESSbLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+GXYkHAQa +TOs9qmdvLdTN/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTg +xSvgGrZgFCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1q +CjqTE5s7FCMTY5w/0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5x +O/fIR/RpbxXyEV6DHpx8Uq79AtoSqFlnGNu8cN2bsWntgM6JQEhqDjXKKWYVIZQs +6GAqm4VKQPNriiTsBhYscw== +-----END CERTIFICATE----- + +# Issuer: CN=AAA Certificate Services O=Comodo CA Limited +# Subject: CN=AAA Certificate Services O=Comodo CA Limited +# Label: "Comodo AAA Services root" +# Serial: 1 +# MD5 Fingerprint: 49:79:04:b0:eb:87:19:ac:47:b0:bc:11:51:9b:74:d0 +# SHA1 Fingerprint: d1:eb:23:a4:6d:17:d6:8f:d9:25:64:c2:f1:f1:60:17:64:d8:e3:49 +# SHA256 Fingerprint: d7:a7:a0:fb:5d:7e:27:31:d7:71:e9:48:4e:bc:de:f7:1d:5f:0c:3e:0a:29:48:78:2b:c8:3e:e0:ea:69:9e:f4 +-----BEGIN CERTIFICATE----- +MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb +MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow +GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj +YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM +GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua +BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe +3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4 +YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR +rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm +ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU +oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF +MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v +QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t +b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF +AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q +GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz +Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2 +G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi +l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3 +smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== +-----END CERTIFICATE----- + +# Issuer: CN=Secure Certificate Services O=Comodo CA Limited +# Subject: CN=Secure Certificate Services O=Comodo CA Limited +# Label: "Comodo Secure Services root" +# Serial: 1 +# MD5 Fingerprint: d3:d9:bd:ae:9f:ac:67:24:b3:c8:1b:52:e1:b9:a9:bd +# SHA1 Fingerprint: 4a:65:d5:f4:1d:ef:39:b8:b8:90:4a:4a:d3:64:81:33:cf:c7:a1:d1 +# SHA256 Fingerprint: bd:81:ce:3b:4f:65:91:d1:1a:67:b5:fc:7a:47:fd:ef:25:52:1b:f9:aa:4e:18:b9:e3:df:2e:34:a7:80:3b:e8 +-----BEGIN CERTIFICATE----- +MIIEPzCCAyegAwIBAgIBATANBgkqhkiG9w0BAQUFADB+MQswCQYDVQQGEwJHQjEb +MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow +GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEkMCIGA1UEAwwbU2VjdXJlIENlcnRp +ZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVow +fjELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxJDAiBgNV +BAMMG1NlY3VyZSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAMBxM4KK0HDrc4eCQNUd5MvJDkKQ+d40uaG6EfQlhfPM +cm3ye5drswfxdySRXyWP9nQ95IDC+DwN879A6vfIUtFyb+/Iq0G4bi4XKpVpDM3S +HpR7LZQdqnXXs5jLrLxkU0C8j6ysNstcrbvd4JQX7NFc0L/vpZXJkMWwrPsbQ996 +CF23uPJAGysnnlDOXmWCiIxe004MeuoIkbY2qitC++rCoznl2yY4rYsK7hljxxwk +3wN42ubqwUcaCwtGCd0C/N7Lh1/XMGNooa7cMqG6vv5Eq2i2pRcV/b3Vp6ea5EQz +6YiO/O1R65NxTq0B50SOqy3LqP4BSUjwwN3HaNiS/j0CAwEAAaOBxzCBxDAdBgNV +HQ4EFgQUPNiTiMLAggnMAZkGkyDpnnAJY08wDgYDVR0PAQH/BAQDAgEGMA8GA1Ud +EwEB/wQFMAMBAf8wgYEGA1UdHwR6MHgwO6A5oDeGNWh0dHA6Ly9jcmwuY29tb2Rv +Y2EuY29tL1NlY3VyZUNlcnRpZmljYXRlU2VydmljZXMuY3JsMDmgN6A1hjNodHRw +Oi8vY3JsLmNvbW9kby5uZXQvU2VjdXJlQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmww +DQYJKoZIhvcNAQEFBQADggEBAIcBbSMdflsXfcFhMs+P5/OKlFlm4J4oqF7Tt/Q0 +5qo5spcWxYJvMqTpjOev/e/C6LlLqqP05tqNZSH7uoDrJiiFGv45jN5bBAS0VPmj +Z55B+glSzAVIqMk/IQQezkhr/IXownuvf7fM+F86/TXGDe+X3EyrEeFryzHRbPtI +gKvcnDe4IRRLDXE97IMzbtFuMhbsmMcWi1mmNKsFVy2T96oTy9IT4rcuO81rUBcJ +aD61JlfutuC23bkpgHl9j6PwpCikFcSF9CfUa7/lXORlAnZUtOM3ZiTTGWHIUhDl +izeauan5Hb/qmZJhlv8BzaFfDbxxvA6sCx1HRR3B7Hzs/Sk= +-----END CERTIFICATE----- + +# Issuer: CN=Trusted Certificate Services O=Comodo CA Limited +# Subject: CN=Trusted Certificate Services O=Comodo CA Limited +# Label: "Comodo Trusted Services root" +# Serial: 1 +# MD5 Fingerprint: 91:1b:3f:6e:cd:9e:ab:ee:07:fe:1f:71:d2:b3:61:27 +# SHA1 Fingerprint: e1:9f:e3:0e:8b:84:60:9e:80:9b:17:0d:72:a8:c5:ba:6e:14:09:bd +# SHA256 Fingerprint: 3f:06:e5:56:81:d4:96:f5:be:16:9e:b5:38:9f:9f:2b:8f:f6:1e:17:08:df:68:81:72:48:49:cd:5d:27:cb:69 +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIBATANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJHQjEb +MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow +GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDElMCMGA1UEAwwcVHJ1c3RlZCBDZXJ0 +aWZpY2F0ZSBTZXJ2aWNlczAeFw0wNDAxMDEwMDAwMDBaFw0yODEyMzEyMzU5NTla +MH8xCzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO +BgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoMEUNvbW9kbyBDQSBMaW1pdGVkMSUwIwYD +VQQDDBxUcnVzdGVkIENlcnRpZmljYXRlIFNlcnZpY2VzMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA33FvNlhTWvI2VFeAxHQIIO0Yfyod5jWaHiWsnOWW +fnJSoBVC21ndZHoa0Lh73TkVvFVIxO06AOoxEbrycXQaZ7jPM8yoMa+j49d/vzMt +TGo87IvDktJTdyR0nAducPy9C1t2ul/y/9c3S0pgePfw+spwtOpZqqPOSC+pw7IL +fhdyFgymBwwbOM/JYrc/oJOlh0Hyt3BAd9i+FHzjqMB6juljatEPmsbS9Is6FARW +1O24zG71++IsWL1/T2sr92AkWCTOJu80kTrV44HQsvAEAtdbtz6SrGsSivnkBbA7 +kUlcsutT6vifR4buv5XAwAaf0lteERv0xwQ1KdJVXOTt6wIDAQABo4HJMIHGMB0G +A1UdDgQWBBTFe1i97doladL3WRaoszLAeydb9DAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zCBgwYDVR0fBHwwejA8oDqgOIY2aHR0cDovL2NybC5jb21v +ZG9jYS5jb20vVHJ1c3RlZENlcnRpZmljYXRlU2VydmljZXMuY3JsMDqgOKA2hjRo +dHRwOi8vY3JsLmNvbW9kby5uZXQvVHJ1c3RlZENlcnRpZmljYXRlU2VydmljZXMu +Y3JsMA0GCSqGSIb3DQEBBQUAA4IBAQDIk4E7ibSvuIQSTI3S8NtwuleGFTQQuS9/ +HrCoiWChisJ3DFBKmwCL2Iv0QeLQg4pKHBQGsKNoBXAxMKdTmw7pSqBYaWcOrp32 +pSxBvzwGa+RZzG0Q8ZZvH9/0BAKkn0U+yNj6NkZEUD+Cl5EfKNsYEYwq5GWDVxIS +jBc/lDb+XbDABHcTuPQV1T84zJQ6VdCsmPW6AF/ghhmBeC8owH7TzEIK9a5QoNE+ +xqFx7D+gIIxmOom0jtTYsU0lR+4viMi14QVFwL4Ucd56/Y57fU0IlqUSc/Atyjcn +dBInTMu2l+nZrghtWjlA3QVHdWpaIbOjGM9O9y5Xt5hwXsjEeLBi +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root Certification Authority O=QuoVadis Limited OU=Root Certification Authority +# Subject: CN=QuoVadis Root Certification Authority O=QuoVadis Limited OU=Root Certification Authority +# Label: "QuoVadis Root CA" +# Serial: 985026699 +# MD5 Fingerprint: 27:de:36:fe:72:b7:00:03:00:9d:f4:f0:1e:6c:04:24 +# SHA1 Fingerprint: de:3f:40:bd:50:93:d3:9b:6c:60:f6:da:bc:07:62:01:00:89:76:c9 +# SHA256 Fingerprint: a4:5e:de:3b:bb:f0:9c:8a:e1:5c:72:ef:c0:72:68:d6:93:a2:1c:99:6f:d5:1e:67:ca:07:94:60:fd:6d:88:73 +-----BEGIN CERTIFICATE----- +MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJC +TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0 +aWZpY2F0aW9uIEF1dGhvcml0eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0 +aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAzMTkxODMzMzNaFw0yMTAzMTcxODMz +MzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUw +IwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVR +dW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Yp +li4kVEAkOPcahdxYTMukJ0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2D +rOpm2RgbaIr1VxqYuvXtdj182d6UajtLF8HVj71lODqV0D1VNk7feVcxKh7YWWVJ +WCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeLYzcS19Dsw3sgQUSj7cug +F+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWenAScOospU +xbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCC +Ak4wPQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVv +dmFkaXNvZmZzaG9yZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREw +ggENMIIBCQYJKwYBBAG+WAABMIH7MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNl +IG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBh +c3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFy +ZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh +Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYI +KwYBBQUHAgEWFmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3T +KbkGGew5Oanwl4Rqy+/fMIGuBgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rq +y+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1p +dGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYD +VQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6tlCL +MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSk +fnIYj9lofFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf8 +7C9TqnN7Az10buYWnuulLsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1R +cHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2xgI4JVrmcGmD+XcHXetwReNDWXcG31a0y +mQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi5upZIof4l/UO/erMkqQW +xFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi5nrQNiOK +SnQ2+Q== +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 2 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 2 O=QuoVadis Limited +# Label: "QuoVadis Root CA 2" +# Serial: 1289 +# MD5 Fingerprint: 5e:39:7b:dd:f8:ba:ec:82:e9:ac:62:ba:0c:54:00:2b +# SHA1 Fingerprint: ca:3a:fb:cf:12:40:36:4b:44:b2:16:20:88:80:48:39:19:93:7c:f7 +# SHA256 Fingerprint: 85:a0:dd:7d:d7:20:ad:b7:ff:05:f8:3d:54:2b:20:9d:c7:ff:45:28:f7:d6:77:b1:83:89:fe:a5:e5:c4:9e:86 +-----BEGIN CERTIFICATE----- +MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x +GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv +b3QgQ0EgMjAeFw0wNjExMjQxODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNV +BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W +YWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCa +GMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6XJxg +Fyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55J +WpzmM+Yklvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bB +rrcCaoF6qUWD4gXmuVbBlDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp ++ARz8un+XJiM9XOva7R+zdRcAitMOeGylZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1 +ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt66/3FsvbzSUr5R/7mp/i +Ucw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1JdxnwQ5hYIiz +PtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og +/zOhD7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UH +oycR7hYQe7xFSkyyBNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuI +yV77zGHcizN300QyNQliBJIWENieJ0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1Ud +EwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBQahGK8SEwzJQTU7tD2 +A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGUa6FJpEcwRTEL +MAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT +ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2f +BluornFdLwUvZ+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzn +g/iN/Ae42l9NLmeyhP3ZRPx3UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2Bl +fF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodmVjB3pjd4M1IQWK4/YY7yarHvGH5K +WWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK+JDSV6IZUaUtl0Ha +B0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrWIozc +hLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPR +TUIZ3Ph1WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWD +mbA4CD/pXvk1B+TJYm5Xf6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0Z +ohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y +4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8VCLAAVBpQ570su9t+Oza +8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 3" +# Serial: 1478 +# MD5 Fingerprint: 31:85:3c:62:94:97:63:b9:aa:fd:89:4e:af:6f:e0:cf +# SHA1 Fingerprint: 1f:49:14:f7:d8:74:95:1d:dd:ae:02:c0:be:fd:3a:2d:82:75:51:85 +# SHA256 Fingerprint: 18:f1:fc:7f:20:5d:f8:ad:dd:eb:7f:e0:07:dd:57:e3:af:37:5a:9c:4d:8d:73:54:6b:f4:f1:fe:d1:e1:8d:35 +-----BEGIN CERTIFICATE----- +MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x +GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv +b3QgQ0EgMzAeFw0wNjExMjQxOTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNV +BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W +YWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDM +V0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNggDhoB +4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUr +H556VOijKTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd +8lyyBTNvijbO0BNO/79KDDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9Cabwv +vWhDFlaJKjdhkf2mrk7AyxRllDdLkgbvBNDInIjbC3uBr7E9KsRlOni27tyAsdLT +mZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwpp5ijJUMv7/FfJuGITfhe +btfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8nT8KKdjc +T5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDt +WAEXMJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZ +c6tsgLjoC2SToJyMGf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A +4iLItLRkT9a6fUg+qGkM17uGcclzuD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYD +VR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHTBgkrBgEEAb5YAAMwgcUwgZMG +CCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmljYXRlIGNvbnN0 +aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0 +aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVu +dC4wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2Nw +czALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4G +A1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4ywLQoUmkRzBFMQswCQYDVQQGEwJC +TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UEAxMSUXVvVmFkaXMg +Um9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZVqyM0 +7ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSem +d1o417+shvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd ++LJ2w/w4E6oM3kJpK27zPOuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B +4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadN +t54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp8kokUvd0/bpO5qgdAm6x +DYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBCbjPsMZ57 +k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6s +zHXug/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0j +Wy10QJLZYxkNc91pvGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeT +mJlglFwjz1onl14LBQaTNx47aTbrqZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK +4SVhM7JZG+Ju1zdXtg2pEto= +-----END CERTIFICATE----- + +# Issuer: O=SECOM Trust.net OU=Security Communication RootCA1 +# Subject: O=SECOM Trust.net OU=Security Communication RootCA1 +# Label: "Security Communication Root CA" +# Serial: 0 +# MD5 Fingerprint: f1:bc:63:6a:54:e0:b5:27:f5:cd:e7:1a:e3:4d:6e:4a +# SHA1 Fingerprint: 36:b1:2b:49:f9:81:9e:d7:4c:9e:bc:38:0f:c6:56:8f:5d:ac:b2:f7 +# SHA256 Fingerprint: e7:5e:72:ed:9f:56:0e:ec:6e:b4:80:00:73:a4:3f:c3:ad:19:19:5a:39:22:82:01:78:95:97:4a:99:02:6b:6c +-----BEGIN CERTIFICATE----- +MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEY +MBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21t +dW5pY2F0aW9uIFJvb3RDQTEwHhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5 +WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYD +VQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw8yl8 +9f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJ +DKaVv0uMDPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9 +Ms+k2Y7CI9eNqPPYJayX5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/N +QV3Is00qVUarH9oe4kA92819uZKAnDfdDJZkndwi92SL32HeFZRSFaB9UslLqCHJ +xrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2JChzAgMBAAGjPzA9MB0G +A1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYwDwYDVR0T +AQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vG +kl3g0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfr +Uj94nK9NrvjVT8+amCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5 +Bw+SUEmK3TGXX8npN6o7WWWXlDLJs58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJU +JRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ6rBK+1YWc26sTfcioU+tHXot +RSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAiFL39vmwLAw== +-----END CERTIFICATE----- + +# Issuer: CN=Sonera Class2 CA O=Sonera +# Subject: CN=Sonera Class2 CA O=Sonera +# Label: "Sonera Class 2 Root CA" +# Serial: 29 +# MD5 Fingerprint: a3:ec:75:0f:2e:88:df:fa:48:01:4e:0b:5c:48:6f:fb +# SHA1 Fingerprint: 37:f7:6d:e6:07:7c:90:c5:b1:3e:93:1a:b7:41:10:b4:f2:e4:9a:27 +# SHA256 Fingerprint: 79:08:b4:03:14:c1:38:10:0b:51:8d:07:35:80:7f:fb:fc:f8:51:8a:00:95:33:71:05:ba:38:6b:15:3d:d9:27 +-----BEGIN CERTIFICATE----- +MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEP +MA0GA1UEChMGU29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAx +MDQwNjA3Mjk0MFoXDTIxMDQwNjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNV +BAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJhIENsYXNzMiBDQTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3/Ei9vX+ALTU74W+o +Z6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybTdXnt +5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s +3TmVToMGf+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2Ej +vOr7nQKV0ba5cTppCD8PtOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu +8nYybieDwnPz3BjotJPqdURrBGAgcVeHnfO+oJAjPYok4doh28MCAwEAAaMzMDEw +DwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITTXjwwCwYDVR0PBAQDAgEG +MA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt0jSv9zil +zqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/ +3DEIcbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvD +FNr450kkkdAdavphOe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6 +Tk6ezAyNlNzZRZxe7EJQY670XcSxEtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2 +ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLHllpwrN9M +-----END CERTIFICATE----- + +# Issuer: CN=Staat der Nederlanden Root CA O=Staat der Nederlanden +# Subject: CN=Staat der Nederlanden Root CA O=Staat der Nederlanden +# Label: "Staat der Nederlanden Root CA" +# Serial: 10000010 +# MD5 Fingerprint: 60:84:7c:5a:ce:db:0c:d4:cb:a7:e9:fe:02:c6:a9:c0 +# SHA1 Fingerprint: 10:1d:fa:3f:d5:0b:cb:bb:9b:b5:60:0c:19:55:a4:1a:f4:73:3a:04 +# SHA256 Fingerprint: d4:1d:82:9e:8c:16:59:82:2a:f9:3f:ce:62:bf:fc:de:26:4f:c8:4e:8b:95:0c:5f:f2:75:d0:52:35:46:95:a3 +-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgIEAJiWijANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJO +TDEeMBwGA1UEChMVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSYwJAYDVQQDEx1TdGFh +dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQTAeFw0wMjEyMTcwOTIzNDlaFw0xNTEy +MTYwOTE1MzhaMFUxCzAJBgNVBAYTAk5MMR4wHAYDVQQKExVTdGFhdCBkZXIgTmVk +ZXJsYW5kZW4xJjAkBgNVBAMTHVN0YWF0IGRlciBOZWRlcmxhbmRlbiBSb290IENB +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmNK1URF6gaYUmHFtvszn +ExvWJw56s2oYHLZhWtVhCb/ekBPHZ+7d89rFDBKeNVU+LCeIQGv33N0iYfXCxw71 +9tV2U02PjLwYdjeFnejKScfST5gTCaI+Ioicf9byEGW07l8Y1Rfj+MX94p2i71MO +hXeiD+EwR+4A5zN9RGcaC1Hoi6CeUJhoNFIfLm0B8mBF8jHrqTFoKbt6QZ7GGX+U +tFE5A3+y3qcym7RHjm+0Sq7lr7HcsBthvJly3uSJt3omXdozSVtSnA71iq3DuD3o +BmrC1SoLbHuEvVYFy4ZlkuxEK7COudxwC0barbxjiDn622r+I/q85Ej0ZytqERAh +SQIDAQABo4GRMIGOMAwGA1UdEwQFMAMBAf8wTwYDVR0gBEgwRjBEBgRVHSAAMDww +OgYIKwYBBQUHAgEWLmh0dHA6Ly93d3cucGtpb3ZlcmhlaWQubmwvcG9saWNpZXMv +cm9vdC1wb2xpY3kwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSofeu8Y6R0E3QA +7Jbg0zTBLL9s+DANBgkqhkiG9w0BAQUFAAOCAQEABYSHVXQ2YcG70dTGFagTtJ+k +/rvuFbQvBgwp8qiSpGEN/KtcCFtREytNwiphyPgJWPwtArI5fZlmgb9uXJVFIGzm +eafR2Bwp/MIgJ1HI8XxdNGdphREwxgDS1/PTfLbwMVcoEoJz6TMvplW0C5GUR5z6 +u3pCMuiufi3IvKwUv9kP2Vv8wfl6leF9fpb8cbDCTMjfRTTJzg3ynGQI0DvDKcWy +7ZAEwbEpkcUwb8GpcjPM/l0WFywRaed+/sWDCN+83CI6LiBpIzlWYGeQiy52OfsR +iJf2fL1LuCAWZwWN4jvBcj+UlTfHXbme2JOhF4//DGYVwSR8MnwDHTuhWEUykw== +-----END CERTIFICATE----- + +# Issuer: CN=UTN - DATACorp SGC O=The USERTRUST Network OU=http://www.usertrust.com +# Subject: CN=UTN - DATACorp SGC O=The USERTRUST Network OU=http://www.usertrust.com +# Label: "UTN DATACorp SGC Root CA" +# Serial: 91374294542884689855167577680241077609 +# MD5 Fingerprint: b3:a5:3e:77:21:6d:ac:4a:c0:c9:fb:d5:41:3d:ca:06 +# SHA1 Fingerprint: 58:11:9f:0e:12:82:87:ea:50:fd:d9:87:45:6f:4f:78:dc:fa:d6:d4 +# SHA256 Fingerprint: 85:fb:2f:91:dd:12:27:5a:01:45:b6:36:53:4f:84:02:4a:d6:8b:69:b8:ee:88:68:4f:f7:11:37:58:05:b3:48 +-----BEGIN CERTIFICATE----- +MIIEXjCCA0agAwIBAgIQRL4Mi1AAIbQR0ypoBqmtaTANBgkqhkiG9w0BAQUFADCB +kzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug +Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho +dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZBgNVBAMTElVUTiAtIERBVEFDb3Jw +IFNHQzAeFw05OTA2MjQxODU3MjFaFw0xOTA2MjQxOTA2MzBaMIGTMQswCQYDVQQG +EwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYD +VQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cu +dXNlcnRydXN0LmNvbTEbMBkGA1UEAxMSVVROIC0gREFUQUNvcnAgU0dDMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLit6 +E5Qbvfa2gI5lBZMAHryv4g+OGQ0SR+ysraP6LnD43m77VkIVni5c7yPeIbkFdicZ +D0/Ww5y0vpQZY/KmEQrrU0icvvIpOxboGqBMpsn0GFlowHDyUwDAXlCCpVZvNvlK +4ESGoE1O1kduSUrLZ9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA9P4yPykq +lXvY8qdOD1R8oQ2AswkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulW +bfXv33i+Ybqypa4ETLyorGkVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQAB +o4GrMIGoMAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRT +MtGzz3/64PGgXYVOktKeRR20TzA9BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY3Js +LnVzZXJ0cnVzdC5jb20vVVROLURBVEFDb3JwU0dDLmNybDAqBgNVHSUEIzAhBggr +BgEFBQcDAQYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GCSqGSIb3DQEBBQUAA4IB +AQAnNZcAiosovcYzMB4p/OL31ZjUQLtgyr+rFywJNn9Q+kHcrpY6CiM+iVnJowft +Gzet/Hy+UUla3joKVAgWRcKZsYfNjGjgaQPpxE6YsjuMFrMOoAyYUJuTqXAJyCyj +j98C5OBxOvG0I3KgqgHf35g+FFCgMSa9KOlaMCZ1+XtgHI3zzVAmbQQnmt/VDUVH +KWss5nbZqSl9Mt3JNjy9rjXxEZ4du5A/EkdOjtd+D2JzHVImOBwYSf0wdJrE5SIv +2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jFVkwPDPafepE39peC4N1xaf92P2BNPM/3 +mfnGV/TJVTl4uix5yaaIK/QI +-----END CERTIFICATE----- + +# Issuer: CN=UTN-USERFirst-Hardware O=The USERTRUST Network OU=http://www.usertrust.com +# Subject: CN=UTN-USERFirst-Hardware O=The USERTRUST Network OU=http://www.usertrust.com +# Label: "UTN USERFirst Hardware Root CA" +# Serial: 91374294542884704022267039221184531197 +# MD5 Fingerprint: 4c:56:41:e5:0d:bb:2b:e8:ca:a3:ed:18:08:ad:43:39 +# SHA1 Fingerprint: 04:83:ed:33:99:ac:36:08:05:87:22:ed:bc:5e:46:00:e3:be:f9:d7 +# SHA256 Fingerprint: 6e:a5:47:41:d0:04:66:7e:ed:1b:48:16:63:4a:a3:a7:9e:6e:4b:96:95:0f:82:79:da:fc:8d:9b:d8:81:21:37 +-----BEGIN CERTIFICATE----- +MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCB +lzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug +Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho +dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3Qt +SGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgxOTIyWjCBlzELMAkG +A1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEe +MBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8v +d3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdh +cmUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn +0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlIwrthdBKWHTxqctU8EGc6Oe0rE81m65UJ +M6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFdtqdt++BxF2uiiPsA3/4a +MXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8i4fDidNd +oI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqI +DsjfPe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9Ksy +oUhbAgMBAAGjgbkwgbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFKFyXyYbKJhDlV0HN9WFlp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0 +dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNFUkZpcnN0LUhhcmR3YXJlLmNy +bDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUFBwMGBggrBgEF +BQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM +//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28Gpgoiskli +CE7/yMgUsogWXecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gE +CJChicsZUN/KHAG8HQQZexB2lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t +3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kniCrVWFCVH/A7HFe7fRQ5YiuayZSS +KqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67nfhmqA== +-----END CERTIFICATE----- + +# Issuer: CN=Chambers of Commerce Root O=AC Camerfirma SA CIF A82743287 OU=http://www.chambersign.org +# Subject: CN=Chambers of Commerce Root O=AC Camerfirma SA CIF A82743287 OU=http://www.chambersign.org +# Label: "Camerfirma Chambers of Commerce Root" +# Serial: 0 +# MD5 Fingerprint: b0:01:ee:14:d9:af:29:18:94:76:8e:f1:69:33:2a:84 +# SHA1 Fingerprint: 6e:3a:55:a4:19:0c:19:5c:93:84:3c:c0:db:72:2e:31:30:61:f0:b1 +# SHA256 Fingerprint: 0c:25:8a:12:a5:67:4a:ef:25:f2:8b:a7:dc:fa:ec:ee:a3:48:e5:41:e6:f5:cc:4e:e6:3b:71:b3:61:60:6a:c3 +-----BEGIN CERTIFICATE----- +MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEn +MCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQL +ExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMg +b2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAxNjEzNDNaFw0zNzA5MzAxNjEzNDRa +MH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZpcm1hIFNBIENJRiBB +ODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3JnMSIw +IAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0B +AQEFAAOCAQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtb +unXF/KGIJPov7coISjlUxFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0d +BmpAPrMMhe5cG3nCYsS4No41XQEMIwRHNaqbYE6gZj3LJgqcQKH0XZi/caulAGgq +7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jWDA+wWFjbw2Y3npuRVDM3 +0pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFVd9oKDMyX +roDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIG +A1UdEwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5j +aGFtYmVyc2lnbi5vcmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p +26EpW1eLTXYGduHRooowDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIA +BzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hhbWJlcnNpZ24ub3JnMCcGA1Ud +EgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYDVR0gBFEwTzBN +BgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz +aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEB +AAxBl8IahsAifJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZd +p0AJPaxJRUXcLo0waLIJuvvDL8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi +1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wNUPf6s+xCX6ndbcj0dc97wXImsQEc +XCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/nADydb47kMgkdTXg0 +eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1erfu +tGWaIZDgqtCYvDi1czyL+Nw= +-----END CERTIFICATE----- + +# Issuer: CN=Global Chambersign Root O=AC Camerfirma SA CIF A82743287 OU=http://www.chambersign.org +# Subject: CN=Global Chambersign Root O=AC Camerfirma SA CIF A82743287 OU=http://www.chambersign.org +# Label: "Camerfirma Global Chambersign Root" +# Serial: 0 +# MD5 Fingerprint: c5:e6:7b:bf:06:d0:4f:43:ed:c4:7a:65:8a:fb:6b:19 +# SHA1 Fingerprint: 33:9b:6b:14:50:24:9b:55:7a:01:87:72:84:d9:e0:2f:c3:d2:d8:e9 +# SHA256 Fingerprint: ef:3c:b4:17:fc:8e:bf:6f:97:87:6c:9e:4e:ce:39:de:1e:a5:fe:64:91:41:d1:02:8b:7d:11:c0:b2:29:8c:ed +-----BEGIN CERTIFICATE----- +MIIExTCCA62gAwIBAgIBADANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJFVTEn +MCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQL +ExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEgMB4GA1UEAxMXR2xvYmFsIENo +YW1iZXJzaWduIFJvb3QwHhcNMDMwOTMwMTYxNDE4WhcNMzcwOTMwMTYxNDE4WjB9 +MQswCQYDVQQGEwJFVTEnMCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgy +NzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEgMB4G +A1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwggEgMA0GCSqGSIb3DQEBAQUA +A4IBDQAwggEIAoIBAQCicKLQn0KuWxfH2H3PFIP8T8mhtxOviteePgQKkotgVvq0 +Mi+ITaFgCPS3CU6gSS9J1tPfnZdan5QEcOw/Wdm3zGaLmFIoCQLfxS+EjXqXd7/s +QJ0lcqu1PzKY+7e3/HKE5TWH+VX6ox8Oby4o3Wmg2UIQxvi1RMLQQ3/bvOSiPGpV +eAp3qdjqGTK3L/5cPxvusZjsyq16aUXjlg9V9ubtdepl6DJWk0aJqCWKZQbua795 +B9Dxt6/tLE2Su8CoX6dnfQTyFQhwrJLWfQTSM/tMtgsL+xrJxI0DqX5c8lCrEqWh +z0hQpe/SyBoT+rB/sYIcd2oPX9wLlY/vQ37mRQklAgEDo4IBUDCCAUwwEgYDVR0T +AQH/BAgwBgEB/wIBDDA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3JsLmNoYW1i +ZXJzaWduLm9yZy9jaGFtYmVyc2lnbnJvb3QuY3JsMB0GA1UdDgQWBBRDnDafsJ4w +TcbOX60Qq+UDpfqpFDAOBgNVHQ8BAf8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgAH +MCoGA1UdEQQjMCGBH2NoYW1iZXJzaWducm9vdEBjaGFtYmVyc2lnbi5vcmcwKgYD +VR0SBCMwIYEfY2hhbWJlcnNpZ25yb290QGNoYW1iZXJzaWduLm9yZzBbBgNVHSAE +VDBSMFAGCysGAQQBgYcuCgEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly9jcHMuY2hh +bWJlcnNpZ24ub3JnL2Nwcy9jaGFtYmVyc2lnbnJvb3QuaHRtbDANBgkqhkiG9w0B +AQUFAAOCAQEAPDtwkfkEVCeR4e3t/mh/YV3lQWVPMvEYBZRqHN4fcNs+ezICNLUM +bKGKfKX0j//U2K0X1S0E0T9YgOKBWYi+wONGkyT+kL0mojAt6JcmVzWJdJYY9hXi +ryQZVgICsroPFOrGimbBhkVVi76SvpykBMdJPJ7oKXqJ1/6v/2j1pReQvayZzKWG +VwlnRtvWFsJG8eSpUPWP0ZIV018+xgBJOm5YstHRJw0lyDL4IBHNfTIzSJRUTN3c +ecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREest2d/ +AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A== +-----END CERTIFICATE----- + +# Issuer: CN=NetLock Kozjegyzoi (Class A) Tanusitvanykiado O=NetLock Halozatbiztonsagi Kft. OU=Tanusitvanykiadok +# Subject: CN=NetLock Kozjegyzoi (Class A) Tanusitvanykiado O=NetLock Halozatbiztonsagi Kft. OU=Tanusitvanykiadok +# Label: "NetLock Notary (Class A) Root" +# Serial: 259 +# MD5 Fingerprint: 86:38:6d:5e:49:63:6c:85:5c:db:6d:dc:94:b7:d0:f7 +# SHA1 Fingerprint: ac:ed:5f:65:53:fd:25:ce:01:5f:1f:7a:48:3b:6a:74:9f:61:78:c6 +# SHA256 Fingerprint: 7f:12:cd:5f:7e:5e:29:0e:c7:d8:51:79:d5:b7:2c:20:a5:be:75:08:ff:db:5b:f8:1a:b9:68:4a:7f:c9:f6:67 +-----BEGIN CERTIFICATE----- +MIIGfTCCBWWgAwIBAgICAQMwDQYJKoZIhvcNAQEEBQAwga8xCzAJBgNVBAYTAkhV +MRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMe +TmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0 +dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBLb3pqZWd5em9pIChDbGFzcyBB +KSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNDIzMTQ0N1oXDTE5MDIxOTIzMTQ0 +N1owga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQHEwhC +dWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQu +MRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBL +b3pqZWd5em9pIChDbGFzcyBBKSBUYW51c2l0dmFueWtpYWRvMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvHSMD7tM9DceqQWC2ObhbHDqeLVu0ThEDaiD +zl3S1tWBxdRL51uUcCbbO51qTGL3cfNk1mE7PetzozfZz+qMkjvN9wfcZnSX9EUi +3fRc4L9t875lM+QVOr/bmJBVOMTtplVjC7B4BPTjbsE/jvxReB+SnoPC/tmwqcm8 +WgD/qaiYdPv2LD4VOQ22BFWoDpggQrOxJa1+mm9dU7GrDPzr4PN6s6iz/0b2Y6LY +Oph7tqyF/7AlT3Rj5xMHpQqPBffAZG9+pyeAlt7ULoZgx2srXnN7F+eRP2QM2Esi +NCubMvJIH5+hCoR64sKtlz2O1cH5VqNQ6ca0+pii7pXmKgOM3wIDAQABo4ICnzCC +ApswDgYDVR0PAQH/BAQDAgAGMBIGA1UdEwEB/wQIMAYBAf8CAQQwEQYJYIZIAYb4 +QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1GSUdZRUxFTSEgRXplbiB0 +YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pvbGdhbHRhdGFz +aSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQu +IEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtm +ZWxlbG9zc2VnLWJpenRvc2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMg +ZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUgYXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVs +amFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJhc2EgbWVndGFsYWxoYXRv +IGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBhIGh0dHBzOi8vd3d3 +Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVub3J6 +ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1 +YW5jZSBhbmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3Qg +dG8gdGhlIE5ldExvY2sgQ1BTIGF2YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRs +b2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBjcHNAbmV0bG9jay5uZXQuMA0G +CSqGSIb3DQEBBAUAA4IBAQBIJEb3ulZv+sgoA0BO5TE5ayZrU3/b39/zcT0mwBQO +xmd7I6gMc90Bu8bKbjc5VdXHjFYgDigKDtIqpLBJUsY4B/6+CgmM0ZjPytoUMaFP +0jn8DxEsQ8Pdq5PHVT5HfBgaANzze9jyf1JsIPQLX2lS9O74silg6+NJMSEN1rUQ +QeJBCWziGppWS3cC9qCbmieH6FUpccKQn0V4GuEVZD3QDtigdp+uxdAu6tYPVuxk +f1qbFFgBJ34TUMdrKuZoPL9coAob4Q566eKAw+np9v1sEZ7Q5SgnK1QyQhSCdeZK +8CtmdWOMovsEPoMOmzbwGOQmIMOM8CgHrTwXZoi1/baI +-----END CERTIFICATE----- + +# Issuer: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com +# Subject: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com +# Label: "XRamp Global CA Root" +# Serial: 107108908803651509692980124233745014957 +# MD5 Fingerprint: a1:0b:44:b3:ca:10:d8:00:6e:9d:0f:d8:0f:92:0a:d1 +# SHA1 Fingerprint: b8:01:86:d1:eb:9c:86:a5:41:04:cf:30:54:f3:4c:52:b7:e5:58:c6 +# SHA256 Fingerprint: ce:cd:dc:90:50:99:d8:da:df:c5:b1:d2:09:b7:37:cb:e2:c1:8c:fb:2c:10:c0:ff:0b:cf:0d:32:86:fc:1a:a2 +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCB +gjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEk +MCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRY +UmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQxMTAxMTcx +NDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3 +dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2Vy +dmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS6 +38eMpSe2OAtp87ZOqCwuIR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCP +KZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMxfoArtYzAQDsRhtDLooY2YKTVMIJt2W7Q +DxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FEzG+gSqmUsE3a56k0enI4 +qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqsAxcZZPRa +JSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNVi +PvryxS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0P +BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASs +jVy16bYbMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0 +eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQEwDQYJKoZIhvcNAQEFBQAD +ggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc/Kh4ZzXxHfAR +vbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt +qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLa +IR9NmXmd4c8nnxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSy +i6mx5O+aGtA9aZnuqCij4Tyz8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQ +O+7ETPTsJ3xCwnR8gooJybQDJbw= +-----END CERTIFICATE----- + +# Issuer: O=The Go Daddy Group, Inc. OU=Go Daddy Class 2 Certification Authority +# Subject: O=The Go Daddy Group, Inc. OU=Go Daddy Class 2 Certification Authority +# Label: "Go Daddy Class 2 CA" +# Serial: 0 +# MD5 Fingerprint: 91:de:06:25:ab:da:fd:32:17:0c:bb:25:17:2a:84:67 +# SHA1 Fingerprint: 27:96:ba:e6:3f:18:01:e2:77:26:1b:a0:d7:77:70:02:8f:20:ee:e4 +# SHA256 Fingerprint: c3:84:6b:f2:4b:9e:93:ca:64:27:4c:0e:c6:7c:1e:cc:5e:02:4f:fc:ac:d2:d7:40:19:35:0e:81:fe:54:6a:e4 +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh +MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE +YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3 +MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo +ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg +MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN +ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA +PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w +wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi +EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY +avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+ +YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE +sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h +/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5 +IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD +ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy +OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P +TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ +HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER +dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf +ReYNnyicsbkqWletNw+vHX/bvZ8= +-----END CERTIFICATE----- + +# Issuer: O=Starfield Technologies, Inc. OU=Starfield Class 2 Certification Authority +# Subject: O=Starfield Technologies, Inc. OU=Starfield Class 2 Certification Authority +# Label: "Starfield Class 2 CA" +# Serial: 0 +# MD5 Fingerprint: 32:4a:4b:bb:c8:63:69:9b:be:74:9a:c6:dd:1d:46:24 +# SHA1 Fingerprint: ad:7e:1c:28:b0:64:ef:8f:60:03:40:20:14:c3:d0:e3:37:0e:b5:8a +# SHA256 Fingerprint: 14:65:fa:20:53:97:b8:76:fa:a6:f0:a9:95:8e:55:90:e4:0f:cc:7f:aa:4f:b7:c2:c8:67:75:21:fb:5f:b6:58 +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl +MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp +U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw +NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE +ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp +ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3 +DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf +8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN ++lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0 +X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa +K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA +1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G +A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR +zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0 +YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD +bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w +DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3 +L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D +eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl +xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp +VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY +WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q= +-----END CERTIFICATE----- + +# Issuer: CN=StartCom Certification Authority O=StartCom Ltd. OU=Secure Digital Certificate Signing +# Subject: CN=StartCom Certification Authority O=StartCom Ltd. OU=Secure Digital Certificate Signing +# Label: "StartCom Certification Authority" +# Serial: 1 +# MD5 Fingerprint: 22:4d:8f:8a:fc:f7:35:c2:bb:57:34:90:7b:8b:22:16 +# SHA1 Fingerprint: 3e:2b:f7:f2:03:1b:96:f3:8c:e6:c4:d8:a8:5d:3e:2d:58:47:6a:0f +# SHA256 Fingerprint: c7:66:a9:be:f2:d4:07:1c:86:3a:31:aa:49:20:e8:13:b2:d1:98:60:8c:b7:b7:cf:e2:11:43:b8:36:df:09:ea +-----BEGIN CERTIFICATE----- +MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW +MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg +Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM2WhcNMzYwOTE3MTk0NjM2WjB9 +MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi +U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh +cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk +pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf +OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C +Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT +Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi +HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM +Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w ++2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+ +Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3 +Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B +26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID +AQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE +FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9j +ZXJ0LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3Js +LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFM +BgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUHAgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0 +Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRwOi8vY2VydC5zdGFy +dGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYgU3Rh +cnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlh +YmlsaXR5LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2Yg +dGhlIFN0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFp +bGFibGUgYXQgaHR0cDovL2NlcnQuc3RhcnRjb20ub3JnL3BvbGljeS5wZGYwEQYJ +YIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNT +TCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOCAgEAFmyZ +9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8 +jhvh3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUW +FjgKXlf2Ysd6AgXmvB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJz +ewT4F+irsfMuXGRuczE6Eri8sxHkfY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1 +ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3fsNrarnDy0RLrHiQi+fHLB5L +EUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZEoalHmdkrQYu +L6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq +yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuC +O3NJo2pXh5Tl1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6V +um0ABj6y6koQOdjQK/W/7HW/lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkySh +NOsF/5oirpt9P/FlUQqmMGqz9IgcgA38corog14= +-----END CERTIFICATE----- + +# Issuer: O=Government Root Certification Authority +# Subject: O=Government Root Certification Authority +# Label: "Taiwan GRCA" +# Serial: 42023070807708724159991140556527066870 +# MD5 Fingerprint: 37:85:44:53:32:45:1f:20:f0:f3:95:e1:25:c4:43:4e +# SHA1 Fingerprint: f4:8b:11:bf:de:ab:be:94:54:20:71:e6:41:de:6b:be:88:2b:40:b9 +# SHA256 Fingerprint: 76:00:29:5e:ef:e8:5b:9e:1f:d6:24:db:76:06:2a:aa:ae:59:81:8a:54:d2:77:4c:d4:c0:b2:c0:11:31:e1:b3 +-----BEGIN CERTIFICATE----- +MIIFcjCCA1qgAwIBAgIQH51ZWtcvwgZEpYAIaeNe9jANBgkqhkiG9w0BAQUFADA/ +MQswCQYDVQQGEwJUVzEwMC4GA1UECgwnR292ZXJubWVudCBSb290IENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5MB4XDTAyMTIwNTEzMjMzM1oXDTMyMTIwNTEzMjMzM1ow +PzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dvdmVybm1lbnQgUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB +AJoluOzMonWoe/fOW1mKydGGEghU7Jzy50b2iPN86aXfTEc2pBsBHH8eV4qNw8XR +IePaJD9IK/ufLqGU5ywck9G/GwGHU5nOp/UKIXZ3/6m3xnOUT0b3EEk3+qhZSV1q +gQdW8or5BtD3cCJNtLdBuTK4sfCxw5w/cP1T3YGq2GN49thTbqGsaoQkclSGxtKy +yhwOeYHWtXBiCAEuTk8O1RGvqa/lmr/czIdtJuTJV6L7lvnM4T9TjGxMfptTCAts +F/tnyMKtsc2AtJfcdgEWFelq16TheEfOhtX7MfP6Mb40qij7cEwdScevLJ1tZqa2 +jWR+tSBqnTuBto9AAGdLiYa4zGX+FVPpBMHWXx1E1wovJ5pGfaENda1UhhXcSTvx +ls4Pm6Dso3pdvtUqdULle96ltqqvKKyskKw4t9VoNSZ63Pc78/1Fm9G7Q3hub/FC +VGqY8A2tl+lSXunVanLeavcbYBT0peS2cWeqH+riTcFCQP5nRhc4L0c/cZyu5SHK +YS1tB6iEfC3uUSXxY5Ce/eFXiGvviiNtsea9P63RPZYLhY3Naye7twWb7LuRqQoH +EgKXTiCQ8P8NHuJBO9NAOueNXdpm5AKwB1KYXA6OM5zCppX7VRluTI6uSw+9wThN +Xo+EHWbNxWCWtFJaBYmOlXqYwZE8lSOyDvR5tMl8wUohAgMBAAGjajBoMB0GA1Ud +DgQWBBTMzO/MKWCkO7GStjz6MmKPrCUVOzAMBgNVHRMEBTADAQH/MDkGBGcqBwAE +MTAvMC0CAQAwCQYFKw4DAhoFADAHBgVnKgMAAAQUA5vwIhP/lSg209yewDL7MTqK +UWUwDQYJKoZIhvcNAQEFBQADggIBAECASvomyc5eMN1PhnR2WPWus4MzeKR6dBcZ +TulStbngCnRiqmjKeKBMmo4sIy7VahIkv9Ro04rQ2JyftB8M3jh+Vzj8jeJPXgyf +qzvS/3WXy6TjZwj/5cAWtUgBfen5Cv8b5Wppv3ghqMKnI6mGq3ZW6A4M9hPdKmaK +ZEk9GhiHkASfQlK3T8v+R0F2Ne//AHY2RTKbxkaFXeIksB7jSJaYV0eUVXoPQbFE +JPPB/hprv4j9wabak2BegUqZIJxIZhm1AHlUD7gsL0u8qV1bYH+Mh6XgUmMqvtg7 +hUAV/h62ZT/FS9p+tXo1KaMuephgIqP0fSdOLeq0dDzpD6QzDxARvBMB1uUO07+1 +EqLhRSPAzAhuYbeJq4PjJB7mXQfnHyA+z2fI56wwbSdLaG5LKlwCCDTb+HbkZ6Mm +nD+iMsJKxYEYMRBWqoTvLQr/uB930r+lWKBi5NdLkXWNiYCYfm3LU05er/ayl4WX +udpVBrkk7tfGOB5jGxI7leFYrPLfhNVfmS8NVVvmONsuP3LpSIXLuykTjx44Vbnz +ssQwmSNOXfJIoRIM3BKQCZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDe +LMDDav7v3Aun+kbfYNucpllQdSNpc5Oy+fwC00fmcc4QAu4njIT/rEUNE1yDMuAl +pYYsfPQS +-----END CERTIFICATE----- + +# Issuer: CN=Swisscom Root CA 1 O=Swisscom OU=Digital Certificate Services +# Subject: CN=Swisscom Root CA 1 O=Swisscom OU=Digital Certificate Services +# Label: "Swisscom Root CA 1" +# Serial: 122348795730808398873664200247279986742 +# MD5 Fingerprint: f8:38:7c:77:88:df:2c:16:68:2e:c2:e2:52:4b:b8:f9 +# SHA1 Fingerprint: 5f:3a:fc:0a:8b:64:f6:86:67:34:74:df:7e:a9:a2:fe:f9:fa:7a:51 +# SHA256 Fingerprint: 21:db:20:12:36:60:bb:2e:d4:18:20:5d:a1:1e:e7:a8:5a:65:e2:bc:6e:55:b5:af:7e:78:99:c8:a2:66:d9:2e +-----BEGIN CERTIFICATE----- +MIIF2TCCA8GgAwIBAgIQXAuFXAvnWUHfV8w/f52oNjANBgkqhkiG9w0BAQUFADBk +MQswCQYDVQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0 +YWwgQ2VydGlmaWNhdGUgU2VydmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3Qg +Q0EgMTAeFw0wNTA4MTgxMjA2MjBaFw0yNTA4MTgyMjA2MjBaMGQxCzAJBgNVBAYT +AmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZp +Y2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAxMIICIjAN +BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0LmwqAzZuz8h+BvVM5OAFmUgdbI9 +m2BtRsiMMW8Xw/qabFbtPMWRV8PNq5ZJkCoZSx6jbVfd8StiKHVFXqrWW/oLJdih +FvkcxC7mlSpnzNApbjyFNDhhSbEAn9Y6cV9Nbc5fuankiX9qUvrKm/LcqfmdmUc/ +TilftKaNXXsLmREDA/7n29uj/x2lzZAeAR81sH8A25Bvxn570e56eqeqDFdvpG3F +EzuwpdntMhy0XmeLVNxzh+XTF3xmUHJd1BpYwdnP2IkCb6dJtDZd0KTeByy2dbco +kdaXvij1mB7qWybJvbCXc9qukSbraMH5ORXWZ0sKbU/Lz7DkQnGMU3nn7uHbHaBu +HYwadzVcFh4rUx80i9Fs/PJnB3r1re3WmquhsUvhzDdf/X/NTa64H5xD+SpYVUNF +vJbNcA78yeNmuk6NO4HLFWR7uZToXTNShXEuT46iBhFRyePLoW4xCGQMwtI89Tbo +19AOeCMgkckkKmUpWyL3Ic6DXqTz3kvTaI9GdVyDCW4pa8RwjPWd1yAv/0bSKzjC +L3UcPX7ape8eYIVpQtPM+GP+HkM5haa2Y0EQs3MevNP6yn0WR+Kn1dCjigoIlmJW +bjTb2QK5MHXjBNLnj8KwEUAKrNVxAmKLMb7dxiNYMUJDLXT5xp6mig/p/r+D5kNX +JLrvRjSq1xIBOO0CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0hBBYw +FDASBgdghXQBUwABBgdghXQBUwABMBIGA1UdEwEB/wQIMAYBAf8CAQcwHwYDVR0j +BBgwFoAUAyUv3m+CATpcLNwroWm1Z9SM0/0wHQYDVR0OBBYEFAMlL95vggE6XCzc +K6FptWfUjNP9MA0GCSqGSIb3DQEBBQUAA4ICAQA1EMvspgQNDQ/NwNurqPKIlwzf +ky9NfEBWMXrrpA9gzXrzvsMnjgM+pN0S734edAY8PzHyHHuRMSG08NBsl9Tpl7Ik +Vh5WwzW9iAUPWxAaZOHHgjD5Mq2eUCzneAXQMbFamIp1TpBcahQq4FJHgmDmHtqB +sfsUC1rxn9KVuj7QG9YVHaO+htXbD8BJZLsuUBlL0iT43R4HVtA4oJVwIHaM190e +3p9xxCPvgxNcoyQVTSlAPGrEqdi3pkSlDfTgnXceQHAm/NrZNuR55LU/vJtlvrsR +ls/bxig5OgjOR1tTWsWZ/l2p3e9M1MalrQLmjAcSHm8D0W+go/MpvRLHUKKwf4ip +mXeascClOS5cfGniLLDqN2qk4Vrh9VDlg++luyqI54zb/W1elxmofmZ1a3Hqv7HH +b6D0jqTsNFFbjCYDcKF31QESVwA12yPeDooomf2xEG9L/zgtYE4snOtnta1J7ksf +rK/7DZBaZmBwXarNeNQk7shBoJMBkpxqnvy5JMWzFYJ+vq6VK+uxwNrjAWALXmms +hFZhvnEX/h0TD/7Gh0Xp/jKgGg0TpJRVcaUWi7rKibCyx/yP2FS1k2Kdzs9Z+z0Y +zirLNRWCXf9UIltxUvu3yf5gmwBBZPCqKuy2QkPOiWaByIufOVQDJdMWNY6E0F/6 +MBr1mmz0DlP5OlvRHA== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root CA" +# Serial: 17154717934120587862167794914071425081 +# MD5 Fingerprint: 87:ce:0b:7b:2a:0e:49:00:e1:58:71:9b:37:a8:93:72 +# SHA1 Fingerprint: 05:63:b8:63:0d:62:d7:5a:bb:c8:ab:1e:4b:df:b5:a8:99:b2:4d:43 +# SHA256 Fingerprint: 3e:90:99:b5:01:5e:8f:48:6c:00:bc:ea:9d:11:1e:e7:21:fa:ba:35:5a:89:bc:f1:df:69:56:1e:3d:c6:32:5c +-----BEGIN CERTIFICATE----- +MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c +JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP +mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+ +wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4 +VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/ +AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB +AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun +pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC +dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf +fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm +NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx +H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe ++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root CA" +# Serial: 10944719598952040374951832963794454346 +# MD5 Fingerprint: 79:e4:a9:84:0d:7d:3a:96:d7:c0:4f:e2:43:4c:89:2e +# SHA1 Fingerprint: a8:98:5d:3a:65:e5:e5:c4:b2:d7:d6:6d:40:c6:dd:2f:b1:9c:54:36 +# SHA256 Fingerprint: 43:48:a0:e9:44:4c:78:cb:26:5e:05:8d:5e:89:44:b4:d8:4f:96:62:bd:26:db:25:7f:89:34:a4:43:c7:01:61 +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD +QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB +CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 +nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt +43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P +T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 +gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO +BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR +TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw +DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr +hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg +06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF +PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls +YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert High Assurance EV Root CA O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert High Assurance EV Root CA O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert High Assurance EV Root CA" +# Serial: 3553400076410547919724730734378100087 +# MD5 Fingerprint: d4:74:de:57:5c:39:b2:d3:9c:85:83:c5:c0:65:49:8a +# SHA1 Fingerprint: 5f:b7:ee:06:33:e2:59:db:ad:0c:4c:9a:e6:d3:8f:1a:61:c7:dc:25 +# SHA256 Fingerprint: 74:31:e5:f4:c3:c1:ce:46:90:77:4f:0b:61:e0:54:40:88:3b:a9:a0:1e:d0:0b:a6:ab:d7:80:6e:d3:b1:18:cf +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j +ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 +LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug +RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm ++9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW +PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM +xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB +Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 +hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg +EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA +FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec +nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z +eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF +hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 +Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep ++OkuE6N36B9K +-----END CERTIFICATE----- + +# Issuer: CN=Class 2 Primary CA O=Certplus +# Subject: CN=Class 2 Primary CA O=Certplus +# Label: "Certplus Class 2 Primary CA" +# Serial: 177770208045934040241468760488327595043 +# MD5 Fingerprint: 88:2c:8c:52:b8:a2:3c:f3:f7:bb:03:ea:ae:ac:42:0b +# SHA1 Fingerprint: 74:20:74:41:72:9c:dd:92:ec:79:31:d8:23:10:8d:c2:81:92:e2:bb +# SHA256 Fingerprint: 0f:99:3c:8a:ef:97:ba:af:56:87:14:0e:d5:9a:d1:82:1b:b4:af:ac:f0:aa:9a:58:b5:d5:7a:33:8a:3a:fb:cb +-----BEGIN CERTIFICATE----- +MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAw +PTELMAkGA1UEBhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFz +cyAyIFByaW1hcnkgQ0EwHhcNOTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9 +MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2VydHBsdXMxGzAZBgNVBAMTEkNsYXNz +IDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANxQ +ltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR5aiR +VhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyL +kcAbmXuZVg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCd +EgETjdyAYveVqUSISnFOYFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yas +H7WLO7dDWWuwJKZtkIvEcupdM5i3y95ee++U8Rs+yskhwcWYAqqi9lt3m/V+llU0 +HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRMECDAGAQH/AgEKMAsGA1Ud +DwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJYIZIAYb4 +QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMu +Y29tL0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/ +AN9WM2K191EBkOvDP9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8 +yfFC82x/xXp8HVGIutIKPidd3i1RTtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMR +FcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+7UCmnYR0ObncHoUW2ikbhiMA +ybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW//1IMwrh3KWB +kJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7 +l7+ijrRU +-----END CERTIFICATE----- + +# Issuer: CN=DST Root CA X3 O=Digital Signature Trust Co. +# Subject: CN=DST Root CA X3 O=Digital Signature Trust Co. +# Label: "DST Root CA X3" +# Serial: 91299735575339953335919266965803778155 +# MD5 Fingerprint: 41:03:52:dc:0f:f7:50:1b:16:f0:02:8e:ba:6f:45:c5 +# SHA1 Fingerprint: da:c9:02:4f:54:d8:f6:df:94:93:5f:b1:73:26:38:ca:6a:d7:7c:13 +# SHA256 Fingerprint: 06:87:26:03:31:a7:24:03:d9:09:f1:05:e6:9b:cf:0d:32:e1:bd:24:93:ff:c6:d9:20:6d:11:bc:d6:77:07:39 +-----BEGIN CERTIFICATE----- +MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/ +MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT +DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow +PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD +Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O +rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq +OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b +xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw +7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD +aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV +HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG +SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69 +ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr +AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz +R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5 +JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo +Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ +-----END CERTIFICATE----- + +# Issuer: CN=DST ACES CA X6 O=Digital Signature Trust OU=DST ACES +# Subject: CN=DST ACES CA X6 O=Digital Signature Trust OU=DST ACES +# Label: "DST ACES CA X6" +# Serial: 17771143917277623872238992636097467865 +# MD5 Fingerprint: 21:d8:4c:82:2b:99:09:33:a2:eb:14:24:8d:8e:5f:e8 +# SHA1 Fingerprint: 40:54:da:6f:1c:3f:40:74:ac:ed:0f:ec:cd:db:79:d1:53:fb:90:1d +# SHA256 Fingerprint: 76:7c:95:5a:76:41:2c:89:af:68:8e:90:a1:c7:0f:55:6c:fd:6b:60:25:db:ea:10:41:6d:7e:b6:83:1f:8c:40 +-----BEGIN CERTIFICATE----- +MIIECTCCAvGgAwIBAgIQDV6ZCtadt3js2AdWO4YV2TANBgkqhkiG9w0BAQUFADBb +MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3Qx +ETAPBgNVBAsTCERTVCBBQ0VTMRcwFQYDVQQDEw5EU1QgQUNFUyBDQSBYNjAeFw0w +MzExMjAyMTE5NThaFw0xNzExMjAyMTE5NThaMFsxCzAJBgNVBAYTAlVTMSAwHgYD +VQQKExdEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdDERMA8GA1UECxMIRFNUIEFDRVMx +FzAVBgNVBAMTDkRTVCBBQ0VTIENBIFg2MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAuT31LMmU3HWKlV1j6IR3dma5WZFcRt2SPp/5DgO0PWGSvSMmtWPu +ktKe1jzIDZBfZIGxqAgNTNj50wUoUrQBJcWVHAx+PhCEdc/BGZFjz+iokYi5Q1K7 +gLFViYsx+tC3dr5BPTCapCIlF3PoHuLTrCq9Wzgh1SpL11V94zpVvddtawJXa+ZH +fAjIgrrep4c9oW24MFbCswKBXy314powGCi4ZtPLAZZv6opFVdbgnf9nKxcCpk4a +ahELfrd755jWjHZvwTvbUJN+5dCOHze4vbrGn2zpfDPyMjwmR/onJALJfh1biEIT +ajV8fTXpLmaRcpPVMibEdPVTo7NdmvYJywIDAQABo4HIMIHFMA8GA1UdEwEB/wQF +MAMBAf8wDgYDVR0PAQH/BAQDAgHGMB8GA1UdEQQYMBaBFHBraS1vcHNAdHJ1c3Rk +c3QuY29tMGIGA1UdIARbMFkwVwYKYIZIAWUDAgEBATBJMEcGCCsGAQUFBwIBFjto +dHRwOi8vd3d3LnRydXN0ZHN0LmNvbS9jZXJ0aWZpY2F0ZXMvcG9saWN5L0FDRVMt +aW5kZXguaHRtbDAdBgNVHQ4EFgQUCXIGThhDD+XWzMNqizF7eI+og7gwDQYJKoZI +hvcNAQEFBQADggEBAKPYjtay284F5zLNAdMEA+V25FYrnJmQ6AgwbN99Pe7lv7Uk +QIRJ4dEorsTCOlMwiPH1d25Ryvr/ma8kXxug/fKshMrfqfBfBC6tFr8hlxCBPeP/ +h40y3JTlR4peahPJlJU90u7INJXQgNStMgiAVDzgvVJT11J8smk/f3rPanTK+gQq +nExaBqXpIK1FZg9p8d2/6eMyi/rgwYZNcjwu2JN4Cir42NInPRmJX1p7ijvMDNpR +rscL9yuwNwXsvFcj4jjSm2jzVhKIT0J8uDHEtdvkyCE06UgRNe76x5JXxZ805Mf2 +9w4LTJxoeHtxMcfrHuBnQfO3oKfN5XozNmr6mis= +-----END CERTIFICATE----- + +# Issuer: CN=TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı O=(c) 2005 TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. +# Subject: CN=TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı O=(c) 2005 TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. +# Label: "TURKTRUST Certificate Services Provider Root 1" +# Serial: 1 +# MD5 Fingerprint: f1:6a:22:18:c9:cd:df:ce:82:1d:1d:b7:78:5c:a9:a5 +# SHA1 Fingerprint: 79:98:a3:08:e1:4d:65:85:e6:c2:1e:15:3a:71:9f:ba:5a:d3:4a:d9 +# SHA256 Fingerprint: 44:04:e3:3b:5e:14:0d:cf:99:80:51:fd:fc:80:28:c7:c8:16:15:c5:ee:73:7b:11:1b:58:82:33:a9:b5:35:a0 +-----BEGIN CERTIFICATE----- +MIID+zCCAuOgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBtzE/MD0GA1UEAww2VMOc +UktUUlVTVCBFbGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sx +c8SxMQswCQYDVQQGDAJUUjEPMA0GA1UEBwwGQU5LQVJBMVYwVAYDVQQKDE0oYykg +MjAwNSBUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8 +dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLjAeFw0wNTA1MTMxMDI3MTdaFw0xNTAz +MjIxMDI3MTdaMIG3MT8wPQYDVQQDDDZUw5xSS1RSVVNUIEVsZWt0cm9uaWsgU2Vy +dGlmaWthIEhpem1ldCBTYcSfbGF5xLFjxLFzxLExCzAJBgNVBAYMAlRSMQ8wDQYD +VQQHDAZBTktBUkExVjBUBgNVBAoMTShjKSAyMDA1IFTDnFJLVFJVU1QgQmlsZ2kg +xLBsZXRpxZ9pbSB2ZSBCaWxpxZ9pbSBHw7x2ZW5sacSfaSBIaXptZXRsZXJpIEEu +xZ4uMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAylIF1mMD2Bxf3dJ7 +XfIMYGFbazt0K3gNfUW9InTojAPBxhEqPZW8qZSwu5GXyGl8hMW0kWxsE2qkVa2k +heiVfrMArwDCBRj1cJ02i67L5BuBf5OI+2pVu32Fks66WJ/bMsW9Xe8iSi9BB35J +YbOG7E6mQW6EvAPs9TscyB/C7qju6hJKjRTP8wrgUDn5CDX4EVmt5yLqS8oUBt5C +urKZ8y1UiBAG6uEaPj1nH/vO+3yC6BFdSsG5FOpU2WabfIl9BJpiyelSPJ6c79L1 +JuTm5Rh8i27fbMx4W09ysstcP4wFjdFMjK2Sx+F4f2VsSQZQLJ4ywtdKxnWKWU51 +b0dewQIDAQABoxAwDjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAV +9VX/N5aAWSGk/KEVTCD21F/aAyT8z5Aa9CEKmu46sWrv7/hg0Uw2ZkUd82YCdAR7 +kjCo3gp2D++Vbr3JN+YaDayJSFvMgzbC9UZcWYJWtNX+I7TYVBxEq8Sn5RTOPEFh +fEPmzcSBCYsk+1Ql1haolgxnB2+zUEfjHCQo3SqYpGH+2+oSN7wBGjSFvW5P55Fy +B0SFHljKVETd96y5y4khctuPwGkplyqjrhgjlxxBKot8KsF8kOipKMDTkcatKIdA +aLX/7KfS0zgYnNN9aV3wxqUeJBujR/xpB2jn5Jq07Q+hh4cCzofSSE7hvP/L8XKS +RGQDJereW26fyfJOrN3H +-----END CERTIFICATE----- + +# Issuer: CN=TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı O=TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. (c) Kasım 2005 +# Subject: CN=TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı O=TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. (c) Kasım 2005 +# Label: "TURKTRUST Certificate Services Provider Root 2" +# Serial: 1 +# MD5 Fingerprint: 37:a5:6e:d4:b1:25:84:97:b7:fd:56:15:7a:f9:a2:00 +# SHA1 Fingerprint: b4:35:d4:e1:11:9d:1c:66:90:a7:49:eb:b3:94:bd:63:7b:a7:82:b7 +# SHA256 Fingerprint: c4:70:cf:54:7e:23:02:b9:77:fb:29:dd:71:a8:9a:7b:6c:1f:60:77:7b:03:29:f5:60:17:f3:28:bf:4f:6b:e6 +-----BEGIN CERTIFICATE----- +MIIEPDCCAySgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvjE/MD0GA1UEAww2VMOc +UktUUlVTVCBFbGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sx +c8SxMQswCQYDVQQGEwJUUjEPMA0GA1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xS +S1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kg +SGl6bWV0bGVyaSBBLsWeLiAoYykgS2FzxLFtIDIwMDUwHhcNMDUxMTA3MTAwNzU3 +WhcNMTUwOTE2MTAwNzU3WjCBvjE/MD0GA1UEAww2VMOcUktUUlVTVCBFbGVrdHJv +bmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJU +UjEPMA0GA1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xSS1RSVVNUIEJpbGdpIMSw +bGV0acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWe +LiAoYykgS2FzxLFtIDIwMDUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQCpNn7DkUNMwxmYCMjHWHtPFoylzkkBH3MOrHUTpvqeLCDe2JAOCtFp0if7qnef +J1Il4std2NiDUBd9irWCPwSOtNXwSadktx4uXyCcUHVPr+G1QRT0mJKIx+XlZEdh +R3n9wFHxwZnn3M5q+6+1ATDcRhzviuyV79z/rxAc653YsKpqhRgNF8k+v/Gb0AmJ +Qv2gQrSdiVFVKc8bcLyEVK3BEx+Y9C52YItdP5qtygy/p1Zbj3e41Z55SZI/4PGX +JHpsmxcPbe9TmJEr5A++WXkHeLuXlfSfadRYhwqp48y2WBmfJiGxxFmNskF1wK1p +zpwACPI2/z7woQ8arBT9pmAPAgMBAAGjQzBBMB0GA1UdDgQWBBTZN7NOBf3Zz58S +Fq62iS/rJTqIHDAPBgNVHQ8BAf8EBQMDBwYAMA8GA1UdEwEB/wQFMAMBAf8wDQYJ +KoZIhvcNAQEFBQADggEBAHJglrfJ3NgpXiOFX7KzLXb7iNcX/nttRbj2hWyfIvwq +ECLsqrkw9qtY1jkQMZkpAL2JZkH7dN6RwRgLn7Vhy506vvWolKMiVW4XSf/SKfE4 +Jl3vpao6+XF75tpYHdN0wgH6PmlYX63LaL4ULptswLbcoCb6dxriJNoaN+BnrdFz +gw2lGh1uEpJ+hGIAF728JRhX8tepb1mIvDS3LoV4nZbcFMMsilKbloxSZj2GFotH +uFEJjOp9zYhys2AzsfAKRO8P9Qk3iCQOLGsgOqL6EfJANZxEaGM7rDNvY7wsu/LS +y3Z9fYjYHcgFHW68lKlmjHdxx/qR+i9Rnuk5UrbnBEI= +-----END CERTIFICATE----- + +# Issuer: CN=SwissSign Gold CA - G2 O=SwissSign AG +# Subject: CN=SwissSign Gold CA - G2 O=SwissSign AG +# Label: "SwissSign Gold CA - G2" +# Serial: 13492815561806991280 +# MD5 Fingerprint: 24:77:d9:a8:91:d1:3b:fa:88:2d:c2:ff:f8:cd:33:93 +# SHA1 Fingerprint: d8:c5:38:8a:b7:30:1b:1b:6e:d4:7a:e6:45:25:3a:6f:9f:1a:27:61 +# SHA256 Fingerprint: 62:dd:0b:e9:b9:f5:0a:16:3e:a0:f8:e7:5c:05:3b:1e:ca:57:ea:55:c8:68:8f:64:7c:68:81:f2:c8:35:7b:95 +-----BEGIN CERTIFICATE----- +MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV +BAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2ln +biBHb2xkIENBIC0gRzIwHhcNMDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBF +MQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dpc3NTaWduIEFHMR8wHQYDVQQDExZT +d2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC +CgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUqt2/8 +76LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+ +bbqBHH5CjCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c +6bM8K8vzARO/Ws/BtQpgvd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqE +emA8atufK+ze3gE/bk3lUIbLtK/tREDFylqM2tIrfKjuvqblCqoOpd8FUrdVxyJd +MmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvRAiTysybUa9oEVeXBCsdt +MDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuendjIj3o02y +MszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69y +FGkOpeUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPi +aG59je883WX0XaxR7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxM +gI93e2CaHt+28kgeDrpOVG2Y4OGiGqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCB +qTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUWyV7 +lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64OfPAeGZe6Drn +8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov +L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe6 +45R88a7A3hfm5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczO +UYrHUDFu4Up+GC9pWbY9ZIEr44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5 +O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOfMke6UiI0HTJ6CVanfCU2qT1L2sCC +bwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6mGu6uLftIdxf+u+yv +GPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxpmo/a +77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCC +hdiDyyJkvC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid3 +92qgQmwLOM7XdVAyksLfKzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEpp +Ld6leNcG2mqeSz53OiATIgHQv2ieY2BrNU0LbbqhPcCT4H8js1WtciVORvnSFu+w +ZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6LqjviOvrv1vA+ACOzB2+htt +Qc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ +-----END CERTIFICATE----- + +# Issuer: CN=SwissSign Silver CA - G2 O=SwissSign AG +# Subject: CN=SwissSign Silver CA - G2 O=SwissSign AG +# Label: "SwissSign Silver CA - G2" +# Serial: 5700383053117599563 +# MD5 Fingerprint: e0:06:a1:c9:7d:cf:c9:fc:0d:c0:56:75:96:d8:62:13 +# SHA1 Fingerprint: 9b:aa:e5:9f:56:ee:21:cb:43:5a:be:25:93:df:a7:f0:40:d1:1d:cb +# SHA256 Fingerprint: be:6c:4d:a2:bb:b9:ba:59:b6:f3:93:97:68:37:42:46:c3:c0:05:99:3f:a9:8f:02:0d:1d:ed:be:d4:8a:81:d5 +-----BEGIN CERTIFICATE----- +MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UE +BhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWdu +IFNpbHZlciBDQSAtIEcyMB4XDTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0Nlow +RzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMY +U3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644N0Mv +Fz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7br +YT7QbNHm+/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieF +nbAVlDLaYQ1HTWBCrpJH6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH +6ATK72oxh9TAtvmUcXtnZLi2kUpCe2UuMGoM9ZDulebyzYLs2aFK7PayS+VFheZt +eJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5hqAaEuSh6XzjZG6k4sIN/ +c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5FZGkECwJ +MoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRH +HTBsROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTf +jNFusB3hB48IHpmccelM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb6 +5i/4z3GcRm25xBWNOHkDRUjvxF3XCO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOB +rDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU +F6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRBtjpbO8tFnb0c +wpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0 +cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIB +AHPGgeAn0i0P4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShp +WJHckRE1qTodvBqlYJ7YH39FkWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9 +xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L3XWgwF15kIwb4FDm3jH+mHtwX6WQ +2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx/uNncqCxv1yL5PqZ +IseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFaDGi8 +aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2X +em1ZqSqPe97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQR +dAtq/gsD/KNVV4n+SsuuWxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/ +OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJDIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+ +hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ubDgEj8Z+7fNzcbBGXJbLy +tGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Primary Certification Authority O=GeoTrust Inc. +# Subject: CN=GeoTrust Primary Certification Authority O=GeoTrust Inc. +# Label: "GeoTrust Primary Certification Authority" +# Serial: 32798226551256963324313806436981982369 +# MD5 Fingerprint: 02:26:c3:01:5e:08:30:37:43:a9:d0:7d:cf:37:e6:bf +# SHA1 Fingerprint: 32:3c:11:8e:1b:f7:b8:b6:52:54:e2:e2:10:0d:d6:02:90:37:f0:96 +# SHA256 Fingerprint: 37:d5:10:06:c5:12:ea:ab:62:64:21:f1:ec:8c:92:01:3f:c5:f8:2a:e9:8e:e5:33:eb:46:19:b8:de:b4:d0:6c +-----BEGIN CERTIFICATE----- +MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBY +MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMo +R2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEx +MjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgxCzAJBgNVBAYTAlVTMRYwFAYDVQQK +Ew1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQcmltYXJ5IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9 +AWbK7hWNb6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjA +ZIVcFU2Ix7e64HXprQU9nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE0 +7e9GceBrAqg1cmuXm2bgyxx5X9gaBGgeRwLmnWDiNpcB3841kt++Z8dtd1k7j53W +kBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGttm/81w7a4DSwDRp35+MI +mO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G +A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJ +KoZIhvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ1 +6CePbJC/kRYkRj5KTs4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl +4b7UVXGYNTq+k+qurUKykG/g/CFNNWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6K +oKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHaFloxt/m0cYASSJlyc1pZU8Fj +UjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG1riR/aYNKxoU +AT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk= +-----END CERTIFICATE----- + +# Issuer: CN=thawte Primary Root CA O=thawte, Inc. OU=Certification Services Division/(c) 2006 thawte, Inc. - For authorized use only +# Subject: CN=thawte Primary Root CA O=thawte, Inc. OU=Certification Services Division/(c) 2006 thawte, Inc. - For authorized use only +# Label: "thawte Primary Root CA" +# Serial: 69529181992039203566298953787712940909 +# MD5 Fingerprint: 8c:ca:dc:0b:22:ce:f5:be:72:ac:41:1a:11:a8:d8:12 +# SHA1 Fingerprint: 91:c6:d6:ee:3e:8a:c8:63:84:e5:48:c2:99:29:5c:75:6c:81:7b:81 +# SHA256 Fingerprint: 8d:72:2f:81:a9:c1:13:c0:79:1d:f1:36:a2:96:6d:b2:6c:95:0a:97:1d:b4:6b:41:99:f4:ea:54:b7:8b:fb:9f +-----BEGIN CERTIFICATE----- +MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCB +qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf +Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw +MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV +BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3MDAwMDAwWhcNMzYw +NzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5j +LjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYG +A1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsoPD7gFnUnMekz52hWXMJEEUMDSxuaPFs +W0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ1CRfBsDMRJSUjQJib+ta +3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGcq/gcfomk +6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6 +Sk/KaAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94J +NqR32HuHUETVPm4pafs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XP +r87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUFAAOCAQEAeRHAS7ORtvzw6WfU +DW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeEuzLlQRHAd9mz +YJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX +xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2 +/qxAeeWsEG89jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/ +LHbTY5xZ3Y+m4Q6gLkH3LpVHz7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7 +jVaMaA== +-----END CERTIFICATE----- + +# Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G5 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2006 VeriSign, Inc. - For authorized use only +# Subject: CN=VeriSign Class 3 Public Primary Certification Authority - G5 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2006 VeriSign, Inc. - For authorized use only +# Label: "VeriSign Class 3 Public Primary Certification Authority - G5" +# Serial: 33037644167568058970164719475676101450 +# MD5 Fingerprint: cb:17:e4:31:67:3e:e2:09:fe:45:57:93:f3:0a:fa:1c +# SHA1 Fingerprint: 4e:b6:d5:78:49:9b:1c:cf:5f:58:1e:ad:56:be:3d:9b:67:44:a5:e5 +# SHA256 Fingerprint: 9a:cf:ab:7e:43:c8:d8:80:d0:6b:26:2a:94:de:ee:e4:b4:65:99:89:c3:d0:ca:f1:9b:af:64:05:e4:1a:b7:df +-----BEGIN CERTIFICATE----- +MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB +yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL +ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp +U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW +ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW +ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp +U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y +aXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1 +nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex +t0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz +SdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG +BO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+ +rCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/ +NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E +BAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH +BgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy +aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv +MzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE +p6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y +5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK +WE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ +4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N +hnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq +-----END CERTIFICATE----- + +# Issuer: CN=SecureTrust CA O=SecureTrust Corporation +# Subject: CN=SecureTrust CA O=SecureTrust Corporation +# Label: "SecureTrust CA" +# Serial: 17199774589125277788362757014266862032 +# MD5 Fingerprint: dc:32:c3:a7:6d:25:57:c7:68:09:9d:ea:2d:a9:a2:d1 +# SHA1 Fingerprint: 87:82:c6:c3:04:35:3b:cf:d2:96:92:d2:59:3e:7d:44:d9:34:ff:11 +# SHA256 Fingerprint: f1:c1:b5:0a:e5:a2:0d:d8:03:0e:c9:f6:bc:24:82:3d:d3:67:b5:25:57:59:b4:e7:1b:61:fc:e9:f7:37:5d:73 +-----BEGIN CERTIFICATE----- +MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBI +MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x +FzAVBgNVBAMTDlNlY3VyZVRydXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIz +MTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAeBgNVBAoTF1NlY3VyZVRydXN0IENv +cnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQXOZEz +Zum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO +0gMdA+9tDWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIao +wW8xQmxSPmjL8xk037uHGFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj +7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b01k/unK8RCSc43Oz969XL0Imnal0ugBS +8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmHursCAwEAAaOBnTCBmjAT +BgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCeg +JYYjaHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGC +NxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt3 +6Z3q059c4EVlew3KW+JwULKUBRSuSceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/ +3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHfmbx8IVQr5Fiiu1cprp6poxkm +D5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZnMUFdAvnZyPS +CPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR +3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE= +-----END CERTIFICATE----- + +# Issuer: CN=Secure Global CA O=SecureTrust Corporation +# Subject: CN=Secure Global CA O=SecureTrust Corporation +# Label: "Secure Global CA" +# Serial: 9751836167731051554232119481456978597 +# MD5 Fingerprint: cf:f4:27:0d:d4:ed:dc:65:16:49:6d:3d:da:bf:6e:de +# SHA1 Fingerprint: 3a:44:73:5a:e5:81:90:1f:24:86:61:46:1e:3b:9c:c4:5f:f5:3a:1b +# SHA256 Fingerprint: 42:00:f5:04:3a:c8:59:0e:bb:52:7d:20:9e:d1:50:30:29:fb:cb:d4:1c:a1:b5:06:ec:27:f1:5a:de:7d:ac:69 +-----BEGIN CERTIFICATE----- +MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBK +MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x +GTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkx +MjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3Qg +Q29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jxYDiJ +iQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa +/FHtaMbQbqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJ +jnIFHovdRIWCQtBJwB1g8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnI +HmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYVHDGA76oYa8J719rO+TMg1fW9ajMtgQT7 +sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi0XPnj3pDAgMBAAGjgZ0w +gZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCsw +KaAnoCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsG +AQQBgjcVAQQDAgEAMA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0L +URYD7xh8yOOvaliTFGCRsoTciE6+OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXO +H0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cnCDpOGR86p1hcF895P4vkp9Mm +I50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/53CYNv6ZHdAbY +iNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc +f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW +-----END CERTIFICATE----- + +# Issuer: CN=COMODO Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO Certification Authority O=COMODO CA Limited +# Label: "COMODO Certification Authority" +# Serial: 104350513648249232941998508985834464573 +# MD5 Fingerprint: 5c:48:dc:f7:42:72:ec:56:94:6d:1c:cc:71:35:80:75 +# SHA1 Fingerprint: 66:31:bf:9e:f7:4f:9e:b6:c9:d5:a6:0c:ba:6a:be:d1:f7:bd:ef:7b +# SHA256 Fingerprint: 0c:2c:d6:3d:f7:80:6f:a3:99:ed:e8:09:11:6b:57:5b:f8:79:89:f0:65:18:f9:80:8c:86:05:03:17:8b:af:66 +-----BEGIN CERTIFICATE----- +MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCB +gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV +BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw +MDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3Jl +YXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01P +RE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3 +UcEbVASY06m/weaKXTuH+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI +2GqGd0S7WWaXUF601CxwRM/aN5VCaTwwxHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8 +Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV4EajcNxo2f8ESIl33rXp ++2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA1KGzqSX+ +DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5O +nKVIrLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW +/zAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6g +PKA6hjhodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9u +QXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOCAQEAPpiem/Yb6dc5t3iuHXIY +SdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CPOGEIqB6BCsAv +IC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ +RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4 +zJVSk/BwJVmcIGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5dd +BA6+C4OmF4O5MBKgxTMVBbkN+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IB +ZQ== +-----END CERTIFICATE----- + +# Issuer: CN=Network Solutions Certificate Authority O=Network Solutions L.L.C. +# Subject: CN=Network Solutions Certificate Authority O=Network Solutions L.L.C. +# Label: "Network Solutions Certificate Authority" +# Serial: 116697915152937497490437556386812487904 +# MD5 Fingerprint: d3:f3:a6:16:c0:fa:6b:1d:59:b1:2d:96:4d:0e:11:2e +# SHA1 Fingerprint: 74:f8:a3:c3:ef:e7:b3:90:06:4b:83:90:3c:21:64:60:20:e5:df:ce +# SHA256 Fingerprint: 15:f0:ba:00:a3:ac:7a:f3:ac:88:4c:07:2b:10:11:a0:77:bd:77:c0:97:f4:01:64:b2:f8:59:8a:bd:83:86:0c +-----BEGIN CERTIFICATE----- +MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBi +MQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu +MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3Jp +dHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMxMjM1OTU5WjBiMQswCQYDVQQGEwJV +UzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydO +ZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwz +c7MEL7xxjOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPP +OCwGJgl6cvf6UDL4wpPTaaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rl +mGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXTcrA/vGp97Eh/jcOrqnErU2lBUzS1sLnF +BgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc/Qzpf14Dl847ABSHJ3A4 +qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMBAAGjgZcw +gZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIB +BjAPBgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwu +bmV0c29sc3NsLmNvbS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3Jp +dHkuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc8 +6fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q4LqILPxFzBiwmZVRDuwduIj/ +h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/GGUsyfJj4akH +/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv +wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHN +pGxlaKFJdlxDydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey +-----END CERTIFICATE----- + +# Issuer: CN=WellsSecure Public Root Certificate Authority O=Wells Fargo WellsSecure OU=Wells Fargo Bank NA +# Subject: CN=WellsSecure Public Root Certificate Authority O=Wells Fargo WellsSecure OU=Wells Fargo Bank NA +# Label: "WellsSecure Public Root Certificate Authority" +# Serial: 1 +# MD5 Fingerprint: 15:ac:a5:c2:92:2d:79:bc:e8:7f:cb:67:ed:02:cf:36 +# SHA1 Fingerprint: e7:b4:f6:9d:61:ec:90:69:db:7e:90:a7:40:1a:3c:f4:7d:4f:e8:ee +# SHA256 Fingerprint: a7:12:72:ae:aa:a3:cf:e8:72:7f:7f:b3:9f:0f:b3:d1:e5:42:6e:90:60:b0:6e:e6:f1:3e:9a:3c:58:33:cd:43 +-----BEGIN CERTIFICATE----- +MIIEvTCCA6WgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMCVVMx +IDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxs +cyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9v +dCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDcxMjEzMTcwNzU0WhcNMjIxMjE0 +MDAwNzU0WjCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdl +bGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQD +DC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDub7S9eeKPCCGeOARBJe+r +WxxTkqxtnt3CxC5FlAM1iGd0V+PfjLindo8796jE2yljDpFoNoqXjopxaAkH5OjU +Dk/41itMpBb570OYj7OeUt9tkTmPOL13i0Nj67eT/DBMHAGTthP796EfvyXhdDcs +HqRePGj4S78NuR4uNuip5Kf4D8uCdXw1LSLWwr8L87T8bJVhHlfXBIEyg1J55oNj +z7fLY4sR4r1e6/aN7ZVyKLSsEmLpSjPmgzKuBXWVvYSV2ypcm44uDLiBK0HmOFaf +SZtsdvqKXfcBeYF8wYNABf5x/Qw/zE5gCQ5lRxAvAcAFP4/4s0HvWkJ+We/Slwxl +AgMBAAGjggE0MIIBMDAPBgNVHRMBAf8EBTADAQH/MDkGA1UdHwQyMDAwLqAsoCqG +KGh0dHA6Ly9jcmwucGtpLndlbGxzZmFyZ28uY29tL3dzcHJjYS5jcmwwDgYDVR0P +AQH/BAQDAgHGMB0GA1UdDgQWBBQmlRkQ2eihl5H/3BnZtQQ+0nMKajCBsgYDVR0j +BIGqMIGngBQmlRkQ2eihl5H/3BnZtQQ+0nMKaqGBi6SBiDCBhTELMAkGA1UEBhMC +VVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNX +ZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMg +Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCAQEwDQYJKoZIhvcNAQEFBQADggEB +ALkVsUSRzCPIK0134/iaeycNzXK7mQDKfGYZUMbVmO2rvwNa5U3lHshPcZeG1eMd +/ZDJPHV3V3p9+N701NX3leZ0bh08rnyd2wIDBSxxSyU+B+NemvVmFymIGjifz6pB +A4SXa5M4esowRBskRDPQ5NHcKDj0E0M1NSljqHyita04pO2t/caaH/+Xc/77szWn +k4bGdpEA5qxRFsQnMlzbc9qlk1eOPm01JghZ1edE13YgY+esE2fDbbFwRnzVlhE9 +iW9dqKHrjQrawx0zbKPqZxmamX9LPYNRKh3KL4YMon4QLSvUFpULB6ouFJJJtylv +2G0xffX8oRAHh84vWdw+WNs= +-----END CERTIFICATE----- + +# Issuer: CN=COMODO ECC Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO ECC Certification Authority O=COMODO CA Limited +# Label: "COMODO ECC Certification Authority" +# Serial: 41578283867086692638256921589707938090 +# MD5 Fingerprint: 7c:62:ff:74:9d:31:53:5e:68:4a:d5:78:aa:1e:bf:23 +# SHA1 Fingerprint: 9f:74:4e:9f:2b:4d:ba:ec:0f:31:2c:50:b6:56:3b:8e:2d:93:c3:11 +# SHA256 Fingerprint: 17:93:92:7a:06:14:54:97:89:ad:ce:2f:8f:34:f7:f0:b6:6d:0f:3a:e3:a3:b8:4d:21:ec:15:db:ba:4f:ad:c7 +-----BEGIN CERTIFICATE----- +MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT +IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAw +MDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy +ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N +T0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSR +FtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J +cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQW +BBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDm +fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv +GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= +-----END CERTIFICATE----- + +# Issuer: CN=IGC/A O=PM/SGDN OU=DCSSI +# Subject: CN=IGC/A O=PM/SGDN OU=DCSSI +# Label: "IGC/A" +# Serial: 245102874772 +# MD5 Fingerprint: 0c:7f:dd:6a:f4:2a:b9:c8:9b:bd:20:7e:a9:db:5c:37 +# SHA1 Fingerprint: 60:d6:89:74:b5:c2:65:9e:8a:0f:c1:88:7c:88:d2:46:69:1b:18:2c +# SHA256 Fingerprint: b9:be:a7:86:0a:96:2e:a3:61:1d:ab:97:ab:6d:a3:e2:1c:10:68:b9:7d:55:57:5e:d0:e1:12:79:c1:1c:89:32 +-----BEGIN CERTIFICATE----- +MIIEAjCCAuqgAwIBAgIFORFFEJQwDQYJKoZIhvcNAQEFBQAwgYUxCzAJBgNVBAYT +AkZSMQ8wDQYDVQQIEwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQ +TS9TR0ROMQ4wDAYDVQQLEwVEQ1NTSTEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG +9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZyMB4XDTAyMTIxMzE0MjkyM1oXDTIw +MTAxNzE0MjkyMlowgYUxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIEwZGcmFuY2UxDjAM +BgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVEQ1NTSTEO +MAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2 +LmZyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsh/R0GLFMzvABIaI +s9z4iPf930Pfeo2aSVz2TqrMHLmh6yeJ8kbpO0px1R2OLc/mratjUMdUC24SyZA2 +xtgv2pGqaMVy/hcKshd+ebUyiHDKcMCWSo7kVc0dJ5S/znIq7Fz5cyD+vfcuiWe4 +u0dzEvfRNWk68gq5rv9GQkaiv6GFGvm/5P9JhfejcIYyHF2fYPepraX/z9E0+X1b +F8bc1g4oa8Ld8fUzaJ1O/Id8NhLWo4DoQw1VYZTqZDdH6nfK0LJYBcNdfrGoRpAx +Vs5wKpayMLh35nnAvSk7/ZR3TL0gzUEl4C7HG7vupARB0l2tEmqKm0f7yd1GQOGd +PDPQtQIDAQABo3cwdTAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBRjAVBgNV +HSAEDjAMMAoGCCqBegF5AQEBMB0GA1UdDgQWBBSjBS8YYFDCiQrdKyFP/45OqDAx +NjAfBgNVHSMEGDAWgBSjBS8YYFDCiQrdKyFP/45OqDAxNjANBgkqhkiG9w0BAQUF +AAOCAQEABdwm2Pp3FURo/C9mOnTgXeQp/wYHE4RKq89toB9RlPhJy3Q2FLwV3duJ +L92PoF189RLrn544pEfMs5bZvpwlqwN+Mw+VgQ39FuCIvjfwbF3QMZsyK10XZZOY +YLxuj7GoPB7ZHPOpJkL5ZB3C55L29B5aqhlSXa/oovdgoPaN8In1buAKBQGVyYsg +Crpa/JosPL3Dt8ldeCUFP1YUmwza+zpI/pdpXsoQhvdOlgQITeywvl3cO45Pwf2a +NjSaTFR+FwNIlQgRHAdvhQh+XU3Endv7rs6y0bO4g2wdsrN58dhwmX7wEwLOXt1R +0982gaEbeC9xs/FZTEYYKKuF0mBWWg== +-----END CERTIFICATE----- + +# Issuer: O=SECOM Trust Systems CO.,LTD. OU=Security Communication EV RootCA1 +# Subject: O=SECOM Trust Systems CO.,LTD. OU=Security Communication EV RootCA1 +# Label: "Security Communication EV RootCA1" +# Serial: 0 +# MD5 Fingerprint: 22:2d:a6:01:ea:7c:0a:f7:f0:6c:56:43:3f:77:76:d3 +# SHA1 Fingerprint: fe:b8:c4:32:dc:f9:76:9a:ce:ae:3d:d8:90:8f:fd:28:86:65:64:7d +# SHA256 Fingerprint: a2:2d:ba:68:1e:97:37:6e:2d:39:7d:72:8a:ae:3a:9b:62:96:b9:fd:ba:60:bc:2e:11:f6:47:f2:c6:75:fb:37 +-----BEGIN CERTIFICATE----- +MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJKUDEl +MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEqMCgGA1UECxMh +U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBFViBSb290Q0ExMB4XDTA3MDYwNjAyMTIz +MloXDTM3MDYwNjAyMTIzMlowYDELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09N +IFRydXN0IFN5c3RlbXMgQ08uLExURC4xKjAoBgNVBAsTIVNlY3VyaXR5IENvbW11 +bmljYXRpb24gRVYgUm9vdENBMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBALx/7FebJOD+nLpCeamIivqA4PUHKUPqjgo0No0c+qe1OXj/l3X3L+SqawSE +RMqm4miO/VVQYg+kcQ7OBzgtQoVQrTyWb4vVog7P3kmJPdZkLjjlHmy1V4qe70gO +zXppFodEtZDkBp2uoQSXWHnvIEqCa4wiv+wfD+mEce3xDuS4GBPMVjZd0ZoeUWs5 +bmB2iDQL87PRsJ3KYeJkHcFGB7hj3R4zZbOOCVVSPbW9/wfrrWFVGCypaZhKqkDF +MxRldAD5kd6vA0jFQFTcD4SQaCDFkpbcLuUCRarAX1T4bepJz11sS6/vmsJWXMY1 +VkJqMF/Cq/biPT+zyRGPMUzXn0kCAwEAAaNCMEAwHQYDVR0OBBYEFDVK9U2vP9eC +OKyrcWUXdYydVZPmMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBBQUAA4IBAQCoh+ns+EBnXcPBZsdAS5f8hxOQWsTvoMpfi7ent/HW +tWS3irO4G8za+6xmiEHO6Pzk2x6Ipu0nUBsCMCRGef4Eh3CXQHPRwMFXGZpppSeZ +q51ihPZRwSzJIxXYKLerJRO1RuGGAv8mjMSIkh1W/hln8lXkgKNrnKt34VFxDSDb +EJrbvXZ5B3eZKK2aXtqxT0QsNY6llsf9g/BYxnnWmHyojf6GPgcWkuF75x3sM3Z+ +Qi5KhfmRiWiEA4Glm5q+4zfFVKtWOxgtQaQM+ELbmaDgcm+7XeEWT1MKZPlO9L9O +VL14bIjqv5wTJMJwaaJ/D8g8rQjJsJhAoyrniIPtd490 +-----END CERTIFICATE----- + +# Issuer: CN=OISTE WISeKey Global Root GA CA O=WISeKey OU=Copyright (c) 2005/OISTE Foundation Endorsed +# Subject: CN=OISTE WISeKey Global Root GA CA O=WISeKey OU=Copyright (c) 2005/OISTE Foundation Endorsed +# Label: "OISTE WISeKey Global Root GA CA" +# Serial: 86718877871133159090080555911823548314 +# MD5 Fingerprint: bc:6c:51:33:a7:e9:d3:66:63:54:15:72:1b:21:92:93 +# SHA1 Fingerprint: 59:22:a1:e1:5a:ea:16:35:21:f8:98:39:6a:46:46:b0:44:1b:0f:a9 +# SHA256 Fingerprint: 41:c9:23:86:6a:b4:ca:d6:b7:ad:57:80:81:58:2e:02:07:97:a6:cb:df:4f:ff:78:ce:83:96:b3:89:37:d7:f5 +-----BEGIN CERTIFICATE----- +MIID8TCCAtmgAwIBAgIQQT1yx/RrH4FDffHSKFTfmjANBgkqhkiG9w0BAQUFADCB +ijELMAkGA1UEBhMCQ0gxEDAOBgNVBAoTB1dJU2VLZXkxGzAZBgNVBAsTEkNvcHly +aWdodCAoYykgMjAwNTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNl +ZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQSBDQTAeFw0w +NTEyMTExNjAzNDRaFw0zNzEyMTExNjA5NTFaMIGKMQswCQYDVQQGEwJDSDEQMA4G +A1UEChMHV0lTZUtleTEbMBkGA1UECxMSQ29weXJpZ2h0IChjKSAyMDA1MSIwIAYD +VQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBX +SVNlS2V5IEdsb2JhbCBSb290IEdBIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAy0+zAJs9Nt350UlqaxBJH+zYK7LG+DKBKUOVTJoZIyEVRd7jyBxR +VVuuk+g3/ytr6dTqvirdqFEr12bDYVxgAsj1znJ7O7jyTmUIms2kahnBAbtzptf2 +w93NvKSLtZlhuAGio9RN1AU9ka34tAhxZK9w8RxrfvbDd50kc3vkDIzh2TbhmYsF +mQvtRTEJysIA2/dyoJaqlYfQjse2YXMNdmaM3Bu0Y6Kff5MTMPGhJ9vZ/yxViJGg +4E8HsChWjBgbl0SOid3gF27nKu+POQoxhILYQBRJLnpB5Kf+42TMwVlxSywhp1t9 +4B3RLoGbw9ho972WG6xwsRYUC9tguSYBBQIDAQABo1EwTzALBgNVHQ8EBAMCAYYw +DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUswN+rja8sHnR3JQmthG+IbJphpQw +EAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBAEuh/wuHbrP5wUOx +SPMowB0uyQlB+pQAHKSkq0lPjz0e701vvbyk9vImMMkQyh2I+3QZH4VFvbBsUfk2 +ftv1TDI6QU9bR8/oCy22xBmddMVHxjtqD6wU2zz0c5ypBd8A3HR4+vg1YFkCExh8 +vPtNsCBtQ7tgMHpnM1zFmdH4LTlSc/uMqpclXHLZCB6rTjzjgTGfA6b7wP4piFXa +hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZi +Fj4A4xylNoEYokxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ +/L7fCg0= +-----END CERTIFICATE----- + +# Issuer: CN=Microsec e-Szigno Root CA O=Microsec Ltd. OU=e-Szigno CA +# Subject: CN=Microsec e-Szigno Root CA O=Microsec Ltd. OU=e-Szigno CA +# Label: "Microsec e-Szigno Root CA" +# Serial: 272122594155480254301341951808045322001 +# MD5 Fingerprint: f0:96:b6:2f:c5:10:d5:67:8e:83:25:32:e8:5e:2e:e5 +# SHA1 Fingerprint: 23:88:c9:d3:71:cc:9e:96:3d:ff:7d:3c:a7:ce:fc:d6:25:ec:19:0d +# SHA256 Fingerprint: 32:7a:3d:76:1a:ba:de:a0:34:eb:99:84:06:27:5c:b1:a4:77:6e:fd:ae:2f:df:6d:01:68:ea:1c:4f:55:67:d0 +-----BEGIN CERTIFICATE----- +MIIHqDCCBpCgAwIBAgIRAMy4579OKRr9otxmpRwsDxEwDQYJKoZIhvcNAQEFBQAw +cjELMAkGA1UEBhMCSFUxETAPBgNVBAcTCEJ1ZGFwZXN0MRYwFAYDVQQKEw1NaWNy +b3NlYyBMdGQuMRQwEgYDVQQLEwtlLVN6aWdubyBDQTEiMCAGA1UEAxMZTWljcm9z +ZWMgZS1Temlnbm8gUm9vdCBDQTAeFw0wNTA0MDYxMjI4NDRaFw0xNzA0MDYxMjI4 +NDRaMHIxCzAJBgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVzdDEWMBQGA1UEChMN +TWljcm9zZWMgTHRkLjEUMBIGA1UECxMLZS1Temlnbm8gQ0ExIjAgBgNVBAMTGU1p +Y3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQDtyADVgXvNOABHzNuEwSFpLHSQDCHZU4ftPkNEU6+r+ICbPHiN1I2u +uO/TEdyB5s87lozWbxXGd36hL+BfkrYn13aaHUM86tnsL+4582pnS4uCzyL4ZVX+ +LMsvfUh6PXX5qqAnu3jCBspRwn5mS6/NoqdNAoI/gqyFxuEPkEeZlApxcpMqyabA +vjxWTHOSJ/FrtfX9/DAFYJLG65Z+AZHCabEeHXtTRbjcQR/Ji3HWVBTji1R4P770 +Yjtb9aPs1ZJ04nQw7wHb4dSrmZsqa/i9phyGI0Jf7Enemotb9HI6QMVJPqW+jqpx +62z69Rrkav17fVVA71hu5tnVvCSrwe+3AgMBAAGjggQ3MIIEMzBnBggrBgEFBQcB +AQRbMFkwKAYIKwYBBQUHMAGGHGh0dHBzOi8vcmNhLmUtc3ppZ25vLmh1L29jc3Aw +LQYIKwYBBQUHMAKGIWh0dHA6Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNydDAP +BgNVHRMBAf8EBTADAQH/MIIBcwYDVR0gBIIBajCCAWYwggFiBgwrBgEEAYGoGAIB +AQEwggFQMCgGCCsGAQUFBwIBFhxodHRwOi8vd3d3LmUtc3ppZ25vLmh1L1NaU1ov +MIIBIgYIKwYBBQUHAgIwggEUHoIBEABBACAAdABhAG4A+gBzAO0AdAB2AOEAbgB5 +ACAA6QByAHQAZQBsAG0AZQB6AOkAcwDpAGgAZQB6ACAA6QBzACAAZQBsAGYAbwBn +AGEAZADhAHMA4QBoAG8AegAgAGEAIABTAHoAbwBsAGcA4QBsAHQAYQB0APMAIABT +AHoAbwBsAGcA4QBsAHQAYQB0AOEAcwBpACAAUwB6AGEAYgDhAGwAeQB6AGEAdABh +ACAAcwB6AGUAcgBpAG4AdAAgAGsAZQBsAGwAIABlAGwAagDhAHIAbgBpADoAIABo +AHQAdABwADoALwAvAHcAdwB3AC4AZQAtAHMAegBpAGcAbgBvAC4AaAB1AC8AUwBa +AFMAWgAvMIHIBgNVHR8EgcAwgb0wgbqggbeggbSGIWh0dHA6Ly93d3cuZS1zemln +bm8uaHUvUm9vdENBLmNybIaBjmxkYXA6Ly9sZGFwLmUtc3ppZ25vLmh1L0NOPU1p +Y3Jvc2VjJTIwZS1Temlnbm8lMjBSb290JTIwQ0EsT1U9ZS1Temlnbm8lMjBDQSxP +PU1pY3Jvc2VjJTIwTHRkLixMPUJ1ZGFwZXN0LEM9SFU/Y2VydGlmaWNhdGVSZXZv +Y2F0aW9uTGlzdDtiaW5hcnkwDgYDVR0PAQH/BAQDAgEGMIGWBgNVHREEgY4wgYuB +EGluZm9AZS1zemlnbm8uaHWkdzB1MSMwIQYDVQQDDBpNaWNyb3NlYyBlLVN6aWdu +w7MgUm9vdCBDQTEWMBQGA1UECwwNZS1TemlnbsOzIEhTWjEWMBQGA1UEChMNTWlj +cm9zZWMgS2Z0LjERMA8GA1UEBxMIQnVkYXBlc3QxCzAJBgNVBAYTAkhVMIGsBgNV +HSMEgaQwgaGAFMegSXUWYYTbMUuE0vE3QJDvTtz3oXakdDByMQswCQYDVQQGEwJI +VTERMA8GA1UEBxMIQnVkYXBlc3QxFjAUBgNVBAoTDU1pY3Jvc2VjIEx0ZC4xFDAS +BgNVBAsTC2UtU3ppZ25vIENBMSIwIAYDVQQDExlNaWNyb3NlYyBlLVN6aWdubyBS +b290IENBghEAzLjnv04pGv2i3GalHCwPETAdBgNVHQ4EFgQUx6BJdRZhhNsxS4TS +8TdAkO9O3PcwDQYJKoZIhvcNAQEFBQADggEBANMTnGZjWS7KXHAM/IO8VbH0jgds +ZifOwTsgqRy7RlRw7lrMoHfqaEQn6/Ip3Xep1fvj1KcExJW4C+FEaGAHQzAxQmHl +7tnlJNUb3+FKG6qfx1/4ehHqE5MAyopYse7tDk2016g2JnzgOsHVV4Lxdbb9iV/a +86g4nzUGCM4ilb7N1fy+W955a9x6qWVmvrElWl/tftOsRm1M9DKHtCAE4Gx4sHfR +hUZLphK3dehKyVZs15KrnfVJONJPU+NVkBHbmJbGSfI+9J8b4PeI3CVimUTYc78/ +MPMMNz7UwiiAc7EBt51alhQBS6kRnSlqLtBdgcDPsiBDxwPgN05dCtxZICU= +-----END CERTIFICATE----- + +# Issuer: CN=Certigna O=Dhimyotis +# Subject: CN=Certigna O=Dhimyotis +# Label: "Certigna" +# Serial: 18364802974209362175 +# MD5 Fingerprint: ab:57:a6:5b:7d:42:82:19:b5:d8:58:26:28:5e:fd:ff +# SHA1 Fingerprint: b1:2e:13:63:45:86:a4:6f:1a:b2:60:68:37:58:2d:c4:ac:fd:94:97 +# SHA256 Fingerprint: e3:b6:a2:db:2e:d7:ce:48:84:2f:7a:c5:32:41:c7:b7:1d:54:14:4b:fb:40:c1:1f:3f:1d:0b:42:f5:ee:a1:2d +-----BEGIN CERTIFICATE----- +MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNV +BAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4X +DTA3MDYyOTE1MTMwNVoXDTI3MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQ +BgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwIQ2VydGlnbmEwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7qXOEm7RFHYeGifBZ4 +QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyHGxny +gQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbw +zBfsV1/pogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q +130yGLMLLGq/jj8UEYkgDncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2 +JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKfIrjxwo1p3Po6WAbfAgMBAAGjgbwwgbkw +DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQtCRZvgHyUtVF9lo53BEw +ZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJBgNVBAYT +AkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzj +AQ/JSP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG +9w0BAQUFAAOCAQEAhQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8h +bV6lUmPOEvjvKtpv6zf+EwLHyzs+ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFnc +fca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1kluPBS1xp81HlDQwY9qcEQCYsuu +HWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY1gkIl2PlwS6w +t0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw +WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== +-----END CERTIFICATE----- + +# Issuer: CN=TC TrustCenter Class 2 CA II O=TC TrustCenter GmbH OU=TC TrustCenter Class 2 CA +# Subject: CN=TC TrustCenter Class 2 CA II O=TC TrustCenter GmbH OU=TC TrustCenter Class 2 CA +# Label: "TC TrustCenter Class 2 CA II" +# Serial: 941389028203453866782103406992443 +# MD5 Fingerprint: ce:78:33:5c:59:78:01:6e:18:ea:b9:36:a0:b9:2e:23 +# SHA1 Fingerprint: ae:50:83:ed:7c:f4:5c:bc:8f:61:c6:21:fe:68:5d:79:42:21:15:6e +# SHA256 Fingerprint: e6:b8:f8:76:64:85:f8:07:ae:7f:8d:ac:16:70:46:1f:07:c0:a1:3e:ef:3a:1f:f7:17:53:8d:7a:ba:d3:91:b4 +-----BEGIN CERTIFICATE----- +MIIEqjCCA5KgAwIBAgIOLmoAAQACH9dSISwRXDswDQYJKoZIhvcNAQEFBQAwdjEL +MAkGA1UEBhMCREUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNV +BAsTGVRDIFRydXN0Q2VudGVyIENsYXNzIDIgQ0ExJTAjBgNVBAMTHFRDIFRydXN0 +Q2VudGVyIENsYXNzIDIgQ0EgSUkwHhcNMDYwMTEyMTQzODQzWhcNMjUxMjMxMjI1 +OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1c3RDZW50ZXIgR21i +SDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQTElMCMGA1UEAxMc +VEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAKuAh5uO8MN8h9foJIIRszzdQ2Lu+MNF2ujhoF/RKrLqk2jf +tMjWQ+nEdVl//OEd+DFwIxuInie5e/060smp6RQvkL4DUsFJzfb95AhmC1eKokKg +uNV/aVyQMrKXDcpK3EY+AlWJU+MaWss2xgdW94zPEfRMuzBwBJWl9jmM/XOBCH2J +XjIeIqkiRUuwZi4wzJ9l/fzLganx4Duvo4bRierERXlQXa7pIXSSTYtZgo+U4+lK +8edJsBTj9WLL1XK9H7nSn6DNqPoByNkN39r8R52zyFTfSUrxIan+GE7uSNQZu+99 +5OKdy1u2bv/jzVrndIIFuoAlOMvkaZ6vQaoahPUCAwEAAaOCATQwggEwMA8GA1Ud +EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTjq1RMgKHbVkO3 +kUrL84J6E1wIqzCB7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRy +dXN0Y2VudGVyLmRlL2NybC92Mi90Y19jbGFzc18yX2NhX0lJLmNybIaBn2xkYXA6 +Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBUcnVzdENlbnRlciUyMENsYXNz +JTIwMiUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21iSCxPVT1yb290 +Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u +TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEAjNfffu4bgBCzg/XbEeprS6iS +GNn3Bzn1LL4GdXpoUxUc6krtXvwjshOg0wn/9vYua0Fxec3ibf2uWWuFHbhOIprt +ZjluS5TmVfwLG4t3wVMTZonZKNaL80VKY7f9ewthXbhtvsPcW3nS7Yblok2+XnR8 +au0WOB9/WIFaGusyiC2y8zl3gK9etmF1KdsjTYjKUCjLhdLTEKJZbtOTVAB6okaV +hgWcqRmY5TFyDADiZ9lA4CQze28suVyrZZ0srHbqNZn1l7kPJOzHdiEoZa5X6AeI +dUpWoNIFOqTmjZKILPPy4cHGYdtBxceb9w4aUUXCYWvcZCcXjFq32nQozZfkvQ== +-----END CERTIFICATE----- + +# Issuer: CN=TC TrustCenter Class 3 CA II O=TC TrustCenter GmbH OU=TC TrustCenter Class 3 CA +# Subject: CN=TC TrustCenter Class 3 CA II O=TC TrustCenter GmbH OU=TC TrustCenter Class 3 CA +# Label: "TC TrustCenter Class 3 CA II" +# Serial: 1506523511417715638772220530020799 +# MD5 Fingerprint: 56:5f:aa:80:61:12:17:f6:67:21:e6:2b:6d:61:56:8e +# SHA1 Fingerprint: 80:25:ef:f4:6e:70:c8:d4:72:24:65:84:fe:40:3b:8a:8d:6a:db:f5 +# SHA256 Fingerprint: 8d:a0:84:fc:f9:9c:e0:77:22:f8:9b:32:05:93:98:06:fa:5c:b8:11:e1:c8:13:f6:a1:08:c7:d3:36:b3:40:8e +-----BEGIN CERTIFICATE----- +MIIEqjCCA5KgAwIBAgIOSkcAAQAC5aBd1j8AUb8wDQYJKoZIhvcNAQEFBQAwdjEL +MAkGA1UEBhMCREUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNV +BAsTGVRDIFRydXN0Q2VudGVyIENsYXNzIDMgQ0ExJTAjBgNVBAMTHFRDIFRydXN0 +Q2VudGVyIENsYXNzIDMgQ0EgSUkwHhcNMDYwMTEyMTQ0MTU3WhcNMjUxMjMxMjI1 +OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1c3RDZW50ZXIgR21i +SDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQTElMCMGA1UEAxMc +VEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBALTgu1G7OVyLBMVMeRwjhjEQY0NVJz/GRcekPewJDRoeIMJW +Ht4bNwcwIi9v8Qbxq63WyKthoy9DxLCyLfzDlml7forkzMA5EpBCYMnMNWju2l+Q +Vl/NHE1bWEnrDgFPZPosPIlY2C8u4rBo6SI7dYnWRBpl8huXJh0obazovVkdKyT2 +1oQDZogkAHhg8fir/gKya/si+zXmFtGt9i4S5Po1auUZuV3bOx4a+9P/FRQI2Alq +ukWdFHlgfa9Aigdzs5OW03Q0jTo3Kd5c7PXuLjHCINy+8U9/I1LZW+Jk2ZyqBwi1 +Rb3R0DHBq1SfqdLDYmAD8bs5SpJKPQq5ncWg/jcCAwEAAaOCATQwggEwMA8GA1Ud +EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTUovyfs8PYA9NX +XAek0CSnwPIA1DCB7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRy +dXN0Y2VudGVyLmRlL2NybC92Mi90Y19jbGFzc18zX2NhX0lJLmNybIaBn2xkYXA6 +Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBUcnVzdENlbnRlciUyMENsYXNz +JTIwMyUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21iSCxPVT1yb290 +Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u +TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEANmDkcPcGIEPZIxpC8vijsrlN +irTzwppVMXzEO2eatN9NDoqTSheLG43KieHPOh6sHfGcMrSOWXaiQYUlN6AT0PV8 +TtXqluJucsG7Kv5sbviRmEb8yRtXW+rIGjs/sFGYPAfaLFkB2otE6OF0/ado3VS6 +g0bsyEa1+K+XwDsJHI/OcpY9M1ZwvJbL2NV9IJqDnxrcOfHFcqMRA/07QlIp2+gB +95tejNaNhk4Z+rwcvsUhpYeeeC422wlxo3I0+GzjBgnyXlal092Y+tTmBvTwtiBj +S+opvaqCZh77gaqnN60TGOaSw4HBM7uIHqHn4rS9MWwOUT1v+5ZWgOI2F9Hc5A== +-----END CERTIFICATE----- + +# Issuer: CN=TC TrustCenter Universal CA I O=TC TrustCenter GmbH OU=TC TrustCenter Universal CA +# Subject: CN=TC TrustCenter Universal CA I O=TC TrustCenter GmbH OU=TC TrustCenter Universal CA +# Label: "TC TrustCenter Universal CA I" +# Serial: 601024842042189035295619584734726 +# MD5 Fingerprint: 45:e1:a5:72:c5:a9:36:64:40:9e:f5:e4:58:84:67:8c +# SHA1 Fingerprint: 6b:2f:34:ad:89:58:be:62:fd:b0:6b:5c:ce:bb:9d:d9:4f:4e:39:f3 +# SHA256 Fingerprint: eb:f3:c0:2a:87:89:b1:fb:7d:51:19:95:d6:63:b7:29:06:d9:13:ce:0d:5e:10:56:8a:8a:77:e2:58:61:67:e7 +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIOHaIAAQAC7LdggHiNtgYwDQYJKoZIhvcNAQEFBQAweTEL +MAkGA1UEBhMCREUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxJDAiBgNV +BAsTG1RDIFRydXN0Q2VudGVyIFVuaXZlcnNhbCBDQTEmMCQGA1UEAxMdVEMgVHJ1 +c3RDZW50ZXIgVW5pdmVyc2FsIENBIEkwHhcNMDYwMzIyMTU1NDI4WhcNMjUxMjMx +MjI1OTU5WjB5MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1c3RDZW50ZXIg +R21iSDEkMCIGA1UECxMbVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBMSYwJAYD +VQQDEx1UQyBUcnVzdENlbnRlciBVbml2ZXJzYWwgQ0EgSTCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAKR3I5ZEr5D0MacQ9CaHnPM42Q9e3s9B6DGtxnSR +JJZ4Hgmgm5qVSkr1YnwCqMqs+1oEdjneX/H5s7/zA1hV0qq34wQi0fiU2iIIAI3T +fCZdzHd55yx4Oagmcw6iXSVphU9VDprvxrlE4Vc93x9UIuVvZaozhDrzznq+VZeu +jRIPFDPiUHDDSYcTvFHe15gSWu86gzOSBnWLknwSaHtwag+1m7Z3W0hZneTvWq3z +wZ7U10VOylY0Ibw+F1tvdwxIAUMpsN0/lm7mlaoMwCC2/T42J5zjXM9OgdwZu5GQ +fezmlwQek8wiSdeXhrYTCjxDI3d+8NzmzSQfO4ObNDqDNOMCAwEAAaNjMGEwHwYD +VR0jBBgwFoAUkqR1LKSevoFE63n8isWVpesQdXMwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFJKkdSyknr6BROt5/IrFlaXrEHVzMA0G +CSqGSIb3DQEBBQUAA4IBAQAo0uCG1eb4e/CX3CJrO5UUVg8RMKWaTzqwOuAGy2X1 +7caXJ/4l8lfmXpWMPmRgFVp/Lw0BxbFg/UU1z/CyvwbZ71q+s2IhtNerNXxTPqYn +8aEt2hojnczd7Dwtnic0XQ/CNnm8yUpiLe1r2X1BQ3y2qsrtYbE3ghUJGooWMNjs +ydZHcnhLEEYUjl8Or+zHL6sQ17bxbuyGssLoDZJz3KL0Dzq/YSMQiZxIQG5wALPT +ujdEWBF6AmqI8Dc08BnprNRlc/ZpjGSUOnmFKbAWKwyCPwacx/0QK54PLLae4xW/ +2TYcuiUaUj0a7CIMHOCkoj3w6DnPgcB77V0fb8XQC9eY +-----END CERTIFICATE----- + +# Issuer: CN=Deutsche Telekom Root CA 2 O=Deutsche Telekom AG OU=T-TeleSec Trust Center +# Subject: CN=Deutsche Telekom Root CA 2 O=Deutsche Telekom AG OU=T-TeleSec Trust Center +# Label: "Deutsche Telekom Root CA 2" +# Serial: 38 +# MD5 Fingerprint: 74:01:4a:91:b1:08:c4:58:ce:47:cd:f0:dd:11:53:08 +# SHA1 Fingerprint: 85:a4:08:c0:9c:19:3e:5d:51:58:7d:cd:d6:13:30:fd:8c:de:37:bf +# SHA256 Fingerprint: b6:19:1a:50:d0:c3:97:7f:7d:a9:9b:cd:aa:c8:6a:22:7d:ae:b9:67:9e:c7:0b:a3:b0:c9:d9:22:71:c1:70:d3 +-----BEGIN CERTIFICATE----- +MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEc +MBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2Vj +IFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENB +IDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5MjM1OTAwWjBxMQswCQYDVQQGEwJE +RTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxl +U2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290 +IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEU +ha88EOQ5bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhC +QN/Po7qCWWqSG6wcmtoIKyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1Mjwr +rFDa1sPeg5TKqAyZMg4ISFZbavva4VhYAUlfckE8FQYBjl2tqriTtM2e66foai1S +NNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aKSe5TBY8ZTNXeWHmb0moc +QqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTVjlsB9WoH +txa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAP +BgNVHRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOC +AQEAlGRZrTlk5ynrE/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756Abrsp +tJh6sTtU6zkXR34ajgv8HzFZMQSyzhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpa +IzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8rZ7/gFnkm0W09juwzTkZmDLl +6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4Gdyd1Lx+4ivn+ +xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU +Cm26OWMohpLzGITY+9HPBVZkVw== +-----END CERTIFICATE----- + +# Issuer: CN=ComSign Secured CA O=ComSign +# Subject: CN=ComSign Secured CA O=ComSign +# Label: "ComSign Secured CA" +# Serial: 264725503855295744117309814499492384489 +# MD5 Fingerprint: 40:01:25:06:8d:21:43:6a:0e:43:00:9c:e7:43:f3:d5 +# SHA1 Fingerprint: f9:cd:0e:2c:da:76:24:c1:8f:bd:f0:f0:ab:b6:45:b8:f7:fe:d5:7a +# SHA256 Fingerprint: 50:79:41:c7:44:60:a0:b4:70:86:22:0d:4e:99:32:57:2a:b5:d1:b5:bb:cb:89:80:ab:1c:b1:76:51:a8:44:d2 +-----BEGIN CERTIFICATE----- +MIIDqzCCApOgAwIBAgIRAMcoRwmzuGxFjB36JPU2TukwDQYJKoZIhvcNAQEFBQAw +PDEbMBkGA1UEAxMSQ29tU2lnbiBTZWN1cmVkIENBMRAwDgYDVQQKEwdDb21TaWdu +MQswCQYDVQQGEwJJTDAeFw0wNDAzMjQxMTM3MjBaFw0yOTAzMTYxNTA0NTZaMDwx +GzAZBgNVBAMTEkNvbVNpZ24gU2VjdXJlZCBDQTEQMA4GA1UEChMHQ29tU2lnbjEL +MAkGA1UEBhMCSUwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGtWhf +HZQVw6QIVS3joFd67+l0Kru5fFdJGhFeTymHDEjWaueP1H5XJLkGieQcPOqs49oh +gHMhCu95mGwfCP+hUH3ymBvJVG8+pSjsIQQPRbsHPaHA+iqYHU4Gk/v1iDurX8sW +v+bznkqH7Rnqwp9D5PGBpX8QTz7RSmKtUxvLg/8HZaWSLWapW7ha9B20IZFKF3ue +Mv5WJDmyVIRD9YTC2LxBkMyd1mja6YJQqTtoz7VdApRgFrFD2UNd3V2Hbuq7s8lr +9gOUCXDeFhF6K+h2j0kQmHe5Y1yLM5d19guMsqtb3nQgJT/j8xH5h2iGNXHDHYwt +6+UarA9z1YJZQIDTAgMBAAGjgacwgaQwDAYDVR0TBAUwAwEB/zBEBgNVHR8EPTA7 +MDmgN6A1hjNodHRwOi8vZmVkaXIuY29tc2lnbi5jby5pbC9jcmwvQ29tU2lnblNl +Y3VyZWRDQS5jcmwwDgYDVR0PAQH/BAQDAgGGMB8GA1UdIwQYMBaAFMFL7XC29z58 +ADsAj8c+DkWfHl3sMB0GA1UdDgQWBBTBS+1wtvc+fAA7AI/HPg5Fnx5d7DANBgkq +hkiG9w0BAQUFAAOCAQEAFs/ukhNQq3sUnjO2QiBq1BW9Cav8cujvR3qQrFHBZE7p +iL1DRYHjZiM/EoZNGeQFsOY3wo3aBijJD4mkU6l1P7CW+6tMM1X5eCZGbxs2mPtC +dsGCuY7e+0X5YxtiOzkGynd6qDwJz2w2PQ8KRUtpFhpFfTMDZflScZAmlaxMDPWL +kz/MdXSFmLr/YnpNH4n+rr2UAJm/EaXc4HnFFgt9AmEd6oX5AhVP51qJThRv4zdL +hfXBPGHg/QVBspJ/wx2g0K5SZGBrGMYmnNj1ZOQ2GmKfig8+/21OGVZOIJFsnzQz +OjRXUDpvgV4GxvU+fE6OK85lBi5d0ipTdF7Tbieejw== +-----END CERTIFICATE----- + +# Issuer: CN=Cybertrust Global Root O=Cybertrust, Inc +# Subject: CN=Cybertrust Global Root O=Cybertrust, Inc +# Label: "Cybertrust Global Root" +# Serial: 4835703278459682877484360 +# MD5 Fingerprint: 72:e4:4a:87:e3:69:40:80:77:ea:bc:e3:f4:ff:f0:e1 +# SHA1 Fingerprint: 5f:43:e5:b1:bf:f8:78:8c:ac:1c:c7:ca:4a:9a:c6:22:2b:cc:34:c6 +# SHA256 Fingerprint: 96:0a:df:00:63:e9:63:56:75:0c:29:65:dd:0a:08:67:da:0b:9c:bd:6e:77:71:4a:ea:fb:23:49:ab:39:3d:a3 +-----BEGIN CERTIFICATE----- +MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYG +A1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2Jh +bCBSb290MB4XDTA2MTIxNTA4MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UE +ChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBS +b290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+Mi8vRRQZhP/8NN5 +7CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW0ozS +J8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2y +HLtgwEZLAfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iP +t3sMpTjr3kfb1V05/Iin89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNz +FtApD0mpSPCzqrdsxacwOUBdrsTiXSZT8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAY +XSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/ +MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2MDSgMqAw +hi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3Js +MB8GA1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUA +A4IBAQBW7wojoFROlZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMj +Wqd8BfP9IjsO0QbE2zZMcwSO5bAi5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUx +XOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2hO0j9n0Hq0V+09+zv+mKts2o +omcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+TX3EJIrduPuoc +A06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW +WL1WMRJOEcgh4LMRkWXbtKaIOM5V +-----END CERTIFICATE----- + +# Issuer: O=Chunghwa Telecom Co., Ltd. OU=ePKI Root Certification Authority +# Subject: O=Chunghwa Telecom Co., Ltd. OU=ePKI Root Certification Authority +# Label: "ePKI Root Certification Authority" +# Serial: 28956088682735189655030529057352760477 +# MD5 Fingerprint: 1b:2e:00:ca:26:06:90:3d:ad:fe:6f:15:68:d3:6b:b3 +# SHA1 Fingerprint: 67:65:0d:f1:7e:8e:7e:5b:82:40:a4:f4:56:4b:cf:e2:3d:69:c6:f0 +# SHA256 Fingerprint: c0:a6:f4:dc:63:a2:4b:fd:cf:54:ef:2a:6a:08:2a:0a:72:de:35:80:3e:2f:f5:ff:52:7a:e5:d8:72:06:df:d5 +-----BEGIN CERTIFICATE----- +MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBe +MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0 +ZC4xKjAoBgNVBAsMIWVQS0kgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe +Fw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMxMjdaMF4xCzAJBgNVBAYTAlRXMSMw +IQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEqMCgGA1UECwwhZVBL +SSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAH +SyZbCUNsIZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAh +ijHyl3SJCRImHJ7K2RKilTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3X +DZoTM1PRYfl61dd4s5oz9wCGzh1NlDivqOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1 +TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX12ruOzjjK9SXDrkb5wdJ +fzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0OWQqraffA +sgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uU +WH1+ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLS +nT0IFaUQAS2zMnaolQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pH +dmX2Os+PYhcZewoozRrSgx4hxyy/vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJip +NiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXiZo1jDiVN1Rmy5nk3pyKdVDEC +AwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/QkqiMAwGA1UdEwQF +MAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH +ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGB +uvl2ICO1J2B01GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6Yl +PwZpVnPDimZI+ymBV3QGypzqKOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkP +JXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdVxrsStZf0X4OFunHB2WyBEXYKCrC/ +gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEPNXubrjlpC2JgQCA2 +j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+rGNm6 +5ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUB +o2M3IUxExJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS +/jQ6fbjpKdx2qcgw+BRxgMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2z +Gp1iro2C6pSe3VkQw63d4k3jMdXH7OjysP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTE +W9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmODBCEIZ43ygknQW/2xzQ+D +hNQ+IIX3Sj0rnP0qCglN6oH4EZw= +-----END CERTIFICATE----- + +# Issuer: CN=TÜBİTAK UEKAE Kök Sertifika Hizmet Sağlayıcısı - Sürüm 3 O=Türkiye Bilimsel ve Teknolojik Araştırma Kurumu - TÜBİTAK OU=Ulusal Elektronik ve Kriptoloji Araştırma Enstitüsü - UEKAE/Kamu Sertifikasyon Merkezi +# Subject: CN=TÜBİTAK UEKAE Kök Sertifika Hizmet Sağlayıcısı - Sürüm 3 O=Türkiye Bilimsel ve Teknolojik Araştırma Kurumu - TÜBİTAK OU=Ulusal Elektronik ve Kriptoloji Araştırma Enstitüsü - UEKAE/Kamu Sertifikasyon Merkezi +# Label: "T\xc3\x9c\x42\xC4\xB0TAK UEKAE K\xC3\xB6k Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1 - S\xC3\xBCr\xC3\xBCm 3" +# Serial: 17 +# MD5 Fingerprint: ed:41:f5:8c:50:c5:2b:9c:73:e6:ee:6c:eb:c2:a8:26 +# SHA1 Fingerprint: 1b:4b:39:61:26:27:6b:64:91:a2:68:6d:d7:02:43:21:2d:1f:1d:96 +# SHA256 Fingerprint: e4:c7:34:30:d7:a5:b5:09:25:df:43:37:0a:0d:21:6e:9a:79:b9:d6:db:83:73:a0:c6:9e:b1:cc:31:c7:c5:2a +-----BEGIN CERTIFICATE----- +MIIFFzCCA/+gAwIBAgIBETANBgkqhkiG9w0BAQUFADCCASsxCzAJBgNVBAYTAlRS +MRgwFgYDVQQHDA9HZWJ6ZSAtIEtvY2FlbGkxRzBFBgNVBAoMPlTDvHJraXllIEJp +bGltc2VsIHZlIFRla25vbG9qaWsgQXJhxZ90xLFybWEgS3VydW11IC0gVMOcQsSw +VEFLMUgwRgYDVQQLDD9VbHVzYWwgRWxla3Ryb25payB2ZSBLcmlwdG9sb2ppIEFy +YcWfdMSxcm1hIEVuc3RpdMO8c8O8IC0gVUVLQUUxIzAhBgNVBAsMGkthbXUgU2Vy +dGlmaWthc3lvbiBNZXJrZXppMUowSAYDVQQDDEFUw5xCxLBUQUsgVUVLQUUgS8O2 +ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSAtIFPDvHLDvG0gMzAe +Fw0wNzA4MjQxMTM3MDdaFw0xNzA4MjExMTM3MDdaMIIBKzELMAkGA1UEBhMCVFIx +GDAWBgNVBAcMD0dlYnplIC0gS29jYWVsaTFHMEUGA1UECgw+VMO8cmtpeWUgQmls +aW1zZWwgdmUgVGVrbm9sb2ppayBBcmHFn3TEsXJtYSBLdXJ1bXUgLSBUw5xCxLBU +QUsxSDBGBgNVBAsMP1VsdXNhbCBFbGVrdHJvbmlrIHZlIEtyaXB0b2xvamkgQXJh +xZ90xLFybWEgRW5zdGl0w7xzw7wgLSBVRUtBRTEjMCEGA1UECwwaS2FtdSBTZXJ0 +aWZpa2FzeW9uIE1lcmtlemkxSjBIBgNVBAMMQVTDnELEsFRBSyBVRUtBRSBLw7Zr +IFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIC0gU8O8csO8bSAzMIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAim1L/xCIOsP2fpTo6iBkcK4h +gb46ezzb8R1Sf1n68yJMlaCQvEhOEav7t7WNeoMojCZG2E6VQIdhn8WebYGHV2yK +O7Rm6sxA/OOqbLLLAdsyv9Lrhc+hDVXDWzhXcLh1xnnRFDDtG1hba+818qEhTsXO +fJlfbLm4IpNQp81McGq+agV/E5wrHur+R84EpW+sky58K5+eeROR6Oqeyjh1jmKw +lZMq5d/pXpduIF9fhHpEORlAHLpVK/swsoHvhOPc7Jg4OQOFCKlUAwUp8MmPi+oL +hmUZEdPpCSPeaJMDyTYcIW7OjGbxmTDY17PDHfiBLqi9ggtm/oLL4eAagsNAgQID +AQABo0IwQDAdBgNVHQ4EFgQUvYiHyY/2pAoLquvF/pEjnatKijIwDgYDVR0PAQH/ +BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAB18+kmP +NOm3JpIWmgV050vQbTlswyb2zrgxvMTfvCr4N5EY3ATIZJkrGG2AA1nJrvhY0D7t +wyOfaTyGOBye79oneNGEN3GKPEs5z35FBtYt2IpNeBLWrcLTy9LQQfMmNkqblWwM +7uXRQydmwYj3erMgbOqwaSvHIOgMA8RBBZniP+Rr+KCGgceExh/VS4ESshYhLBOh +gLJeDEoTniDYYkCrkOpkSi+sDQESeUWoL4cZaMjihccwsnX5OD+ywJO0a+IDRM5n +oN+J1q2MdqMTw5RhK2vZbMEHCiIHhWyFJEapvj+LeISCfiQMnf2BN+MlqO02TpUs +yZyQ2uypQjyttgI= +-----END CERTIFICATE----- + +# Issuer: CN=Buypass Class 2 CA 1 O=Buypass AS-983163327 +# Subject: CN=Buypass Class 2 CA 1 O=Buypass AS-983163327 +# Label: "Buypass Class 2 CA 1" +# Serial: 1 +# MD5 Fingerprint: b8:08:9a:f0:03:cc:1b:0d:c8:6c:0b:76:a1:75:64:23 +# SHA1 Fingerprint: a0:a1:ab:90:c9:fc:84:7b:3b:12:61:e8:97:7d:5f:d3:22:61:d3:cc +# SHA256 Fingerprint: 0f:4e:9c:dd:26:4b:02:55:50:d1:70:80:63:40:21:4f:e9:44:34:c9:b0:2f:69:7e:c7:10:fc:5f:ea:fb:5e:38 +-----BEGIN CERTIFICATE----- +MIIDUzCCAjugAwIBAgIBATANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3Mg +Q2xhc3MgMiBDQSAxMB4XDTA2MTAxMzEwMjUwOVoXDTE2MTAxMzEwMjUwOVowSzEL +MAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MR0wGwYD +VQQDDBRCdXlwYXNzIENsYXNzIDIgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAIs8B0XY9t/mx8q6jUPFR42wWsE425KEHK8T1A9vNkYgxC7McXA0 +ojTTNy7Y3Tp3L8DrKehc0rWpkTSHIln+zNvnma+WwajHQN2lFYxuyHyXA8vmIPLX +l18xoS830r7uvqmtqEyeIWZDO6i88wmjONVZJMHCR3axiFyCO7srpgTXjAePzdVB +HfCuuCkslFJgNJQ72uA40Z0zPhX0kzLFANq1KWYOOngPIVJfAuWSeyXTkh4vFZ2B +5J2O6O+JzhRMVB0cgRJNcKi+EAUXfh/RuFdV7c27UsKwHnjCTTZoy1YmwVLBvXb3 +WNVyfh9EdrsAiR0WnVE1703CVu9r4Iw7DekCAwEAAaNCMEAwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUP42aWYv8e3uco684sDntkHGA1sgwDgYDVR0PAQH/BAQD +AgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAVGn4TirnoB6NLJzKyQJHyIdFkhb5jatLP +gcIV1Xp+DCmsNx4cfHZSldq1fyOhKXdlyTKdqC5Wq2B2zha0jX94wNWZUYN/Xtm+ +DKhQ7SLHrQVMdvvt7h5HZPb3J31cKA9FxVxiXqaakZG3Uxcu3K1gnZZkOb1naLKu +BctN518fV4bVIJwo+28TOPX2EZL2fZleHwzoq0QkKXJAPTZSr4xYkHPB7GEseaHs +h7U/2k3ZIQAw3pDaDtMaSKk+hQsUi4y8QZ5q9w5wwDX3OaJdZtB7WZ+oRxKaJyOk +LY4ng5IgodcVf/EuGO70SH8vf/GhGLWhC5SgYiAynB321O+/TIho +-----END CERTIFICATE----- + +# Issuer: CN=Buypass Class 3 CA 1 O=Buypass AS-983163327 +# Subject: CN=Buypass Class 3 CA 1 O=Buypass AS-983163327 +# Label: "Buypass Class 3 CA 1" +# Serial: 2 +# MD5 Fingerprint: df:3c:73:59:81:e7:39:50:81:04:4c:34:a2:cb:b3:7b +# SHA1 Fingerprint: 61:57:3a:11:df:0e:d8:7e:d5:92:65:22:ea:d0:56:d7:44:b3:23:71 +# SHA256 Fingerprint: b7:b1:2b:17:1f:82:1d:aa:99:0c:d0:fe:50:87:b1:28:44:8b:a8:e5:18:4f:84:c5:1e:02:b5:c8:fb:96:2b:24 +-----BEGIN CERTIFICATE----- +MIIDUzCCAjugAwIBAgIBAjANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3Mg +Q2xhc3MgMyBDQSAxMB4XDTA1MDUwOTE0MTMwM1oXDTE1MDUwOTE0MTMwM1owSzEL +MAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MR0wGwYD +VQQDDBRCdXlwYXNzIENsYXNzIDMgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAKSO13TZKWTeXx+HgJHqTjnmGcZEC4DVC69TB4sSveZn8AKxifZg +isRbsELRwCGoy+Gb72RRtqfPFfV0gGgEkKBYouZ0plNTVUhjP5JW3SROjvi6K//z +NIqeKNc0n6wv1g/xpC+9UrJJhW05NfBEMJNGJPO251P7vGGvqaMU+8IXF4Rs4HyI ++MkcVyzwPX6UvCWThOiaAJpFBUJXgPROztmuOfbIUxAMZTpHe2DC1vqRycZxbL2R +hzyRhkmr8w+gbCZ2Xhysm3HljbybIR6c1jh+JIAVMYKWsUnTYjdbiAwKYjT+p0h+ +mbEwi5A3lRyoH6UsjfRVyNvdWQrCrXig9IsCAwEAAaNCMEAwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUOBTmyPCppAP0Tj4io1vy1uCtQHQwDgYDVR0PAQH/BAQD +AgEGMA0GCSqGSIb3DQEBBQUAA4IBAQABZ6OMySU9E2NdFm/soT4JXJEVKirZgCFP +Bdy7pYmrEzMqnji3jG8CcmPHc3ceCQa6Oyh7pEfJYWsICCD8igWKH7y6xsL+z27s +EzNxZy5p+qksP2bAEllNC1QCkoS72xLvg3BweMhT+t/Gxv/ciC8HwEmdMldg0/L2 +mSlf56oBzKwzqBwKu5HEA6BvtjT5htOzdlSY9EqBs1OdTUDs5XcTRa9bqh/YL0yC +e/4qxFi7T/ye/QNlGioOw6UgFpRreaaiErS7GqQjel/wroQk5PMr+4okoyeYZdow +dXb8GZHo2+ubPzK/QJcHJrrM85SFSnonk8+QQtS4Wxam58tAA915 +-----END CERTIFICATE----- + +# Issuer: CN=EBG Elektronik Sertifika Hizmet Sağlayıcısı O=EBG Bilişim Teknolojileri ve Hizmetleri A.Ş. +# Subject: CN=EBG Elektronik Sertifika Hizmet Sağlayıcısı O=EBG Bilişim Teknolojileri ve Hizmetleri A.Ş. +# Label: "EBG Elektronik Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1" +# Serial: 5525761995591021570 +# MD5 Fingerprint: 2c:20:26:9d:cb:1a:4a:00:85:b5:b7:5a:ae:c2:01:37 +# SHA1 Fingerprint: 8c:96:ba:eb:dd:2b:07:07:48:ee:30:32:66:a0:f3:98:6e:7c:ae:58 +# SHA256 Fingerprint: 35:ae:5b:dd:d8:f7:ae:63:5c:ff:ba:56:82:a8:f0:0b:95:f4:84:62:c7:10:8e:e9:a0:e5:29:2b:07:4a:af:b2 +-----BEGIN CERTIFICATE----- +MIIF5zCCA8+gAwIBAgIITK9zQhyOdAIwDQYJKoZIhvcNAQEFBQAwgYAxODA2BgNV +BAMML0VCRyBFbGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sx +c8SxMTcwNQYDVQQKDC5FQkcgQmlsacWfaW0gVGVrbm9sb2ppbGVyaSB2ZSBIaXpt +ZXRsZXJpIEEuxZ4uMQswCQYDVQQGEwJUUjAeFw0wNjA4MTcwMDIxMDlaFw0xNjA4 +MTQwMDMxMDlaMIGAMTgwNgYDVQQDDC9FQkcgRWxla3Ryb25payBTZXJ0aWZpa2Eg +SGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTE3MDUGA1UECgwuRUJHIEJpbGnFn2ltIFRl +a25vbG9qaWxlcmkgdmUgSGl6bWV0bGVyaSBBLsWeLjELMAkGA1UEBhMCVFIwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDuoIRh0DpqZhAy2DE4f6en5f2h +4fuXd7hxlugTlkaDT7byX3JWbhNgpQGR4lvFzVcfd2NR/y8927k/qqk153nQ9dAk +tiHq6yOU/im/+4mRDGSaBUorzAzu8T2bgmmkTPiab+ci2hC6X5L8GCcKqKpE+i4s +tPtGmggDg3KriORqcsnlZR9uKg+ds+g75AxuetpX/dfreYteIAbTdgtsApWjluTL +dlHRKJ2hGvxEok3MenaoDT2/F08iiFD9rrbskFBKW5+VQarKD7JK/oCZTqNGFav4 +c0JqwmZ2sQomFd2TkuzbqV9UIlKRcF0T6kjsbgNs2d1s/OsNA/+mgxKb8amTD8Um +TDGyY5lhcucqZJnSuOl14nypqZoaqsNW2xCaPINStnuWt6yHd6i58mcLlEOzrz5z ++kI2sSXFCjEmN1ZnuqMLfdb3ic1nobc6HmZP9qBVFCVMLDMNpkGMvQQxahByCp0O +Lna9XvNRiYuoP1Vzv9s6xiQFlpJIqkuNKgPlV5EQ9GooFW5Hd4RcUXSfGenmHmMW +OeMRFeNYGkS9y8RsZteEBt8w9DeiQyJ50hBs37vmExH8nYQKE3vwO9D8owrXieqW +fo1IhR5kX9tUoqzVegJ5a9KK8GfaZXINFHDk6Y54jzJ0fFfy1tb0Nokb+Clsi7n2 +l9GkLqq+CxnCRelwXQIDAJ3Zo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB +/wQEAwIBBjAdBgNVHQ4EFgQU587GT/wWZ5b6SqMHwQSny2re2kcwHwYDVR0jBBgw +FoAU587GT/wWZ5b6SqMHwQSny2re2kcwDQYJKoZIhvcNAQEFBQADggIBAJuYml2+ +8ygjdsZs93/mQJ7ANtyVDR2tFcU22NU57/IeIl6zgrRdu0waypIN30ckHrMk2pGI +6YNw3ZPX6bqz3xZaPt7gyPvT/Wwp+BVGoGgmzJNSroIBk5DKd8pNSe/iWtkqvTDO +TLKBtjDOWU/aWR1qeqRFsIImgYZ29fUQALjuswnoT4cCB64kXPBfrAowzIpAoHME +wfuJJPaaHFy3PApnNgUIMbOv2AFoKuB4j3TeuFGkjGwgPaL7s9QJ/XvCgKqTbCmY +Iai7FvOpEl90tYeY8pUm3zTvilORiF0alKM/fCL414i6poyWqD1SNGKfAB5UVUJn +xk1Gj7sURT0KlhaOEKGXmdXTMIXM3rRyt7yKPBgpaP3ccQfuJDlq+u2lrDgv+R4Q +DgZxGhBM/nV+/x5XOULK1+EVoVZVWRvRo68R2E7DpSvvkL/A7IITW43WciyTTo9q +Kd+FPNMN4KIYEsxVL0e3p5sC/kH2iExt2qkBR4NkJ2IQgtYSe14DHzSpyZH+r11t +hie3I6p1GMog57AP14kOpmciY/SDQSsGS7tY1dHXt7kQY9iJSrSq3RZj9W6+YKH4 +7ejWkE8axsWgKdOnIaj1Wjz3x0miIZpKlVIglnKaZsv30oZDfCK+lvm9AahH3eU7 +QPl1K5srRmSGjR70j/sHd9DqSaIcjVIUpgqT +-----END CERTIFICATE----- + +# Issuer: O=certSIGN OU=certSIGN ROOT CA +# Subject: O=certSIGN OU=certSIGN ROOT CA +# Label: "certSIGN ROOT CA" +# Serial: 35210227249154 +# MD5 Fingerprint: 18:98:c0:d6:e9:3a:fc:f9:b0:f5:0c:f7:4b:01:44:17 +# SHA1 Fingerprint: fa:b7:ee:36:97:26:62:fb:2d:b0:2a:f6:bf:03:fd:e8:7c:4b:2f:9b +# SHA256 Fingerprint: ea:a9:62:c4:fa:4a:6b:af:eb:e4:15:19:6d:35:1c:cd:88:8d:4f:53:f3:fa:8a:e6:d7:c4:66:a9:4e:60:42:bb +-----BEGIN CERTIFICATE----- +MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYT +AlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBD +QTAeFw0wNjA3MDQxNzIwMDRaFw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJP +MREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7IJUqOtdu0KBuqV5Do +0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHHrfAQ +UySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5d +RdY4zTW2ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQ +OA7+j0xbm0bqQfWwCHTD0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwv +JoIQ4uNllAoEwF73XVv4EOLQunpL+943AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08C +AwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAcYwHQYDVR0O +BBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IBAQA+0hyJ +LjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecY +MnQ8SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ +44gx+FkagQnIl6Z0x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6I +Jd1hJyMctTEHBDa0GpC9oHRxUIltvBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNw +i/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7NzTogVZ96edhBiIL5VaZVDADlN +9u6wWk5JRFRYX0KD +-----END CERTIFICATE----- + +# Issuer: CN=CNNIC ROOT O=CNNIC +# Subject: CN=CNNIC ROOT O=CNNIC +# Label: "CNNIC ROOT" +# Serial: 1228079105 +# MD5 Fingerprint: 21:bc:82:ab:49:c4:13:3b:4b:b2:2b:5c:6b:90:9c:19 +# SHA1 Fingerprint: 8b:af:4c:9b:1d:f0:2a:92:f7:da:12:8e:b9:1b:ac:f4:98:60:4b:6f +# SHA256 Fingerprint: e2:83:93:77:3d:a8:45:a6:79:f2:08:0c:c7:fb:44:a3:b7:a1:c3:79:2c:b7:eb:77:29:fd:cb:6a:8d:99:ae:a7 +-----BEGIN CERTIFICATE----- +MIIDVTCCAj2gAwIBAgIESTMAATANBgkqhkiG9w0BAQUFADAyMQswCQYDVQQGEwJD +TjEOMAwGA1UEChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1QwHhcNMDcwNDE2 +MDcwOTE0WhcNMjcwNDE2MDcwOTE0WjAyMQswCQYDVQQGEwJDTjEOMAwGA1UEChMF +Q05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1QwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDTNfc/c3et6FtzF8LRb+1VvG7q6KR5smzDo+/hn7E7SIX1mlwh +IhAsxYLO2uOabjfhhyzcuQxauohV3/2q2x8x6gHx3zkBwRP9SFIhxFXf2tizVHa6 +dLG3fdfA6PZZxU3Iva0fFNrfWEQlMhkqx35+jq44sDB7R3IJMfAw28Mbdim7aXZO +V/kbZKKTVrdvmW7bCgScEeOAH8tjlBAKqeFkgjH5jCftppkA9nCTGPihNIaj3XrC +GHn2emU1z5DrvTOTn1OrczvmmzQgLx3vqR1jGqCA2wMv+SYahtKNu6m+UjqHZ0gN +v7Sg2Ca+I19zN38m5pIEo3/PIKe38zrKy5nLAgMBAAGjczBxMBEGCWCGSAGG+EIB +AQQEAwIABzAfBgNVHSMEGDAWgBRl8jGtKvf33VKWCscCwQ7vptU7ETAPBgNVHRMB +Af8EBTADAQH/MAsGA1UdDwQEAwIB/jAdBgNVHQ4EFgQUZfIxrSr3991SlgrHAsEO +76bVOxEwDQYJKoZIhvcNAQEFBQADggEBAEs17szkrr/Dbq2flTtLP1se31cpolnK +OOK5Gv+e5m4y3R6u6jW39ZORTtpC4cMXYFDy0VwmuYK36m3knITnA3kXr5g9lNvH +ugDnuL8BV8F3RTIMO/G0HAiw/VGgod2aHRM2mm23xzy54cXZF/qD1T0VoDy7Hgvi +yJA/qIYM/PmLXoXLT1tLYhFHxUV8BS9BsZ4QaRuZluBVeftOhpm4lNqGOGqTo+fL +buXf6iFViZx9fX+Y9QCJ7uOEwFyWtcVG6kbghVW2G8kS1sHNzYDzAgE8yGnLRUhj +2JTQ7IUOO04RZfSCjKY9ri4ilAnIXOo8gV0WKgOXFlUJ24pBgp5mmxE= +-----END CERTIFICATE----- + +# Issuer: O=Japanese Government OU=ApplicationCA +# Subject: O=Japanese Government OU=ApplicationCA +# Label: "ApplicationCA - Japanese Government" +# Serial: 49 +# MD5 Fingerprint: 7e:23:4e:5b:a7:a5:b4:25:e9:00:07:74:11:62:ae:d6 +# SHA1 Fingerprint: 7f:8a:b0:cf:d0:51:87:6a:66:f3:36:0f:47:c8:8d:8c:d3:35:fc:74 +# SHA256 Fingerprint: 2d:47:43:7d:e1:79:51:21:5a:12:f3:c5:8e:51:c7:29:a5:80:26:ef:1f:cc:0a:5f:b3:d9:dc:01:2f:60:0d:19 +-----BEGIN CERTIFICATE----- +MIIDoDCCAoigAwIBAgIBMTANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJKUDEc +MBoGA1UEChMTSmFwYW5lc2UgR292ZXJubWVudDEWMBQGA1UECxMNQXBwbGljYXRp +b25DQTAeFw0wNzEyMTIxNTAwMDBaFw0xNzEyMTIxNTAwMDBaMEMxCzAJBgNVBAYT +AkpQMRwwGgYDVQQKExNKYXBhbmVzZSBHb3Zlcm5tZW50MRYwFAYDVQQLEw1BcHBs +aWNhdGlvbkNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp23gdE6H +j6UG3mii24aZS2QNcfAKBZuOquHMLtJqO8F6tJdhjYq+xpqcBrSGUeQ3DnR4fl+K +f5Sk10cI/VBaVuRorChzoHvpfxiSQE8tnfWuREhzNgaeZCw7NCPbXCbkcXmP1G55 +IrmTwcrNwVbtiGrXoDkhBFcsovW8R0FPXjQilbUfKW1eSvNNcr5BViCH/OlQR9cw +FO5cjFW6WY2H/CPek9AEjP3vbb3QesmlOmpyM8ZKDQUXKi17safY1vC+9D/qDiht +QWEjdnjDuGWk81quzMKq2edY3rZ+nYVunyoKb58DKTCXKB28t89UKU5RMfkntigm +/qJj5kEW8DOYRwIDAQABo4GeMIGbMB0GA1UdDgQWBBRUWssmP3HMlEYNllPqa0jQ +k/5CdTAOBgNVHQ8BAf8EBAMCAQYwWQYDVR0RBFIwUKROMEwxCzAJBgNVBAYTAkpQ +MRgwFgYDVQQKDA/ml6XmnKzlm73mlL/lupwxIzAhBgNVBAsMGuOCouODl+ODquOC +seODvOOCt+ODp+ODs0NBMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD +ggEBADlqRHZ3ODrso2dGD/mLBqj7apAxzn7s2tGJfHrrLgy9mTLnsCTWw//1sogJ +hyzjVOGjprIIC8CFqMjSnHH2HZ9g/DgzE+Ge3Atf2hZQKXsvcJEPmbo0NI2VdMV+ +eKlmXb3KIXdCEKxmJj3ekav9FfBv7WxfEPjzFvYDio+nEhEMy/0/ecGc/WLuo89U +DNErXxc+4z6/wCs+CZv+iKZ+tJIX/COUgb1up8WMwusRRdv4QcmWdupwX3kSa+Sj +B1oF7ydJzyGfikwJcGapJsErEU4z0g781mzSDjJkaP+tBXhfAx2o45CsJOAPQKdL +rosot4LKGAfmt1t06SAZf7IbiVQ= +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Primary Certification Authority - G3 O=GeoTrust Inc. OU=(c) 2008 GeoTrust Inc. - For authorized use only +# Subject: CN=GeoTrust Primary Certification Authority - G3 O=GeoTrust Inc. OU=(c) 2008 GeoTrust Inc. - For authorized use only +# Label: "GeoTrust Primary Certification Authority - G3" +# Serial: 28809105769928564313984085209975885599 +# MD5 Fingerprint: b5:e8:34:36:c9:10:44:58:48:70:6d:2e:83:d4:b8:05 +# SHA1 Fingerprint: 03:9e:ed:b8:0b:e7:a0:3c:69:53:89:3b:20:d2:d9:32:3a:4c:2a:fd +# SHA256 Fingerprint: b4:78:b8:12:25:0d:f8:78:63:5c:2a:a7:ec:7d:15:5e:aa:62:5e:e8:29:16:e2:cd:29:43:61:88:6c:d1:fb:d4 +-----BEGIN CERTIFICATE----- +MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCB +mDELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsT +MChjKSAyMDA4IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25s +eTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv +cml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIzNTk1OVowgZgxCzAJ +BgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg +MjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0 +BgNVBAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz ++uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5jK/BGvESyiaHAKAxJcCGVn2TAppMSAmUm +hsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdEc5IiaacDiGydY8hS2pgn +5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3CIShwiP/W +JmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exAL +DmKudlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZC +huOl1UcCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw +HQYDVR0OBBYEFMR5yo6hTgMdHNxr2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IB +AQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9cr5HqQ6XErhK8WTTOd8lNNTB +zU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbEAp7aDHdlDkQN +kv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD +AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUH +SJsMC8tJP33st/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2G +spki4cErx5z481+oghLrGREt +-----END CERTIFICATE----- + +# Issuer: CN=thawte Primary Root CA - G2 O=thawte, Inc. OU=(c) 2007 thawte, Inc. - For authorized use only +# Subject: CN=thawte Primary Root CA - G2 O=thawte, Inc. OU=(c) 2007 thawte, Inc. - For authorized use only +# Label: "thawte Primary Root CA - G2" +# Serial: 71758320672825410020661621085256472406 +# MD5 Fingerprint: 74:9d:ea:60:24:c4:fd:22:53:3e:cc:3a:72:d9:29:4f +# SHA1 Fingerprint: aa:db:bc:22:23:8f:c4:01:a1:27:bb:38:dd:f4:1d:db:08:9e:f0:12 +# SHA256 Fingerprint: a4:31:0d:50:af:18:a6:44:71:90:37:2a:86:af:af:8b:95:1f:fb:43:1d:83:7f:1e:56:88:b4:59:71:ed:15:57 +-----BEGIN CERTIFICATE----- +MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMp +IDIwMDcgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAi +BgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMjAeFw0wNzExMDUwMDAw +MDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh +d3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBGb3Ig +YXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9v +dCBDQSAtIEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/ +BebfowJPDQfGAFG6DAJSLSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6 +papu+7qzcMBniKI11KOasf2twu8x+qi58/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUmtgAMADna3+FGO6Lts6K +DPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUNG4k8VIZ3 +KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41ox +XZ3Krr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg== +-----END CERTIFICATE----- + +# Issuer: CN=thawte Primary Root CA - G3 O=thawte, Inc. OU=Certification Services Division/(c) 2008 thawte, Inc. - For authorized use only +# Subject: CN=thawte Primary Root CA - G3 O=thawte, Inc. OU=Certification Services Division/(c) 2008 thawte, Inc. - For authorized use only +# Label: "thawte Primary Root CA - G3" +# Serial: 127614157056681299805556476275995414779 +# MD5 Fingerprint: fb:1b:5d:43:8a:94:cd:44:c6:76:f2:43:4b:47:e7:31 +# SHA1 Fingerprint: f1:8b:53:8d:1b:e9:03:b6:a6:f0:56:43:5b:17:15:89:ca:f3:6b:f2 +# SHA256 Fingerprint: 4b:03:f4:58:07:ad:70:f2:1b:fc:2c:ae:71:c9:fd:e4:60:4c:06:4c:f5:ff:b6:86:ba:e5:db:aa:d7:fd:d3:4c +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCB +rjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf +Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw +MDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNV +BAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0wODA0MDIwMDAwMDBa +Fw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3Rl +LCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9u +MTgwNgYDVQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXpl +ZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEcz +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsr8nLPvb2FvdeHsbnndm +gcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2AtP0LMqmsywCPLLEHd5N/8 +YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC+BsUa0Lf +b1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS9 +9irY7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2S +zhkGcuYMXDhpxwTWvGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUk +OQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNV +HQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJKoZIhvcNAQELBQADggEBABpA +2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweKA3rD6z8KLFIW +oCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu +t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7c +KUGRIjxpp7sC8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fM +m7v/OeZWYdMKp8RcTGB7BXcmer/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZu +MdRAGmI0Nj81Aa6sY6A= +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Primary Certification Authority - G2 O=GeoTrust Inc. OU=(c) 2007 GeoTrust Inc. - For authorized use only +# Subject: CN=GeoTrust Primary Certification Authority - G2 O=GeoTrust Inc. OU=(c) 2007 GeoTrust Inc. - For authorized use only +# Label: "GeoTrust Primary Certification Authority - G2" +# Serial: 80682863203381065782177908751794619243 +# MD5 Fingerprint: 01:5e:d8:6b:bd:6f:3d:8e:a1:31:f8:12:e0:98:73:6a +# SHA1 Fingerprint: 8d:17:84:d5:37:f3:03:7d:ec:70:fe:57:8b:51:9a:99:e6:10:d7:b0 +# SHA256 Fingerprint: 5e:db:7a:c4:3b:82:a0:6a:87:61:e8:d7:be:49:79:eb:f2:61:1f:7d:d7:9b:f9:1c:1c:6b:56:6a:21:9e:d7:66 +-----BEGIN CERTIFICATE----- +MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDEL +MAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChj +KSAyMDA3IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2 +MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 +eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1OVowgZgxCzAJBgNV +BAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykgMjAw +NyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNV +BAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBH +MjB2MBAGByqGSM49AgEGBSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcL +So17VDs6bl8VAsBQps8lL33KSLjHUGMcKiEIfJo22Av+0SbFWDEwKCXzXV2juLal +tJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+EVXVMAoG +CCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGT +qQ7mndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBucz +rD6ogRLQy7rQkgu2npaqBA+K +-----END CERTIFICATE----- + +# Issuer: CN=VeriSign Universal Root Certification Authority O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2008 VeriSign, Inc. - For authorized use only +# Subject: CN=VeriSign Universal Root Certification Authority O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2008 VeriSign, Inc. - For authorized use only +# Label: "VeriSign Universal Root Certification Authority" +# Serial: 85209574734084581917763752644031726877 +# MD5 Fingerprint: 8e:ad:b5:01:aa:4d:81:e4:8c:1d:d1:e1:14:00:95:19 +# SHA1 Fingerprint: 36:79:ca:35:66:87:72:30:4d:30:a5:fb:87:3b:0f:a7:7b:b7:0d:54 +# SHA256 Fingerprint: 23:99:56:11:27:a5:71:25:de:8c:ef:ea:61:0d:df:2f:a0:78:b5:c8:06:7f:4e:82:82:90:bf:b8:60:e8:4b:3c +-----BEGIN CERTIFICATE----- +MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCB +vTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL +ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJp +U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MTgwNgYDVQQDEy9W +ZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe +Fw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJVUzEX +MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0 +IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9y +IGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNh +bCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj1mCOkdeQmIN65lgZOIzF +9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGPMiJhgsWH +H26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+H +LL729fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN +/BMReYTtXlT2NJ8IAfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPT +rJ9VAMf2CGqUuV/c4DPxhGD5WycRtPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1Ud +EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0GCCsGAQUFBwEMBGEwX6FdoFsw +WTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2Oa8PPgGrUSBgs +exkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud +DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4 +sAPmLGd75JR3Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+ +seQxIcaBlVZaDrHC1LGmWazxY8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz +4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTxP/jgdFcrGJ2BtMQo2pSXpXDrrB2+ +BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+PwGZsY6rp2aQW9IHR +lRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4mJO3 +7M2CYfE45k+XmCpajQ== +-----END CERTIFICATE----- + +# Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G4 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2007 VeriSign, Inc. - For authorized use only +# Subject: CN=VeriSign Class 3 Public Primary Certification Authority - G4 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2007 VeriSign, Inc. - For authorized use only +# Label: "VeriSign Class 3 Public Primary Certification Authority - G4" +# Serial: 63143484348153506665311985501458640051 +# MD5 Fingerprint: 3a:52:e1:e7:fd:6f:3a:e3:6f:f3:6f:99:1b:f9:22:41 +# SHA1 Fingerprint: 22:d5:d8:df:8f:02:31:d1:8d:f7:9d:b7:cf:8a:2d:64:c9:3f:6c:3a +# SHA256 Fingerprint: 69:dd:d7:ea:90:bb:57:c9:3e:13:5d:c8:5e:a6:fc:d5:48:0b:60:32:39:bd:c4:54:fc:75:8b:2a:26:cf:7f:79 +-----BEGIN CERTIFICATE----- +MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW +ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp +U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y +aXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjELMAkG +A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJp +U2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwg +SW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2ln +biBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8Utpkmw4tXNherJI9/gHm +GUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGzrl0Bp3ve +fLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJ +aW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYj +aHR0cDovL2xvZ28udmVyaXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMW +kf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMDA2gAMGUCMGYhDBgmYFo4e1ZC +4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIxAJw9SDkjOVga +FRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA== +-----END CERTIFICATE----- + +# Issuer: CN=NetLock Arany (Class Gold) Főtanúsítvány O=NetLock Kft. OU=Tanúsítványkiadók (Certification Services) +# Subject: CN=NetLock Arany (Class Gold) Főtanúsítvány O=NetLock Kft. OU=Tanúsítványkiadók (Certification Services) +# Label: "NetLock Arany (Class Gold) Főtanúsítvány" +# Serial: 80544274841616 +# MD5 Fingerprint: c5:a1:b7:ff:73:dd:d6:d7:34:32:18:df:fc:3c:ad:88 +# SHA1 Fingerprint: 06:08:3f:59:3f:15:a1:04:a0:69:a4:6b:a9:03:d0:06:b7:97:09:91 +# SHA256 Fingerprint: 6c:61:da:c3:a2:de:f0:31:50:6b:e0:36:d2:a6:fe:40:19:94:fb:d1:3d:f9:c8:d4:66:59:92:74:c4:46:ec:98 +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQG +EwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3 +MDUGA1UECwwuVGFuw7pzw610dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNl +cnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBBcmFueSAoQ2xhc3MgR29sZCkgRsWR +dGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgxMjA2MTUwODIxWjCB +pzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxOZXRM +b2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlm +aWNhdGlvbiBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNz +IEdvbGQpIEbFkXRhbsO6c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAxCRec75LbRTDofTjl5Bu0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrT +lF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw/HpYzY6b7cNGbIRwXdrz +AZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAkH3B5r9s5 +VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRG +ILdwfzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2 +BJtr+UBdADTHLpl1neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAG +AQH/AgEEMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2M +U9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwWqZw8UQCgwBEIBaeZ5m8BiFRh +bvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTtaYtOUZcTh5m2C ++C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC +bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2F +uLjbvrW5KfnaNwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2 +XjG4Kvte9nHfRCaexOYNkbQudZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= +-----END CERTIFICATE----- + +# Issuer: CN=Staat der Nederlanden Root CA - G2 O=Staat der Nederlanden +# Subject: CN=Staat der Nederlanden Root CA - G2 O=Staat der Nederlanden +# Label: "Staat der Nederlanden Root CA - G2" +# Serial: 10000012 +# MD5 Fingerprint: 7c:a5:0f:f8:5b:9a:7d:6d:30:ae:54:5a:e3:42:a2:8a +# SHA1 Fingerprint: 59:af:82:79:91:86:c7:b4:75:07:cb:cf:03:57:46:eb:04:dd:b7:16 +# SHA256 Fingerprint: 66:8c:83:94:7d:a6:3b:72:4b:ec:e1:74:3c:31:a0:e6:ae:d0:db:8e:c5:b3:1b:e3:77:bb:78:4f:91:b6:71:6f +-----BEGIN CERTIFICATE----- +MIIFyjCCA7KgAwIBAgIEAJiWjDANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJO +TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFh +dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQSAtIEcyMB4XDTA4MDMyNjExMTgxN1oX +DTIwMDMyNTExMDMxMFowWjELMAkGA1UEBhMCTkwxHjAcBgNVBAoMFVN0YWF0IGRl +ciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5lZGVybGFuZGVuIFJv +b3QgQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMVZ5291 +qj5LnLW4rJ4L5PnZyqtdj7U5EILXr1HgO+EASGrP2uEGQxGZqhQlEq0i6ABtQ8Sp +uOUfiUtnvWFI7/3S4GCI5bkYYCjDdyutsDeqN95kWSpGV+RLufg3fNU254DBtvPU +Z5uW6M7XxgpT0GtJlvOjCwV3SPcl5XCsMBQgJeN/dVrlSPhOewMHBPqCYYdu8DvE +pMfQ9XQ+pV0aCPKbJdL2rAQmPlU6Yiile7Iwr/g3wtG61jj99O9JMDeZJiFIhQGp +5Rbn3JBV3w/oOM2ZNyFPXfUib2rFEhZgF1XyZWampzCROME4HYYEhLoaJXhena/M +UGDWE4dS7WMfbWV9whUYdMrhfmQpjHLYFhN9C0lK8SgbIHRrxT3dsKpICT0ugpTN +GmXZK4iambwYfp/ufWZ8Pr2UuIHOzZgweMFvZ9C+X+Bo7d7iscksWXiSqt8rYGPy +5V6548r6f1CGPqI0GAwJaCgRHOThuVw+R7oyPxjMW4T182t0xHJ04eOLoEq9jWYv +6q012iDTiIJh8BIitrzQ1aTsr1SIJSQ8p22xcik/Plemf1WvbibG/ufMQFxRRIEK +eN5KzlW/HdXZt1bv8Hb/C3m1r737qWmRRpdogBQ2HbN/uymYNqUg+oJgYjOk7Na6 +B6duxc8UpufWkjTYgfX8HV2qXB72o007uPc5AgMBAAGjgZcwgZQwDwYDVR0TAQH/ +BAUwAwEB/zBSBgNVHSAESzBJMEcGBFUdIAAwPzA9BggrBgEFBQcCARYxaHR0cDov +L3d3dy5wa2lvdmVyaGVpZC5ubC9wb2xpY2llcy9yb290LXBvbGljeS1HMjAOBgNV +HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJFoMocVHYnitfGsNig0jQt8YojrMA0GCSqG +SIb3DQEBCwUAA4ICAQCoQUpnKpKBglBu4dfYszk78wIVCVBR7y29JHuIhjv5tLyS +CZa59sCrI2AGeYwRTlHSeYAz+51IvuxBQ4EffkdAHOV6CMqqi3WtFMTC6GY8ggen +5ieCWxjmD27ZUD6KQhgpxrRW/FYQoAUXvQwjf/ST7ZwaUb7dRUG/kSS0H4zpX897 +IZmflZ85OkYcbPnNe5yQzSipx6lVu6xiNGI1E0sUOlWDuYaNkqbG9AclVMwWVxJK +gnjIFNkXgiYtXSAfea7+1HAWFpWD2DU5/1JddRwWxRNVz0fMdWVSSt7wsKfkCpYL ++63C4iWEst3kvX5ZbJvw8NjnyvLplzh+ib7M+zkXYT9y2zqR2GUBGR2tUKRXCnxL +vJxxcypFURmFzI79R6d0lR2o0a9OF7FpJsKqeFdbxU2n5Z4FF5TKsl+gSRiNNOkm +bEgeqmiSBeGCc1qb3AdbCG19ndeNIdn8FCCqwkXfP+cAslHkwvgFuXkajDTznlvk +N1trSt8sV4pAWja63XVECDdCcAz+3F4hoKOKwJCcaNpQ5kUQR3i2TtJlycM33+FC +Y7BXN0Ute4qcvwXqZVUz9zkQxSgqIXobisQk+T8VyJoVIPVVYpbtbZNQvOSqeK3Z +ywplh6ZmwcSBo3c6WB4L7oOLnR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm66+KAQ== +-----END CERTIFICATE----- + +# Issuer: CN=CA Disig O=Disig a.s. +# Subject: CN=CA Disig O=Disig a.s. +# Label: "CA Disig" +# Serial: 1 +# MD5 Fingerprint: 3f:45:96:39:e2:50:87:f7:bb:fe:98:0c:3c:20:98:e6 +# SHA1 Fingerprint: 2a:c8:d5:8b:57:ce:bf:2f:49:af:f2:fc:76:8f:51:14:62:90:7a:41 +# SHA256 Fingerprint: 92:bf:51:19:ab:ec:ca:d0:b1:33:2d:c4:e1:d0:5f:ba:75:b5:67:90:44:ee:0c:a2:6e:93:1f:74:4f:2f:33:cf +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBATANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQGEwJTSzET +MBEGA1UEBxMKQnJhdGlzbGF2YTETMBEGA1UEChMKRGlzaWcgYS5zLjERMA8GA1UE +AxMIQ0EgRGlzaWcwHhcNMDYwMzIyMDEzOTM0WhcNMTYwMzIyMDEzOTM0WjBKMQsw +CQYDVQQGEwJTSzETMBEGA1UEBxMKQnJhdGlzbGF2YTETMBEGA1UEChMKRGlzaWcg +YS5zLjERMA8GA1UEAxMIQ0EgRGlzaWcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCS9jHBfYj9mQGp2HvycXXxMcbzdWb6UShGhJd4NLxs/LxFWYgmGErE +Nx+hSkS943EE9UQX4j/8SFhvXJ56CbpRNyIjZkMhsDxkovhqFQ4/61HhVKndBpnX +mjxUizkDPw/Fzsbrg3ICqB9x8y34dQjbYkzo+s7552oftms1grrijxaSfQUMbEYD +XcDtab86wYqg6I7ZuUUohwjstMoVvoLdtUSLLa2GDGhibYVW8qwUYzrG0ZmsNHhW +S8+2rT+MitcE5eN4TPWGqvWP+j1scaMtymfraHtuM6kMgiioTGohQBUgDCZbg8Kp +FhXAJIJdKxatymP2dACw30PEEGBWZ2NFAgMBAAGjgf8wgfwwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUjbJJaJ1yCCW5wCf1UJNWSEZx+Y8wDgYDVR0PAQH/BAQD +AgEGMDYGA1UdEQQvMC2BE2Nhb3BlcmF0b3JAZGlzaWcuc2uGFmh0dHA6Ly93d3cu +ZGlzaWcuc2svY2EwZgYDVR0fBF8wXTAtoCugKYYnaHR0cDovL3d3dy5kaXNpZy5z +ay9jYS9jcmwvY2FfZGlzaWcuY3JsMCygKqAohiZodHRwOi8vY2EuZGlzaWcuc2sv +Y2EvY3JsL2NhX2Rpc2lnLmNybDAaBgNVHSAEEzARMA8GDSuBHpGT5goAAAABAQEw +DQYJKoZIhvcNAQEFBQADggEBAF00dGFMrzvY/59tWDYcPQuBDRIrRhCA/ec8J9B6 +yKm2fnQwM6M6int0wHl5QpNt/7EpFIKrIYwvF/k/Ji/1WcbvgAa3mkkp7M5+cTxq +EEHA9tOasnxakZzArFvITV734VP/Q3f8nktnbNfzg9Gg4H8l37iYC5oyOGwwoPP/ +CBUz91BKez6jPiCp3C9WgArtQVCwyfTssuMmRAAOb54GvCKWU3BlxFAKRmukLyeB +EicTXxChds6KezfqwzlhA5WYOudsiCUI/HloDYd9Yvi0X/vF2Ey9WLw/Q1vUHgFN +PGO+I++MzVpQuGhU+QqZMxEA4Z7CRneC9VkGjCFMhwnN5ag= +-----END CERTIFICATE----- + +# Issuer: CN=Juur-SK O=AS Sertifitseerimiskeskus +# Subject: CN=Juur-SK O=AS Sertifitseerimiskeskus +# Label: "Juur-SK" +# Serial: 999181308 +# MD5 Fingerprint: aa:8e:5d:d9:f8:db:0a:58:b7:8d:26:87:6c:82:35:55 +# SHA1 Fingerprint: 40:9d:4b:d9:17:b5:5c:27:b6:9b:64:cb:98:22:44:0d:cd:09:b8:89 +# SHA256 Fingerprint: ec:c3:e9:c3:40:75:03:be:e0:91:aa:95:2f:41:34:8f:f8:8b:aa:86:3b:22:64:be:fa:c8:07:90:15:74:e9:39 +-----BEGIN CERTIFICATE----- +MIIE5jCCA86gAwIBAgIEO45L/DANBgkqhkiG9w0BAQUFADBdMRgwFgYJKoZIhvcN +AQkBFglwa2lAc2suZWUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKExlBUyBTZXJ0aWZp +dHNlZXJpbWlza2Vza3VzMRAwDgYDVQQDEwdKdXVyLVNLMB4XDTAxMDgzMDE0MjMw +MVoXDTE2MDgyNjE0MjMwMVowXTEYMBYGCSqGSIb3DQEJARYJcGtpQHNrLmVlMQsw +CQYDVQQGEwJFRTEiMCAGA1UEChMZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1czEQ +MA4GA1UEAxMHSnV1ci1TSzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AIFxNj4zB9bjMI0TfncyRsvPGbJgMUaXhvSYRqTCZUXP00B841oiqBB4M8yIsdOB +SvZiF3tfTQou0M+LI+5PAk676w7KvRhj6IAcjeEcjT3g/1tf6mTll+g/mX8MCgkz +ABpTpyHhOEvWgxutr2TC+Rx6jGZITWYfGAriPrsfB2WThbkasLnE+w0R9vXW+RvH +LCu3GFH+4Hv2qEivbDtPL+/40UceJlfwUR0zlv/vWT3aTdEVNMfqPxZIe5EcgEMP +PbgFPtGzlc3Yyg/CQ2fbt5PgIoIuvvVoKIO5wTtpeyDaTpxt4brNj3pssAki14sL +2xzVWiZbDcDq5WDQn/413z8CAwEAAaOCAawwggGoMA8GA1UdEwEB/wQFMAMBAf8w +ggEWBgNVHSAEggENMIIBCTCCAQUGCisGAQQBzh8BAQEwgfYwgdAGCCsGAQUFBwIC +MIHDHoHAAFMAZQBlACAAcwBlAHIAdABpAGYAaQBrAGEAYQB0ACAAbwBuACAAdgDk +AGwAagBhAHMAdABhAHQAdQBkACAAQQBTAC0AaQBzACAAUwBlAHIAdABpAGYAaQB0 +AHMAZQBlAHIAaQBtAGkAcwBrAGUAcwBrAHUAcwAgAGEAbABhAG0ALQBTAEsAIABz +AGUAcgB0AGkAZgBpAGsAYQBhAHQAaQBkAGUAIABrAGkAbgBuAGkAdABhAG0AaQBz +AGUAawBzMCEGCCsGAQUFBwIBFhVodHRwOi8vd3d3LnNrLmVlL2Nwcy8wKwYDVR0f +BCQwIjAgoB6gHIYaaHR0cDovL3d3dy5zay5lZS9qdXVyL2NybC8wHQYDVR0OBBYE +FASqekej5ImvGs8KQKcYP2/v6X2+MB8GA1UdIwQYMBaAFASqekej5ImvGs8KQKcY +P2/v6X2+MA4GA1UdDwEB/wQEAwIB5jANBgkqhkiG9w0BAQUFAAOCAQEAe8EYlFOi +CfP+JmeaUOTDBS8rNXiRTHyoERF5TElZrMj3hWVcRrs7EKACr81Ptcw2Kuxd/u+g +kcm2k298gFTsxwhwDY77guwqYHhpNjbRxZyLabVAyJRld/JXIWY7zoVAtjNjGr95 +HvxcHdMdkxuLDF2FvZkwMhgJkVLpfKG6/2SSmuz+Ne6ML678IIbsSt4beDI3poHS +na9aEhbKmVv8b20OxaAehsmR0FyYgl9jDIpaq9iVpszLita/ZEuOyoqysOkhMp6q +qIWYNIE5ITuoOlIyPfZrN4YGWhWY3PARZv40ILcD9EEQfTmEeZZyY7aWAuVrua0Z +TbvGRNs2yyqcjg== +-----END CERTIFICATE----- + +# Issuer: CN=Hongkong Post Root CA 1 O=Hongkong Post +# Subject: CN=Hongkong Post Root CA 1 O=Hongkong Post +# Label: "Hongkong Post Root CA 1" +# Serial: 1000 +# MD5 Fingerprint: a8:0d:6f:39:78:b9:43:6d:77:42:6d:98:5a:cc:23:ca +# SHA1 Fingerprint: d6:da:a8:20:8d:09:d2:15:4d:24:b5:2f:cb:34:6e:b2:58:b2:8a:58 +# SHA256 Fingerprint: f9:e6:7d:33:6c:51:00:2a:c0:54:c6:32:02:2d:66:dd:a2:e7:e3:ff:f1:0a:d0:61:ed:31:d8:bb:b4:10:cf:b2 +-----BEGIN CERTIFICATE----- +MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsx +FjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3Qg +Um9vdCBDQSAxMB4XDTAzMDUxNTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkG +A1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdr +b25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1ApzQ +jVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEn +PzlTCeqrauh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjh +ZY4bXSNmO7ilMlHIhqqhqZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9 +nnV0ttgCXjqQesBCNnLsak3c78QA3xMYV18meMjWCnl3v/evt3a5pQuEF10Q6m/h +q5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNVHRMBAf8ECDAGAQH/AgED +MA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7ih9legYsC +mEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI3 +7piol7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clB +oiMBdDhViw+5LmeiIAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJs +EhTkYY2sEJCehFC78JZvRZ+K88psT/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpO +fMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilTc4afU9hDDl3WY4JxHYB0yvbi +AmvZWg== +-----END CERTIFICATE----- + +# Issuer: CN=SecureSign RootCA11 O=Japan Certification Services, Inc. +# Subject: CN=SecureSign RootCA11 O=Japan Certification Services, Inc. +# Label: "SecureSign RootCA11" +# Serial: 1 +# MD5 Fingerprint: b7:52:74:e2:92:b4:80:93:f2:75:e4:cc:d7:f2:ea:26 +# SHA1 Fingerprint: 3b:c4:9f:48:f8:f3:73:a0:9c:1e:bd:f8:5b:b1:c3:65:c7:d8:11:b3 +# SHA256 Fingerprint: bf:0f:ee:fb:9e:3a:58:1a:d5:f9:e9:db:75:89:98:57:43:d2:61:08:5c:4d:31:4f:6f:5d:72:59:aa:42:16:12 +-----BEGIN CERTIFICATE----- +MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDEr +MCkGA1UEChMiSmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoG +A1UEAxMTU2VjdXJlU2lnbiBSb290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0 +MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSswKQYDVQQKEyJKYXBhbiBDZXJ0aWZp +Y2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1cmVTaWduIFJvb3RD +QTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvLTJsz +i1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8 +h9uuywGOwvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOV +MdrAG/LuYpmGYz+/3ZMqg6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9 +UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rPO7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni +8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitAbpSACW22s293bzUIUPsC +h8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZXt94wDgYD +VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEB +AKChOBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xm +KbabfSVSSUOrTC4rbnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQ +X5Ucv+2rIrVls4W6ng+4reV6G4pQOh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWr +QbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01y8hSyn+B/tlr0/cR7SXf+Of5 +pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061lgeLKBObjBmN +QSdJQO7e5iNEOdyhIta6A/I= +-----END CERTIFICATE----- + +# Issuer: CN=ACEDICOM Root O=EDICOM OU=PKI +# Subject: CN=ACEDICOM Root O=EDICOM OU=PKI +# Label: "ACEDICOM Root" +# Serial: 7029493972724711941 +# MD5 Fingerprint: 42:81:a0:e2:1c:e3:55:10:de:55:89:42:65:96:22:e6 +# SHA1 Fingerprint: e0:b4:32:2e:b2:f6:a5:68:b6:54:53:84:48:18:4a:50:36:87:43:84 +# SHA256 Fingerprint: 03:95:0f:b4:9a:53:1f:3e:19:91:94:23:98:df:a9:e0:ea:32:d7:ba:1c:dd:9b:c8:5d:b5:7e:d9:40:0b:43:4a +-----BEGIN CERTIFICATE----- +MIIFtTCCA52gAwIBAgIIYY3HhjsBggUwDQYJKoZIhvcNAQEFBQAwRDEWMBQGA1UE +AwwNQUNFRElDT00gUm9vdDEMMAoGA1UECwwDUEtJMQ8wDQYDVQQKDAZFRElDT00x +CzAJBgNVBAYTAkVTMB4XDTA4MDQxODE2MjQyMloXDTI4MDQxMzE2MjQyMlowRDEW +MBQGA1UEAwwNQUNFRElDT00gUm9vdDEMMAoGA1UECwwDUEtJMQ8wDQYDVQQKDAZF +RElDT00xCzAJBgNVBAYTAkVTMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC +AgEA/5KV4WgGdrQsyFhIyv2AVClVYyT/kGWbEHV7w2rbYgIB8hiGtXxaOLHkWLn7 +09gtn70yN78sFW2+tfQh0hOR2QetAQXW8713zl9CgQr5auODAKgrLlUTY4HKRxx7 +XBZXehuDYAQ6PmXDzQHe3qTWDLqO3tkE7hdWIpuPY/1NFgu3e3eM+SW10W2ZEi5P +Grjm6gSSrj0RuVFCPYewMYWveVqc/udOXpJPQ/yrOq2lEiZmueIM15jO1FillUAK +t0SdE3QrwqXrIhWYENiLxQSfHY9g5QYbm8+5eaA9oiM/Qj9r+hwDezCNzmzAv+Yb +X79nuIQZ1RXve8uQNjFiybwCq0Zfm/4aaJQ0PZCOrfbkHQl/Sog4P75n/TSW9R28 +MHTLOO7VbKvU/PQAtwBbhTIWdjPp2KOZnQUAqhbm84F9b32qhm2tFXTTxKJxqvQU +fecyuB+81fFOvW8XAjnXDpVCOscAPukmYxHqC9FK/xidstd7LzrZlvvoHpKuE1XI +2Sf23EgbsCTBheN3nZqk8wwRHQ3ItBTutYJXCb8gWH8vIiPYcMt5bMlL8qkqyPyH +K9caUPgn6C9D4zq92Fdx/c6mUlv53U3t5fZvie27k5x2IXXwkkwp9y+cAS7+UEae +ZAwUswdbxcJzbPEHXEUkFDWug/FqTYl6+rPYLWbwNof1K1MCAwEAAaOBqjCBpzAP +BgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKaz4SsrSbbXc6GqlPUB53NlTKxQ +MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUprPhKytJttdzoaqU9QHnc2VMrFAw +RAYDVR0gBD0wOzA5BgRVHSAAMDEwLwYIKwYBBQUHAgEWI2h0dHA6Ly9hY2VkaWNv +bS5lZGljb21ncm91cC5jb20vZG9jMA0GCSqGSIb3DQEBBQUAA4ICAQDOLAtSUWIm +fQwng4/F9tqgaHtPkl7qpHMyEVNEskTLnewPeUKzEKbHDZ3Ltvo/Onzqv4hTGzz3 +gvoFNTPhNahXwOf9jU8/kzJPeGYDdwdY6ZXIfj7QeQCM8htRM5u8lOk6e25SLTKe +I6RF+7YuE7CLGLHdztUdp0J/Vb77W7tH1PwkzQSulgUV1qzOMPPKC8W64iLgpq0i +5ALudBF/TP94HTXa5gI06xgSYXcGCRZj6hitoocf8seACQl1ThCojz2GuHURwCRi +ipZ7SkXp7FnFvmuD5uHorLUwHv4FB4D54SMNUI8FmP8sX+g7tq3PgbUhh8oIKiMn +MCArz+2UW6yyetLHKKGKC5tNSixthT8Jcjxn4tncB7rrZXtaAWPWkFtPF2Y9fwsZ +o5NjEFIqnxQWWOLcpfShFosOkYuByptZ+thrkQdlVV9SH686+5DdaaVbnG0OLLb6 +zqylfDJKZ0DcMDQj3dcEI2bw/FWAp/tmGYI1Z2JwOV5vx+qQQEQIHriy1tvuWacN +GHk0vFQYXlPKNFHtRQrmjseCNj6nOGOpMCwXEGCSn1WHElkQwg9naRHMTh5+Spqt +r0CodaxWkHS4oJyleW/c6RrIaQXpuvoDs3zk4E7Czp3otkYNbn5XOmeUwssfnHdK +Z05phkOTOPu220+DkdRgfks+KzgHVZhepA== +-----END CERTIFICATE----- + +# Issuer: CN=Microsec e-Szigno Root CA 2009 O=Microsec Ltd. +# Subject: CN=Microsec e-Szigno Root CA 2009 O=Microsec Ltd. +# Label: "Microsec e-Szigno Root CA 2009" +# Serial: 14014712776195784473 +# MD5 Fingerprint: f8:49:f4:03:bc:44:2d:83:be:48:69:7d:29:64:fc:b1 +# SHA1 Fingerprint: 89:df:74:fe:5c:f4:0f:4a:80:f9:e3:37:7d:54:da:91:e1:01:31:8e +# SHA256 Fingerprint: 3c:5f:81:fe:a5:fa:b8:2c:64:bf:a2:ea:ec:af:cd:e8:e0:77:fc:86:20:a7:ca:e5:37:16:3d:f3:6e:db:f3:78 +-----BEGIN CERTIFICATE----- +MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYD +VQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0 +ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0G +CSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTAeFw0wOTA2MTYxMTMwMThaFw0y +OTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3Qx +FjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3pp +Z25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o +dTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvP +kd6mJviZpWNwrZuuyjNAfW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tc +cbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG0IMZfcChEhyVbUr02MelTTMuhTlAdX4U +fIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKApxn1ntxVUwOXewdI/5n7 +N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm1HxdrtbC +xkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1 ++rUCAwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPM +Pcu1SCOhGnqmKrs0aDAbBgNVHREEFDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqG +SIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0olZMEyL/azXm4Q5DwpL7v8u8h +mLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfXI/OMn74dseGk +ddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 +tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c +2Pm2G2JwCz02yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5t +HMN1Rq41Bab2XD0h7lbwyYIiLXpUq3DDfSJlgnCW +-----END CERTIFICATE----- + +# Issuer: CN=e-Guven Kok Elektronik Sertifika Hizmet Saglayicisi O=Elektronik Bilgi Guvenligi A.S. +# Subject: CN=e-Guven Kok Elektronik Sertifika Hizmet Saglayicisi O=Elektronik Bilgi Guvenligi A.S. +# Label: "E-Guven Kok Elektronik Sertifika Hizmet Saglayicisi" +# Serial: 91184789765598910059173000485363494069 +# MD5 Fingerprint: 3d:41:29:cb:1e:aa:11:74:cd:5d:b0:62:af:b0:43:5b +# SHA1 Fingerprint: dd:e1:d2:a9:01:80:2e:1d:87:5e:84:b3:80:7e:4b:b1:fd:99:41:34 +# SHA256 Fingerprint: e6:09:07:84:65:a4:19:78:0c:b6:ac:4c:1c:0b:fb:46:53:d9:d9:cc:6e:b3:94:6e:b7:f3:d6:99:97:ba:d5:98 +-----BEGIN CERTIFICATE----- +MIIDtjCCAp6gAwIBAgIQRJmNPMADJ72cdpW56tustTANBgkqhkiG9w0BAQUFADB1 +MQswCQYDVQQGEwJUUjEoMCYGA1UEChMfRWxla3Ryb25payBCaWxnaSBHdXZlbmxp +Z2kgQS5TLjE8MDoGA1UEAxMzZS1HdXZlbiBLb2sgRWxla3Ryb25payBTZXJ0aWZp +a2EgSGl6bWV0IFNhZ2xheWljaXNpMB4XDTA3MDEwNDExMzI0OFoXDTE3MDEwNDEx +MzI0OFowdTELMAkGA1UEBhMCVFIxKDAmBgNVBAoTH0VsZWt0cm9uaWsgQmlsZ2kg +R3V2ZW5saWdpIEEuUy4xPDA6BgNVBAMTM2UtR3V2ZW4gS29rIEVsZWt0cm9uaWsg +U2VydGlmaWthIEhpem1ldCBTYWdsYXlpY2lzaTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAMMSIJ6wXgBljU5Gu4Bc6SwGl9XzcslwuedLZYDBS75+PNdU +MZTe1RK6UxYC6lhj71vY8+0qGqpxSKPcEC1fX+tcS5yWCEIlKBHMilpiAVDV6wlT +L/jDj/6z/P2douNffb7tC+Bg62nsM+3YjfsSSYMAyYuXjDtzKjKzEve5TfL0TW3H +5tYmNwjy2f1rXKPlSFxYvEK+A1qBuhw1DADT9SN+cTAIJjjcJRFHLfO6IxClv7wC +90Nex/6wN1CZew+TzuZDLMN+DfIcQ2Zgy2ExR4ejT669VmxMvLz4Bcpk9Ok0oSy1 +c+HCPujIyTQlCFzz7abHlJ+tiEMl1+E5YP6sOVkCAwEAAaNCMEAwDgYDVR0PAQH/ +BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFJ/uRLOU1fqRTy7ZVZoE +VtstxNulMA0GCSqGSIb3DQEBBQUAA4IBAQB/X7lTW2M9dTLn+sR0GstG30ZpHFLP +qk/CaOv/gKlR6D1id4k9CnU58W5dF4dvaAXBlGzZXd/aslnLpRCKysw5zZ/rTt5S +/wzw9JKp8mxTq5vSR6AfdPebmvEvFZ96ZDAYBzwqD2fK/A+JYZ1lpTzlvBNbCNvj +/+27BrtqBrF6T2XGgv0enIu1De5Iu7i9qgi0+6N8y5/NkHZchpZ4Vwpm+Vganf2X +KWDeEaaQHBkc7gGWIjQ0LpH5t8Qn0Xvmv/uARFoW5evg1Ao4vOSR49XrXMGs3xtq +fJ7lddK2l4fbzIcrQzqECK+rPNv3PGYxhrCdU3nt+CPeQuMtgvEP5fqX +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3 +# Label: "GlobalSign Root CA - R3" +# Serial: 4835703278459759426209954 +# MD5 Fingerprint: c5:df:b8:49:ca:05:13:55:ee:2d:ba:1a:c3:3e:b0:28 +# SHA1 Fingerprint: d6:9b:56:11:48:f0:1c:77:c5:45:78:c1:09:26:df:5b:85:69:76:ad +# SHA256 Fingerprint: cb:b5:22:d7:b7:f1:27:ad:6a:01:13:86:5b:df:1c:d4:10:2e:7d:07:59:af:63:5a:7c:f4:72:0d:c9:63:c5:3b +-----BEGIN CERTIFICATE----- +MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G +A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp +Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4 +MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG +A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8 +RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT +gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm +KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd +QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ +XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw +DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o +LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU +RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp +jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK +6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX +mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs +Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH +WD9f +-----END CERTIFICATE----- + +# Issuer: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 +# Subject: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 +# Label: "Autoridad de Certificacion Firmaprofesional CIF A62634068" +# Serial: 6047274297262753887 +# MD5 Fingerprint: 73:3a:74:7a:ec:bb:a3:96:a6:c2:e4:e2:c8:9b:c0:c3 +# SHA1 Fingerprint: ae:c5:fb:3f:c8:e1:bf:c4:e5:4f:03:07:5a:9a:e8:00:b7:f7:b6:fa +# SHA256 Fingerprint: 04:04:80:28:bf:1f:28:64:d4:8f:9a:d4:d8:32:94:36:6a:82:88:56:55:3f:3b:14:30:3f:90:14:7f:5d:40:ef +-----BEGIN CERTIFICATE----- +MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UE +BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h +cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEy +MzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUg +Q2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjgwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDDUtd9 +thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQM +cas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefG +L9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i +NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/h +X68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2b +m8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCy +Z/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirja +EbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/T +KI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF +6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVh +OSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1UdEwEB/wQIMAYBAf8CAQEwDgYD +VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNHDhpkLzCBpgYD +VR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp +cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBv +ACAAZABlACAAbABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBl +AGwAbwBuAGEAIAAwADgAMAAxADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF +661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx51tkljYyGOylMnfX40S2wBEqgLk9 +am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qkR71kMrv2JYSiJ0L1 +ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaPT481 +PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS +3a/DTg4fJl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5k +SeTy36LssUzAKh3ntLFlosS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF +3dvd6qJ2gHN99ZwExEWN57kci57q13XRcrHedUTnQn3iV2t93Jm8PYMo6oCTjcVM +ZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoRsaS8I8nkvof/uZS2+F0g +StRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTDKCOM/icz +Q0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQB +jLMi6Et8Vcad+qMUu2WFbm5PEn4KPJ2V +-----END CERTIFICATE----- + +# Issuer: CN=Izenpe.com O=IZENPE S.A. +# Subject: CN=Izenpe.com O=IZENPE S.A. +# Label: "Izenpe.com" +# Serial: 917563065490389241595536686991402621 +# MD5 Fingerprint: a6:b0:cd:85:80:da:5c:50:34:a3:39:90:2f:55:67:73 +# SHA1 Fingerprint: 2f:78:3d:25:52:18:a7:4a:65:39:71:b5:2c:a2:9c:45:15:6f:e9:19 +# SHA256 Fingerprint: 25:30:cc:8e:98:32:15:02:ba:d9:6f:9b:1f:ba:1b:09:9e:2d:29:9e:0f:45:48:bb:91:4f:36:3b:c0:d4:53:1f +-----BEGIN CERTIFICATE----- +MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4 +MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6 +ZW5wZS5jb20wHhcNMDcxMjEzMTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYD +VQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5j +b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ03rKDx6sp4boFmVq +scIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAKClaO +xdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6H +LmYRY2xU+zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFX +uaOKmMPsOzTFlUFpfnXCPCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQD +yCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxTOTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+ +JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbKF7jJeodWLBoBHmy+E60Q +rLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK0GqfvEyN +BjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8L +hij+0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIB +QFqNeb+Lz0vPqhbBleStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+ +HMh3/1uaD7euBUbl8agW7EekFwIDAQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2lu +Zm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+SVpFTlBFIFMuQS4gLSBDSUYg +QTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBGNjIgUzgxQzBB +BgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx +MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUA +A4ICAQB4pgwWSp9MiDrAyw6lFn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWb +laQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbgakEyrkgPH7UIBzg/YsfqikuFgba56 +awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8qhT/AQKM6WfxZSzwo +JNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Csg1lw +LDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCT +VyvehQP5aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGk +LhObNA5me0mrZJfQRsN5nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJb +UjWumDqtujWTI6cfSN01RpiyEGjkpTHCClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/ +QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZoQ0iy2+tzJOeRf1SktoA+ +naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1ZWrOZyGls +QyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw== +-----END CERTIFICATE----- + +# Issuer: CN=Chambers of Commerce Root - 2008 O=AC Camerfirma S.A. +# Subject: CN=Chambers of Commerce Root - 2008 O=AC Camerfirma S.A. +# Label: "Chambers of Commerce Root - 2008" +# Serial: 11806822484801597146 +# MD5 Fingerprint: 5e:80:9e:84:5a:0e:65:0b:17:02:f3:55:18:2a:3e:d7 +# SHA1 Fingerprint: 78:6a:74:ac:76:ab:14:7f:9c:6a:30:50:ba:9e:a8:7e:fe:9a:ce:3c +# SHA256 Fingerprint: 06:3e:4a:fa:c4:91:df:d3:32:f3:08:9b:85:42:e9:46:17:d8:93:d7:fe:94:4e:10:a7:93:7e:e2:9d:96:93:c0 +-----BEGIN CERTIFICATE----- +MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYD +VQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0 +IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3 +MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xKTAnBgNVBAMTIENoYW1iZXJz +IG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEyMjk1MFoXDTM4MDcz +MTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBj +dXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIw +EAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEp +MCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0G +CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW9 +28sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKAXuFixrYp4YFs8r/lfTJq +VKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorjh40G072Q +DuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR +5gN/ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfL +ZEFHcpOrUMPrCXZkNNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05a +Sd+pZgvMPMZ4fKecHePOjlO+Bd5gD2vlGts/4+EhySnB8esHnFIbAURRPHsl18Tl +UlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331lubKgdaX8ZSD6e2wsWsSaR6s ++12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ0wlf2eOKNcx5 +Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj +ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAx +hduub+84Mxh2EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNV +HQ4EFgQU+SSsD7K1+HnA+mCIG8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1 ++HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpN +YWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29t +L2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVy +ZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAt +IDIwMDiCCQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRV +HSAAMCowKAYIKwYBBQUHAgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20w +DQYJKoZIhvcNAQEFBQADggIBAJASryI1wqM58C7e6bXpeHxIvj99RZJe6dqxGfwW +PJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH3qLPaYRgM+gQDROpI9CF +5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbURWpGqOt1 +glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaH +FoI6M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2 +pSB7+R5KBWIBpih1YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MD +xvbxrN8y8NmBGuScvfaAFPDRLLmF9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QG +tjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcKzBIKinmwPQN/aUv0NCB9szTq +jktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvGnrDQWzilm1De +fhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg +OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZ +d0jQ +-----END CERTIFICATE----- + +# Issuer: CN=Global Chambersign Root - 2008 O=AC Camerfirma S.A. +# Subject: CN=Global Chambersign Root - 2008 O=AC Camerfirma S.A. +# Label: "Global Chambersign Root - 2008" +# Serial: 14541511773111788494 +# MD5 Fingerprint: 9e:80:ff:78:01:0c:2e:c1:36:bd:fe:96:90:6e:08:f3 +# SHA1 Fingerprint: 4a:bd:ee:ec:95:0d:35:9c:89:ae:c7:52:a1:2c:5b:29:f6:d6:aa:0c +# SHA256 Fingerprint: 13:63:35:43:93:34:a7:69:80:16:a0:d3:24:de:72:28:4e:07:9d:7b:52:20:bb:8f:bd:74:78:16:ee:be:ba:ca +-----BEGIN CERTIFICATE----- +MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYD +VQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0 +IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3 +MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD +aGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMxNDBaFw0zODA3MzEx +MjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3Vy +cmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAG +A1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAl +BgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZI +hvcNAQEBBQADggIPADCCAgoCggIBAMDfVtPkOpt2RbQT2//BthmLN0EYlVJH6xed +KYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXfXjaOcNFccUMd2drvXNL7 +G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0ZJJ0YPP2 +zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4 +ddPB/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyG +HoiMvvKRhI9lNNgATH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2 +Id3UwD2ln58fQ1DJu7xsepeY7s2MH/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3V +yJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfeOx2YItaswTXbo6Al/3K1dh3e +beksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSFHTynyQbehP9r +6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh +wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsog +zCtLkykPAgMBAAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQW +BBS5CcqcHtvTbDprru1U8VuTBjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDpr +ru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UEBhMCRVUxQzBBBgNVBAcTOk1hZHJp +ZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJmaXJtYS5jb20vYWRk +cmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJmaXJt +YSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiC +CQDJzdPp1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCow +KAYIKwYBBQUHAgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZI +hvcNAQEFBQADggIBAICIf3DekijZBZRG/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZ +UohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6ReAJ3spED8IXDneRRXoz +X1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/sdZ7LoR/x +fxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVz +a2Mg9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yyd +Yhz2rXzdpjEetrHHfoUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMd +SqlapskD7+3056huirRXhOukP9DuqqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9O +AP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETrP3iZ8ntxPjzxmKfFGBI/5rso +M0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVqc5iJWzouE4ge +v8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z +09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B +-----END CERTIFICATE----- + +# Issuer: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. +# Subject: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. +# Label: "Go Daddy Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: 80:3a:bc:22:c1:e6:fb:8d:9b:3b:27:4a:32:1b:9a:01 +# SHA1 Fingerprint: 47:be:ab:c9:22:ea:e8:0e:78:78:34:62:a7:9f:45:c2:54:fd:e6:8b +# SHA256 Fingerprint: 45:14:0b:32:47:eb:9c:c8:c5:b4:f0:d7:b5:30:91:f7:32:92:08:9e:6e:5a:63:e2:74:9d:d3:ac:a9:19:8e:da +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT +EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp +ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz +NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH +EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE +AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD +E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH +/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy +DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh +GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR +tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA +AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX +WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu +9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr +gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo +2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO +LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI +4uJEvlz36hz1 +-----END CERTIFICATE----- + +# Issuer: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Subject: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Label: "Starfield Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: d6:39:81:c6:52:7e:96:69:fc:fc:ca:66:ed:05:f2:96 +# SHA1 Fingerprint: b5:1c:06:7c:ee:2b:0c:3d:f8:55:ab:2d:92:f4:fe:39:d4:e7:0f:0e +# SHA256 Fingerprint: 2c:e1:cb:0b:f9:d2:f9:e1:02:99:3f:be:21:51:52:c3:b2:dd:0c:ab:de:1c:68:e5:31:9b:83:91:54:db:b7:f5 +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs +ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw +MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj +aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp +Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg +nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1 +HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N +Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN +dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0 +HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G +CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU +sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3 +4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg +8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K +pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1 +mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 +-----END CERTIFICATE----- + +# Issuer: CN=Starfield Services Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Subject: CN=Starfield Services Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Label: "Starfield Services Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: 17:35:74:af:7b:61:1c:eb:f4:f9:3c:e2:ee:40:f9:a2 +# SHA1 Fingerprint: 92:5a:8f:8d:2c:6d:04:e0:66:5f:59:6a:ff:22:d8:63:e8:25:6f:3f +# SHA256 Fingerprint: 56:8d:69:05:a2:c8:87:08:a4:b3:02:51:90:ed:cf:ed:b1:97:4a:60:6a:13:c6:e5:29:0f:cb:2a:e6:3e:da:b5 +-----BEGIN CERTIFICATE----- +MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs +ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 +MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD +VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy +ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy +dmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p +OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2 +8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K +Ts9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe +hRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk +6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw +DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q +AdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI +bw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB +ve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z +qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd +iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn +0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN +sSi6 +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Commercial O=AffirmTrust +# Subject: CN=AffirmTrust Commercial O=AffirmTrust +# Label: "AffirmTrust Commercial" +# Serial: 8608355977964138876 +# MD5 Fingerprint: 82:92:ba:5b:ef:cd:8a:6f:a6:3d:55:f9:84:f6:d6:b7 +# SHA1 Fingerprint: f9:b5:b6:32:45:5f:9c:be:ec:57:5f:80:dc:e9:6e:2c:c7:b2:78:b7 +# SHA256 Fingerprint: 03:76:ab:1d:54:c5:f9:80:3c:e4:b2:e2:01:a0:ee:7e:ef:7b:57:b6:36:e8:a9:3c:9b:8d:48:60:c9:6f:5f:a7 +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz +dCBDb21tZXJjaWFsMB4XDTEwMDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDEL +MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp +cm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6EqdbDuKP +Hx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yr +ba0F8PrVC8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPAL +MeIrJmqbTFeurCA+ukV6BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1 +yHp52UKqK39c/s4mT6NmgTWvRLpUHhwwMmWd5jyTXlBOeuM61G7MGvv50jeuJCqr +VwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNVHQ4EFgQUnZPGU4teyq8/ +nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ +KoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYG +XUPGhi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNj +vbz4YYCanrHOQnDiqX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivt +Z8SOyUOyXGsViQK8YvxO8rUzqrJv0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9g +N53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0khsUlHRUe072o0EclNmsxZt9YC +nlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Networking O=AffirmTrust +# Subject: CN=AffirmTrust Networking O=AffirmTrust +# Label: "AffirmTrust Networking" +# Serial: 8957382827206547757 +# MD5 Fingerprint: 42:65:ca:be:01:9a:9a:4c:a9:8c:41:49:cd:c0:d5:7f +# SHA1 Fingerprint: 29:36:21:02:8b:20:ed:02:f5:66:c5:32:d1:d6:ed:90:9f:45:00:2f +# SHA256 Fingerprint: 0a:81:ec:5a:92:97:77:f1:45:90:4a:f3:8d:5d:50:9f:66:b5:e2:c5:8f:cd:b5:31:05:8b:0e:17:f3:f0:b4:1b +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz +dCBOZXR3b3JraW5nMB4XDTEwMDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDEL +MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp +cm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SEHi3y +YJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbua +kCNrmreIdIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRL +QESxG9fhwoXA3hA/Pe24/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp +6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gbh+0t+nvujArjqWaJGctB+d1ENmHP4ndG +yH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNVHQ4EFgQUBx/S55zawm6i +QLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ +KoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfO +tDIuUFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzu +QY0x2+c06lkh1QF612S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZ +Lgo/bNjR9eUJtGxUAArgFU2HdW23WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4u +olu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9/ZFvgrG+CJPbFEfxojfHRZ48 +x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Premium O=AffirmTrust +# Subject: CN=AffirmTrust Premium O=AffirmTrust +# Label: "AffirmTrust Premium" +# Serial: 7893706540734352110 +# MD5 Fingerprint: c4:5d:0e:48:b6:ac:28:30:4e:0a:bc:f9:38:16:87:57 +# SHA1 Fingerprint: d8:a6:33:2c:e0:03:6f:b1:85:f6:63:4f:7d:6a:06:65:26:32:28:27 +# SHA256 Fingerprint: 70:a7:3f:7f:37:6b:60:07:42:48:90:45:34:b1:14:82:d5:bf:0e:69:8e:cc:49:8d:f5:25:77:eb:f2:e9:3b:9a +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVz +dCBQcmVtaXVtMB4XDTEwMDEyOTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkG +A1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1U +cnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxBLf +qV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtnBKAQ +JG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ ++jjeRFcV5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrS +s8PhaJyJ+HoAVt70VZVs+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5 +HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmdGPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d7 +70O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5Rp9EixAqnOEhss/n/fauG +V+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NIS+LI+H+S +qHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S +5u046uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4Ia +C1nEWTJ3s7xgaVY5/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TX +OwF0lkLgAOIua+rF7nKsu7/+6qqo+Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYE +FJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByvMiPIs0laUZx2 +KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg +Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B +8OWycvpEgjNC6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQ +MKSOyARiqcTtNd56l+0OOF6SL5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc +0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK+4w1IX2COPKpVJEZNZOUbWo6xbLQ +u4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmVBtWVyuEklut89pMF +u+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFgIxpH +YoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8 +GKa1qF60g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaO +RtGdFNrHF+QFlozEJLUbzxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6e +KeC2uAloGRwYQw== +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Premium ECC O=AffirmTrust +# Subject: CN=AffirmTrust Premium ECC O=AffirmTrust +# Label: "AffirmTrust Premium ECC" +# Serial: 8401224907861490260 +# MD5 Fingerprint: 64:b0:09:55:cf:b1:d5:99:e2:be:13:ab:a6:5d:ea:4d +# SHA1 Fingerprint: b8:23:6b:00:2f:1d:16:86:53:01:55:6c:11:a4:37:ca:eb:ff:c3:bb +# SHA256 Fingerprint: bd:71:fd:f6:da:97:e4:cf:62:d1:64:7a:dd:25:81:b0:7d:79:ad:f8:39:7e:b4:ec:ba:9c:5e:84:88:82:14:23 +-----BEGIN CERTIFICATE----- +MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMC +VVMxFDASBgNVBAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQ +cmVtaXVtIEVDQzAeFw0xMDAxMjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJ +BgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEgMB4GA1UEAwwXQWZmaXJt +VHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNMF4bFZ0D +0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQN8O9 +ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0G +A1UdDgQWBBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4G +A1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/Vs +aobgxCd05DhT1wV/GzTjxi+zygk8N53X57hG8f2h4nECMEJZh0PUUd+60wkyWs6I +flc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKMeQ== +-----END CERTIFICATE----- + +# Issuer: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Subject: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Label: "Certum Trusted Network CA" +# Serial: 279744 +# MD5 Fingerprint: d5:e9:81:40:c5:18:69:fc:46:2c:89:75:62:0f:aa:78 +# SHA1 Fingerprint: 07:e0:32:e0:20:b7:2c:3f:19:2f:06:28:a2:59:3a:19:a7:0f:06:9e +# SHA256 Fingerprint: 5c:58:46:8d:55:f5:8e:49:7e:74:39:82:d2:b5:00:10:b6:d1:65:37:4a:cf:83:a7:d4:a3:2d:b7:68:c4:40:8e +-----BEGIN CERTIFICATE----- +MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBM +MSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5D +ZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBU +cnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIyMTIwNzM3WhcNMjkxMjMxMTIwNzM3 +WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMg +Uy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSIw +IAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rH +UV+rpDKmYYe2bg+G0jACl/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LM +TXPb865Px1bVWqeWifrzq2jUI4ZZJ88JJ7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVU +BBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4fOQtf/WsX+sWn7Et0brM +kUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0cvW0QM8x +AcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNV +HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15y +sHhE49wcrwn9I0j6vSrEuVUEtRCjjSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfL +I9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1mS1FhIrlQgnXdAIv94nYmem8 +J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5ajZt3hrvJBW8qY +VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI +03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= +-----END CERTIFICATE----- + +# Issuer: CN=Certinomis - Autorité Racine O=Certinomis OU=0002 433998903 +# Subject: CN=Certinomis - Autorité Racine O=Certinomis OU=0002 433998903 +# Label: "Certinomis - Autorité Racine" +# Serial: 1 +# MD5 Fingerprint: 7f:30:78:8c:03:e3:ca:c9:0a:e2:c9:ea:1e:aa:55:1a +# SHA1 Fingerprint: 2e:14:da:ec:28:f0:fa:1e:8e:38:9a:4e:ab:eb:26:c0:0a:d3:83:c3 +# SHA256 Fingerprint: fc:bf:e2:88:62:06:f7:2b:27:59:3c:8b:07:02:97:e1:2d:76:9e:d1:0e:d7:93:07:05:a8:09:8e:ff:c1:4d:17 +-----BEGIN CERTIFICATE----- +MIIFnDCCA4SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJGUjET +MBEGA1UEChMKQ2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxJjAk +BgNVBAMMHUNlcnRpbm9taXMgLSBBdXRvcml0w6kgUmFjaW5lMB4XDTA4MDkxNzA4 +Mjg1OVoXDTI4MDkxNzA4Mjg1OVowYzELMAkGA1UEBhMCRlIxEzARBgNVBAoTCkNl +cnRpbm9taXMxFzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMSYwJAYDVQQDDB1DZXJ0 +aW5vbWlzIC0gQXV0b3JpdMOpIFJhY2luZTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +ADCCAgoCggIBAJ2Fn4bT46/HsmtuM+Cet0I0VZ35gb5j2CN2DpdUzZlMGvE5x4jY +F1AMnmHawE5V3udauHpOd4cN5bjr+p5eex7Ezyh0x5P1FMYiKAT5kcOrJ3NqDi5N +8y4oH3DfVS9O7cdxbwlyLu3VMpfQ8Vh30WC8Tl7bmoT2R2FFK/ZQpn9qcSdIhDWe +rP5pqZ56XjUl+rSnSTV3lqc2W+HN3yNw2F1MpQiD8aYkOBOo7C+ooWfHpi2GR+6K +/OybDnT0K0kCe5B1jPyZOQE51kqJ5Z52qz6WKDgmi92NjMD2AR5vpTESOH2VwnHu +7XSu5DaiQ3XV8QCb4uTXzEIDS3h65X27uK4uIJPT5GHfceF2Z5c/tt9qc1pkIuVC +28+BA5PY9OMQ4HL2AHCs8MF6DwV/zzRpRbWT5BnbUhYjBYkOjUjkJW+zeL9i9Qf6 +lSTClrLooyPCXQP8w9PlfMl1I9f09bze5N/NgL+RiH2nE7Q5uiy6vdFrzPOlKO1E +nn1So2+WLhl+HPNbxxaOu2B9d2ZHVIIAEWBsMsGoOBvrbpgT1u449fCfDu/+MYHB +0iSVL1N6aaLwD4ZFjliCK0wi1F6g530mJ0jfJUaNSih8hp75mxpZuWW/Bd22Ql09 +5gBIgl4g9xGC3srYn+Y3RyYe63j3YcNBZFgCQfna4NH4+ej9Uji29YnfAgMBAAGj +WzBZMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBQN +jLZh2kS40RR9w759XkjwzspqsDAXBgNVHSAEEDAOMAwGCiqBegFWAgIAAQEwDQYJ +KoZIhvcNAQEFBQADggIBACQ+YAZ+He86PtvqrxyaLAEL9MW12Ukx9F1BjYkMTv9s +ov3/4gbIOZ/xWqndIlgVqIrTseYyCYIDbNc/CMf4uboAbbnW/FIyXaR/pDGUu7ZM +OH8oMDX/nyNTt7buFHAAQCvaR6s0fl6nVjBhK4tDrP22iCj1a7Y+YEq6QpA0Z43q +619FVDsXrIvkxmUP7tCMXWY5zjKn2BCXwH40nJ+U8/aGH88bc62UeYdocMMzpXDn +2NU4lG9jeeu/Cg4I58UvD0KgKxRA/yHgBcUn4YQRE7rWhh1BCxMjidPJC+iKunqj +o3M3NYB9Ergzd0A4wPpeMNLytqOx1qKVl4GbUu1pTP+A5FPbVFsDbVRfsbjvJL1v +nxHDx2TCDyhihWZeGnuyt++uNckZM6i4J9szVb9o4XVIRFb7zdNIu0eJOqxp9YDG +5ERQL1TEqkPFMTFYvZbF6nVsmnWxTfj3l/+WFvKXTej28xH5On2KOG4Ey+HTRRWq +pdEdnV1j6CTmNhTih60bWfVEm/vXd3wfAXBioSAaosUaKPQhA+4u2cGA6rnZgtZb +dsLLO7XSAPCjDuGtbkD326C00EauFddEwk01+dIL8hf2rGbVJLJP0RyZwG71fet0 +BLj5TXcJ17TPBzAJ8bgAVtkXFhYKK4bfjwEZGuW7gmP/vgt2Fl43N+bYdJeimUV5 +-----END CERTIFICATE----- + +# Issuer: CN=Root CA Generalitat Valenciana O=Generalitat Valenciana OU=PKIGVA +# Subject: CN=Root CA Generalitat Valenciana O=Generalitat Valenciana OU=PKIGVA +# Label: "Root CA Generalitat Valenciana" +# Serial: 994436456 +# MD5 Fingerprint: 2c:8c:17:5e:b1:54:ab:93:17:b5:36:5a:db:d1:c6:f2 +# SHA1 Fingerprint: a0:73:e5:c5:bd:43:61:0d:86:4c:21:13:0a:85:58:57:cc:9c:ea:46 +# SHA256 Fingerprint: 8c:4e:df:d0:43:48:f3:22:96:9e:7e:29:a4:cd:4d:ca:00:46:55:06:1c:16:e1:b0:76:42:2e:f3:42:ad:63:0e +-----BEGIN CERTIFICATE----- +MIIGizCCBXOgAwIBAgIEO0XlaDANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJF +UzEfMB0GA1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJ +R1ZBMScwJQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwHhcN +MDEwNzA2MTYyMjQ3WhcNMjEwNzAxMTUyMjQ3WjBoMQswCQYDVQQGEwJFUzEfMB0G +A1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScw +JQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGKqtXETcvIorKA3Qdyu0togu8M1JAJke+ +WmmmO3I2F0zo37i7L3bhQEZ0ZQKQUgi0/6iMweDHiVYQOTPvaLRfX9ptI6GJXiKj +SgbwJ/BXufjpTjJ3Cj9BZPPrZe52/lSqfR0grvPXdMIKX/UIKFIIzFVd0g/bmoGl +u6GzwZTNVOAydTGRGmKy3nXiz0+J2ZGQD0EbtFpKd71ng+CT516nDOeB0/RSrFOy +A8dEJvt55cs0YFAQexvba9dHq198aMpunUEDEO5rmXteJajCq+TA81yc477OMUxk +Hl6AovWDfgzWyoxVjr7gvkkHD6MkQXpYHYTqWBLI4bft75PelAgxAgMBAAGjggM7 +MIIDNzAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3NwLnBr +aS5ndmEuZXMwEgYDVR0TAQH/BAgwBgEB/wIBAjCCAjQGA1UdIASCAiswggInMIIC +IwYKKwYBBAG/VQIBADCCAhMwggHoBggrBgEFBQcCAjCCAdoeggHWAEEAdQB0AG8A +cgBpAGQAYQBkACAAZABlACAAQwBlAHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAFIA +YQDtAHoAIABkAGUAIABsAGEAIABHAGUAbgBlAHIAYQBsAGkAdABhAHQAIABWAGEA +bABlAG4AYwBpAGEAbgBhAC4ADQAKAEwAYQAgAEQAZQBjAGwAYQByAGEAYwBpAPMA +bgAgAGQAZQAgAFAAcgDhAGMAdABpAGMAYQBzACAAZABlACAAQwBlAHIAdABpAGYA +aQBjAGEAYwBpAPMAbgAgAHEAdQBlACAAcgBpAGcAZQAgAGUAbAAgAGYAdQBuAGMA +aQBvAG4AYQBtAGkAZQBuAHQAbwAgAGQAZQAgAGwAYQAgAHAAcgBlAHMAZQBuAHQA +ZQAgAEEAdQB0AG8AcgBpAGQAYQBkACAAZABlACAAQwBlAHIAdABpAGYAaQBjAGEA +YwBpAPMAbgAgAHMAZQAgAGUAbgBjAHUAZQBuAHQAcgBhACAAZQBuACAAbABhACAA +ZABpAHIAZQBjAGMAaQDzAG4AIAB3AGUAYgAgAGgAdAB0AHAAOgAvAC8AdwB3AHcA +LgBwAGsAaQAuAGcAdgBhAC4AZQBzAC8AYwBwAHMwJQYIKwYBBQUHAgEWGWh0dHA6 +Ly93d3cucGtpLmd2YS5lcy9jcHMwHQYDVR0OBBYEFHs100DSHHgZZu90ECjcPk+y +eAT8MIGVBgNVHSMEgY0wgYqAFHs100DSHHgZZu90ECjcPk+yeAT8oWykajBoMQsw +CQYDVQQGEwJFUzEfMB0GA1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0G +A1UECxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVu +Y2lhbmGCBDtF5WgwDQYJKoZIhvcNAQEFBQADggEBACRhTvW1yEICKrNcda3Fbcrn +lD+laJWIwVTAEGmiEi8YPyVQqHxK6sYJ2fR1xkDar1CdPaUWu20xxsdzCkj+IHLt +b8zog2EWRpABlUt9jppSCS/2bxzkoXHPjCpaF3ODR00PNvsETUlR4hTJZGH71BTg +9J63NI8KJr2XXPR5OkowGcytT6CYirQxlyric21+eLj4iIlPsSKRZEv1UN4D2+XF +ducTZnV+ZfsBn5OHiJ35Rld8TWCvmHMTI6QgkYH60GFmuH3Rr9ZvHmw96RH9qfmC +IoaZM3Fa6hlXPZHNqcCjbgcTpsnt+GijnsNacgmHKNHEc8RzGF9QdRYxn7fofMM= +-----END CERTIFICATE----- + +# Issuer: CN=A-Trust-nQual-03 O=A-Trust Ges. f. Sicherheitssysteme im elektr. Datenverkehr GmbH OU=A-Trust-nQual-03 +# Subject: CN=A-Trust-nQual-03 O=A-Trust Ges. f. Sicherheitssysteme im elektr. Datenverkehr GmbH OU=A-Trust-nQual-03 +# Label: "A-Trust-nQual-03" +# Serial: 93214 +# MD5 Fingerprint: 49:63:ae:27:f4:d5:95:3d:d8:db:24:86:b8:9c:07:53 +# SHA1 Fingerprint: d3:c0:63:f2:19:ed:07:3e:34:ad:5d:75:0b:32:76:29:ff:d5:9a:f2 +# SHA256 Fingerprint: 79:3c:bf:45:59:b9:fd:e3:8a:b2:2d:f1:68:69:f6:98:81:ae:14:c4:b0:13:9a:c7:88:a7:8a:1a:fc:ca:02:fb +-----BEGIN CERTIFICATE----- +MIIDzzCCAregAwIBAgIDAWweMA0GCSqGSIb3DQEBBQUAMIGNMQswCQYDVQQGEwJB +VDFIMEYGA1UECgw/QS1UcnVzdCBHZXMuIGYuIFNpY2hlcmhlaXRzc3lzdGVtZSBp +bSBlbGVrdHIuIERhdGVudmVya2VociBHbWJIMRkwFwYDVQQLDBBBLVRydXN0LW5R +dWFsLTAzMRkwFwYDVQQDDBBBLVRydXN0LW5RdWFsLTAzMB4XDTA1MDgxNzIyMDAw +MFoXDTE1MDgxNzIyMDAwMFowgY0xCzAJBgNVBAYTAkFUMUgwRgYDVQQKDD9BLVRy +dXN0IEdlcy4gZi4gU2ljaGVyaGVpdHNzeXN0ZW1lIGltIGVsZWt0ci4gRGF0ZW52 +ZXJrZWhyIEdtYkgxGTAXBgNVBAsMEEEtVHJ1c3QtblF1YWwtMDMxGTAXBgNVBAMM +EEEtVHJ1c3QtblF1YWwtMDMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQCtPWFuA/OQO8BBC4SAzewqo51ru27CQoT3URThoKgtUaNR8t4j8DRE/5TrzAUj +lUC5B3ilJfYKvUWG6Nm9wASOhURh73+nyfrBJcyFLGM/BWBzSQXgYHiVEEvc+RFZ +znF/QJuKqiTfC0Li21a8StKlDJu3Qz7dg9MmEALP6iPESU7l0+m0iKsMrmKS1GWH +2WrX9IWf5DMiJaXlyDO6w8dB3F/GaswADm0yqLaHNgBid5seHzTLkDx4iHQF63n1 +k3Flyp3HaxgtPVxO59X4PzF9j4fsCiIvI+n+u33J4PTs63zEsMMtYrWacdaxaujs +2e3Vcuy+VwHOBVWf3tFgiBCzAgMBAAGjNjA0MA8GA1UdEwEB/wQFMAMBAf8wEQYD +VR0OBAoECERqlWdVeRFPMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOC +AQEAVdRU0VlIXLOThaq/Yy/kgM40ozRiPvbY7meIMQQDbwvUB/tOdQ/TLtPAF8fG +KOwGDREkDg6lXb+MshOWcdzUzg4NCmgybLlBMRmrsQd7TZjTXLDR8KdCoLXEjq/+ +8T/0709GAHbrAvv5ndJAlseIOrifEXnzgGWovR/TeIGgUUw3tKZdJXDRZslo+S4R +FGjxVJgIrCaSD96JntT6s3kr0qN51OyLrIdTaEJMUVF0HhsnLuP1Hyl0Te2v9+GS +mYHovjrHF1D2t8b8m7CKa9aIA5GPBnc6hQLdmNVDeD/GMBWsm2vLV7eJUYs66MmE +DNuxUCAKGkq6ahq97BvIxYSazQ== +-----END CERTIFICATE----- + +# Issuer: CN=TWCA Root Certification Authority O=TAIWAN-CA OU=Root CA +# Subject: CN=TWCA Root Certification Authority O=TAIWAN-CA OU=Root CA +# Label: "TWCA Root Certification Authority" +# Serial: 1 +# MD5 Fingerprint: aa:08:8f:f6:f9:7b:b7:f2:b1:a7:1e:9b:ea:ea:bd:79 +# SHA1 Fingerprint: cf:9e:87:6d:d3:eb:fc:42:26:97:a3:b5:a3:7a:a0:76:a9:06:23:48 +# SHA256 Fingerprint: bf:d8:8f:e1:10:1c:41:ae:3e:80:1b:f8:be:56:35:0e:e9:ba:d1:a6:b9:bd:51:5e:dc:5c:6d:5b:87:11:ac:44 +-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzES +MBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFU +V0NBIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMz +WhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJVEFJV0FO +LUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFE +AcK0HMMxQhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HH +K3XLfJ+utdGdIzdjp9xCoi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeX +RfwZVzsrb+RH9JlF/h3x+JejiB03HFyP4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/z +rX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1ry+UPizgN7gr8/g+YnzAx +3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkq +hkiG9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeC +MErJk/9q56YAf4lCmtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdls +XebQ79NqZp4VKIV66IIArB6nCWlWQtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62D +lhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVYT0bf+215WfKEIlKuD8z7fDvn +aspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocnyYh0igzyXxfkZ +YiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== +-----END CERTIFICATE----- + +# Issuer: O=SECOM Trust Systems CO.,LTD. OU=Security Communication RootCA2 +# Subject: O=SECOM Trust Systems CO.,LTD. OU=Security Communication RootCA2 +# Label: "Security Communication RootCA2" +# Serial: 0 +# MD5 Fingerprint: 6c:39:7d:a4:0e:55:59:b2:3f:d6:41:b1:12:50:de:43 +# SHA1 Fingerprint: 5f:3b:8c:f2:f8:10:b3:7d:78:b4:ce:ec:19:19:c3:73:34:b9:c7:74 +# SHA256 Fingerprint: 51:3b:2c:ec:b8:10:d4:cd:e5:dd:85:39:1a:df:c6:c2:dd:60:d8:7b:b7:36:d2:b5:21:48:4a:a4:7a:0e:be:f6 +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDEl +MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMe +U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoX +DTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRy +dXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3VyaXR5IENvbW11bmlj +YXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANAV +OVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGr +zbl+dp+++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVM +VAX3NuRFg3sUZdbcDE3R3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQ +hNBqyjoGADdH5H5XTz+L62e4iKrFvlNVspHEfbmwhRkGeC7bYRr6hfVKkaHnFtWO +ojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1KEOtOghY6rCcMU/Gt1SSw +awNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8QIH4D5cs +OPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpF +coJxDjrSzG+ntKEju/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXc +okgfGT+Ok+vx+hfuzU7jBBJV1uXk3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8 +t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6qtnRGEmyR7jTV7JqR50S+kDFy +1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29mvVXIwAHIRc/ +SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 +-----END CERTIFICATE----- + +# Issuer: CN=Hellenic Academic and Research Institutions RootCA 2011 O=Hellenic Academic and Research Institutions Cert. Authority +# Subject: CN=Hellenic Academic and Research Institutions RootCA 2011 O=Hellenic Academic and Research Institutions Cert. Authority +# Label: "Hellenic Academic and Research Institutions RootCA 2011" +# Serial: 0 +# MD5 Fingerprint: 73:9f:4c:4b:73:5b:79:e9:fa:ba:1c:ef:6e:cb:d5:c9 +# SHA1 Fingerprint: fe:45:65:9b:79:03:5b:98:a1:61:b5:51:2e:ac:da:58:09:48:22:4d +# SHA256 Fingerprint: bc:10:4f:15:a4:8b:e7:09:dc:a5:42:a7:e1:d4:b9:df:6f:05:45:27:e8:02:ea:a9:2d:59:54:44:25:8a:fe:71 +-----BEGIN CERTIFICATE----- +MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1Ix +RDBCBgNVBAoTO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1 +dGlvbnMgQ2VydC4gQXV0aG9yaXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1p +YyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIFJvb3RDQSAyMDExMB4XDTExMTIw +NjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYTAkdSMUQwQgYDVQQK +EztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIENl +cnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl +c2VhcmNoIEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPz +dYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJ +fel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa71HFK9+WXesyHgLacEns +bgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u8yBRQlqD +75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSP +FEDH3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNV +HRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp +5dgTBCPuQSUwRwYDVR0eBEAwPqA8MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQu +b3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQub3JnMA0GCSqGSIb3DQEBBQUA +A4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVtXdMiKahsog2p +6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8 +TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7 +dIsXRSZMFpGD/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8Acys +Nnq/onN694/BtZqhFLKPM58N7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXI +l7WdmplNsDz4SgCbZN2fOUvRJ9e4 +-----END CERTIFICATE----- + +# Issuer: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967 +# Subject: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967 +# Label: "Actalis Authentication Root CA" +# Serial: 6271844772424770508 +# MD5 Fingerprint: 69:c1:0d:4f:07:a3:1b:c3:fe:56:3d:04:bc:11:f6:a6 +# SHA1 Fingerprint: f3:73:b3:87:06:5a:28:84:8a:f2:f3:4a:ce:19:2b:dd:c7:8e:9c:ac +# SHA256 Fingerprint: 55:92:60:84:ec:96:3a:64:b9:6e:2a:be:01:ce:0b:a8:6a:64:fb:fe:bc:c7:aa:b5:af:c1:55:b3:7f:d7:60:66 +-----BEGIN CERTIFICATE----- +MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UE +BhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8w +MzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290 +IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDkyMjExMjIwMlowazELMAkGA1UEBhMC +SVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1 +ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENB +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNv +UTufClrJwkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX +4ay8IMKx4INRimlNAJZaby/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9 +KK3giq0itFZljoZUj5NDKd45RnijMCO6zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/ +gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1fYVEiVRvjRuPjPdA1Yprb +rxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2oxgkg4YQ +51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2F +be8lEfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxe +KF+w6D9Fz8+vm2/7hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4F +v6MGn8i1zeQf1xcGDXqVdFUNaBr8EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbn +fpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5jF66CyCU3nuDuP/jVo23Eek7 +jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLYiDrIn3hm7Ynz +ezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt +ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAL +e3KHwGCmSUyIWOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70 +jsNjLiNmsGe+b7bAEzlgqqI0JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDz +WochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKxK3JCaKygvU5a2hi/a5iB0P2avl4V +SM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+Xlff1ANATIGk0k9j +pwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC4yyX +X04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+Ok +fcvHlXHo2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7R +K4X9p2jIugErsWx0Hbhzlefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btU +ZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXemOR/qnuOf0GZvBeyqdn6/axag67XH/JJU +LysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9vwGYT7JZVEc+NHt4bVaT +LnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== +-----END CERTIFICATE----- + +# Issuer: O=Trustis Limited OU=Trustis FPS Root CA +# Subject: O=Trustis Limited OU=Trustis FPS Root CA +# Label: "Trustis FPS Root CA" +# Serial: 36053640375399034304724988975563710553 +# MD5 Fingerprint: 30:c9:e7:1e:6b:e6:14:eb:65:b2:16:69:20:31:67:4d +# SHA1 Fingerprint: 3b:c0:38:0b:33:c3:f6:a6:0c:86:15:22:93:d9:df:f5:4b:81:c0:04 +# SHA256 Fingerprint: c1:b4:82:99:ab:a5:20:8f:e9:63:0a:ce:55:ca:68:a0:3e:da:5a:51:9c:88:02:a0:d3:a6:73:be:8f:8e:55:7d +-----BEGIN CERTIFICATE----- +MIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBF +MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPVHJ1c3RpcyBMaW1pdGVkMRwwGgYDVQQL +ExNUcnVzdGlzIEZQUyBSb290IENBMB4XDTAzMTIyMzEyMTQwNloXDTI0MDEyMTEx +MzY1NFowRTELMAkGA1UEBhMCR0IxGDAWBgNVBAoTD1RydXN0aXMgTGltaXRlZDEc +MBoGA1UECxMTVHJ1c3RpcyBGUFMgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAMVQe547NdDfxIzNjpvto8A2mfRC6qc+gIMPpqdZh8mQRUN+ +AOqGeSoDvT03mYlmt+WKVoaTnGhLaASMk5MCPjDSNzoiYYkchU59j9WvezX2fihH +iTHcDnlkH5nSW7r+f2C/revnPDgpai/lkQtV/+xvWNUtyd5MZnGPDNcE2gfmHhjj +vSkCqPoc4Vu5g6hBSLwacY3nYuUtsuvffM/bq1rKMfFMIvMFE/eC+XN5DL7XSxzA +0RU8k0Fk0ea+IxciAIleH2ulrG6nS4zto3Lmr2NNL4XSFDWaLk6M6jKYKIahkQlB +OrTh4/L68MkKokHdqeMDx4gVOxzUGpTXn2RZEm0CAwEAAaNTMFEwDwYDVR0TAQH/ +BAUwAwEB/zAfBgNVHSMEGDAWgBS6+nEleYtXQSUhhgtx67JkDoshZzAdBgNVHQ4E +FgQUuvpxJXmLV0ElIYYLceuyZA6LIWcwDQYJKoZIhvcNAQEFBQADggEBAH5Y//01 +GX2cGE+esCu8jowU/yyg2kdbw++BLa8F6nRIW/M+TgfHbcWzk88iNVy2P3UnXwmW +zaD+vkAMXBJV+JOCyinpXj9WV4s4NvdFGkwozZ5BuO1WTISkQMi4sKUraXAEasP4 +1BIy+Q7DsdwyhEQsb8tGD+pmQQ9P8Vilpg0ND2HepZ5dfWWhPBfnqFVO76DH7cZE +f1T1o+CP8HxVIo8ptoGj4W1OLBuAZ+ytIJ8MYmHVl/9D7S3B2l0pKoU/rGXuhg8F +jZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYliB6XzCGcKQEN +ZetX2fNXlrtIzYE= +-----END CERTIFICATE----- + +# Issuer: CN=StartCom Certification Authority O=StartCom Ltd. OU=Secure Digital Certificate Signing +# Subject: CN=StartCom Certification Authority O=StartCom Ltd. OU=Secure Digital Certificate Signing +# Label: "StartCom Certification Authority" +# Serial: 45 +# MD5 Fingerprint: c9:3b:0d:84:41:fc:a4:76:79:23:08:57:de:10:19:16 +# SHA1 Fingerprint: a3:f1:33:3f:e2:42:bf:cf:c5:d1:4e:8f:39:42:98:40:68:10:d1:a0 +# SHA256 Fingerprint: e1:78:90:ee:09:a3:fb:f4:f4:8b:9c:41:4a:17:d6:37:b7:a5:06:47:e9:bc:75:23:22:72:7f:cc:17:42:a9:11 +-----BEGIN CERTIFICATE----- +MIIHhzCCBW+gAwIBAgIBLTANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJJTDEW +MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg +Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM3WhcNMzYwOTE3MTk0NjM2WjB9 +MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi +U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh +cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk +pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf +OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C +Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT +Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi +HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM +Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w ++2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+ +Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3 +Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B +26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID +AQABo4ICEDCCAgwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD +VR0OBBYEFE4L7xqkQFulF2mHMMo0aEPQQa7yMB8GA1UdIwQYMBaAFE4L7xqkQFul +F2mHMMo0aEPQQa7yMIIBWgYDVR0gBIIBUTCCAU0wggFJBgsrBgEEAYG1NwEBATCC +ATgwLgYIKwYBBQUHAgEWImh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5w +ZGYwNAYIKwYBBQUHAgEWKGh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL2ludGVybWVk +aWF0ZS5wZGYwgc8GCCsGAQUFBwICMIHCMCcWIFN0YXJ0IENvbW1lcmNpYWwgKFN0 +YXJ0Q29tKSBMdGQuMAMCAQEagZZMaW1pdGVkIExpYWJpbGl0eSwgcmVhZCB0aGUg +c2VjdGlvbiAqTGVnYWwgTGltaXRhdGlvbnMqIG9mIHRoZSBTdGFydENvbSBDZXJ0 +aWZpY2F0aW9uIEF1dGhvcml0eSBQb2xpY3kgYXZhaWxhYmxlIGF0IGh0dHA6Ly93 +d3cuc3RhcnRzc2wuY29tL3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgG +CWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1 +dGhvcml0eTANBgkqhkiG9w0BAQsFAAOCAgEAjo/n3JR5fPGFf59Jb2vKXfuM/gTF +wWLRfUKKvFO3lANmMD+x5wqnUCBVJX92ehQN6wQOQOY+2IirByeDqXWmN3PH/UvS +Ta0XQMhGvjt/UfzDtgUx3M2FIk5xt/JxXrAaxrqTi3iSSoX4eA+D/i+tLPfkpLst +0OcNOrg+zvZ49q5HJMqjNTbOx8aHmNrs++myziebiMMEofYLWWivydsQD032ZGNc +pRJvkrKTlMeIFw6Ttn5ii5B/q06f/ON1FE8qMt9bDeD1e5MNq6HPh+GlBEXoPBKl +CcWw0bdT82AUuoVpaiF8H3VhFyAXe2w7QSlc4axa0c2Mm+tgHRns9+Ww2vl5GKVF +P0lDV9LdJNUso/2RjSe15esUBppMeyG7Oq0wBhjA2MFrLH9ZXF2RsXAiV+uKa0hK +1Q8p7MZAwC+ITGgBF3f0JBlPvfrhsiAhS90a2Cl9qrjeVOwhVYBsHvUwyKMQ5bLm +KhQxw4UtjJixhlpPiVktucf3HMiKf8CdBUrmQk9io20ppB+Fq9vlgcitKj1MXVuE +JnHEhV5xJMqlG2zYYdMa4FTbzrqpMrUi9nNBCV24F10OD5mQ1kfabwo6YigUZ4LZ +8dCAWZvLMdibD4x3TrVoivJs9iQOLWxwxXPR3hTQcY+203sC9uO41Alua551hDnm +fyWl8kgAwKQB2j8= +-----END CERTIFICATE----- + +# Issuer: CN=StartCom Certification Authority G2 O=StartCom Ltd. +# Subject: CN=StartCom Certification Authority G2 O=StartCom Ltd. +# Label: "StartCom Certification Authority G2" +# Serial: 59 +# MD5 Fingerprint: 78:4b:fb:9e:64:82:0a:d3:b8:4c:62:f3:64:f2:90:64 +# SHA1 Fingerprint: 31:f1:fd:68:22:63:20:ee:c6:3b:3f:9d:ea:4a:3e:53:7c:7c:39:17 +# SHA256 Fingerprint: c7:ba:65:67:de:93:a7:98:ae:1f:aa:79:1e:71:2d:37:8f:ae:1f:93:c4:39:7f:ea:44:1b:b7:cb:e6:fd:59:95 +-----BEGIN CERTIFICATE----- +MIIFYzCCA0ugAwIBAgIBOzANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJJTDEW +MBQGA1UEChMNU3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkgRzIwHhcNMTAwMTAxMDEwMDAxWhcNMzkxMjMxMjM1 +OTAxWjBTMQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjEsMCoG +A1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgRzIwggIiMA0G +CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2iTZbB7cgNr2Cu+EWIAOVeq8Oo1XJ +JZlKxdBWQYeQTSFgpBSHO839sj60ZwNq7eEPS8CRhXBF4EKe3ikj1AENoBB5uNsD +vfOpL9HG4A/LnooUCri99lZi8cVytjIl2bLzvWXFDSxu1ZJvGIsAQRSCb0AgJnoo +D/Uefyf3lLE3PbfHkffiAez9lInhzG7TNtYKGXmu1zSCZf98Qru23QumNK9LYP5/ +Q0kGi4xDuFby2X8hQxfqp0iVAXV16iulQ5XqFYSdCI0mblWbq9zSOdIxHWDirMxW +RST1HFSr7obdljKF+ExP6JV2tgXdNiNnvP8V4so75qbsO+wmETRIjfaAKxojAuuK +HDp2KntWFhxyKrOq42ClAJ8Em+JvHhRYW6Vsi1g8w7pOOlz34ZYrPu8HvKTlXcxN +nw3h3Kq74W4a7I/htkxNeXJdFzULHdfBR9qWJODQcqhaX2YtENwvKhOuJv4KHBnM +0D4LnMgJLvlblnpHnOl68wVQdJVznjAJ85eCXuaPOQgeWeU1FEIT/wCc976qUM/i +UUjXuG+v+E5+M5iSFGI6dWPPe/regjupuznixL0sAA7IF6wT700ljtizkC+p2il9 +Ha90OrInwMEePnWjFqmveiJdnxMaz6eg6+OGCtP95paV1yPIN93EfKo2rJgaErHg +TuixO/XWb/Ew1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE +AwIBBjAdBgNVHQ4EFgQUS8W0QGutHLOlHGVuRjaJhwUMDrYwDQYJKoZIhvcNAQEL +BQADggIBAHNXPyzVlTJ+N9uWkusZXn5T50HsEbZH77Xe7XRcxfGOSeD8bpkTzZ+K +2s06Ctg6Wgk/XzTQLwPSZh0avZyQN8gMjgdalEVGKua+etqhqaRpEpKwfTbURIfX +UfEpY9Z1zRbkJ4kd+MIySP3bmdCPX1R0zKxnNBFi2QwKN4fRoxdIjtIXHfbX/dtl +6/2o1PXWT6RbdejF0mCy2wl+JYt7ulKSnj7oxXehPOBKc2thz4bcQ///If4jXSRK +9dNtD2IEBVeC2m6kMyV5Sy5UGYvMLD0w6dEG/+gyRr61M3Z3qAFdlsHB1b6uJcDJ +HgoJIIihDsnzb02CVAAgp9KP5DlUFy6NHrgbuxu9mk47EDTcnIhT76IxW1hPkWLI +wpqazRVdOKnWvvgTtZ8SafJQYqz7Fzf07rh1Z2AQ+4NQ+US1dZxAF7L+/XldblhY +XzD8AK6vM8EOTmy6p6ahfzLbOOCxchcKK5HsamMm7YnUeMx0HgX4a/6ManY5Ka5l +IxKVCCIcl85bBu4M4ru8H0ST9tg4RQUh7eStqxK2A6RCLi3ECToDZ2mEmuFZkIoo +hdVddLHRDiBYmxOlsGOm7XtH/UVVMKTumtTm4ofvmMkyghEpIrwACjFeLQ/Ajulr +so8uBtjRkcfGEvRM/TAXw8HaOFvjqermobp573PYtlNXLfbQ4ddI +-----END CERTIFICATE----- + +# Issuer: CN=Buypass Class 2 Root CA O=Buypass AS-983163327 +# Subject: CN=Buypass Class 2 Root CA O=Buypass AS-983163327 +# Label: "Buypass Class 2 Root CA" +# Serial: 2 +# MD5 Fingerprint: 46:a7:d2:fe:45:fb:64:5a:a8:59:90:9b:78:44:9b:29 +# SHA1 Fingerprint: 49:0a:75:74:de:87:0a:47:fe:58:ee:f6:c7:6b:eb:c6:0b:12:40:99 +# SHA256 Fingerprint: 9a:11:40:25:19:7c:5b:b9:5d:94:e6:3d:55:cd:43:79:08:47:b6:46:b2:3c:df:11:ad:a4:a0:0e:ff:15:fb:48 +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg +Q2xhc3MgMiBSb290IENBMB4XDTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1ow +TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw +HgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1g1Lr +6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPV +L4O2fuPn9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC91 +1K2GScuVr1QGbNgGE41b/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHx +MlAQTn/0hpPshNOOvEu/XAFOBz3cFIqUCqTqc/sLUegTBxj6DvEr0VQVfTzh97QZ +QmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeffawrbD02TTqigzXsu8lkB +arcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgIzRFo1clr +Us3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLi +FRhnBkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRS +P/TizPJhk9H9Z2vXUq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN +9SG9dKpN6nIDSdvHXx1iY8f93ZHsM+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxP +AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMmAd+BikoL1Rpzz +uvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAU18h +9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s +A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3t +OluwlN5E40EIosHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo ++fsicdl9sz1Gv7SEr5AcD48Saq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7 +KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYdDnkM/crqJIByw5c/8nerQyIKx+u2 +DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWDLfJ6v9r9jv6ly0Us +H8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0oyLQ +I+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK7 +5t98biGCwWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h +3PFaTWwyI0PurKju7koSCTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPz +Y11aWOIv4x3kqdbQCtCev9eBCfHJxyYNrJgWVqA= +-----END CERTIFICATE----- + +# Issuer: CN=Buypass Class 3 Root CA O=Buypass AS-983163327 +# Subject: CN=Buypass Class 3 Root CA O=Buypass AS-983163327 +# Label: "Buypass Class 3 Root CA" +# Serial: 2 +# MD5 Fingerprint: 3d:3b:18:9e:2c:64:5a:e8:d5:88:ce:0e:f9:37:c2:ec +# SHA1 Fingerprint: da:fa:f7:fa:66:84:ec:06:8f:14:50:bd:c7:c2:81:a5:bc:a9:64:57 +# SHA256 Fingerprint: ed:f7:eb:bc:a2:7a:2a:38:4d:38:7b:7d:40:10:c6:66:e2:ed:b4:84:3e:4c:29:b4:ae:1d:5b:93:32:e6:b2:4d +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg +Q2xhc3MgMyBSb290IENBMB4XDTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFow +TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw +HgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRHsJ8Y +ZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3E +N3coTRiR5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9 +tznDDgFHmV0ST9tD+leh7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX +0DJq1l1sDPGzbjniazEuOQAnFN44wOwZZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c +/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH2xc519woe2v1n/MuwU8X +KhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV/afmiSTY +zIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvS +O1UQRwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D +34xFMFbG02SrZvPAXpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgP +K9Dx2hzLabjKSWJtyNBjYt1gD1iqj6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3 +AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEe4zf/lb+74suwv +Tg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAACAj +QTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV +cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXS +IGrs/CIBKM+GuIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2 +HJLw5QY33KbmkJs4j1xrG0aGQ0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsa +O5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8ZORK15FTAaggiG6cX0S5y2CBNOxv +033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2KSb12tjE8nVhz36u +dmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz6MkE +kbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg41 +3OEMXbugUZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvD +u79leNKGef9JOxqDDPDeeOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq +4/g7u9xN12TyUb7mqqta6THuBrxzvxNiCp/HuZc= +-----END CERTIFICATE----- + +# Issuer: CN=T-TeleSec GlobalRoot Class 3 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Subject: CN=T-TeleSec GlobalRoot Class 3 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Label: "T-TeleSec GlobalRoot Class 3" +# Serial: 1 +# MD5 Fingerprint: ca:fb:40:a8:4e:39:92:8a:1d:fe:8e:2f:c4:27:ea:ef +# SHA1 Fingerprint: 55:a6:72:3e:cb:f2:ec:cd:c3:23:74:70:19:9d:2a:be:11:e3:81:d1 +# SHA256 Fingerprint: fd:73:da:d3:1c:64:4f:f1:b4:3b:ef:0c:cd:da:96:71:0b:9c:d9:87:5e:ca:7e:31:70:7a:f3:e9:6d:52:2b:bd +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx +KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd +BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl +YyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgxMDAxMTAyOTU2WhcNMzMxMDAxMjM1 +OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy +aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 +ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN +8ELg63iIVl6bmlQdTQyK9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/ +RLyTPWGrTs0NvvAgJ1gORH8EGoel15YUNpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4 +hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZFiP0Zf3WHHx+xGwpzJFu5 +ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W0eDrXltM +EnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1 +A/d2O2GCahKqGFPrAyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOy +WL6ukK2YJ5f+AbGwUgC4TeQbIXQbfsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ +1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzTucpH9sry9uetuUg/vBa3wW30 +6gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7hP0HHRwA11fXT +91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml +e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4p +TpPDpFQUWw== +-----END CERTIFICATE----- + +# Issuer: CN=EE Certification Centre Root CA O=AS Sertifitseerimiskeskus +# Subject: CN=EE Certification Centre Root CA O=AS Sertifitseerimiskeskus +# Label: "EE Certification Centre Root CA" +# Serial: 112324828676200291871926431888494945866 +# MD5 Fingerprint: 43:5e:88:d4:7d:1a:4a:7e:fd:84:2e:52:eb:01:d4:6f +# SHA1 Fingerprint: c9:a8:b9:e7:55:80:5e:58:e3:53:77:a7:25:eb:af:c3:7b:27:cc:d7 +# SHA256 Fingerprint: 3e:84:ba:43:42:90:85:16:e7:75:73:c0:99:2f:09:79:ca:08:4e:46:85:68:1f:f1:95:cc:ba:8a:22:9b:8a:76 +-----BEGIN CERTIFICATE----- +MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1 +MQswCQYDVQQGEwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1 +czEoMCYGA1UEAwwfRUUgQ2VydGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYG +CSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIwMTAxMDMwMTAxMDMwWhgPMjAzMDEy +MTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlBUyBTZXJ0aWZpdHNl +ZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRyZSBS +b290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUy +euuOF0+W2Ap7kaJjbMeMTC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvO +bntl8jixwKIy72KyaOBhU8E2lf/slLo2rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIw +WFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw93X2PaRka9ZP585ArQ/d +MtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtNP2MbRMNE +1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYD +VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/ +zQas8fElyalL1BSZMEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYB +BQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEF +BQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+RjxY6hUFaTlrg4wCQiZrxTFGGV +v9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqMlIpPnTX/dqQG +E5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u +uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIW +iAYLtqZLICjU3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/v +GVCJYMzpJJUPwssd8m92kMfMdcGWxZ0= +-----END CERTIFICATE----- + +# Issuer: CN=TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı O=TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. (c) Aralık 2007 +# Subject: CN=TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı O=TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. (c) Aralık 2007 +# Label: "TURKTRUST Certificate Services Provider Root 2007" +# Serial: 1 +# MD5 Fingerprint: 2b:70:20:56:86:82:a0:18:c8:07:53:12:28:70:21:72 +# SHA1 Fingerprint: f1:7f:6f:b6:31:dc:99:e3:a3:c8:7f:fe:1c:f1:81:10:88:d9:60:33 +# SHA256 Fingerprint: 97:8c:d9:66:f2:fa:a0:7b:a7:aa:95:00:d9:c0:2e:9d:77:f2:cd:ad:a6:ad:6b:a7:4a:f4:b9:1c:66:59:3c:50 +-----BEGIN CERTIFICATE----- +MIIEPTCCAyWgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvzE/MD0GA1UEAww2VMOc +UktUUlVTVCBFbGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sx +c8SxMQswCQYDVQQGEwJUUjEPMA0GA1UEBwwGQW5rYXJhMV4wXAYDVQQKDFVUw5xS +S1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kg +SGl6bWV0bGVyaSBBLsWeLiAoYykgQXJhbMSxayAyMDA3MB4XDTA3MTIyNTE4Mzcx +OVoXDTE3MTIyMjE4MzcxOVowgb8xPzA9BgNVBAMMNlTDnFJLVFJVU1QgRWxla3Ry +b25payBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTELMAkGA1UEBhMC +VFIxDzANBgNVBAcMBkFua2FyYTFeMFwGA1UECgxVVMOcUktUUlVTVCBCaWxnaSDE +sGxldGnFn2ltIHZlIEJpbGnFn2ltIEfDvHZlbmxpxJ9pIEhpem1ldGxlcmkgQS7F +ni4gKGMpIEFyYWzEsWsgMjAwNzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAKu3PgqMyKVYFeaK7yc9SrToJdPNM8Ig3BnuiD9NYvDdE3ePYakqtdTyuTFY +KTsvP2qcb3N2Je40IIDu6rfwxArNK4aUyeNgsURSsloptJGXg9i3phQvKUmi8wUG ++7RP2qFsmmaf8EMJyupyj+sA1zU511YXRxcw9L6/P8JorzZAwan0qafoEGsIiveG +HtyaKhUG9qPw9ODHFNRRf8+0222vR5YXm3dx2KdxnSQM9pQ/hTEST7ruToK4uT6P +IzdezKKqdfcYbwnTrqdUKDT74eA7YH2gvnmJhsifLfkKS8RQouf9eRbHegsYz85M +733WB2+Y8a+xwXrXgTW4qhe04MsCAwEAAaNCMEAwHQYDVR0OBBYEFCnFkKslrxHk +Yb+j/4hhkeYO/pyBMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBBQUAA4IBAQAQDdr4Ouwo0RSVgrESLFF6QSU2TJ/sPx+EnWVUXKgW +AkD6bho3hO9ynYYKVZ1WKKxmLNA6VpM0ByWtCLCPyA8JWcqdmBzlVPi5RX9ql2+I +aE1KBiY3iAIOtsbWcpnOa3faYjGkVh+uX4132l32iPwa2Z61gfAyuOOI0JzzaqC5 +mxRZNTZPz/OOXl0XrRWV2N2y1RVuAE6zS89mlOTgzbUF2mNXi+WzqtvALhyQRNsa +XRik7r4EW5nVcV9VZWRi1aKbBFmGyGJ353yCRWo9F7/snXUMrqNvWtMvmDb08PUZ +qxFdyKbjKlhqQgnDvZImZjINXQhVdP+MmNAKpoRq0Tl9 +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH +# Subject: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH +# Label: "D-TRUST Root Class 3 CA 2 2009" +# Serial: 623603 +# MD5 Fingerprint: cd:e0:25:69:8d:47:ac:9c:89:35:90:f7:fd:51:3d:2f +# SHA1 Fingerprint: 58:e8:ab:b0:36:15:33:fb:80:f7:9b:1b:6d:29:d3:ff:8d:5f:00:f0 +# SHA256 Fingerprint: 49:e7:a4:42:ac:f0:ea:62:87:05:00:54:b5:25:64:b6:50:e4:f4:9e:42:e3:48:d6:aa:38:e0:39:e9:57:b1:c1 +-----BEGIN CERTIFICATE----- +MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBD +bGFzcyAzIENBIDIgMjAwOTAeFw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NTha +ME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMM +HkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOADER03 +UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42 +tSHKXzlABF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9R +ySPocq60vFYJfxLLHLGvKZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsM +lFqVlNpQmvH/pStmMaTJOKDfHR+4CS7zp+hnUquVH+BGPtikw8paxTGA6Eian5Rp +/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUCAwEAAaOCARowggEWMA8G +A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ4PGEMA4G +A1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVj +dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUy +MENBJTIwMiUyMDIwMDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRl +cmV2b2NhdGlvbmxpc3QwQ6BBoD+GPWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3Js +L2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAwOS5jcmwwDQYJKoZIhvcNAQEL +BQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm2H6NMLVwMeni +acfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 +o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4K +zCUqNQT4YJEVdT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8 +PIWmawomDeCTmGCufsYkl4phX5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3Y +Johw1+qRzT65ysCQblrGXnRl11z+o+I= +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST Root Class 3 CA 2 EV 2009 O=D-Trust GmbH +# Subject: CN=D-TRUST Root Class 3 CA 2 EV 2009 O=D-Trust GmbH +# Label: "D-TRUST Root Class 3 CA 2 EV 2009" +# Serial: 623604 +# MD5 Fingerprint: aa:c6:43:2c:5e:2d:cd:c4:34:c0:50:4f:11:02:4f:b6 +# SHA1 Fingerprint: 96:c9:1b:0b:95:b4:10:98:42:fa:d0:d8:22:79:fe:60:fa:b9:16:83 +# SHA256 Fingerprint: ee:c5:49:6b:98:8c:e9:86:25:b9:34:09:2e:ec:29:08:be:d0:b0:f3:16:c2:d4:73:0c:84:ea:f1:f3:d3:48:81 +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBD +bGFzcyAzIENBIDIgRVYgMjAwOTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUw +NDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNV +BAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAwOTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfSegpn +ljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM0 +3TP1YtHhzRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6Z +qQTMFexgaDbtCHu39b+T7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lR +p75mpoo6Kr3HGrHhFPC+Oh25z1uxav60sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8 +HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure3511H3a6UCAwEAAaOCASQw +ggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyvcop9Ntea +HNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFw +Oi8vZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xh +c3MlMjAzJTIwQ0ElMjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1E +RT9jZXJ0aWZpY2F0ZXJldm9jYXRpb25saXN0MEagRKBChkBodHRwOi8vd3d3LmQt +dHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xhc3NfM19jYV8yX2V2XzIwMDku +Y3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+PPoeUSbrh/Yp +3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 +nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNF +CSuGdXzfX2lXANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7na +xpeG0ILD5EJt/rDiZE4OJudANCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqX +KVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVvw9y4AyHqnxbxLFS1 +-----END CERTIFICATE----- + +# Issuer: CN=Autoridad de Certificacion Raiz del Estado Venezolano O=Sistema Nacional de Certificacion Electronica OU=Superintendencia de Servicios de Certificacion Electronica +# Subject: CN=PSCProcert O=Sistema Nacional de Certificacion Electronica OU=Proveedor de Certificados PROCERT +# Label: "PSCProcert" +# Serial: 11 +# MD5 Fingerprint: e6:24:e9:12:01:ae:0c:de:8e:85:c4:ce:a3:12:dd:ec +# SHA1 Fingerprint: 70:c1:8d:74:b4:28:81:0a:e4:fd:a5:75:d7:01:9f:99:b0:3d:50:74 +# SHA256 Fingerprint: 3c:fc:3c:14:d1:f6:84:ff:17:e3:8c:43:ca:44:0c:00:b9:67:ec:93:3e:8b:fe:06:4c:a1:d7:2c:90:f2:ad:b0 +-----BEGIN CERTIFICATE----- +MIIJhjCCB26gAwIBAgIBCzANBgkqhkiG9w0BAQsFADCCAR4xPjA8BgNVBAMTNUF1 +dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIFJhaXogZGVsIEVzdGFkbyBWZW5lem9s +YW5vMQswCQYDVQQGEwJWRTEQMA4GA1UEBxMHQ2FyYWNhczEZMBcGA1UECBMQRGlz +dHJpdG8gQ2FwaXRhbDE2MDQGA1UEChMtU2lzdGVtYSBOYWNpb25hbCBkZSBDZXJ0 +aWZpY2FjaW9uIEVsZWN0cm9uaWNhMUMwQQYDVQQLEzpTdXBlcmludGVuZGVuY2lh +IGRlIFNlcnZpY2lvcyBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMSUwIwYJ +KoZIhvcNAQkBFhZhY3JhaXpAc3VzY2VydGUuZ29iLnZlMB4XDTEwMTIyODE2NTEw +MFoXDTIwMTIyNTIzNTk1OVowgdExJjAkBgkqhkiG9w0BCQEWF2NvbnRhY3RvQHBy +b2NlcnQubmV0LnZlMQ8wDQYDVQQHEwZDaGFjYW8xEDAOBgNVBAgTB01pcmFuZGEx +KjAoBgNVBAsTIVByb3ZlZWRvciBkZSBDZXJ0aWZpY2Fkb3MgUFJPQ0VSVDE2MDQG +A1UEChMtU2lzdGVtYSBOYWNpb25hbCBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9u +aWNhMQswCQYDVQQGEwJWRTETMBEGA1UEAxMKUFNDUHJvY2VydDCCAiIwDQYJKoZI +hvcNAQEBBQADggIPADCCAgoCggIBANW39KOUM6FGqVVhSQ2oh3NekS1wwQYalNo9 +7BVCwfWMrmoX8Yqt/ICV6oNEolt6Vc5Pp6XVurgfoCfAUFM+jbnADrgV3NZs+J74 +BCXfgI8Qhd19L3uA3VcAZCP4bsm+lU/hdezgfl6VzbHvvnpC2Mks0+saGiKLt38G +ieU89RLAu9MLmV+QfI4tL3czkkohRqipCKzx9hEC2ZUWno0vluYC3XXCFCpa1sl9 +JcLB/KpnheLsvtF8PPqv1W7/U0HU9TI4seJfxPmOEO8GqQKJ/+MMbpfg353bIdD0 +PghpbNjU5Db4g7ayNo+c7zo3Fn2/omnXO1ty0K+qP1xmk6wKImG20qCZyFSTXai2 +0b1dCl53lKItwIKOvMoDKjSuc/HUtQy9vmebVOvh+qBa7Dh+PsHMosdEMXXqP+UH +0quhJZb25uSgXTcYOWEAM11G1ADEtMo88aKjPvM6/2kwLkDd9p+cJsmWN63nOaK/ +6mnbVSKVUyqUtd+tFjiBdWbjxywbk5yqjKPK2Ww8F22c3HxT4CAnQzb5EuE8XL1m +v6JpIzi4mWCZDlZTOpx+FIywBm/xhnaQr/2v/pDGj59/i5IjnOcVdo/Vi5QTcmn7 +K2FjiO/mpF7moxdqWEfLcU8UC17IAggmosvpr2uKGcfLFFb14dq12fy/czja+eev +bqQ34gcnAgMBAAGjggMXMIIDEzASBgNVHRMBAf8ECDAGAQH/AgEBMDcGA1UdEgQw +MC6CD3N1c2NlcnRlLmdvYi52ZaAbBgVghl4CAqASDBBSSUYtRy0yMDAwNDAzNi0w +MB0GA1UdDgQWBBRBDxk4qpl/Qguk1yeYVKIXTC1RVDCCAVAGA1UdIwSCAUcwggFD +gBStuyIdxuDSAaj9dlBSk+2YwU2u06GCASakggEiMIIBHjE+MDwGA1UEAxM1QXV0 +b3JpZGFkIGRlIENlcnRpZmljYWNpb24gUmFpeiBkZWwgRXN0YWRvIFZlbmV6b2xh +bm8xCzAJBgNVBAYTAlZFMRAwDgYDVQQHEwdDYXJhY2FzMRkwFwYDVQQIExBEaXN0 +cml0byBDYXBpdGFsMTYwNAYDVQQKEy1TaXN0ZW1hIE5hY2lvbmFsIGRlIENlcnRp +ZmljYWNpb24gRWxlY3Ryb25pY2ExQzBBBgNVBAsTOlN1cGVyaW50ZW5kZW5jaWEg +ZGUgU2VydmljaW9zIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExJTAjBgkq +hkiG9w0BCQEWFmFjcmFpekBzdXNjZXJ0ZS5nb2IudmWCAQowDgYDVR0PAQH/BAQD +AgEGME0GA1UdEQRGMESCDnByb2NlcnQubmV0LnZloBUGBWCGXgIBoAwMClBTQy0w +MDAwMDKgGwYFYIZeAgKgEgwQUklGLUotMzE2MzUzNzMtNzB2BgNVHR8EbzBtMEag +RKBChkBodHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52ZS9sY3IvQ0VSVElGSUNBRE8t +UkFJWi1TSEEzODRDUkxERVIuY3JsMCOgIaAfhh1sZGFwOi8vYWNyYWl6LnN1c2Nl +cnRlLmdvYi52ZTA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9v +Y3NwLnN1c2NlcnRlLmdvYi52ZTBBBgNVHSAEOjA4MDYGBmCGXgMBAjAsMCoGCCsG +AQUFBwIBFh5odHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52ZS9kcGMwDQYJKoZIhvcN +AQELBQADggIBACtZ6yKZu4SqT96QxtGGcSOeSwORR3C7wJJg7ODU523G0+1ng3dS +1fLld6c2suNUvtm7CpsR72H0xpkzmfWvADmNg7+mvTV+LFwxNG9s2/NkAZiqlCxB +3RWGymspThbASfzXg0gTB1GEMVKIu4YXx2sviiCtxQuPcD4quxtxj7mkoP3Yldmv +Wb8lK5jpY5MvYB7Eqvh39YtsL+1+LrVPQA3uvFd359m21D+VJzog1eWuq2w1n8Gh +HVnchIHuTQfiSLaeS5UtQbHh6N5+LwUeaO6/u5BlOsju6rEYNxxik6SgMexxbJHm +pHmJWhSnFFAFTKQAVzAswbVhltw+HoSvOULP5dAssSS830DD7X9jSr3hTxJkhpXz +sOfIt+FTvZLm8wyWuevo5pLtp4EJFAv8lXrPj9Y0TzYS3F7RNHXGRoAvlQSMx4bE +qCaJqD8Zm4G7UaRKhqsLEQ+xrmNTbSjq3TNWOByyrYDT13K9mmyZY+gAu0F2Bbdb +mRiKw7gSXFbPVgx96OLP7bx0R/vu0xdOIk9W/1DzLuY5poLWccret9W6aAjtmcz9 +opLLabid+Qqkpj5PkygqYWwHJgD/ll9ohri4zspV4KuxPX+Y1zMOWj3YeMLEYC/H +YvBhkdI4sPaeVdtAgAUSM84dkpvRabP/v/GSCmE1P93+hvS84Bpxs2Km +-----END CERTIFICATE----- + +# Issuer: CN=China Internet Network Information Center EV Certificates Root O=China Internet Network Information Center +# Subject: CN=China Internet Network Information Center EV Certificates Root O=China Internet Network Information Center +# Label: "China Internet Network Information Center EV Certificates Root" +# Serial: 1218379777 +# MD5 Fingerprint: 55:5d:63:00:97:bd:6a:97:f5:67:ab:4b:fb:6e:63:15 +# SHA1 Fingerprint: 4f:99:aa:93:fb:2b:d1:37:26:a1:99:4a:ce:7f:f0:05:f2:93:5d:1e +# SHA256 Fingerprint: 1c:01:c6:f4:db:b2:fe:fc:22:55:8b:2b:ca:32:56:3f:49:84:4a:cf:c3:2b:7b:e4:b0:ff:59:9f:9e:8c:7a:f7 +-----BEGIN CERTIFICATE----- +MIID9zCCAt+gAwIBAgIESJ8AATANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMC +Q04xMjAwBgNVBAoMKUNoaW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24g +Q2VudGVyMUcwRQYDVQQDDD5DaGluYSBJbnRlcm5ldCBOZXR3b3JrIEluZm9ybWF0 +aW9uIENlbnRlciBFViBDZXJ0aWZpY2F0ZXMgUm9vdDAeFw0xMDA4MzEwNzExMjVa +Fw0zMDA4MzEwNzExMjVaMIGKMQswCQYDVQQGEwJDTjEyMDAGA1UECgwpQ2hpbmEg +SW50ZXJuZXQgTmV0d29yayBJbmZvcm1hdGlvbiBDZW50ZXIxRzBFBgNVBAMMPkNo +aW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyIEVWIENlcnRp +ZmljYXRlcyBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm35z +7r07eKpkQ0H1UN+U8i6yjUqORlTSIRLIOTJCBumD1Z9S7eVnAztUwYyZmczpwA// +DdmEEbK40ctb3B75aDFk4Zv6dOtouSCV98YPjUesWgbdYavi7NifFy2cyjw1l1Vx +zUOFsUcW9SxTgHbP0wBkvUCZ3czY28Sf1hNfQYOL+Q2HklY0bBoQCxfVWhyXWIQ8 +hBouXJE0bhlffxdpxWXvayHG1VA6v2G5BY3vbzQ6sm8UY78WO5upKv23KzhmBsUs +4qpnHkWnjQRmQvaPK++IIGmPMowUc9orhpFjIpryp9vOiYurXccUwVswah+xt54u +gQEC7c+WXmPbqOY4twIDAQABo2MwYTAfBgNVHSMEGDAWgBR8cks5x8DbYqVPm6oY +NJKiyoOCWTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4E +FgQUfHJLOcfA22KlT5uqGDSSosqDglkwDQYJKoZIhvcNAQEFBQADggEBACrDx0M3 +j92tpLIM7twUbY8opJhJywyA6vPtI2Z1fcXTIWd50XPFtQO3WKwMVC/GVhMPMdoG +52U7HW8228gd+f2ABsqjPWYWqJ1MFn3AlUa1UeTiH9fqBk1jjZaM7+czV0I664zB +echNdn3e9rG3geCg+aF4RhcaVpjwTj2rHO3sOdwHSPdj/gauwqRcalsyiMXHM4Ws +ZkJHwlgkmeHlPuV1LI5D1l08eB6olYIpUNHRFrrvwb562bTYzB5MRuF3sTGrvSrI +zo9uoV1/A3U05K2JRVRevq4opbs/eHnrc7MKDf2+yfdWrPa37S+bISnHOLaVxATy +wy39FCqQmbkHzJ8= +-----END CERTIFICATE----- + +# Issuer: CN=Swisscom Root CA 2 O=Swisscom OU=Digital Certificate Services +# Subject: CN=Swisscom Root CA 2 O=Swisscom OU=Digital Certificate Services +# Label: "Swisscom Root CA 2" +# Serial: 40698052477090394928831521023204026294 +# MD5 Fingerprint: 5b:04:69:ec:a5:83:94:63:18:a7:86:d0:e4:f2:6e:19 +# SHA1 Fingerprint: 77:47:4f:c6:30:e4:0f:4c:47:64:3f:84:ba:b8:c6:95:4a:8a:41:ec +# SHA256 Fingerprint: f0:9b:12:2c:71:14:f4:a0:9b:d4:ea:4f:4a:99:d5:58:b4:6e:4c:25:cd:81:14:0d:29:c0:56:13:91:4c:38:41 +-----BEGIN CERTIFICATE----- +MIIF2TCCA8GgAwIBAgIQHp4o6Ejy5e/DfEoeWhhntjANBgkqhkiG9w0BAQsFADBk +MQswCQYDVQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0 +YWwgQ2VydGlmaWNhdGUgU2VydmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3Qg +Q0EgMjAeFw0xMTA2MjQwODM4MTRaFw0zMTA2MjUwNzM4MTRaMGQxCzAJBgNVBAYT +AmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZp +Y2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAyMIICIjAN +BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAlUJOhJ1R5tMJ6HJaI2nbeHCOFvEr +jw0DzpPMLgAIe6szjPTpQOYXTKueuEcUMncy3SgM3hhLX3af+Dk7/E6J2HzFZ++r +0rk0X2s682Q2zsKwzxNoysjL67XiPS4h3+os1OD5cJZM/2pYmLcX5BtS5X4HAB1f +2uY+lQS3aYg5oUFgJWFLlTloYhyxCwWJwDaCFCE/rtuh/bxvHGCGtlOUSbkrRsVP +ACu/obvLP+DHVxxX6NZp+MEkUp2IVd3Chy50I9AU/SpHWrumnf2U5NGKpV+GY3aF +y6//SSj8gO1MedK75MDvAe5QQQg1I3ArqRa0jG6F6bYRzzHdUyYb3y1aSgJA/MTA +tukxGggo5WDDH8SQjhBiYEQN7Aq+VRhxLKX0srwVYv8c474d2h5Xszx+zYIdkeNL +6yxSNLCK/RJOlrDrcH+eOfdmQrGrrFLadkBXeyq96G4DsguAhYidDMfCd7Camlf0 +uPoTXGiTOmekl9AbmbeGMktg2M7v0Ax/lZ9vh0+Hio5fCHyqW/xavqGRn1V9TrAL +acywlKinh/LTSlDcX3KwFnUey7QYYpqwpzmqm59m2I2mbJYV4+by+PGDYmy7Velh +k6M99bFXi08jsJvllGov34zflVEpYKELKeRcVVi3qPyZ7iVNTA6z00yPhOgpD/0Q +VAKFyPnlw4vP5w8CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0hBBYw +FDASBgdghXQBUwIBBgdghXQBUwIBMBIGA1UdEwEB/wQIMAYBAf8CAQcwHQYDVR0O +BBYEFE0mICKJS9PVpAqhb97iEoHF8TwuMB8GA1UdIwQYMBaAFE0mICKJS9PVpAqh +b97iEoHF8TwuMA0GCSqGSIb3DQEBCwUAA4ICAQAyCrKkG8t9voJXiblqf/P0wS4R +fbgZPnm3qKhyN2abGu2sEzsOv2LwnN+ee6FTSA5BesogpxcbtnjsQJHzQq0Qw1zv +/2BZf82Fo4s9SBwlAjxnffUy6S8w5X2lejjQ82YqZh6NM4OKb3xuqFp1mrjX2lhI +REeoTPpMSQpKwhI3qEAMw8jh0FcNlzKVxzqfl9NX+Ave5XLzo9v/tdhZsnPdTSpx +srpJ9csc1fV5yJmz/MFMdOO0vSk3FQQoHt5FRnDsr7p4DooqzgB53MBfGWcsa0vv +aGgLQ+OswWIJ76bdZWGgr4RVSJFSHMYlkSrQwSIjYVmvRRGFHQEkNI/Ps/8XciAT +woCqISxxOQ7Qj1zB09GOInJGTB2Wrk9xseEFKZZZ9LuedT3PDTcNYtsmjGOpI99n +Bjx8Oto0QuFmtEYE3saWmA9LSHokMnWRn6z3aOkquVVlzl1h0ydw2Df+n7mvoC5W +t6NlUe07qxS/TFED6F+KBZvuim6c779o+sjaC+NCydAXFJy3SuCvkychVSa1ZC+N +8f+mQAWFBVzKBxlcCxMoTFh/wqXvRdpg065lYZ1Tg3TCrvJcwhbtkj6EPnNgiLx2 +9CzP0H1907he0ZESEOnN3col49XtmS++dYFLJPlFRpTJKSFTnCZFqhMX5OfNeOI5 +wSsSnqaeG8XmDtkx2Q== +-----END CERTIFICATE----- + +# Issuer: CN=Swisscom Root EV CA 2 O=Swisscom OU=Digital Certificate Services +# Subject: CN=Swisscom Root EV CA 2 O=Swisscom OU=Digital Certificate Services +# Label: "Swisscom Root EV CA 2" +# Serial: 322973295377129385374608406479535262296 +# MD5 Fingerprint: 7b:30:34:9f:dd:0a:4b:6b:35:ca:31:51:28:5d:ae:ec +# SHA1 Fingerprint: e7:a1:90:29:d3:d5:52:dc:0d:0f:c6:92:d3:ea:88:0d:15:2e:1a:6b +# SHA256 Fingerprint: d9:5f:ea:3c:a4:ee:dc:e7:4c:d7:6e:75:fc:6d:1f:f6:2c:44:1f:0f:a8:bc:77:f0:34:b1:9e:5d:b2:58:01:5d +-----BEGIN CERTIFICATE----- +MIIF4DCCA8igAwIBAgIRAPL6ZOJ0Y9ON/RAdBB92ylgwDQYJKoZIhvcNAQELBQAw +ZzELMAkGA1UEBhMCY2gxETAPBgNVBAoTCFN3aXNzY29tMSUwIwYDVQQLExxEaWdp +dGFsIENlcnRpZmljYXRlIFNlcnZpY2VzMR4wHAYDVQQDExVTd2lzc2NvbSBSb290 +IEVWIENBIDIwHhcNMTEwNjI0MDk0NTA4WhcNMzEwNjI1MDg0NTA4WjBnMQswCQYD +VQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0YWwgQ2Vy +dGlmaWNhdGUgU2VydmljZXMxHjAcBgNVBAMTFVN3aXNzY29tIFJvb3QgRVYgQ0Eg +MjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMT3HS9X6lds93BdY7Bx +UglgRCgzo3pOCvrY6myLURYaVa5UJsTMRQdBTxB5f3HSek4/OE6zAMaVylvNwSqD +1ycfMQ4jFrclyxy0uYAyXhqdk/HoPGAsp15XGVhRXrwsVgu42O+LgrQ8uMIkqBPH +oCE2G3pXKSinLr9xJZDzRINpUKTk4RtiGZQJo/PDvO/0vezbE53PnUgJUmfANykR +HvvSEaeFGHR55E+FFOtSN+KxRdjMDUN/rhPSays/p8LiqG12W0OfvrSdsyaGOx9/ +5fLoZigWJdBLlzin5M8J0TbDC77aO0RYjb7xnglrPvMyxyuHxuxenPaHZa0zKcQv +idm5y8kDnftslFGXEBuGCxobP/YCfnvUxVFkKJ3106yDgYjTdLRZncHrYTNaRdHL +OdAGalNgHa/2+2m8atwBz735j9m9W8E6X47aD0upm50qKGsaCnw8qyIL5XctcfaC +NYGu+HuB5ur+rPQam3Rc6I8k9l2dRsQs0h4rIWqDJ2dVSqTjyDKXZpBy2uPUZC5f +46Fq9mDU5zXNysRojddxyNMkM3OxbPlq4SjbX8Y96L5V5jcb7STZDxmPX2MYWFCB +UWVv8p9+agTnNCRxunZLWB4ZvRVgRaoMEkABnRDixzgHcgplwLa7JSnaFp6LNYth +7eVxV4O1PHGf40+/fh6Bn0GXAgMBAAGjgYYwgYMwDgYDVR0PAQH/BAQDAgGGMB0G +A1UdIQQWMBQwEgYHYIV0AVMCAgYHYIV0AVMCAjASBgNVHRMBAf8ECDAGAQH/AgED +MB0GA1UdDgQWBBRF2aWBbj2ITY1x0kbBbkUe88SAnTAfBgNVHSMEGDAWgBRF2aWB +bj2ITY1x0kbBbkUe88SAnTANBgkqhkiG9w0BAQsFAAOCAgEAlDpzBp9SSzBc1P6x +XCX5145v9Ydkn+0UjrgEjihLj6p7jjm02Vj2e6E1CqGdivdj5eu9OYLU43otb98T +PLr+flaYC/NUn81ETm484T4VvwYmneTwkLbUwp4wLh/vx3rEUMfqe9pQy3omywC0 +Wqu1kx+AiYQElY2NfwmTv9SoqORjbdlk5LgpWgi/UOGED1V7XwgiG/W9mR4U9s70 +WBCCswo9GcG/W6uqmdjyMb3lOGbcWAXH7WMaLgqXfIeTK7KK4/HsGOV1timH59yL +Gn602MnTihdsfSlEvoqq9X46Lmgxk7lq2prg2+kupYTNHAq4Sgj5nPFhJpiTt3tm +7JFe3VE/23MPrQRYCd0EApUKPtN236YQHoA96M2kZNEzx5LH4k5E4wnJTsJdhw4S +nr8PyQUQ3nqjsTzyP6WqJ3mtMX0f/fwZacXduT98zca0wjAefm6S139hdlqP65VN +vBFuIXxZN5nQBrz5Bm0yFqXZaajh3DyAHmBR3NdUIR7KYndP+tiPsys6DXhyyWhB +WkdKwqPrGtcKqzwyVcgKEZzfdNbwQBUdyLmPtTbFr/giuMod89a2GQ+fYWVq6nTI +fI/DT11lgh/ZDYnadXL77/FHZxOzyNEZiCcmmpl5fx7kLD977vHeTYuWl8PVP3wb +I+2ksx0WckNLIOFZfsLorSa/ovc= +-----END CERTIFICATE----- + +# Issuer: CN=CA Disig Root R1 O=Disig a.s. +# Subject: CN=CA Disig Root R1 O=Disig a.s. +# Label: "CA Disig Root R1" +# Serial: 14052245610670616104 +# MD5 Fingerprint: be:ec:11:93:9a:f5:69:21:bc:d7:c1:c0:67:89:cc:2a +# SHA1 Fingerprint: 8e:1c:74:f8:a6:20:b9:e5:8a:f4:61:fa:ec:2b:47:56:51:1a:52:c6 +# SHA256 Fingerprint: f9:6f:23:f4:c3:e7:9c:07:7a:46:98:8d:5a:f5:90:06:76:a0:f0:39:cb:64:5d:d1:75:49:b2:16:c8:24:40:ce +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAMMDmu5QkG4oMA0GCSqGSIb3DQEBBQUAMFIxCzAJBgNV +BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu +MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIxMB4XDTEyMDcxOTA5MDY1NloXDTQy +MDcxOTA5MDY1NlowUjELMAkGA1UEBhMCU0sxEzARBgNVBAcTCkJyYXRpc2xhdmEx +EzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERpc2lnIFJvb3QgUjEw +ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCqw3j33Jijp1pedxiy3QRk +D2P9m5YJgNXoqqXinCaUOuiZc4yd39ffg/N4T0Dhf9Kn0uXKE5Pn7cZ3Xza1lK/o +OI7bm+V8u8yN63Vz4STN5qctGS7Y1oprFOsIYgrY3LMATcMjfF9DCCMyEtztDK3A +fQ+lekLZWnDZv6fXARz2m6uOt0qGeKAeVjGu74IKgEH3G8muqzIm1Cxr7X1r5OJe +IgpFy4QxTaz+29FHuvlglzmxZcfe+5nkCiKxLU3lSCZpq+Kq8/v8kiky6bM+TR8n +oc2OuRf7JT7JbvN32g0S9l3HuzYQ1VTW8+DiR0jm3hTaYVKvJrT1cU/J19IG32PK +/yHoWQbgCNWEFVP3Q+V8xaCJmGtzxmjOZd69fwX3se72V6FglcXM6pM6vpmumwKj +rckWtc7dXpl4fho5frLABaTAgqWjR56M6ly2vGfb5ipN0gTco65F97yLnByn1tUD +3AjLLhbKXEAz6GfDLuemROoRRRw1ZS0eRWEkG4IupZ0zXWX4Qfkuy5Q/H6MMMSRE +7cderVC6xkGbrPAXZcD4XW9boAo0PO7X6oifmPmvTiT6l7Jkdtqr9O3jw2Dv1fkC +yC2fg69naQanMVXVz0tv/wQFx1isXxYb5dKj6zHbHzMVTdDypVP1y+E9Tmgt2BLd +qvLmTZtJ5cUoobqwWsagtQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud +DwEB/wQEAwIBBjAdBgNVHQ4EFgQUiQq0OJMa5qvum5EY+fU8PjXQ04IwDQYJKoZI +hvcNAQEFBQADggIBADKL9p1Kyb4U5YysOMo6CdQbzoaz3evUuii+Eq5FLAR0rBNR +xVgYZk2C2tXck8An4b58n1KeElb21Zyp9HWc+jcSjxyT7Ff+Bw+r1RL3D65hXlaA +SfX8MPWbTx9BLxyE04nH4toCdu0Jz2zBuByDHBb6lM19oMgY0sidbvW9adRtPTXo +HqJPYNcHKfyyo6SdbhWSVhlMCrDpfNIZTUJG7L399ldb3Zh+pE3McgODWF3vkzpB +emOqfDqo9ayk0d2iLbYq/J8BjuIQscTK5GfbVSUZP/3oNn6z4eGBrxEWi1CXYBmC +AMBrTXO40RMHPuq2MU/wQppt4hF05ZSsjYSVPCGvxdpHyN85YmLLW1AL14FABZyb +7bq2ix4Eb5YgOe2kfSnbSM6C3NQCjR0EMVrHS/BsYVLXtFHCgWzN4funodKSds+x +DzdYpPJScWc/DIh4gInByLUfkmO+p3qKViwaqKactV2zY9ATIKHrkWzQjX2v3wvk +F7mGnjixlAxYjOBVqjtjbZqJYLhkKpLGN/R+Q0O3c+gB53+XD9fyexn9GtePyfqF +a3qdnom2piiZk4hA9z7NUaPK6u95RyG1/jLix8NRb76AdPCkwzryT+lf3xkK8jsT +Q6wxpLPn6/wY1gGp8yqPNg7rtLG8t0zJa7+h89n07eLw4+1knj0vllJPgFOL +-----END CERTIFICATE----- + +# Issuer: CN=CA Disig Root R2 O=Disig a.s. +# Subject: CN=CA Disig Root R2 O=Disig a.s. +# Label: "CA Disig Root R2" +# Serial: 10572350602393338211 +# MD5 Fingerprint: 26:01:fb:d8:27:a7:17:9a:45:54:38:1a:43:01:3b:03 +# SHA1 Fingerprint: b5:61:eb:ea:a4:de:e4:25:4b:69:1a:98:a5:57:47:c2:34:c7:d9:71 +# SHA256 Fingerprint: e2:3d:4a:03:6d:7b:70:e9:f5:95:b1:42:20:79:d2:b9:1e:df:bb:1f:b6:51:a0:63:3e:aa:8a:9d:c5:f8:07:03 +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNV +BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu +MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQy +MDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sxEzARBgNVBAcTCkJyYXRpc2xhdmEx +EzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERpc2lnIFJvb3QgUjIw +ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbCw3Oe +NcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNH +PWSb6WiaxswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3I +x2ymrdMxp7zo5eFm1tL7A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbe +QTg06ov80egEFGEtQX6sx3dOy1FU+16SGBsEWmjGycT6txOgmLcRK7fWV8x8nhfR +yyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqVg8NTEQxzHQuyRpDRQjrO +QG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa5Beny912 +H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJ +QfYEkoopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUD +i/ZnWejBBhG93c+AAk9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORs +nLMOPReisjQS1n6yqEm70XooQL6iFh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1 +rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud +DwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5uQu0wDQYJKoZI +hvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM +tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqf +GopTpti72TVVsRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkb +lvdhuDvEK7Z4bLQjb/D907JedR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka ++elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W81k/BfDxujRNt+3vrMNDcTa/F1bal +TFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjxmHHEt38OFdAlab0i +nSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01utI3 +gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18Dr +G5gPcFw0sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3Os +zMOl6W8KjptlwlCFtaOgUxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8x +L4ysEr3vQCj8KWefshNPZiTEUxnpHikV7+ZtsH8tZ/3zbBt1RqPlShfppNcL +-----END CERTIFICATE----- + +# Issuer: CN=ACCVRAIZ1 O=ACCV OU=PKIACCV +# Subject: CN=ACCVRAIZ1 O=ACCV OU=PKIACCV +# Label: "ACCVRAIZ1" +# Serial: 6828503384748696800 +# MD5 Fingerprint: d0:a0:5a:ee:05:b6:09:94:21:a1:7d:f1:b2:29:82:02 +# SHA1 Fingerprint: 93:05:7a:88:15:c6:4f:ce:88:2f:fa:91:16:52:28:78:bc:53:64:17 +# SHA256 Fingerprint: 9a:6e:c0:12:e1:a7:da:9d:be:34:19:4d:47:8a:d7:c0:db:18:22:fb:07:1d:f1:29:81:49:6e:d1:04:38:41:13 +-----BEGIN CERTIFICATE----- +MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UE +AwwJQUNDVlJBSVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQsw +CQYDVQQGEwJFUzAeFw0xMTA1MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQ +BgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwHUEtJQUNDVjENMAsGA1UECgwEQUND +VjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCb +qau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gMjmoY +HtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWo +G2ioPej0RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpA +lHPrzg5XPAOBOp0KoVdDaaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhr +IA8wKFSVf+DuzgpmndFALW4ir50awQUZ0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/ +0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDGWuzndN9wrqODJerWx5eH +k6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs78yM2x/47 +4KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMO +m3WR5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpa +cXpkatcnYGMN285J9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPl +uUsXQA+xtrn13k/c4LOsOxFwYIRKQ26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYI +KwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRwOi8vd3d3LmFjY3YuZXMvZmls +ZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEuY3J0MB8GCCsG +AQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2 +VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeT +VfZW6oHlNsyMHj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIG +CCsGAQUFBwICMIIBFB6CARAAQQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUA +cgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBhAO0AegAgAGQAZQAgAGwAYQAgAEEA +QwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUAYwBuAG8AbABvAGcA +7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBjAHQA +cgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAA +QwBQAFMAIABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUA +czAwBggrBgEFBQcCARYkaHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2Mu +aHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRt +aW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2MV9kZXIuY3JsMA4GA1Ud +DwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZIhvcNAQEF +BQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdp +D70ER9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gU +JyCpZET/LtZ1qmxNYEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+m +AM/EKXMRNt6GGT6d7hmKG9Ww7Y49nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepD +vV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJTS+xJlsndQAJxGJ3KQhfnlms +tn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3sCPdK6jT2iWH +7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h +I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szA +h1xA2syVP1XgNce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xF +d3+YJ5oyXSrjhO7FmGYvliAd3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2H +pPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3pEfbRD0tVNEYqi4Y7 +-----END CERTIFICATE----- + +# Issuer: CN=TWCA Global Root CA O=TAIWAN-CA OU=Root CA +# Subject: CN=TWCA Global Root CA O=TAIWAN-CA OU=Root CA +# Label: "TWCA Global Root CA" +# Serial: 3262 +# MD5 Fingerprint: f9:03:7e:cf:e6:9e:3c:73:7a:2a:90:07:69:ff:2b:96 +# SHA1 Fingerprint: 9c:bb:48:53:f6:a4:f6:d3:52:a4:e8:32:52:55:60:13:f5:ad:af:65 +# SHA256 Fingerprint: 59:76:90:07:f7:68:5d:0f:cd:50:87:2f:9f:95:d5:75:5a:5b:2b:45:7d:81:f3:69:2b:61:0a:98:67:2f:0e:1b +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcx +EjAQBgNVBAoTCVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMT +VFdDQSBHbG9iYWwgUm9vdCBDQTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5 +NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQKEwlUQUlXQU4tQ0ExEDAOBgNVBAsT +B1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3QgQ0EwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2CnJfF +10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz +0ALfUPZVr2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfCh +MBwqoJimFb3u/Rk28OKRQ4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbH +zIh1HrtsBv+baz4X7GGqcXzGHaL3SekVtTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc +46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1WKKD+u4ZqyPpcC1jcxkt2 +yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99sy2sbZCi +laLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYP +oA/pyJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQA +BDzfuBSO6N+pjWxnkjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcE +qYSjMq+u7msXi7Kx/mzhkIyIqJdIzshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm +4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6gcFGn90xHNcgL +1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn +LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WF +H6vPNOw/KP4M8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNo +RI2T9GRwoD2dKAXDOXC4Ynsg/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+ +nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlglPx4mI88k1HtQJAH32RjJMtOcQWh +15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryPA9gK8kxkRr05YuWW +6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3mi4TW +nsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5j +wa19hAM8EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWz +aGHQRiapIVJpLesux+t3zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmy +KwbQBM0= +-----END CERTIFICATE----- + +# Issuer: CN=TeliaSonera Root CA v1 O=TeliaSonera +# Subject: CN=TeliaSonera Root CA v1 O=TeliaSonera +# Label: "TeliaSonera Root CA v1" +# Serial: 199041966741090107964904287217786801558 +# MD5 Fingerprint: 37:41:49:1b:18:56:9a:26:f5:ad:c2:66:fb:40:a5:4c +# SHA1 Fingerprint: 43:13:bb:96:f1:d5:86:9b:c1:4e:6a:92:f6:cf:f6:34:69:87:82:37 +# SHA256 Fingerprint: dd:69:36:fe:21:f8:f0:77:c1:23:a1:a5:21:c1:22:24:f7:22:55:b7:3e:03:a7:26:06:93:e8:a2:4b:0f:a3:89 +-----BEGIN CERTIFICATE----- +MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAw +NzEUMBIGA1UECgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJv +b3QgQ0EgdjEwHhcNMDcxMDE4MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYD +VQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwWVGVsaWFTb25lcmEgUm9vdCBDQSB2 +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+6yfwIaPzaSZVfp3F +VRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA3GV1 +7CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+X +Z75Ljo1kB1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+ +/jXh7VB7qTCNGdMJjmhnXb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs +81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxHoLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkm +dtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3F0fUTPHSiXk+TT2YqGHe +Oh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJoWjiUIMu +sDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4 +pgd7gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fs +slESl1MpWtTwEhDcTwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQ +arMCpgKIv7NHfirZ1fpoeDVNAgMBAAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYD +VR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qWDNXr+nuqF+gTEjANBgkqhkiG +9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNmzqjMDfz1mgbl +dxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx +0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1Tj +TQpgcmLNkQfWpb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBed +Y2gea+zDTYa4EzAvXUYNR0PVG6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7 +Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpcc41teyWRyu5FrgZLAMzTsVlQ2jqI +OylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOTJsjrDNYmiLbAJM+7 +vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2qReW +t88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcn +HL/EVlP6Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVx +SK236thZiNSQvxaz2emsWWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY= +-----END CERTIFICATE----- + +# Issuer: CN=E-Tugra Certification Authority O=E-Tuğra EBG Bilişim Teknolojileri ve Hizmetleri A.Ş. OU=E-Tugra Sertifikasyon Merkezi +# Subject: CN=E-Tugra Certification Authority O=E-Tuğra EBG Bilişim Teknolojileri ve Hizmetleri A.Ş. OU=E-Tugra Sertifikasyon Merkezi +# Label: "E-Tugra Certification Authority" +# Serial: 7667447206703254355 +# MD5 Fingerprint: b8:a1:03:63:b0:bd:21:71:70:8a:6f:13:3a:bb:79:49 +# SHA1 Fingerprint: 51:c6:e7:08:49:06:6e:f3:92:d4:5c:a0:0d:6d:a3:62:8f:c3:52:39 +# SHA256 Fingerprint: b0:bf:d5:2b:b0:d7:d9:bd:92:bf:5d:4d:c1:3d:a2:55:c0:2c:54:2f:37:83:65:ea:89:39:11:f5:5e:55:f2:3c +-----BEGIN CERTIFICATE----- +MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNV +BAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBC +aWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNV +BAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQDDB9FLVR1 +Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMwNTEyMDk0OFoXDTIz +MDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+ +BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhp +em1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN +ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4vU/kwVRHoViVF56C/UY +B4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vdhQd2h8y/L5VMzH2nPbxH +D5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5KCKpbknSF +Q9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEo +q1+gElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3D +k14opz8n8Y4e0ypQBaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcH +fC425lAcP9tDJMW/hkd5s3kc91r0E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsut +dEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gzrt48Ue7LE3wBf4QOXVGUnhMM +ti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAqjqFGOjGY5RH8 +zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn +rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUX +U8u3Zg5mTPj5dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6 +Jyr+zE7S6E5UMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5 +XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAF +Nzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAKkEh47U6YA5n+KGCR +HTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jOXKqY +GwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c +77NCR807VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3 ++GbHeJAAFS6LrVE1Uweoa2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WK +vJUawSg5TB9D0pH0clmKuVb8P7Sd2nCcdlqMQ1DujjByTd//SffGqWfZbawCEeI6 +FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEVKV0jq9BgoRJP3vQXzTLl +yb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gTDx4JnW2P +AJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpD +y4Q08ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8d +NL/+I5c30jn6PQ0GC7TbO6Orb1wdtn7os4I07QZcJA== +-----END CERTIFICATE----- + +# Issuer: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Subject: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Label: "T-TeleSec GlobalRoot Class 2" +# Serial: 1 +# MD5 Fingerprint: 2b:9b:9e:e4:7b:6c:1f:00:72:1a:cc:c1:77:79:df:6a +# SHA1 Fingerprint: 59:0d:2d:7d:88:4f:40:2e:61:7e:a5:62:32:17:65:cf:17:d8:94:e9 +# SHA256 Fingerprint: 91:e2:f5:78:8d:58:10:eb:a7:ba:58:73:7d:e1:54:8a:8e:ca:cd:01:45:98:bc:0b:14:3e:04:1b:17:05:25:52 +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx +KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd +BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl +YyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgxMDAxMTA0MDE0WhcNMzMxMDAxMjM1 +OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy +aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 +ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUd +AqSzm1nzHoqvNK38DcLZSBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiC +FoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/FvudocP05l03Sx5iRUKrERLMjfTlH6VJi +1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx9702cu+fjOlbpSD8DT6Iavq +jnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGVWOHAD3bZ +wI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/ +WSA2AHmgoCJrjNXyYdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhy +NsZt+U2e+iKo4YFWz827n+qrkRk4r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPAC +uvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNfvNoBYimipidx5joifsFvHZVw +IEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR3p1m0IvVVGb6 +g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN +9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlP +BSeOE6Fuwg== +-----END CERTIFICATE----- + +# Issuer: CN=Atos TrustedRoot 2011 O=Atos +# Subject: CN=Atos TrustedRoot 2011 O=Atos +# Label: "Atos TrustedRoot 2011" +# Serial: 6643877497813316402 +# MD5 Fingerprint: ae:b9:c4:32:4b:ac:7f:5d:66:cc:77:94:bb:2a:77:56 +# SHA1 Fingerprint: 2b:b1:f5:3e:55:0c:1d:c5:f1:d4:e6:b7:6a:46:4b:55:06:02:ac:21 +# SHA256 Fingerprint: f3:56:be:a2:44:b7:a9:1e:b3:5d:53:ca:9a:d7:86:4a:ce:01:8e:2d:35:d5:f8:f9:6d:df:68:a6:f4:1a:a4:74 +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UE +AwwVQXRvcyBUcnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQG +EwJERTAeFw0xMTA3MDcxNDU4MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMM +FUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsGA1UECgwEQXRvczELMAkGA1UEBhMC +REUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCVhTuXbyo7LjvPpvMp +Nb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr54rM +VD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+ +SZFhyBH+DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ +4J7sVaE3IqKHBAUsR320HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0L +cp2AMBYHlT8oDv3FdU9T1nSatCQujgKRz3bFmx5VdJx4IbHwLfELn8LVlhgf8FQi +eowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7Rl+lwrrw7GWzbITAPBgNV +HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZbNshMBgG +A1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3 +DQEBCwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8j +vZfza1zv7v1Apt+hk6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kP +DpFrdRbhIfzYJsdHt6bPWHJxfrrhTZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pc +maHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a961qn8FYiqTxlVMYVqL2Gns2D +lmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G3mB/ufNPRJLv +KrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 1 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 1 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 1 G3" +# Serial: 687049649626669250736271037606554624078720034195 +# MD5 Fingerprint: a4:bc:5b:3f:fe:37:9a:fa:64:f0:e2:fa:05:3d:0b:ab +# SHA1 Fingerprint: 1b:8e:ea:57:96:29:1a:c9:39:ea:b8:0a:81:1a:73:73:c0:93:79:67 +# SHA256 Fingerprint: 8a:86:6f:d1:b2:76:b5:7e:57:8e:92:1c:65:82:8a:2b:ed:58:e9:f2:f2:88:05:41:34:b7:f1:f4:bf:c9:cc:74 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00 +MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakEPBtV +wedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWe +rNrwU8lmPNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF341 +68Xfuw6cwI2H44g4hWf6Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh +4Pw5qlPafX7PGglTvF0FBM+hSo+LdoINofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXp +UhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/lg6AnhF4EwfWQvTA9xO+o +abw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV7qJZjqlc +3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/G +KubX9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSt +hfbZxbGL0eUQMk1fiyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KO +Tk0k+17kBL5yG6YnLUlamXrXXAkgt3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOt +zCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZIhvcNAQELBQAD +ggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC +MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2 +cDMT/uFPpiN3GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUN +qXsCHKnQO18LwIE6PWThv6ctTr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5 +YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP+V04ikkwj+3x6xn0dxoxGE1nVGwv +b2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh3jRJjehZrJ3ydlo2 +8hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fawx/k +NSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNj +ZgKAvQU6O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhp +q1467HxpvMc7hU6eFbm0FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFt +nh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOVhMJKzRwuJIczYOXD +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 2 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 2 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 2 G3" +# Serial: 390156079458959257446133169266079962026824725800 +# MD5 Fingerprint: af:0c:86:6e:bf:40:2d:7f:0b:3e:12:50:ba:12:3d:06 +# SHA1 Fingerprint: 09:3c:61:f3:8b:8b:dc:7d:55:df:75:38:02:05:00:e1:25:f5:c8:36 +# SHA256 Fingerprint: 8f:e4:fb:0a:f9:3a:4d:0d:67:db:0b:eb:b2:3e:37:c7:1b:f3:25:dc:bc:dd:24:0e:a0:4d:af:58:b4:7e:18:40 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00 +MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf +qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW +n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym +c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+ +O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1 +o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j +IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq +IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz +8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh +vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l +7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG +cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD +ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 +AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC +roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga +W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n +lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE ++V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV +csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd +dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg +KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM +HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4 +WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 3 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 3 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 3 G3" +# Serial: 268090761170461462463995952157327242137089239581 +# MD5 Fingerprint: df:7d:b9:ad:54:6f:68:a1:df:89:57:03:97:43:b0:d7 +# SHA1 Fingerprint: 48:12:bd:92:3c:a8:c4:39:06:e7:30:6d:27:96:e6:a4:cf:22:2e:7d +# SHA256 Fingerprint: 88:ef:81:de:20:2e:b0:18:45:2e:43:f8:64:72:5c:ea:5f:bd:1f:c2:d9:d2:05:73:07:09:c5:d8:b8:69:0f:46 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00 +MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286IxSR +/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNu +FoM7pmRLMon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXR +U7Ox7sWTaYI+FrUoRqHe6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+c +ra1AdHkrAj80//ogaX3T7mH1urPnMNA3I4ZyYUUpSFlob3emLoG+B01vr87ERROR +FHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3UVDmrJqMz6nWB2i3ND0/k +A9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f75li59wzw +eyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634Ryl +sSqiMd5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBp +VzgeAVuNVejH38DMdyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0Q +A4XN8f+MFrXBsj6IbGB/kE+V9/YtrQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+ +ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZIhvcNAQELBQAD +ggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px +KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnI +FUBhynLWcKzSt/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5Wvv +oxXqA/4Ti2Tk08HS6IT7SdEQTXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFg +u/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9DuDcpmvJRPpq3t/O5jrFc/ZSXPsoaP +0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGibIh6BJpsQBJFxwAYf +3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmDhPbl +8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+ +DhcI00iX0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HN +PlopNLk9hM6xZdRZkZFWdSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ +ywaZWWDYWGWVjUTR939+J399roD1B0y2PpxxVJkES/1Y+Zj0 +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root G2" +# Serial: 15385348160840213938643033620894905419 +# MD5 Fingerprint: 92:38:b9:f8:63:24:82:65:2c:57:33:e6:fe:81:8f:9d +# SHA1 Fingerprint: a1:4b:48:d9:43:ee:0a:0e:40:90:4f:3c:e0:a4:c0:91:93:51:5d:3f +# SHA256 Fingerprint: 7d:05:eb:b6:82:33:9f:8c:94:51:ee:09:4e:eb:fe:fa:79:53:a1:14:ed:b2:f4:49:49:45:2f:ab:7d:2f:c1:85 +-----BEGIN CERTIFICATE----- +MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSA +n61UQbVH35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4Htecc +biJVMWWXvdMX0h5i89vqbFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9Hp +EgjAALAcKxHad3A2m67OeYfcgnDmCXRwVWmvo2ifv922ebPynXApVfSr/5Vh88lA +bx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OPYLfykqGxvYmJHzDNw6Yu +YjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+RnlTGNAgMB +AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQW +BBTOw0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPI +QW5pJ6d1Ee88hjZv0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I +0jJmwYrA8y8678Dj1JGG0VDjA9tzd29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4Gni +lmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAWhsI6yLETcDbYz+70CjTVW0z9 +B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0MjomZmWzwPDCv +ON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo +IhNzbM8m9Yop5w== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root G3" +# Serial: 15459312981008553731928384953135426796 +# MD5 Fingerprint: 7c:7f:65:31:0c:81:df:8d:ba:3e:99:e2:5c:ad:6e:fb +# SHA1 Fingerprint: f5:17:a2:4f:9a:48:c6:c9:f8:a2:00:26:9f:dc:0f:48:2c:ab:30:89 +# SHA256 Fingerprint: 7e:37:cb:8b:4c:47:09:0c:ab:36:55:1b:a6:f4:5d:b8:40:68:0f:ba:16:6a:95:2d:b1:00:71:7f:43:05:3f:c2 +-----BEGIN CERTIFICATE----- +MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg +RzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJf +Zn4f5dwbRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17Q +RSAPWXYQ1qAk8C3eNvJsKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQD +AwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlY +JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv +6pZjamVFkpUBtA== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root G2" +# Serial: 4293743540046975378534879503202253541 +# MD5 Fingerprint: e4:a6:8a:c8:54:ac:52:42:46:0a:fd:72:48:1b:2a:44 +# SHA1 Fingerprint: df:3c:24:f9:bf:d6:66:76:1b:26:80:73:fe:06:d1:cc:8d:4f:82:a4 +# SHA256 Fingerprint: cb:3c:cb:b7:60:31:e5:e0:13:8f:8d:d3:9a:23:f9:de:47:ff:c3:5e:43:c1:14:4c:ea:27:d4:6a:5a:b1:cb:5f +-----BEGIN CERTIFICATE----- +MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH +MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI +2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx +1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ +q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz +tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ +vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV +5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY +1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4 +NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG +Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91 +8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe +pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl +MrY= +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root G3" +# Serial: 7089244469030293291760083333884364146 +# MD5 Fingerprint: f5:5d:a4:50:a5:fb:28:7e:1e:0f:0d:cc:96:57:56:ca +# SHA1 Fingerprint: 7e:04:de:89:6a:3e:66:6d:00:e6:87:d3:3f:fa:d9:3b:e8:3d:34:9e +# SHA256 Fingerprint: 31:ad:66:48:f8:10:41:38:c7:38:f3:9e:a4:32:01:33:39:3e:3a:18:cc:02:29:6e:f9:7c:2a:c9:ef:67:31:d0 +-----BEGIN CERTIFICATE----- +MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe +Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw +EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x +IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF +K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG +fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO +Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd +BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx +AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/ +oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8 +sycX +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Trusted Root G4" +# Serial: 7451500558977370777930084869016614236 +# MD5 Fingerprint: 78:f2:fc:aa:60:1f:2f:b4:eb:c9:37:ba:53:2e:75:49 +# SHA1 Fingerprint: dd:fb:16:cd:49:31:c9:73:a2:03:7d:3f:c8:3a:4d:7d:77:5d:05:e4 +# SHA256 Fingerprint: 55:2f:7b:dc:f1:a7:af:9e:6c:e6:72:01:7f:4f:12:ab:f7:72:40:c7:8e:76:1a:c2:03:d1:d9:d2:0a:c8:99:88 +-----BEGIN CERTIFICATE----- +MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg +RzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3y +ithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1If +xp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDV +ySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiO +DCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQ +jdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/ +CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCi +EhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADM +fRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QY +uKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXK +chYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t +9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +hjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD +ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2 +SV1EY+CtnJYYZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd ++SeuMIW59mdNOj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWc +fFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqa +sjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9N +cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N +0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie +4u1Ki7wb/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mI +r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1 +/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm +gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+ +-----END CERTIFICATE----- + +# Issuer: CN=Certification Authority of WoSign O=WoSign CA Limited +# Subject: CN=Certification Authority of WoSign O=WoSign CA Limited +# Label: "WoSign" +# Serial: 125491772294754854453622855443212256657 +# MD5 Fingerprint: a1:f2:f9:b5:d2:c8:7a:74:b8:f3:05:f1:d7:e1:84:8d +# SHA1 Fingerprint: b9:42:94:bf:91:ea:8f:b6:4b:e6:10:97:c7:fb:00:13:59:b6:76:cb +# SHA256 Fingerprint: 4b:22:d5:a6:ae:c9:9f:3c:db:79:aa:5e:c0:68:38:47:9c:d5:ec:ba:71:64:f7:f2:2d:c1:d6:5f:63:d8:57:08 +-----BEGIN CERTIFICATE----- +MIIFdjCCA16gAwIBAgIQXmjWEXGUY1BWAGjzPsnFkTANBgkqhkiG9w0BAQUFADBV +MQswCQYDVQQGEwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxKjAoBgNV +BAMTIUNlcnRpZmljYXRpb24gQXV0aG9yaXR5IG9mIFdvU2lnbjAeFw0wOTA4MDgw +MTAwMDFaFw0zOTA4MDgwMTAwMDFaMFUxCzAJBgNVBAYTAkNOMRowGAYDVQQKExFX +b1NpZ24gQ0EgTGltaXRlZDEqMCgGA1UEAxMhQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkgb2YgV29TaWduMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvcqN +rLiRFVaXe2tcesLea9mhsMMQI/qnobLMMfo+2aYpbxY94Gv4uEBf2zmoAHqLoE1U +fcIiePyOCbiohdfMlZdLdNiefvAA5A6JrkkoRBoQmTIPJYhTpA2zDxIIFgsDcScc +f+Hb0v1naMQFXQoOXXDX2JegvFNBmpGN9J42Znp+VsGQX+axaCA2pIwkLCxHC1l2 +ZjC1vt7tj/id07sBMOby8w7gLJKA84X5KIq0VC6a7fd2/BVoFutKbOsuEo/Uz/4M +x1wdC34FMr5esAkqQtXJTpCzWQ27en7N1QhatH/YHGkR+ScPewavVIMYe+HdVHpR +aG53/Ma/UkpmRqGyZxq7o093oL5d//xWC0Nyd5DKnvnyOfUNqfTq1+ezEC8wQjch +zDBwyYaYD8xYTYO7feUapTeNtqwylwA6Y3EkHp43xP901DfA4v6IRmAR3Qg/UDar +uHqklWJqbrDKaiFaafPz+x1wOZXzp26mgYmhiMU7ccqjUu6Du/2gd/Tkb+dC221K +mYo0SLwX3OSACCK28jHAPwQ+658geda4BmRkAjHXqc1S+4RFaQkAKtxVi8QGRkvA +Sh0JWzko/amrzgD5LkhLJuYwTKVYyrREgk/nkR4zw7CT/xH8gdLKH3Ep3XZPkiWv +HYG3Dy+MwwbMLyejSuQOmbp8HkUff6oZRZb9/D0CAwEAAaNCMEAwDgYDVR0PAQH/ +BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFOFmzw7R8bNLtwYgFP6H +EtX2/vs+MA0GCSqGSIb3DQEBBQUAA4ICAQCoy3JAsnbBfnv8rWTjMnvMPLZdRtP1 +LOJwXcgu2AZ9mNELIaCJWSQBnfmvCX0KI4I01fx8cpm5o9dU9OpScA7F9dY74ToJ +MuYhOZO9sxXqT2r09Ys/L3yNWC7F4TmgPsc9SnOeQHrAK2GpZ8nzJLmzbVUsWh2e +JXLOC62qx1ViC777Y7NhRCOjy+EaDveaBk3e1CNOIZZbOVtXHS9dCF4Jef98l7VN +g64N1uajeeAz0JmWAjCnPv/So0M/BVoG6kQC2nz4SNAzqfkHx5Xh9T71XXG68pWp +dIhhWeO/yloTunK0jF02h+mmxTwTv97QRCbut+wucPrXnbes5cVAWubXbHssw1ab +R80LzvobtCHXt2a49CUwi1wNuepnsvRtrtWhnk/Yn+knArAdBtaP4/tIEp9/EaEQ +PkxROpaw0RPxx9gmrjrKkcRpnd8BKWRRb2jaFOwIQZeQjdCygPLPwj2/kWjFgGce +xGATVdVhmVd8upUPYUk6ynW8yQqTP2cOEvIo4jEbwFcW3wh8GcF+Dx+FHgo2fFt+ +J7x6v+Db9NpSvd4MVHAxkUOVyLzwPt0JfjBkUO1/AaQzZ01oT74V77D2AhGiGxMl +OtzCWfHjXEa7ZywCRuoeSKbmW9m1vFGikpbbqsY3Iqb+zCB0oy2pLmvLwIIRIbWT +ee5Ehr7XHuQe+w== +-----END CERTIFICATE----- + +# Issuer: CN=CA 沃通根证书 O=WoSign CA Limited +# Subject: CN=CA 沃通根证书 O=WoSign CA Limited +# Label: "WoSign China" +# Serial: 106921963437422998931660691310149453965 +# MD5 Fingerprint: 78:83:5b:52:16:76:c4:24:3b:83:78:e8:ac:da:9a:93 +# SHA1 Fingerprint: 16:32:47:8d:89:f9:21:3a:92:00:85:63:f5:a4:a7:d3:12:40:8a:d6 +# SHA256 Fingerprint: d6:f0:34:bd:94:aa:23:3f:02:97:ec:a4:24:5b:28:39:73:e4:47:aa:59:0f:31:0c:77:f4:8f:df:83:11:22:54 +-----BEGIN CERTIFICATE----- +MIIFWDCCA0CgAwIBAgIQUHBrzdgT/BtOOzNy0hFIjTANBgkqhkiG9w0BAQsFADBG +MQswCQYDVQQGEwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxGzAZBgNV +BAMMEkNBIOayg+mAmuagueivgeS5pjAeFw0wOTA4MDgwMTAwMDFaFw0zOTA4MDgw +MTAwMDFaMEYxCzAJBgNVBAYTAkNOMRowGAYDVQQKExFXb1NpZ24gQ0EgTGltaXRl +ZDEbMBkGA1UEAwwSQ0Eg5rKD6YCa5qC56K+B5LmmMIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA0EkhHiX8h8EqwqzbdoYGTufQdDTc7WU1/FDWiD+k8H/r +D195L4mx/bxjWDeTmzj4t1up+thxx7S8gJeNbEvxUNUqKaqoGXqW5pWOdO2XCld1 +9AXbbQs5uQF/qvbW2mzmBeCkTVL829B0txGMe41P/4eDrv8FAxNXUDf+jJZSEExf +v5RxadmWPgxDT74wwJ85dE8GRV2j1lY5aAfMh09Qd5Nx2UQIsYo06Yms25tO4dnk +UkWMLhQfkWsZHWgpLFbE4h4TV2TwYeO5Ed+w4VegG63XX9Gv2ystP9Bojg/qnw+L +NVgbExz03jWhCl3W6t8Sb8D7aQdGctyB9gQjF+BNdeFyb7Ao65vh4YOhn0pdr8yb ++gIgthhid5E7o9Vlrdx8kHccREGkSovrlXLp9glk3Kgtn3R46MGiCWOc76DbT52V +qyBPt7D3h1ymoOQ3OMdc4zUPLK2jgKLsLl3Az+2LBcLmc272idX10kaO6m1jGx6K +yX2m+Jzr5dVjhU1zZmkR/sgO9MHHZklTfuQZa/HpelmjbX7FF+Ynxu8b22/8DU0G +AbQOXDBGVWCvOGU6yke6rCzMRh+yRpY/8+0mBe53oWprfi1tWFxK1I5nuPHa1UaK +J/kR8slC/k7e3x9cxKSGhxYzoacXGKUN5AXlK8IrC6KVkLn9YDxOiT7nnO4fuwEC +AwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O +BBYEFOBNv9ybQV0T6GTwp+kVpOGBwboxMA0GCSqGSIb3DQEBCwUAA4ICAQBqinA4 +WbbaixjIvirTthnVZil6Xc1bL3McJk6jfW+rtylNpumlEYOnOXOvEESS5iVdT2H6 +yAa+Tkvv/vMx/sZ8cApBWNromUuWyXi8mHwCKe0JgOYKOoICKuLJL8hWGSbueBwj +/feTZU7n85iYr83d2Z5AiDEoOqsuC7CsDCT6eiaY8xJhEPRdF/d+4niXVOKM6Cm6 +jBAyvd0zaziGfjk9DgNyp115j0WKWa5bIW4xRtVZjc8VX90xJc/bYNaBRHIpAlf2 +ltTW/+op2znFuCyKGo3Oy+dCMYYFaA6eFN0AkLppRQjbbpCBhqcqBT/mhDn4t/lX +X0ykeVoQDF7Va/81XwVRHmyjdanPUIPTfPRm94KNPQx96N97qA4bLJyuQHCH2u2n +FoJavjVsIE4iYdm8UXrNemHcSxH5/mc0zy4EZmFcV5cjjPOGG0jfKq+nwf/Yjj4D +u9gqsPoUJbJRa4ZDhS4HIxaAjUz7tGM7zMN07RujHv41D198HRaG9Q7DlfEvr10l +O1Hm13ZBONFLAzkopR6RctR9q5czxNM+4Gm2KHmgCY0c0f9BckgG/Jou5yD5m6Le +ie2uPAmvylezkolwQOQvT8Jwg0DXJCxr5wkf09XHwQj02w47HAcLQxGEIYbpgNR1 +2KvxAmLBsX5VYc8T1yaw15zLKYs4SgsOkI26oQ== +-----END CERTIFICATE----- + +# Issuer: CN=COMODO RSA Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO RSA Certification Authority O=COMODO CA Limited +# Label: "COMODO RSA Certification Authority" +# Serial: 101909084537582093308941363524873193117 +# MD5 Fingerprint: 1b:31:b0:71:40:36:cc:14:36:91:ad:c4:3e:fd:ec:18 +# SHA1 Fingerprint: af:e5:d2:44:a8:d1:19:42:30:ff:47:9f:e2:f8:97:bb:cd:7a:8c:b4 +# SHA256 Fingerprint: 52:f0:e1:c4:e5:8e:c6:29:29:1b:60:31:7f:07:46:71:b8:5d:7e:a8:0d:5b:07:27:34:63:53:4b:32:b4:02:34 +-----BEGIN CERTIFICATE----- +MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB +hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV +BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5 +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT +EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR +Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR +6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X +pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC +9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV +/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf +Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z ++pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w +qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah +SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC +u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf +Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq +crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E +FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB +/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl +wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM +4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV +2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna +FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ +CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK +boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke +jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL +S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb +QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl +0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB +NVOFBkpdn627G190 +-----END CERTIFICATE----- + +# Issuer: CN=USERTrust RSA Certification Authority O=The USERTRUST Network +# Subject: CN=USERTrust RSA Certification Authority O=The USERTRUST Network +# Label: "USERTrust RSA Certification Authority" +# Serial: 2645093764781058787591871645665788717 +# MD5 Fingerprint: 1b:fe:69:d1:91:b7:19:33:a3:72:a8:0f:e1:55:e5:b5 +# SHA1 Fingerprint: 2b:8f:1b:57:33:0d:bb:a2:d0:7a:6c:51:f7:0e:e9:0d:da:b9:ad:8e +# SHA256 Fingerprint: e7:93:c9:b0:2f:d8:aa:13:e2:1c:31:22:8a:cc:b0:81:19:64:3b:74:9c:89:89:64:b1:74:6d:46:c3:d4:cb:d2 +-----BEGIN CERTIFICATE----- +MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB +iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl +cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV +BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw +MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV +BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU +aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B +3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY +tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/ +Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2 +VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT +79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6 +c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT +Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l +c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee +UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE +Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd +BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G +A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF +Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO +VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3 +ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs +8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR +iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze +Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ +XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/ +qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB +VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB +L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG +jjxDah2nGN59PRbxYvnKkKj9 +-----END CERTIFICATE----- + +# Issuer: CN=USERTrust ECC Certification Authority O=The USERTRUST Network +# Subject: CN=USERTrust ECC Certification Authority O=The USERTRUST Network +# Label: "USERTrust ECC Certification Authority" +# Serial: 123013823720199481456569720443997572134 +# MD5 Fingerprint: fa:68:bc:d9:b5:7f:ad:fd:c9:1d:06:83:28:cc:24:c1 +# SHA1 Fingerprint: d1:cb:ca:5d:b2:d5:2a:7f:69:3b:67:4d:e5:f0:5a:1d:0c:95:7d:f0 +# SHA256 Fingerprint: 4f:f4:60:d5:4b:9c:86:da:bf:bc:fc:57:12:e0:40:0d:2b:ed:3f:bc:4d:4f:bd:aa:86:e0:6a:dc:d2:a9:ad:7a +-----BEGIN CERTIFICATE----- +MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL +MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl +eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT +JVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAx +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT +Ck5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUg +VVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqflo +I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng +o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0G +A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB +zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW +RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg= +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4 +# Label: "GlobalSign ECC Root CA - R4" +# Serial: 14367148294922964480859022125800977897474 +# MD5 Fingerprint: 20:f0:27:68:d1:7e:a0:9d:0e:e6:2a:ca:df:5c:89:8e +# SHA1 Fingerprint: 69:69:56:2e:40:80:f4:24:a1:e7:19:9f:14:ba:f3:ee:58:ab:6a:bb +# SHA256 Fingerprint: be:c9:49:11:c2:95:56:76:db:6c:0a:55:09:86:d7:6e:3b:a0:05:66:7c:44:2c:97:62:b4:fb:b7:73:de:22:8c +-----BEGIN CERTIFICATE----- +MIIB4TCCAYegAwIBAgIRKjikHJYKBN5CsiilC+g0mAIwCgYIKoZIzj0EAwIwUDEk +MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpH +bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX +DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD +QSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuMZ5049sJQ6fLjkZHAOkrprlOQcJ +FspjsbmG+IpXwVfOQvpzofdlQv8ewQCybnMO/8ch5RikqtlxP6jUuc6MHaNCMEAw +DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFSwe61F +uOJAf/sKbvu+M8k8o4TVMAoGCCqGSM49BAMCA0gAMEUCIQDckqGgE6bPA7DmxCGX +kPoUVy0D7O48027KqGx2vKLeuwIgJ6iFJzWbVsaj8kfSt24bAgAXqmemFZHe+pTs +ewv4n4Q= +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5 +# Label: "GlobalSign ECC Root CA - R5" +# Serial: 32785792099990507226680698011560947931244 +# MD5 Fingerprint: 9f:ad:3b:1c:02:1e:8a:ba:17:74:38:81:0c:a2:bc:08 +# SHA1 Fingerprint: 1f:24:c6:30:cd:a4:18:ef:20:69:ff:ad:4f:dd:5f:46:3a:1b:69:aa +# SHA256 Fingerprint: 17:9f:bc:14:8a:3d:d0:0f:d2:4e:a1:34:58:cc:43:bf:a7:f5:9c:81:82:d7:83:a5:13:f6:eb:ec:10:0c:89:24 +-----BEGIN CERTIFICATE----- +MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEk +MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpH +bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX +DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD +QSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6SFkc +8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8ke +hOvRnkmSh5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYI +KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg +515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7yFz9SO8NdCKoCOJuxUnO +xwy8p2Fp8fc74SrL+SvzZpA3 +-----END CERTIFICATE----- diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 00000000..cc4b239f --- /dev/null +++ b/examples/README.md @@ -0,0 +1,450 @@ + +# Getting started + +Welcome to the developer documentation for gRPC, a language-neutral, +platform-neutral remote procedure call (RPC) system developed at Google. + +This document introduces you to gRPC with a quick overview and a simple +Hello World example. You'll find more tutorials and reference docs in this repository - more documentation is coming soon! + + +## Quick start +You can find quick start guides for each language, including installation instructions, examples, and tutorials here: +* [C++](cpp) +* [Java](https://github.com/grpc/grpc-java/tree/master/examples) +* [Go](https://github.com/grpc/grpc-go/tree/master/examples) +* [Ruby](ruby) +* [Node.js](node) +* [Android Java](https://github.com/grpc/grpc-java/tree/master/examples/android) +* [Python](python/helloworld) +* [C#](csharp) +* [Objective-C](objective-c/route_guide) +* [PHP](php) + +## What's in this repository? + +The `examples` directory contains documentation, resources, and examples +for all gRPC users. You can find examples and instructions specific to your +favourite language in the relevant subdirectory. + +You can find out about the gRPC source code repositories in +[`grpc`](https://github.com/grpc/grpc). Each repository provides instructions +for building the appropriate libraries for your language. + + +## What is gRPC? + +In gRPC a *client* application can directly call +methods on a *server* application on a different machine as if it was a +local object, making it easier for you to create distributed applications and +services. As in many RPC systems, gRPC is based around the idea of defining +a *service*, specifying the methods that can be called remotely with their +parameters and return types. On the server side, the server implements this +interface and runs a gRPC server to handle client calls. On the client side, +the client has a *stub* that provides exactly the same methods as the server. + + + +gRPC clients and servers can run and talk to each other in a variety of +environments - from servers inside Google to your own desktop - and can +be written in any of gRPC's [supported languages](#quickstart). So, for +example, you can easily create a gRPC server in Java with clients in Go, +Python, or Ruby. In addition, the latest Google APIs will have gRPC versions +of their interfaces, letting you easily build Google functionality into +your applications. + + +### Working with protocol buffers + +By default gRPC uses *protocol buffers*, Google’s +mature open source mechanism for serializing structured data (although it +can be used with other data formats such as JSON). As you'll +see in our example below, you define gRPC services using *proto files*, +with method parameters and return types specified as protocol buffer message +types. You +can find out lots more about protocol buffers in the [Protocol Buffers +documentation](https://developers.google.com/protocol-buffers/docs/overview). + +#### Protocol buffer versions + +While protocol buffers have been available for open source users for some +time, our examples use a new flavour of protocol buffers called proto3, +which has a slightly simplified syntax, some useful new features, and supports +lots more languages. This is currently available as an alpha release in +Java, C++, Java_nano (Android Java), Python, and Ruby from [the protocol buffers Github +repo](https://github.com/google/protobuf/releases), as well as a Go language +generator from [the golang/protobuf Github repo](https://github.com/golang/protobuf), with more languages in development. You can find out more in the [proto3 language guide](https://developers.google.com/protocol-buffers/docs/proto3), and see +the major differences from the current default version in the [release notes](https://github.com/google/protobuf/releases). More proto3 documentation is coming soon. + +In general, while you *can* use proto2 (the current default protocol buffers version), we recommend that you use proto3 with gRPC as it lets you use the full range of gRPC-supported languages, as well as avoiding compatibility +issues with proto2 clients talking to proto3 servers and vice versa. + + +## Hello gRPC! + +Now that you know a bit more about gRPC, the easiest way to see how it +works is to look at a simple example. Our Hello World walks you through the +construction of a simple gRPC client-server application, showing you how to: + +- Create a protocol buffers schema that defines a simple RPC service with +a single +Hello World method. +- Create a Java server that implements this interface. +- Create a Java client that accesses the Java server. +- Create a Go client that accesses +the same Java server. + +The complete code for the example is available in the `examples` +directory. We use the Git versioning system for source code management: +however, you don't need to know anything about Git to follow along other +than how to install and run a few git commands. + +This is an introductory example rather than a comprehensive tutorial, so +don't worry if you're not a Go or +Java developer - the concepts are similar for all languages, and you can +find more implementations of our Hello World example in other languages (and full tutorials where available) in +the [language-specific folders](#quickstart) in this repository. Complete tutorials and +reference documentation for all gRPC languages are coming soon. + + +### Setup + +This section explains how to set up your local machine to work with +the example code. If you just want to read the example, you can go straight +to the [next step](#servicedef). + +#### Install Git + +You can download and install Git from http://git-scm.com/download. Once +installed you should have access to the git command line tool. The main +commands that you will need to use are: + +- git clone ... : clone a remote repository onto your local machine +- git checkout ... : check out a particular branch or a tagged version of +the code to hack on + +#### Install gRPC + +To build and install gRPC plugins and related tools: +- For Java, see the [Java quick start](https://github.com/grpc/grpc-java). +- For Go, see the [Go quick start](https://github.com/grpc/grpc-go). + +#### Get the source code + +The example code for our Java example lives in the `grpc-java` +GitHub repository. Clone this repository to your local machine by running the +following command: + + +``` +git clone https://github.com/grpc/grpc-java.git +``` + +Change your current directory to grpc-java/examples + +``` +cd grpc-java/examples +``` + + + + +### Defining a service + +The first step in creating our example is to define a *service*: an RPC +service specifies the methods that can be called remotely with their parameters +and return types. As you saw in the +[overview](#protocolbuffers) above, gRPC does this using [protocol +buffers](https://developers.google.com/protocol-buffers/docs/overview). We +use the protocol buffers interface definition language (IDL) to define our +service methods, and define the parameters and return +types as protocol buffer message types. Both the client and the +server use interface code generated from the service definition. + +Here's our example service definition, defined using protocol buffers IDL in +[helloworld.proto](https://github.com/grpc/grpc-java/tree/master/examples/src/main/proto). The `Greeter` +service has one method, `SayHello`, that lets the server receive a single +`HelloRequest` +message from the remote client containing the user's name, then send back +a greeting in a single `HelloReply`. This is the simplest type of RPC you +can specify in gRPC - you can find out about other types in the tutorial for your chosen language. + +```proto +syntax = "proto3"; + +option java_package = "io.grpc.examples"; + +package helloworld; + +// The greeter service definition. +service Greeter { + // Sends a greeting + rpc SayHello (HelloRequest) returns (HelloReply) {} +} + +// The request message containing the user's name. +message HelloRequest { + string name = 1; +} + +// The response message containing the greetings +message HelloReply { + string message = 1; +} + +``` + + +### Generating gRPC code + +Once we've defined our service, we use the protocol buffer compiler +`protoc` to generate the special client and server code we need to create +our application - right now we're going to generate Java code, though you +can generate gRPC code in any gRPC-supported language (as you'll see later +in this example). The generated code contains both stub code for clients to +use and an abstract interface for servers to implement, both with the method +defined in our `Greeter` service. + +(If you didn't install the gRPC plugins and protoc on your system and are just reading along with +the example, you can skip this step and move +onto the next one where we examine the generated code.) + +For simplicity, we've provided a [Gradle build file](https://github.com/grpc/grpc-java/blob/master/examples/build.gradle) with our Java examples that runs `protoc` for you with the appropriate plugin, input, and output: + +```shell +../gradlew build +``` + +This generates the following classes from our .proto, which contain all the generated code +we need to create our example: + +- `Helloworld.java`, which +has all the protocol buffer code to populate, serialize, and retrieve our +`HelloRequest` and `HelloReply` message types +- `GreeterGrpc.java`, which contains (along with some other useful code): + - an interface for `Greeter` servers to implement + + ```java + public static interface Greeter { + public void sayHello(io.grpc.examples.Helloworld.HelloRequest request, + io.grpc.stub.StreamObserver responseObserver); + } + ``` + + - _stub_ classes that clients can use to talk to a `Greeter` server. As you can see, they also implement the `Greeter` interface. + + ```java + public static class GreeterStub extends + io.grpc.stub.AbstractStub + implements Greeter { + ... + } + ``` + + +### Writing a server + +Now let's write some code! First we'll create a server application to implement +our service. Note that we're not going to go into a lot of detail about how +to create a server in this section. More detailed information will be in the +tutorial for your chosen language: check if there's one available yet in the relevant [quick start](#quickstart). + +Our server application has two classes: + +- a main server class that hosts the service implementation and allows access over the +network: [HelloWorldServer.java](https://github.com/grpc/grpc-java/blob/master/examples/src/main/java/io/grpc/examples/helloworld/HelloWorldServer.java). + + +- a simple service implementation class [GreeterImpl.java](https://github.com/grpc/grpc-java/blob/master/examples/src/main/java/io/grpc/examples/helloworld/HelloWorldServer.java#L51). + + +#### Service implementation + +[GreeterImpl.java](https://github.com/grpc/grpc-java/blob/master/examples/src/main/java/io/grpc/examples/helloworld/HelloWorldServer.java#L51) +actually implements our `Greeter` service's required behaviour. + +As you can see, the class `GreeterImpl` implements the interface +`GreeterGrpc.Greeter` that we [generated](#generating) from our proto +[IDL](https://github.com/grpc/grpc-java/tree/master/examples/src/main/proto) by implementing the method `sayHello`: + +```java + @Override + public void sayHello(HelloRequest req, StreamObserver responseObserver) { + HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + req.getName()).build(); + responseObserver.onValue(reply); + responseObserver.onCompleted(); + } +``` +- `sayHello` takes two parameters: + - `HelloRequest`: the request + - `StreamObserver`: a response observer, which is + a special interface for the server to call with its response + +To return our response to the client and complete the call: + +1. We construct and populate a `HelloReply` response object with our exciting +message, as specified in our interface definition. +2. We return the `HelloReply` to the client and then specify that we've finished dealing with the RPC. + + +#### Server implementation + +[HelloWorldServer.java](https://github.com/grpc/grpc-java/blob/master/examples/src/main/java/io/grpc/examples/helloworld/HelloWorldServer.java) +shows the other main feature required to provide a gRPC service; making the service +implementation available from the network. + +```java + /* The port on which the server should run */ + private int port = 50051; + private ServerImpl server; + + private void start() throws Exception { + server = NettyServerBuilder.forPort(port) + .addService(GreeterGrpc.bindService(new GreeterImpl())) + .build().start(); + logger.info("Server started, listening on " + port); + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + // Use stderr here since the logger may have been reset by its JVM shutdown hook. + System.err.println("*** shutting down gRPC server since JVM is shutting down"); + HelloWorldServer.this.stop(); + System.err.println("*** server shut down"); + } + }); + } + +``` + +Here we create an appropriate gRPC server, binding the `Greeter` service +implementation that we created to a port. Then we start the server running: the server is now ready to receive +requests from `Greeter` service clients on our specified port. We'll cover +how all this works in a bit more detail in our language-specific documentation. + + +### Writing a client + +Client-side gRPC is pretty simple. In this step, we'll use the generated code +to write a simple client that can access the `Greeter` server we created +in the [previous section](#server). You can see the complete client code in +[HelloWorldClient.java](https://github.com/grpc/grpc-java/blob/master/examples/src/main/java/io/grpc/examples/helloworld/HelloWorldClient.java). + +Again, we're not going to go into much detail about how to implement a client; +we'll leave that for the tutorial. + +#### Connecting to the service + +First let's look at how we connect to the `Greeter` server. First we need +to create a gRPC channel, specifying the hostname and port of the server we +want to connect to. Then we use the channel to construct the stub instance. + + +```java + private final ChannelImpl channel; + private final GreeterGrpc.GreeterBlockingStub blockingStub; + + public HelloWorldClient(String host, int port) { + channel = + NettyChannelBuilder.forAddress(host, port).negotiationType(NegotiationType.PLAINTEXT) + .build(); + blockingStub = GreeterGrpc.newBlockingStub(channel); + } + +``` + +In this case, we create a blocking stub. This means that the RPC call waits +for the server to respond, and will either return a response or raise an +exception. gRPC Java has other kinds of stubs that make non-blocking calls +to the server, where the response is returned asynchronously. + +#### Calling an RPC + +Now we can contact the service and obtain a greeting: + +1. We construct and fill in a `HelloRequest` to send to the service. +2. We call the stub's `hello()` RPC with our request and get a `HelloReply` +back, from which we can get our greeting. + + +```java + HelloRequest req = HelloRequest.newBuilder().setName(name).build(); + HelloReply reply = blockingStub.sayHello(req); + +``` + + +### Try it out! + +Our [Gradle build file](https://github.com/grpc/grpc-java/blob/master/examples/build.gradle) simplifies building and running the examples. + +You can build and run the server from the `grpc-java` root folder with: + +```sh +$ ./gradlew :grpc-examples:helloWorldServer +``` + +and in another terminal window confirm that it receives a message. + +```sh +$ ./gradlew :grpc-examples:helloWorldClient +``` + +### Adding another client + +Finally, let's look at one of gRPC's most useful features - interoperability +between code in different languages. So far, we've just looked at Java code +generated from and implementing our `Greeter` service definition. However, +as you'll see if you look at the language-specific subdirectories +in this repository, we've also generated and implemented `Greeter` +in some of gRPC's other supported languages. Each service +and client uses interface code generated from the same proto +that we used for the Java example. + +So, for example, if we visit the [`go` example +directory](https://github.com/grpc/grpc-go/tree/master/examples) and look at the +[`greeter_client`](https://github.com/grpc/grpc-go/blob/master/examples/greeter_client/main.go), +we can see that like the Java client, it connects to a `Greeter` service +at `localhost:50051` and uses a stub to call the `SayHello` method with a +`HelloRequest`: + +```go +const ( + address = "localhost:50051" + defaultName = "world" +) + +func main() { + // Set up a connection to the server. + conn, err := grpc.Dial(address) + if err != nil { + log.Fatalf("did not connect: %v", err) + } + defer conn.Close() + c := pb.NewGreeterClient(conn) + + // Contact the server and print out its response. + name := defaultName + if len(os.Args) > 1 { + name = os.Args[1] + } + r, err := c.SayHello(context.Background(), &pb.HelloRequest{Name: + name}) + if err != nil { + log.Fatalf("could not greet: %v", err) + } + log.Printf("Greeting: %s", r.Message) +} +``` + + +If we run the Java server from earlier in another terminal window, we can +run the Go client and connect to it just like the Java client, even though +it's written in a different language. + +``` +$ greeter_client +``` +## Read more! + +- You can find links to language-specific tutorials, examples, and other docs in each language's [quick start](#quickstart). +- [gRPC Authentication Support](doc/grpc-auth-support.md) introduces authentication support in gRPC with supported mechanisms and examples. diff --git a/examples/cpp/README.md b/examples/cpp/README.md new file mode 100644 index 00000000..85c49509 --- /dev/null +++ b/examples/cpp/README.md @@ -0,0 +1,64 @@ +#gRPC in 3 minutes (C++) + +## Installation + +To install gRPC on your system, follow the instructions [here](../../INSTALL). + +## Hello C++ gRPC! + +Here's how to build and run the C++ implementation of the [Hello World](../protos/helloworld.proto) example used in [Getting started](..). + +The example code for this and our other examples lives in the `examples` +directory. Clone this repository to your local machine by running the +following command: + + +```sh +$ git clone https://github.com/grpc/grpc.git +``` + +Change your current directory to examples/cpp/helloworld + +```sh +$ cd examples/cpp/helloworld/ +``` + + +### Generating gRPC code + +To generate the client and server side interfaces: + +```sh +$ make helloworld.grpc.pb.cc helloworld.pb.cc +``` +Which internally invokes the proto-compiler as: + +```sh +$ protoc -I ../../protos/ --grpc_out=. --plugin=protoc-gen-grpc=grpc_cpp_plugin ../../protos/helloworld.proto +$ protoc -I ../../protos/ --cpp_out=. ../../protos/helloworld.proto +``` + +### Client and server implementations + +The client implementation is at [greeter_client.cc](helloworld/greeter_client.cc). + +The server implementation is at [greeter_server.cc](helloworld/greeter_server.cc). + +### Try it! +Build client and server: +```sh +$ make +``` +Run the server, which will listen on port 50051: +```sh +$ ./greeter_server +``` +Run the client (in a different terminal): +```sh +$ ./greeter_client +``` +If things go smoothly, you will see the "Greeter received: Hello world" in the client side output. + +## Tutorial + +You can find a more detailed tutorial in [gRPC Basics: C++](cpptutorial.md) diff --git a/examples/cpp/cpptutorial.md b/examples/cpp/cpptutorial.md new file mode 100644 index 00000000..78de014f --- /dev/null +++ b/examples/cpp/cpptutorial.md @@ -0,0 +1,365 @@ +#gRPC Basics: C++ + +This tutorial provides a basic C++ programmer's introduction to working with gRPC. By walking through this example you'll learn how to: + +- Define a service in a .proto file. +- Generate server and client code using the protocol buffer compiler. +- Use the C++ gRPC API to write a simple client and server for your service. + +It assumes that you have read the [Getting started](..) guide and are familiar with [protocol buffers] (https://developers.google.com/protocol-buffers/docs/overview). Note that the example in this tutorial uses the proto3 version of the protocol buffers language, which is currently in alpha release: you can find out more in the [proto3 language guide](https://developers.google.com/protocol-buffers/docs/proto3) and see the [release notes](https://github.com/google/protobuf/releases) for the new version in the protocol buffers Github repository. + +This isn't a comprehensive guide to using gRPC in C++: more reference documentation is coming soon. + +## Why use gRPC? + +Our example is a simple route mapping application that lets clients get information about features on their route, create a summary of their route, and exchange route information such as traffic updates with the server and other clients. + +With gRPC we can define our service once in a .proto file and implement clients and servers in any of gRPC's supported languages, which in turn can be run in environments ranging from servers inside Google to your own tablet - all the complexity of communication between different languages and environments is handled for you by gRPC. We also get all the advantages of working with protocol buffers, including efficient serialization, a simple IDL, and easy interface updating. + +## Example code and setup + +The example code for our tutorial is in [examples/cpp/route_guide](route_guide). To download the example, clone this repository by running the following command: +```shell +$ git clone https://github.com/grpc/grpc.git +``` + +Then change your current directory to `examples/cpp/route_guide`: +```shell +$ cd examples/cpp/route_guide +``` + +You also should have the relevant tools installed to generate the server and client interface code - if you don't already, follow the setup instructions in [gRPC in 3 minutes](README.md). + + +## Defining the service + +Our first step (as you'll know from [Getting started](..) is to define the gRPC *service* and the method *request* and *response* types using [protocol buffers] (https://developers.google.com/protocol-buffers/docs/overview). You can see the complete .proto file in [`examples/protos/route_guide.proto`](../protos/route_guide.proto). + +To define a service, you specify a named `service` in your .proto file: + +``` +service RouteGuide { + ... +} +``` + +Then you define `rpc` methods inside your service definition, specifying their request and response types. gRPC lets you define four kinds of service method, all of which are used in the `RouteGuide` service: + +- A *simple RPC* where the client sends a request to the server using the stub and waits for a response to come back, just like a normal function call. +``` + // Obtains the feature at a given position. + rpc GetFeature(Point) returns (Feature) {} +``` + +- A *server-side streaming RPC* where the client sends a request to the server and gets a stream to read a sequence of messages back. The client reads from the returned stream until there are no more messages. As you can see in our example, you specify a server-side streaming method by placing the `stream` keyword before the *response* type. +``` + // Obtains the Features available within the given Rectangle. Results are + // streamed rather than returned at once (e.g. in a response message with a + // repeated field), as the rectangle may cover a large area and contain a + // huge number of features. + rpc ListFeatures(Rectangle) returns (stream Feature) {} +``` + +- A *client-side streaming RPC* where the client writes a sequence of messages and sends them to the server, again using a provided stream. Once the client has finished writing the messages, it waits for the server to read them all and return its response. You specify a client-side streaming method by placing the `stream` keyword before the *request* type. +``` + // Accepts a stream of Points on a route being traversed, returning a + // RouteSummary when traversal is completed. + rpc RecordRoute(stream Point) returns (RouteSummary) {} +``` + +- A *bidirectional streaming RPC* where both sides send a sequence of messages using a read-write stream. The two streams operate independently, so clients and servers can read and write in whatever order they like: for example, the server could wait to receive all the client messages before writing its responses, or it could alternately read a message then write a message, or some other combination of reads and writes. The order of messages in each stream is preserved. You specify this type of method by placing the `stream` keyword before both the request and the response. +``` + // Accepts a stream of RouteNotes sent while a route is being traversed, + // while receiving other RouteNotes (e.g. from other users). + rpc RouteChat(stream RouteNote) returns (stream RouteNote) {} +``` + +Our .proto file also contains protocol buffer message type definitions for all the request and response types used in our service methods - for example, here's the `Point` message type: +``` +// Points are represented as latitude-longitude pairs in the E7 representation +// (degrees multiplied by 10**7 and rounded to the nearest integer). +// Latitudes should be in the range +/- 90 degrees and longitude should be in +// the range +/- 180 degrees (inclusive). +message Point { + int32 latitude = 1; + int32 longitude = 2; +} +``` + + +## Generating client and server code + +Next we need to generate the gRPC client and server interfaces from our .proto service definition. We do this using the protocol buffer compiler `protoc` with a special gRPC C++ plugin. + +For simplicity, we've provided a [makefile](route_guide/Makefile) that runs `protoc` for you with the appropriate plugin, input, and output (if you want to run this yourself, make sure you've installed protoc and followed the gRPC code [installation instructions](../../INSTALL) first): + +```shell +$ make route_guide.grpc.pb.cc route_guide.pb.cc +``` + +which actually runs: + +```shell +$ protoc -I ../../protos --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` ../../protos/route_guide.proto +$ protoc -I ../../protos --cpp_out=. ../../protos/route_guide.proto +``` + +Running this command generates the following files in your current directory: +- `route_guide.pb.h`, the header which declares your generated message classes +- `route_guide.pb.cc`, which contains the implementation of your message classes +- `route_guide.grpc.pb.h`, the header which declares your generated service classes +- `route_guide.grpc.pb.cc`, which contains the implementation of your service classes + +These contain: +- All the protocol buffer code to populate, serialize, and retrieve our request and response message types +- A class called `RouteGuide` that contains + - a remote interface type (or *stub*) for clients to call with the methods defined in the `RouteGuide` service. + - two abstract interfaces for servers to implement, also with the methods defined in the `RouteGuide` service. + + + +## Creating the server + +First let's look at how we create a `RouteGuide` server. If you're only interested in creating gRPC clients, you can skip this section and go straight to [Creating the client](#client) (though you might find it interesting anyway!). + +There are two parts to making our `RouteGuide` service do its job: +- Implementing the service interface generated from our service definition: doing the actual "work" of our service. +- Running a gRPC server to listen for requests from clients and return the service responses. + +You can find our example `RouteGuide` server in [route_guide/route_guide_server.cc](route_guide/route_guide_server.cc). Let's take a closer look at how it works. + +### Implementing RouteGuide + +As you can see, our server has a `RouteGuideImpl` class that implements the generated `RouteGuide::Service` interface: + +```cpp +class RouteGuideImpl final : public RouteGuide::Service { +... +} +``` +In this case we're implementing the *synchronous* version of `RouteGuide`, which provides our default gRPC server behaviour. It's also possible to implement an asynchronous interface, `RouteGuide::AsyncService`, which allows you to further customize your server's threading behaviour, though we won't look at this in this tutorial. + +`RouteGuideImpl` implements all our service methods. Let's look at the simplest type first, `GetFeature`, which just gets a `Point` from the client and returns the corresponding feature information from its database in a `Feature`. + +```cpp + Status GetFeature(ServerContext* context, const Point* point, + Feature* feature) override { + feature->set_name(GetFeatureName(*point, feature_list_)); + feature->mutable_location()->CopyFrom(*point); + return Status::OK; + } +``` + +The method is passed a context object for the RPC, the client's `Point` protocol buffer request, and a `Feature` protocol buffer to fill in with the response information. In the method we populate the `Feature` with the appropriate information, and then `return` with an `OK` status to tell gRPC that we've finished dealing with the RPC and that the `Feature` can be returned to the client. + +Now let's look at something a bit more complicated - a streaming RPC. `ListFeatures` is a server-side streaming RPC, so we need to send back multiple `Feature`s to our client. + +```cpp + Status ListFeatures(ServerContext* context, const Rectangle* rectangle, + ServerWriter* writer) override { + auto lo = rectangle->lo(); + auto hi = rectangle->hi(); + long left = std::min(lo.longitude(), hi.longitude()); + long right = std::max(lo.longitude(), hi.longitude()); + long top = std::max(lo.latitude(), hi.latitude()); + long bottom = std::min(lo.latitude(), hi.latitude()); + for (const Feature& f : feature_list_) { + if (f.location().longitude() >= left && + f.location().longitude() <= right && + f.location().latitude() >= bottom && + f.location().latitude() <= top) { + writer->Write(f); + } + } + return Status::OK; + } +``` + +As you can see, instead of getting simple request and response objects in our method parameters, this time we get a request object (the `Rectangle` in which our client wants to find `Feature`s) and a special `ServerWriter` object. In the method, we populate as many `Feature` objects as we need to return, writing them to the `ServerWriter` using its `Write()` method. Finally, as in our simple RPC, we `return Status::OK` to tell gRPC that we've finished writing responses. + +If you look at the client-side streaming method `RecordRoute` you'll see it's quite similar, except this time we get a `ServerReader` instead of a request object and a single response. We use the `ServerReader`s `Read()` method to repeatedly read in our client's requests to a request object (in this case a `Point`) until there are no more messages: the server needs to check the return value of `Read()` after each call. If `true`, the stream is still good and it can continue reading; if `false` the message stream has ended. + +```cpp +while (stream->Read(&point)) { + ...//process client input +} +``` +Finally, let's look at our bidirectional streaming RPC `RouteChat()`. + +```cpp + Status RouteChat(ServerContext* context, + ServerReaderWriter* stream) override { + std::vector received_notes; + RouteNote note; + while (stream->Read(¬e)) { + for (const RouteNote& n : received_notes) { + if (n.location().latitude() == note.location().latitude() && + n.location().longitude() == note.location().longitude()) { + stream->Write(n); + } + } + received_notes.push_back(note); + } + + return Status::OK; + } +``` + +This time we get a `ServerReaderWriter` that can be used to read *and* write messages. The syntax for reading and writing here is exactly the same as for our client-streaming and server-streaming methods. Although each side will always get the other's messages in the order they were written, both the client and server can read and write in any order — the streams operate completely independently. + +### Starting the server + +Once we've implemented all our methods, we also need to start up a gRPC server so that clients can actually use our service. The following snippet shows how we do this for our `RouteGuide` service: + +```cpp +void RunServer(const std::string& db_path) { + std::string server_address("0.0.0.0:50051"); + RouteGuideImpl service(db_path); + + ServerBuilder builder; + builder.AddListeningPort(server_address, grpc::InsecureServerCredentials()); + builder.RegisterService(&service); + std::unique_ptr server(builder.BuildAndStart()); + std::cout << "Server listening on " << server_address << std::endl; + server->Wait(); +} +``` +As you can see, we build and start our server using a `ServerBuilder`. To do this, we: + +1. Create an instance of our service implementation class `RouteGuideImpl`. +2. Create an instance of the factory `ServerBuilder` class. +3. Specify the address and port we want to use to listen for client requests using the builder's `AddListeningPort()` method. +4. Register our service implementation with the builder. +5. Call `BuildAndStart()` on the builder to create and start an RPC server for our service. +5. Call `Wait()` on the server to do a blocking wait until process is killed or `Shutdown()` is called. + + +## Creating the client + +In this section, we'll look at creating a C++ client for our `RouteGuide` service. You can see our complete example client code in [route_guide/route_guide_client.cc](route_guide/route_guide_client.cc). + +### Creating a stub + +To call service methods, we first need to create a *stub*. + +First we need to create a gRPC *channel* for our stub, specifying the server address and port we want to connect to without SSL: + +```cpp +grpc::CreateChannel("localhost:50051", grpc::InsecureCredentials()); +``` + +Now we can use the channel to create our stub using the `NewStub` method provided in the `RouteGuide` class we generated from our .proto. + +```cpp + public: + RouteGuideClient(std::shared_ptr channel, const std::string& db) + : stub_(RouteGuide::NewStub(channel)) { + ... + } +``` + +### Calling service methods + +Now let's look at how we call our service methods. Note that in this tutorial we're calling the *blocking/synchronous* versions of each method: this means that the RPC call waits for the server to respond, and will either return a response or raise an exception. + +#### Simple RPC + +Calling the simple RPC `GetFeature` is nearly as straightforward as calling a local method. + +```cpp + Point point; + Feature feature; + point = MakePoint(409146138, -746188906); + GetOneFeature(point, &feature); + +... + + bool GetOneFeature(const Point& point, Feature* feature) { + ClientContext context; + Status status = stub_->GetFeature(&context, point, feature); + ... + } +``` + +As you can see, we create and populate a request protocol buffer object (in our case `Point`), and create a response protocol buffer object for the server to fill in. We also create a `ClientContext` object for our call - you can optionally set RPC configuration values on this object, such as deadlines, though for now we'll use the default settings. Note that you cannot reuse this object between calls. Finally, we call the method on the stub, passing it the context, request, and response. If the method returns `OK`, then we can read the response information from the server from our response object. + +```cpp + std::cout << "Found feature called " << feature->name() << " at " + << feature->location().latitude()/kCoordFactor_ << ", " + << feature->location().longitude()/kCoordFactor_ << std::endl; +``` + +#### Streaming RPCs + +Now let's look at our streaming methods. If you've already read [Creating the server](#server) some of this may look very familiar - streaming RPCs are implemented in a similar way on both sides. Here's where we call the server-side streaming method `ListFeatures`, which returns a stream of geographical `Feature`s: + +```cpp + std::unique_ptr > reader( + stub_->ListFeatures(&context, rect)); + while (reader->Read(&feature)) { + std::cout << "Found feature called " + << feature.name() << " at " + << feature.location().latitude()/kCoordFactor_ << ", " + << feature.location().longitude()/kCoordFactor_ << std::endl; + } + Status status = reader->Finish(); +``` + +Instead of passing the method a context, request, and response, we pass it a context and request and get a `ClientReader` object back. The client can use the `ClientReader` to read the server's responses. We use the `ClientReader`s `Read()` method to repeatedly read in the server's responses to a response protocol buffer object (in this case a `Feature`) until there are no more messages: the client needs to check the return value of `Read()` after each call. If `true`, the stream is still good and it can continue reading; if `false` the message stream has ended. Finally, we call `Finish()` on the stream to complete the call and get our RPC status. + +The client-side streaming method `RecordRoute` is similar, except there we pass the method a context and response object and get back a `ClientWriter`. + +```cpp + std::unique_ptr > writer( + stub_->RecordRoute(&context, &stats)); + for (int i = 0; i < kPoints; i++) { + const Feature& f = feature_list_[feature_distribution(generator)]; + std::cout << "Visiting point " + << f.location().latitude()/kCoordFactor_ << ", " + << f.location().longitude()/kCoordFactor_ << std::endl; + if (!writer->Write(f.location())) { + // Broken stream. + break; + } + std::this_thread::sleep_for(std::chrono::milliseconds( + delay_distribution(generator))); + } + writer->WritesDone(); + Status status = writer->Finish(); + if (status.IsOk()) { + std::cout << "Finished trip with " << stats.point_count() << " points\n" + << "Passed " << stats.feature_count() << " features\n" + << "Travelled " << stats.distance() << " meters\n" + << "It took " << stats.elapsed_time() << " seconds" + << std::endl; + } else { + std::cout << "RecordRoute rpc failed." << std::endl; + } +``` + +Once we've finished writing our client's requests to the stream using `Write()`, we need to call `WritesDone()` on the stream to let gRPC know that we've finished writing, then `Finish()` to complete the call and get our RPC status. If the status is `OK`, our response object that we initially passed to `RecordRoute()` will be populated with the server's response. + +Finally, let's look at our bidirectional streaming RPC `RouteChat()`. In this case, we just pass a context to the method and get back a `ClientReaderWriter`, which we can use to both write and read messages. + +```cpp + std::shared_ptr > stream( + stub_->RouteChat(&context)); +``` + +The syntax for reading and writing here is exactly the same as for our client-streaming and server-streaming methods. Although each side will always get the other's messages in the order they were written, both the client and server can read and write in any order — the streams operate completely independently. + +## Try it out! + +Build client and server: +```shell +$ make +``` +Run the server, which will listen on port 50051: +```shell +$ ./route_guide_server +``` +Run the client (in a different terminal): +```shell +$ ./route_guide_client +``` + diff --git a/examples/cpp/helloworld/Makefile b/examples/cpp/helloworld/Makefile new file mode 100644 index 00000000..f2093afa --- /dev/null +++ b/examples/cpp/helloworld/Makefile @@ -0,0 +1,119 @@ +# +# Copyright 2015, 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. +# + +CXX = g++ +CPPFLAGS += -I/usr/local/include -pthread +CXXFLAGS += -std=c++11 +LDFLAGS += -L/usr/local/lib -lgrpc++_unsecure -lgrpc -lgpr -lprotobuf -lpthread -ldl +PROTOC = protoc +GRPC_CPP_PLUGIN = grpc_cpp_plugin +GRPC_CPP_PLUGIN_PATH ?= `which $(GRPC_CPP_PLUGIN)` + +PROTOS_PATH = ../../protos + +vpath %.proto $(PROTOS_PATH) + +all: system-check greeter_client greeter_server greeter_async_client greeter_async_server + +greeter_client: helloworld.pb.o helloworld.grpc.pb.o greeter_client.o + $(CXX) $^ $(LDFLAGS) -o $@ + +greeter_server: helloworld.pb.o helloworld.grpc.pb.o greeter_server.o + $(CXX) $^ $(LDFLAGS) -o $@ + +greeter_async_client: helloworld.pb.o helloworld.grpc.pb.o greeter_async_client.o + $(CXX) $^ $(LDFLAGS) -o $@ + +greeter_async_server: helloworld.pb.o helloworld.grpc.pb.o greeter_async_server.o + $(CXX) $^ $(LDFLAGS) -o $@ + +%.grpc.pb.cc: %.proto + $(PROTOC) -I $(PROTOS_PATH) --grpc_out=. --plugin=protoc-gen-grpc=$(GRPC_CPP_PLUGIN_PATH) $< + +%.pb.cc: %.proto + $(PROTOC) -I $(PROTOS_PATH) --cpp_out=. $< + +clean: + rm -f *.o *.pb.cc *.pb.h greeter_client greeter_server greeter_async_client greeter_async_server + + +# The following is to test your system and ensure a smoother experience. +# They are by no means necessary to actually compile a grpc-enabled software. + +PROTOC_CMD = which $(PROTOC) +PROTOC_CHECK_CMD = $(PROTOC) --version | grep -q libprotoc.3 +PLUGIN_CHECK_CMD = which $(GRPC_CPP_PLUGIN) +HAS_PROTOC = $(shell $(PROTOC_CMD) > /dev/null && echo true || echo false) +ifeq ($(HAS_PROTOC),true) +HAS_VALID_PROTOC = $(shell $(PROTOC_CHECK_CMD) 2> /dev/null && echo true || echo false) +endif +HAS_PLUGIN = $(shell $(PLUGIN_CHECK_CMD) > /dev/null && echo true || echo false) + +SYSTEM_OK = false +ifeq ($(HAS_VALID_PROTOC),true) +ifeq ($(HAS_PLUGIN),true) +SYSTEM_OK = true +endif +endif + +system-check: +ifneq ($(HAS_VALID_PROTOC),true) + @echo " DEPENDENCY ERROR" + @echo + @echo "You don't have protoc 3.0.0 installed in your path." + @echo "Please install Google protocol buffers 3.0.0 and its compiler." + @echo "You can find it here:" + @echo + @echo " https://github.com/google/protobuf/releases/tag/v3.0.0-alpha-1" + @echo + @echo "Here is what I get when trying to evaluate your version of protoc:" + @echo + -$(PROTOC) --version + @echo + @echo +endif +ifneq ($(HAS_PLUGIN),true) + @echo " DEPENDENCY ERROR" + @echo + @echo "You don't have the grpc c++ protobuf plugin installed in your path." + @echo "Please install grpc. You can find it here:" + @echo + @echo " https://github.com/grpc/grpc" + @echo + @echo "Here is what I get when trying to detect if you have the plugin:" + @echo + -which $(GRPC_CPP_PLUGIN) + @echo + @echo +endif +ifneq ($(SYSTEM_OK),true) + @false +endif diff --git a/examples/cpp/helloworld/README.md b/examples/cpp/helloworld/README.md new file mode 100644 index 00000000..b16c0845 --- /dev/null +++ b/examples/cpp/helloworld/README.md @@ -0,0 +1,260 @@ +# gRPC C++ Hello World Tutorial + +### Install gRPC +Make sure you have installed gRPC on your system. Follow the instructions here: +[https://github.com/grpc/grpc/blob/master/INSTALL](../../../INSTALL). + +### Get the tutorial source code + +The example code for this and our other examples lives in the `examples` +directory. Clone this repository to your local machine by running the +following command: + + +```sh +$ git clone https://github.com/grpc/grpc.git +``` + +Change your current directory to examples/cpp/helloworld + +```sh +$ cd examples/cpp/helloworld/ +``` + +### Defining a service + +The first step in creating our example is to define a *service*: an RPC +service specifies the methods that can be called remotely with their parameters +and return types. As you saw in the +[overview](#protocolbuffers) above, gRPC does this using [protocol +buffers](https://developers.google.com/protocol-buffers/docs/overview). We +use the protocol buffers interface definition language (IDL) to define our +service methods, and define the parameters and return +types as protocol buffer message types. Both the client and the +server use interface code generated from the service definition. + +Here's our example service definition, defined using protocol buffers IDL in +[helloworld.proto](../../protos/helloworld.proto). The `Greeting` +service has one method, `hello`, that lets the server receive a single +`HelloRequest` +message from the remote client containing the user's name, then send back +a greeting in a single `HelloReply`. This is the simplest type of RPC you +can specify in gRPC - we'll look at some other types later in this document. + +``` +syntax = "proto3"; + +option java_package = "ex.grpc"; + +package helloworld; + +// The greeting service definition. +service Greeter { + // Sends a greeting + rpc SayHello (HelloRequest) returns (HelloReply) {} +} + +// The request message containing the user's name. +message HelloRequest { + string name = 1; +} + +// The response message containing the greetings +message HelloReply { + string message = 1; +} + +``` + + +### Generating gRPC code + +Once we've defined our service, we use the protocol buffer compiler +`protoc` to generate the special client and server code we need to create +our application. The generated code contains both stub code for clients to +use and an abstract interface for servers to implement, both with the method +defined in our `Greeting` service. + +To generate the client and server side interfaces: + +```sh +$ make helloworld.grpc.pb.cc helloworld.pb.cc +``` +Which internally invokes the proto-compiler as: + +```sh +$ protoc -I ../../protos/ --grpc_out=. --plugin=protoc-gen-grpc=grpc_cpp_plugin ../../protos/helloworld.proto +$ protoc -I ../../protos/ --cpp_out=. ../../protos/helloworld.proto +``` + +### Writing a client + +- Create a channel. A channel is a logical connection to an endpoint. A gRPC + channel can be created with the target address, credentials to use and + arguments as follows + + ``` + auto channel = CreateChannel("localhost:50051", InsecureCredentials()); + ``` + +- Create a stub. A stub implements the rpc methods of a service and in the + generated code, a method is provided to created a stub with a channel: + + ``` + auto stub = helloworld::Greeter::NewStub(channel); + ``` + +- Make a unary rpc, with `ClientContext` and request/response proto messages. + + ``` + ClientContext context; + HelloRequest request; + request.set_name("hello"); + HelloReply reply; + Status status = stub->SayHello(&context, request, &reply); + ``` + +- Check returned status and response. + + ``` + if (status.ok()) { + // check reply.message() + } else { + // rpc failed. + } + ``` + +For a working example, refer to [greeter_client.cc](greeter_client.cc). + +### Writing a server + +- Implement the service interface + + ``` + class GreeterServiceImpl final : public Greeter::Service { + Status SayHello(ServerContext* context, const HelloRequest* request, + HelloReply* reply) override { + std::string prefix("Hello "); + reply->set_message(prefix + request->name()); + return Status::OK; + } + }; + + ``` + +- Build a server exporting the service + + ``` + GreeterServiceImpl service; + ServerBuilder builder; + builder.AddListeningPort("0.0.0.0:50051", grpc::InsecureServerCredentials()); + builder.RegisterService(&service); + std::unique_ptr server(builder.BuildAndStart()); + ``` + +For a working example, refer to [greeter_server.cc](greeter_server.cc). + +### Writing asynchronous client and server + +gRPC uses `CompletionQueue` API for asynchronous operations. The basic work flow +is +- bind a `CompletionQueue` to a rpc call +- do something like a read or write, present with a unique `void*` tag +- call `CompletionQueue::Next` to wait for operations to complete. If a tag + appears, it indicates that the corresponding operation is complete. + +#### Async client + +The channel and stub creation code is the same as the sync client. + +- Initiate the rpc and create a handle for the rpc. Bind the rpc to a + `CompletionQueue`. + + ``` + CompletionQueue cq; + auto rpc = stub->AsyncSayHello(&context, request, &cq); + ``` + +- Ask for reply and final status, with a unique tag + + ``` + Status status; + rpc->Finish(&reply, &status, (void*)1); + ``` + +- Wait for the completion queue to return the next tag. The reply and status are + ready once the tag passed into the corresponding `Finish()` call is returned. + + ``` + void* got_tag; + bool ok = false; + cq.Next(&got_tag, &ok); + if (ok && got_tag == (void*)1) { + // check reply and status + } + ``` + +For a working example, refer to [greeter_async_client.cc](greeter_async_client.cc). + +#### Async server + +The server implementation requests a rpc call with a tag and then wait for the +completion queue to return the tag. The basic flow is + +- Build a server exporting the async service + + ``` + helloworld::Greeter::AsyncService service; + ServerBuilder builder; + builder.AddListeningPort("0.0.0.0:50051", InsecureServerCredentials()); + builder.RegisterAsyncService(&service); + auto cq = builder.AddCompletionQueue(); + auto server = builder.BuildAndStart(); + ``` + +- Request one rpc + + ``` + ServerContext context; + HelloRequest request; + ServerAsyncResponseWriter responder; + service.RequestSayHello(&context, &request, &responder, &cq, &cq, (void*)1); + ``` + +- Wait for the completion queue to return the tag. The context, request and + responder are ready once the tag is retrieved. + + ``` + HelloReply reply; + Status status; + void* got_tag; + bool ok = false; + cq.Next(&got_tag, &ok); + if (ok && got_tag == (void*)1) { + // set reply and status + responder.Finish(reply, status, (void*)2); + } + ``` + +- Wait for the completion queue to return the tag. The rpc is finished when the + tag is back. + + ``` + void* got_tag; + bool ok = false; + cq.Next(&got_tag, &ok); + if (ok && got_tag == (void*)2) { + // clean up + } + ``` + +To handle multiple rpcs, the async server creates an object `CallData` to +maintain the state of each rpc and use the address of it as the unique tag. For +simplicity the server only uses one completion queue for all events, and runs a +main loop in `HandleRpcs` to query the queue. + +For a working example, refer to [greeter_async_server.cc](greeter_async_server.cc). + + + + diff --git a/examples/cpp/helloworld/greeter_async_client.cc b/examples/cpp/helloworld/greeter_async_client.cc new file mode 100644 index 00000000..923c8ffa --- /dev/null +++ b/examples/cpp/helloworld/greeter_async_client.cc @@ -0,0 +1,125 @@ +/* + * + * Copyright 2015, 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 +#include +#include + +#include + +#include "helloworld.grpc.pb.h" + +using grpc::Channel; +using grpc::ClientAsyncResponseReader; +using grpc::ClientContext; +using grpc::CompletionQueue; +using grpc::Status; +using helloworld::HelloRequest; +using helloworld::HelloReply; +using helloworld::Greeter; + +class GreeterClient { + public: + explicit GreeterClient(std::shared_ptr channel) + : stub_(Greeter::NewStub(channel)) {} + + // Assambles the client's payload, sends it and presents the response back + // from the server. + std::string SayHello(const std::string& user) { + // Data we are sending to the server. + HelloRequest request; + request.set_name(user); + + // Container for the data we expect from the server. + HelloReply reply; + + // Context for the client. It could be used to convey extra information to + // the server and/or tweak certain RPC behaviors. + ClientContext context; + + // The producer-consumer queue we use to communicate asynchronously with the + // gRPC runtime. + CompletionQueue cq; + + // Storage for the status of the RPC upon completion. + Status status; + + // stub_->AsyncSayHello() perform the RPC call, returning an instance we + // store in "rpc". Because we are using the asynchronous API, we need the + // hold on to the "rpc" instance in order to get updates on the ongoig RPC. + std::unique_ptr > rpc( + stub_->AsyncSayHello(&context, request, &cq)); + + // Request that, upon completion of the RPC, "reply" be updated with the + // server's response; "status" with the indication of whether the operation + // was successful. Tag the request with the integer 1. + rpc->Finish(&reply, &status, (void*)1); + void* got_tag; + bool ok = false; + // Block until the next result is available in the completion queue "cq". + cq.Next(&got_tag, &ok); + + // Verify that the result from "cq" corresponds, by its tag, our previous + // request. + GPR_ASSERT(got_tag == (void*)1); + // ... and that the request was completed successfully. Note that "ok" + // corresponds solely to the request for updates introduced by Finish(). + GPR_ASSERT(ok); + + // Act upon the status of the actual RPC. + if (status.ok()) { + return reply.message(); + } else { + return "RPC failed"; + } + } + + private: + // Out of the passed in Channel comes the stub, stored here, our view of the + // server's exposed services. + std::unique_ptr stub_; +}; + +int main(int argc, char** argv) { + // Instantiate the client. It requires a channel, out of which the actual RPCs + // are created. This channel models a connection to an endpoint (in this case, + // localhost at port 50051). We indicate that the channel isn't authenticated + // (use of InsecureCredentials()). + GreeterClient greeter( + grpc::CreateChannel("localhost:50051", grpc::InsecureCredentials())); + std::string user("world"); + std::string reply = greeter.SayHello(user); // The actual RPC call! + std::cout << "Greeter received: " << reply << std::endl; + + return 0; +} diff --git a/examples/cpp/helloworld/greeter_async_server.cc b/examples/cpp/helloworld/greeter_async_server.cc new file mode 100644 index 00000000..b2047a8c --- /dev/null +++ b/examples/cpp/helloworld/greeter_async_server.cc @@ -0,0 +1,178 @@ +/* + * + * Copyright 2015, 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 +#include +#include +#include + +#include + +#include "helloworld.grpc.pb.h" + +using grpc::Server; +using grpc::ServerAsyncResponseWriter; +using grpc::ServerBuilder; +using grpc::ServerContext; +using grpc::ServerCompletionQueue; +using grpc::Status; +using helloworld::HelloRequest; +using helloworld::HelloReply; +using helloworld::Greeter; + +class ServerImpl final { + public: + ~ServerImpl() { + server_->Shutdown(); + // Always shutdown the completion queue after the server. + cq_->Shutdown(); + } + + // There is no shutdown handling in this code. + void Run() { + std::string server_address("0.0.0.0:50051"); + + ServerBuilder builder; + // Listen on the given address without any authentication mechanism. + builder.AddListeningPort(server_address, grpc::InsecureServerCredentials()); + // Register "service_" as the instance through which we'll communicate with + // clients. In this case it corresponds to an *asynchronous* service. + builder.RegisterAsyncService(&service_); + // Get hold of the completion queue used for the asynchronous communication + // with the gRPC runtime. + cq_ = builder.AddCompletionQueue(); + // Finally assemble the server. + server_ = builder.BuildAndStart(); + std::cout << "Server listening on " << server_address << std::endl; + + // Proceed to the server's main loop. + HandleRpcs(); + } + + private: + // Class encompasing the state and logic needed to serve a request. + class CallData { + public: + // Take in the "service" instance (in this case representing an asynchronous + // server) and the completion queue "cq" used for asynchronous communication + // with the gRPC runtime. + CallData(Greeter::AsyncService* service, ServerCompletionQueue* cq) + : service_(service), cq_(cq), responder_(&ctx_), status_(CREATE) { + // Invoke the serving logic right away. + Proceed(); + } + + void Proceed() { + if (status_ == CREATE) { + // As part of the initial CREATE state, we *request* that the system + // start processing SayHello requests. In this request, "this" acts are + // the tag uniquely identifying the request (so that different CallData + // instances can serve different requests concurrently), in this case + // the memory address of this CallData instance. + service_->RequestSayHello(&ctx_, &request_, &responder_, cq_, cq_, + this); + // Make this instance progress to the PROCESS state. + status_ = PROCESS; + } else if (status_ == PROCESS) { + // Spawn a new CallData instance to serve new clients while we process + // the one for this CallData. The instance will deallocate itself as + // part of its FINISH state. + new CallData(service_, cq_); + + // The actual processing. + std::string prefix("Hello "); + reply_.set_message(prefix + request_.name()); + + // And we are done! Let the gRPC runtime know we've finished, using the + // memory address of this instance as the uniquely identifying tag for + // the event. + responder_.Finish(reply_, Status::OK, this); + status_ = FINISH; + } else { + GPR_ASSERT(status_ == FINISH); + // Once in the FINISH state, deallocate ourselves (CallData). + delete this; + } + } + + private: + // The means of communication with the gRPC runtime for an asynchronous + // server. + Greeter::AsyncService* service_; + // The producer-consumer queue where for asynchronous server notifications. + ServerCompletionQueue* cq_; + // Context for the rpc, allowing to tweak aspects of it such as the use + // of compression, authentication, as well as to send metadata back to the + // client. + ServerContext ctx_; + + // What we get from the client. + HelloRequest request_; + // What we send back to the client. + HelloReply reply_; + + // The means to get back to the client. + ServerAsyncResponseWriter responder_; + + // Let's implement a tiny state machine with the following states. + enum CallStatus { CREATE, PROCESS, FINISH }; + CallStatus status_; // The current serving state. + }; + + // This can be run in multiple threads if needed. + void HandleRpcs() { + // Spawn a new CallData instance to serve new clients. + new CallData(&service_, cq_.get()); + void* tag; // uniquely identifies a request. + bool ok; + while (true) { + // Block waiting to read the next event from the completion queue. The + // event is uniquely identified by its tag, which in this case is the + // memory address of a CallData instance. + cq_->Next(&tag, &ok); + GPR_ASSERT(ok); + static_cast(tag)->Proceed(); + } + } + + std::unique_ptr cq_; + Greeter::AsyncService service_; + std::unique_ptr server_; +}; + +int main(int argc, char** argv) { + ServerImpl server; + server.Run(); + + return 0; +} diff --git a/examples/cpp/helloworld/greeter_client.cc b/examples/cpp/helloworld/greeter_client.cc new file mode 100644 index 00000000..6cd8353a --- /dev/null +++ b/examples/cpp/helloworld/greeter_client.cc @@ -0,0 +1,95 @@ +/* + * + * Copyright 2015, 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 +#include +#include + +#include + +#include "helloworld.grpc.pb.h" + +using grpc::Channel; +using grpc::ClientContext; +using grpc::Status; +using helloworld::HelloRequest; +using helloworld::HelloReply; +using helloworld::Greeter; + +class GreeterClient { + public: + GreeterClient(std::shared_ptr channel) + : stub_(Greeter::NewStub(channel)) {} + + // Assambles the client's payload, sends it and presents the response back + // from the server. + std::string SayHello(const std::string& user) { + // Data we are sending to the server. + HelloRequest request; + request.set_name(user); + + // Container for the data we expect from the server. + HelloReply reply; + + // Context for the client. It could be used to convey extra information to + // the server and/or tweak certain RPC behaviors. + ClientContext context; + + // The actual RPC. + Status status = stub_->SayHello(&context, request, &reply); + + // Act upon its status. + if (status.ok()) { + return reply.message(); + } else { + return "RPC failed"; + } + } + + private: + std::unique_ptr stub_; +}; + +int main(int argc, char** argv) { + // Instantiate the client. It requires a channel, out of which the actual RPCs + // are created. This channel models a connection to an endpoint (in this case, + // localhost at port 50051). We indicate that the channel isn't authenticated + // (use of InsecureCredentials()). + GreeterClient greeter( + grpc::CreateChannel("localhost:50051", grpc::InsecureCredentials())); + std::string user("world"); + std::string reply = greeter.SayHello(user); + std::cout << "Greeter received: " << reply << std::endl; + + return 0; +} diff --git a/examples/cpp/helloworld/greeter_server.cc b/examples/cpp/helloworld/greeter_server.cc new file mode 100644 index 00000000..9eab32c6 --- /dev/null +++ b/examples/cpp/helloworld/greeter_server.cc @@ -0,0 +1,83 @@ +/* + * + * Copyright 2015, 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 +#include +#include + +#include + +#include "helloworld.grpc.pb.h" + +using grpc::Server; +using grpc::ServerBuilder; +using grpc::ServerContext; +using grpc::Status; +using helloworld::HelloRequest; +using helloworld::HelloReply; +using helloworld::Greeter; + +// Logic and data behind the server's behavior. +class GreeterServiceImpl final : public Greeter::Service { + Status SayHello(ServerContext* context, const HelloRequest* request, + HelloReply* reply) override { + std::string prefix("Hello "); + reply->set_message(prefix + request->name()); + return Status::OK; + } +}; + +void RunServer() { + std::string server_address("0.0.0.0:50051"); + GreeterServiceImpl service; + + ServerBuilder builder; + // Listen on the given address without any authentication mechanism. + builder.AddListeningPort(server_address, grpc::InsecureServerCredentials()); + // Register "service" as the instance through which we'll communicate with + // clients. In this case it corresponds to an *synchronous* service. + builder.RegisterService(&service); + // Finally assemble the server. + std::unique_ptr server(builder.BuildAndStart()); + std::cout << "Server listening on " << server_address << std::endl; + + // Wait for the server to shutdown. Note that some other thread must be + // responsible for shutting down the server for this call to ever return. + server->Wait(); +} + +int main(int argc, char** argv) { + RunServer(); + + return 0; +} diff --git a/examples/cpp/route_guide/Makefile b/examples/cpp/route_guide/Makefile new file mode 100644 index 00000000..b906177a --- /dev/null +++ b/examples/cpp/route_guide/Makefile @@ -0,0 +1,113 @@ +# +# Copyright 2015, 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. +# + +CXX = g++ +CPPFLAGS += -I/usr/local/include -pthread +CXXFLAGS += -std=c++11 +LDFLAGS += -L/usr/local/lib -lgrpc++_unsecure -lgrpc -lgpr -lprotobuf -lpthread -ldl +PROTOC = protoc +GRPC_CPP_PLUGIN = grpc_cpp_plugin +GRPC_CPP_PLUGIN_PATH ?= `which $(GRPC_CPP_PLUGIN)` + +PROTOS_PATH = ../../protos + +vpath %.proto $(PROTOS_PATH) + +all: system-check route_guide_client route_guide_server + +route_guide_client: route_guide.pb.o route_guide.grpc.pb.o route_guide_client.o helper.o + $(CXX) $^ $(LDFLAGS) -o $@ + +route_guide_server: route_guide.pb.o route_guide.grpc.pb.o route_guide_server.o helper.o + $(CXX) $^ $(LDFLAGS) -o $@ + +%.grpc.pb.cc: %.proto + $(PROTOC) -I $(PROTOS_PATH) --grpc_out=. --plugin=protoc-gen-grpc=$(GRPC_CPP_PLUGIN_PATH) $< + +%.pb.cc: %.proto + $(PROTOC) -I $(PROTOS_PATH) --cpp_out=. $< + +clean: + rm -f *.o *.pb.cc *.pb.h route_guide_client route_guide_server + + +# The following is to test your system and ensure a smoother experience. +# They are by no means necessary to actually compile a grpc-enabled software. + +PROTOC_CMD = which $(PROTOC) +PROTOC_CHECK_CMD = $(PROTOC) --version | grep -q libprotoc.3 +PLUGIN_CHECK_CMD = which $(GRPC_CPP_PLUGIN) +HAS_PROTOC = $(shell $(PROTOC_CMD) > /dev/null && echo true || echo false) +ifeq ($(HAS_PROTOC),true) +HAS_VALID_PROTOC = $(shell $(PROTOC_CHECK_CMD) 2> /dev/null && echo true || echo false) +endif +HAS_PLUGIN = $(shell $(PLUGIN_CHECK_CMD) > /dev/null && echo true || echo false) + +SYSTEM_OK = false +ifeq ($(HAS_VALID_PROTOC),true) +ifeq ($(HAS_PLUGIN),true) +SYSTEM_OK = true +endif +endif + +system-check: +ifneq ($(HAS_VALID_PROTOC),true) + @echo " DEPENDENCY ERROR" + @echo + @echo "You don't have protoc 3.0.0 installed in your path." + @echo "Please install Google protocol buffers 3.0.0 and its compiler." + @echo "You can find it here:" + @echo + @echo " https://github.com/google/protobuf/releases/tag/v3.0.0-alpha-1" + @echo + @echo "Here is what I get when trying to evaluate your version of protoc:" + @echo + -$(PROTOC) --version + @echo + @echo +endif +ifneq ($(HAS_PLUGIN),true) + @echo " DEPENDENCY ERROR" + @echo + @echo "You don't have the grpc c++ protobuf plugin installed in your path." + @echo "Please install grpc. You can find it here:" + @echo + @echo " https://github.com/grpc/grpc" + @echo + @echo "Here is what I get when trying to detect if you have the plugin:" + @echo + -which $(GRPC_CPP_PLUGIN) + @echo + @echo +endif +ifneq ($(SYSTEM_OK),true) + @false +endif diff --git a/examples/cpp/route_guide/helper.cc b/examples/cpp/route_guide/helper.cc new file mode 100644 index 00000000..f76990f6 --- /dev/null +++ b/examples/cpp/route_guide/helper.cc @@ -0,0 +1,178 @@ +/* + * + * Copyright 2015, 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 +#include +#include +#include +#include +#include +#include +#include "route_guide.grpc.pb.h" + +namespace routeguide { + +std::string GetDbFileContent(int argc, char** argv) { + std::string db_path; + std::string arg_str("--db_path"); + if (argc > 1) { + std::string argv_1 = argv[1]; + size_t start_position = argv_1.find(arg_str); + if (start_position != std::string::npos) { + start_position += arg_str.size(); + if (argv_1[start_position] == ' ' || + argv_1[start_position] == '=') { + db_path = argv_1.substr(start_position + 1); + } + } + } else { + db_path = "route_guide_db.json"; + } + std::ifstream db_file(db_path); + if (!db_file.is_open()) { + std::cout << "Failed to open " << db_path << std::endl; + return ""; + } + std::stringstream db; + db << db_file.rdbuf(); + return db.str(); +} + +// A simple parser for the json db file. It requires the db file to have the +// exact form of [{"location": { "latitude": 123, "longitude": 456}, "name": +// "the name can be empty" }, { ... } ... The spaces will be stripped. +class Parser { + public: + explicit Parser(const std::string& db) : db_(db) { + // Remove all spaces. + db_.erase( + std::remove_if(db_.begin(), db_.end(), isspace), + db_.end()); + if (!Match("[")) { + SetFailedAndReturnFalse(); + } + } + + bool Finished() { + return current_ >= db_.size(); + } + + bool TryParseOne(Feature* feature) { + if (failed_ || Finished() || !Match("{")) { + return SetFailedAndReturnFalse(); + } + if (!Match(location_) || !Match("{") || !Match(latitude_)) { + return SetFailedAndReturnFalse(); + } + long temp = 0; + ReadLong(&temp); + feature->mutable_location()->set_latitude(temp); + if (!Match(",") || !Match(longitude_)) { + return SetFailedAndReturnFalse(); + } + ReadLong(&temp); + feature->mutable_location()->set_longitude(temp); + if (!Match("},") || !Match(name_) || !Match("\"")) { + return SetFailedAndReturnFalse(); + } + size_t name_start = current_; + while (current_ != db_.size() && db_[current_++] != '"') { + } + if (current_ == db_.size()) { + return SetFailedAndReturnFalse(); + } + feature->set_name(db_.substr(name_start, current_-name_start-1)); + if (!Match("},")) { + if (db_[current_ - 1] == ']' && current_ == db_.size()) { + return true; + } + return SetFailedAndReturnFalse(); + } + return true; + } + + private: + + bool SetFailedAndReturnFalse() { + failed_ = true; + return false; + } + + bool Match(const std::string& prefix) { + bool eq = db_.substr(current_, prefix.size()) == prefix; + current_ += prefix.size(); + return eq; + } + + void ReadLong(long* l) { + size_t start = current_; + while (current_ != db_.size() && db_[current_] != ',' && db_[current_] != '}') { + current_++; + } + // It will throw an exception if fails. + *l = std::stol(db_.substr(start, current_ - start)); + } + + bool failed_ = false; + std::string db_; + size_t current_ = 0; + const std::string location_ = "\"location\":"; + const std::string latitude_ = "\"latitude\":"; + const std::string longitude_ = "\"longitude\":"; + const std::string name_ = "\"name\":"; +}; + +void ParseDb(const std::string& db, std::vector* feature_list) { + feature_list->clear(); + std::string db_content(db); + db_content.erase( + std::remove_if(db_content.begin(), db_content.end(), isspace), + db_content.end()); + + Parser parser(db_content); + Feature feature; + while (!parser.Finished()) { + feature_list->push_back(Feature()); + if (!parser.TryParseOne(&feature_list->back())) { + std::cout << "Error parsing the db file"; + feature_list->clear(); + break; + } + } + std::cout << "DB parsed, loaded " << feature_list->size() + << " features." << std::endl; +} + + +} // namespace routeguide + diff --git a/examples/cpp/route_guide/helper.h b/examples/cpp/route_guide/helper.h new file mode 100644 index 00000000..db36596f --- /dev/null +++ b/examples/cpp/route_guide/helper.h @@ -0,0 +1,50 @@ +/* + * + * Copyright 2015, 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_COMMON_CPP_ROUTE_GUIDE_HELPER_H_ +#define GRPC_COMMON_CPP_ROUTE_GUIDE_HELPER_H_ + +#include +#include + +namespace routeguide { +class Feature; + +std::string GetDbFileContent(int argc, char** argv); + +void ParseDb(const std::string& db, std::vector* feature_list); + +} // namespace routeguide + +#endif // GRPC_COMMON_CPP_ROUTE_GUIDE_HELPER_H_ + diff --git a/examples/cpp/route_guide/route_guide_client.cc b/examples/cpp/route_guide/route_guide_client.cc new file mode 100644 index 00000000..85173a39 --- /dev/null +++ b/examples/cpp/route_guide/route_guide_client.cc @@ -0,0 +1,250 @@ +/* + * + * Copyright 2015, 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "helper.h" +#include "route_guide.grpc.pb.h" + +using grpc::Channel; +using grpc::ClientContext; +using grpc::ClientReader; +using grpc::ClientReaderWriter; +using grpc::ClientWriter; +using grpc::Status; +using routeguide::Point; +using routeguide::Feature; +using routeguide::Rectangle; +using routeguide::RouteSummary; +using routeguide::RouteNote; +using routeguide::RouteGuide; + +Point MakePoint(long latitude, long longitude) { + Point p; + p.set_latitude(latitude); + p.set_longitude(longitude); + return p; +} + +Feature MakeFeature(const std::string& name, + long latitude, long longitude) { + Feature f; + f.set_name(name); + f.mutable_location()->CopyFrom(MakePoint(latitude, longitude)); + return f; +} + +RouteNote MakeRouteNote(const std::string& message, + long latitude, long longitude) { + RouteNote n; + n.set_message(message); + n.mutable_location()->CopyFrom(MakePoint(latitude, longitude)); + return n; +} + +class RouteGuideClient { + public: + RouteGuideClient(std::shared_ptr channel, const std::string& db) + : stub_(RouteGuide::NewStub(channel)) { + routeguide::ParseDb(db, &feature_list_); + } + + void GetFeature() { + Point point; + Feature feature; + point = MakePoint(409146138, -746188906); + GetOneFeature(point, &feature); + point = MakePoint(0, 0); + GetOneFeature(point, &feature); + } + + void ListFeatures() { + routeguide::Rectangle rect; + Feature feature; + ClientContext context; + + rect.mutable_lo()->set_latitude(400000000); + rect.mutable_lo()->set_longitude(-750000000); + rect.mutable_hi()->set_latitude(420000000); + rect.mutable_hi()->set_longitude(-730000000); + std::cout << "Looking for features between 40, -75 and 42, -73" + << std::endl; + + std::unique_ptr > reader( + stub_->ListFeatures(&context, rect)); + while (reader->Read(&feature)) { + std::cout << "Found feature called " + << feature.name() << " at " + << feature.location().latitude()/kCoordFactor_ << ", " + << feature.location().longitude()/kCoordFactor_ << std::endl; + } + Status status = reader->Finish(); + if (status.ok()) { + std::cout << "ListFeatures rpc succeeded." << std::endl; + } else { + std::cout << "ListFeatures rpc failed." << std::endl; + } + } + + void RecordRoute() { + Point point; + RouteSummary stats; + ClientContext context; + const int kPoints = 10; + unsigned seed = std::chrono::system_clock::now().time_since_epoch().count(); + + std::default_random_engine generator(seed); + std::uniform_int_distribution feature_distribution( + 0, feature_list_.size() - 1); + std::uniform_int_distribution delay_distribution( + 500, 1500); + + std::unique_ptr > writer( + stub_->RecordRoute(&context, &stats)); + for (int i = 0; i < kPoints; i++) { + const Feature& f = feature_list_[feature_distribution(generator)]; + std::cout << "Visiting point " + << f.location().latitude()/kCoordFactor_ << ", " + << f.location().longitude()/kCoordFactor_ << std::endl; + if (!writer->Write(f.location())) { + // Broken stream. + break; + } + std::this_thread::sleep_for(std::chrono::milliseconds( + delay_distribution(generator))); + } + writer->WritesDone(); + Status status = writer->Finish(); + if (status.ok()) { + std::cout << "Finished trip with " << stats.point_count() << " points\n" + << "Passed " << stats.feature_count() << " features\n" + << "Travelled " << stats.distance() << " meters\n" + << "It took " << stats.elapsed_time() << " seconds" + << std::endl; + } else { + std::cout << "RecordRoute rpc failed." << std::endl; + } + } + + void RouteChat() { + ClientContext context; + + std::shared_ptr > stream( + stub_->RouteChat(&context)); + + std::thread writer([stream]() { + std::vector notes{ + MakeRouteNote("First message", 0, 0), + MakeRouteNote("Second message", 0, 1), + MakeRouteNote("Third message", 1, 0), + MakeRouteNote("Fourth message", 0, 0)}; + for (const RouteNote& note : notes) { + std::cout << "Sending message " << note.message() + << " at " << note.location().latitude() << ", " + << note.location().longitude() << std::endl; + stream->Write(note); + } + stream->WritesDone(); + }); + + RouteNote server_note; + while (stream->Read(&server_note)) { + std::cout << "Got message " << server_note.message() + << " at " << server_note.location().latitude() << ", " + << server_note.location().longitude() << std::endl; + } + writer.join(); + Status status = stream->Finish(); + if (!status.ok()) { + std::cout << "RouteChat rpc failed." << std::endl; + } + } + + private: + + bool GetOneFeature(const Point& point, Feature* feature) { + ClientContext context; + Status status = stub_->GetFeature(&context, point, feature); + if (!status.ok()) { + std::cout << "GetFeature rpc failed." << std::endl; + return false; + } + if (!feature->has_location()) { + std::cout << "Server returns incomplete feature." << std::endl; + return false; + } + if (feature->name().empty()) { + std::cout << "Found no feature at " + << feature->location().latitude()/kCoordFactor_ << ", " + << feature->location().longitude()/kCoordFactor_ << std::endl; + } else { + std::cout << "Found feature called " << feature->name() << " at " + << feature->location().latitude()/kCoordFactor_ << ", " + << feature->location().longitude()/kCoordFactor_ << std::endl; + } + return true; + } + + const float kCoordFactor_ = 10000000.0; + std::unique_ptr stub_; + std::vector feature_list_; +}; + +int main(int argc, char** argv) { + // Expect only arg: --db_path=path/to/route_guide_db.json. + std::string db = routeguide::GetDbFileContent(argc, argv); + RouteGuideClient guide( + grpc::CreateChannel("localhost:50051", grpc::InsecureCredentials()), + db); + + std::cout << "-------------- GetFeature --------------" << std::endl; + guide.GetFeature(); + std::cout << "-------------- ListFeatures --------------" << std::endl; + guide.ListFeatures(); + std::cout << "-------------- RecordRoute --------------" << std::endl; + guide.RecordRoute(); + std::cout << "-------------- RouteChat --------------" << std::endl; + guide.RouteChat(); + + return 0; +} diff --git a/examples/cpp/route_guide/route_guide_db.json b/examples/cpp/route_guide/route_guide_db.json new file mode 100644 index 00000000..9d6a980a --- /dev/null +++ b/examples/cpp/route_guide/route_guide_db.json @@ -0,0 +1,601 @@ +[{ + "location": { + "latitude": 407838351, + "longitude": -746143763 + }, + "name": "Patriots Path, Mendham, NJ 07945, USA" +}, { + "location": { + "latitude": 408122808, + "longitude": -743999179 + }, + "name": "101 New Jersey 10, Whippany, NJ 07981, USA" +}, { + "location": { + "latitude": 413628156, + "longitude": -749015468 + }, + "name": "U.S. 6, Shohola, PA 18458, USA" +}, { + "location": { + "latitude": 419999544, + "longitude": -740371136 + }, + "name": "5 Conners Road, Kingston, NY 12401, USA" +}, { + "location": { + "latitude": 414008389, + "longitude": -743951297 + }, + "name": "Mid Hudson Psychiatric Center, New Hampton, NY 10958, USA" +}, { + "location": { + "latitude": 419611318, + "longitude": -746524769 + }, + "name": "287 Flugertown Road, Livingston Manor, NY 12758, USA" +}, { + "location": { + "latitude": 406109563, + "longitude": -742186778 + }, + "name": "4001 Tremley Point Road, Linden, NJ 07036, USA" +}, { + "location": { + "latitude": 416802456, + "longitude": -742370183 + }, + "name": "352 South Mountain Road, Wallkill, NY 12589, USA" +}, { + "location": { + "latitude": 412950425, + "longitude": -741077389 + }, + "name": "Bailey Turn Road, Harriman, NY 10926, USA" +}, { + "location": { + "latitude": 412144655, + "longitude": -743949739 + }, + "name": "193-199 Wawayanda Road, Hewitt, NJ 07421, USA" +}, { + "location": { + "latitude": 415736605, + "longitude": -742847522 + }, + "name": "406-496 Ward Avenue, Pine Bush, NY 12566, USA" +}, { + "location": { + "latitude": 413843930, + "longitude": -740501726 + }, + "name": "162 Merrill Road, Highland Mills, NY 10930, USA" +}, { + "location": { + "latitude": 410873075, + "longitude": -744459023 + }, + "name": "Clinton Road, West Milford, NJ 07480, USA" +}, { + "location": { + "latitude": 412346009, + "longitude": -744026814 + }, + "name": "16 Old Brook Lane, Warwick, NY 10990, USA" +}, { + "location": { + "latitude": 402948455, + "longitude": -747903913 + }, + "name": "3 Drake Lane, Pennington, NJ 08534, USA" +}, { + "location": { + "latitude": 406337092, + "longitude": -740122226 + }, + "name": "6324 8th Avenue, Brooklyn, NY 11220, USA" +}, { + "location": { + "latitude": 406421967, + "longitude": -747727624 + }, + "name": "1 Merck Access Road, Whitehouse Station, NJ 08889, USA" +}, { + "location": { + "latitude": 416318082, + "longitude": -749677716 + }, + "name": "78-98 Schalck Road, Narrowsburg, NY 12764, USA" +}, { + "location": { + "latitude": 415301720, + "longitude": -748416257 + }, + "name": "282 Lakeview Drive Road, Highland Lake, NY 12743, USA" +}, { + "location": { + "latitude": 402647019, + "longitude": -747071791 + }, + "name": "330 Evelyn Avenue, Hamilton Township, NJ 08619, USA" +}, { + "location": { + "latitude": 412567807, + "longitude": -741058078 + }, + "name": "New York State Reference Route 987E, Southfields, NY 10975, USA" +}, { + "location": { + "latitude": 416855156, + "longitude": -744420597 + }, + "name": "103-271 Tempaloni Road, Ellenville, NY 12428, USA" +}, { + "location": { + "latitude": 404663628, + "longitude": -744820157 + }, + "name": "1300 Airport Road, North Brunswick Township, NJ 08902, USA" +}, { + "location": { + "latitude": 407113723, + "longitude": -749746483 + }, + "name": "" +}, { + "location": { + "latitude": 402133926, + "longitude": -743613249 + }, + "name": "" +}, { + "location": { + "latitude": 400273442, + "longitude": -741220915 + }, + "name": "" +}, { + "location": { + "latitude": 411236786, + "longitude": -744070769 + }, + "name": "" +}, { + "location": { + "latitude": 411633782, + "longitude": -746784970 + }, + "name": "211-225 Plains Road, Augusta, NJ 07822, USA" +}, { + "location": { + "latitude": 415830701, + "longitude": -742952812 + }, + "name": "" +}, { + "location": { + "latitude": 413447164, + "longitude": -748712898 + }, + "name": "165 Pedersen Ridge Road, Milford, PA 18337, USA" +}, { + "location": { + "latitude": 405047245, + "longitude": -749800722 + }, + "name": "100-122 Locktown Road, Frenchtown, NJ 08825, USA" +}, { + "location": { + "latitude": 418858923, + "longitude": -746156790 + }, + "name": "" +}, { + "location": { + "latitude": 417951888, + "longitude": -748484944 + }, + "name": "650-652 Willi Hill Road, Swan Lake, NY 12783, USA" +}, { + "location": { + "latitude": 407033786, + "longitude": -743977337 + }, + "name": "26 East 3rd Street, New Providence, NJ 07974, USA" +}, { + "location": { + "latitude": 417548014, + "longitude": -740075041 + }, + "name": "" +}, { + "location": { + "latitude": 410395868, + "longitude": -744972325 + }, + "name": "" +}, { + "location": { + "latitude": 404615353, + "longitude": -745129803 + }, + "name": "" +}, { + "location": { + "latitude": 406589790, + "longitude": -743560121 + }, + "name": "611 Lawrence Avenue, Westfield, NJ 07090, USA" +}, { + "location": { + "latitude": 414653148, + "longitude": -740477477 + }, + "name": "18 Lannis Avenue, New Windsor, NY 12553, USA" +}, { + "location": { + "latitude": 405957808, + "longitude": -743255336 + }, + "name": "82-104 Amherst Avenue, Colonia, NJ 07067, USA" +}, { + "location": { + "latitude": 411733589, + "longitude": -741648093 + }, + "name": "170 Seven Lakes Drive, Sloatsburg, NY 10974, USA" +}, { + "location": { + "latitude": 412676291, + "longitude": -742606606 + }, + "name": "1270 Lakes Road, Monroe, NY 10950, USA" +}, { + "location": { + "latitude": 409224445, + "longitude": -748286738 + }, + "name": "509-535 Alphano Road, Great Meadows, NJ 07838, USA" +}, { + "location": { + "latitude": 406523420, + "longitude": -742135517 + }, + "name": "652 Garden Street, Elizabeth, NJ 07202, USA" +}, { + "location": { + "latitude": 401827388, + "longitude": -740294537 + }, + "name": "349 Sea Spray Court, Neptune City, NJ 07753, USA" +}, { + "location": { + "latitude": 410564152, + "longitude": -743685054 + }, + "name": "13-17 Stanley Street, West Milford, NJ 07480, USA" +}, { + "location": { + "latitude": 408472324, + "longitude": -740726046 + }, + "name": "47 Industrial Avenue, Teterboro, NJ 07608, USA" +}, { + "location": { + "latitude": 412452168, + "longitude": -740214052 + }, + "name": "5 White Oak Lane, Stony Point, NY 10980, USA" +}, { + "location": { + "latitude": 409146138, + "longitude": -746188906 + }, + "name": "Berkshire Valley Management Area Trail, Jefferson, NJ, USA" +}, { + "location": { + "latitude": 404701380, + "longitude": -744781745 + }, + "name": "1007 Jersey Avenue, New Brunswick, NJ 08901, USA" +}, { + "location": { + "latitude": 409642566, + "longitude": -746017679 + }, + "name": "6 East Emerald Isle Drive, Lake Hopatcong, NJ 07849, USA" +}, { + "location": { + "latitude": 408031728, + "longitude": -748645385 + }, + "name": "1358-1474 New Jersey 57, Port Murray, NJ 07865, USA" +}, { + "location": { + "latitude": 413700272, + "longitude": -742135189 + }, + "name": "367 Prospect Road, Chester, NY 10918, USA" +}, { + "location": { + "latitude": 404310607, + "longitude": -740282632 + }, + "name": "10 Simon Lake Drive, Atlantic Highlands, NJ 07716, USA" +}, { + "location": { + "latitude": 409319800, + "longitude": -746201391 + }, + "name": "11 Ward Street, Mount Arlington, NJ 07856, USA" +}, { + "location": { + "latitude": 406685311, + "longitude": -742108603 + }, + "name": "300-398 Jefferson Avenue, Elizabeth, NJ 07201, USA" +}, { + "location": { + "latitude": 419018117, + "longitude": -749142781 + }, + "name": "43 Dreher Road, Roscoe, NY 12776, USA" +}, { + "location": { + "latitude": 412856162, + "longitude": -745148837 + }, + "name": "Swan Street, Pine Island, NY 10969, USA" +}, { + "location": { + "latitude": 416560744, + "longitude": -746721964 + }, + "name": "66 Pleasantview Avenue, Monticello, NY 12701, USA" +}, { + "location": { + "latitude": 405314270, + "longitude": -749836354 + }, + "name": "" +}, { + "location": { + "latitude": 414219548, + "longitude": -743327440 + }, + "name": "" +}, { + "location": { + "latitude": 415534177, + "longitude": -742900616 + }, + "name": "565 Winding Hills Road, Montgomery, NY 12549, USA" +}, { + "location": { + "latitude": 406898530, + "longitude": -749127080 + }, + "name": "231 Rocky Run Road, Glen Gardner, NJ 08826, USA" +}, { + "location": { + "latitude": 407586880, + "longitude": -741670168 + }, + "name": "100 Mount Pleasant Avenue, Newark, NJ 07104, USA" +}, { + "location": { + "latitude": 400106455, + "longitude": -742870190 + }, + "name": "517-521 Huntington Drive, Manchester Township, NJ 08759, USA" +}, { + "location": { + "latitude": 400066188, + "longitude": -746793294 + }, + "name": "" +}, { + "location": { + "latitude": 418803880, + "longitude": -744102673 + }, + "name": "40 Mountain Road, Napanoch, NY 12458, USA" +}, { + "location": { + "latitude": 414204288, + "longitude": -747895140 + }, + "name": "" +}, { + "location": { + "latitude": 414777405, + "longitude": -740615601 + }, + "name": "" +}, { + "location": { + "latitude": 415464475, + "longitude": -747175374 + }, + "name": "48 North Road, Forestburgh, NY 12777, USA" +}, { + "location": { + "latitude": 404062378, + "longitude": -746376177 + }, + "name": "" +}, { + "location": { + "latitude": 405688272, + "longitude": -749285130 + }, + "name": "" +}, { + "location": { + "latitude": 400342070, + "longitude": -748788996 + }, + "name": "" +}, { + "location": { + "latitude": 401809022, + "longitude": -744157964 + }, + "name": "" +}, { + "location": { + "latitude": 404226644, + "longitude": -740517141 + }, + "name": "9 Thompson Avenue, Leonardo, NJ 07737, USA" +}, { + "location": { + "latitude": 410322033, + "longitude": -747871659 + }, + "name": "" +}, { + "location": { + "latitude": 407100674, + "longitude": -747742727 + }, + "name": "" +}, { + "location": { + "latitude": 418811433, + "longitude": -741718005 + }, + "name": "213 Bush Road, Stone Ridge, NY 12484, USA" +}, { + "location": { + "latitude": 415034302, + "longitude": -743850945 + }, + "name": "" +}, { + "location": { + "latitude": 411349992, + "longitude": -743694161 + }, + "name": "" +}, { + "location": { + "latitude": 404839914, + "longitude": -744759616 + }, + "name": "1-17 Bergen Court, New Brunswick, NJ 08901, USA" +}, { + "location": { + "latitude": 414638017, + "longitude": -745957854 + }, + "name": "35 Oakland Valley Road, Cuddebackville, NY 12729, USA" +}, { + "location": { + "latitude": 412127800, + "longitude": -740173578 + }, + "name": "" +}, { + "location": { + "latitude": 401263460, + "longitude": -747964303 + }, + "name": "" +}, { + "location": { + "latitude": 412843391, + "longitude": -749086026 + }, + "name": "" +}, { + "location": { + "latitude": 418512773, + "longitude": -743067823 + }, + "name": "" +}, { + "location": { + "latitude": 404318328, + "longitude": -740835638 + }, + "name": "42-102 Main Street, Belford, NJ 07718, USA" +}, { + "location": { + "latitude": 419020746, + "longitude": -741172328 + }, + "name": "" +}, { + "location": { + "latitude": 404080723, + "longitude": -746119569 + }, + "name": "" +}, { + "location": { + "latitude": 401012643, + "longitude": -744035134 + }, + "name": "" +}, { + "location": { + "latitude": 404306372, + "longitude": -741079661 + }, + "name": "" +}, { + "location": { + "latitude": 403966326, + "longitude": -748519297 + }, + "name": "" +}, { + "location": { + "latitude": 405002031, + "longitude": -748407866 + }, + "name": "" +}, { + "location": { + "latitude": 409532885, + "longitude": -742200683 + }, + "name": "" +}, { + "location": { + "latitude": 416851321, + "longitude": -742674555 + }, + "name": "" +}, { + "location": { + "latitude": 406411633, + "longitude": -741722051 + }, + "name": "3387 Richmond Terrace, Staten Island, NY 10303, USA" +}, { + "location": { + "latitude": 413069058, + "longitude": -744597778 + }, + "name": "261 Van Sickle Road, Goshen, NY 10924, USA" +}, { + "location": { + "latitude": 418465462, + "longitude": -746859398 + }, + "name": "" +}, { + "location": { + "latitude": 411733222, + "longitude": -744228360 + }, + "name": "" +}, { + "location": { + "latitude": 410248224, + "longitude": -747127767 + }, + "name": "3 Hasta Way, Newton, NJ 07860, USA" +}] diff --git a/examples/cpp/route_guide/route_guide_server.cc b/examples/cpp/route_guide/route_guide_server.cc new file mode 100644 index 00000000..2be57428 --- /dev/null +++ b/examples/cpp/route_guide/route_guide_server.cc @@ -0,0 +1,202 @@ +/* + * + * Copyright 2015, 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "helper.h" +#include "route_guide.grpc.pb.h" + +using grpc::Server; +using grpc::ServerBuilder; +using grpc::ServerContext; +using grpc::ServerReader; +using grpc::ServerReaderWriter; +using grpc::ServerWriter; +using grpc::Status; +using routeguide::Point; +using routeguide::Feature; +using routeguide::Rectangle; +using routeguide::RouteSummary; +using routeguide::RouteNote; +using routeguide::RouteGuide; +using std::chrono::system_clock; + + +float ConvertToRadians(float num) { + return num * 3.1415926 /180; +} + +float GetDistance(const Point& start, const Point& end) { + const float kCoordFactor = 10000000.0; + float lat_1 = start.latitude() / kCoordFactor; + float lat_2 = end.latitude() / kCoordFactor; + float lon_1 = start.longitude() / kCoordFactor; + float lon_2 = end.longitude() / kCoordFactor; + float lat_rad_1 = ConvertToRadians(lat_1); + float lat_rad_2 = ConvertToRadians(lat_2); + float delta_lat_rad = ConvertToRadians(lat_2-lat_1); + float delta_lon_rad = ConvertToRadians(lon_2-lon_1); + + float a = pow(sin(delta_lat_rad/2), 2) + cos(lat_rad_1) * cos(lat_rad_2) * + pow(sin(delta_lon_rad/2), 2); + float c = 2 * atan2(sqrt(a), sqrt(1-a)); + int R = 6371000; // metres + + return R * c; +} + +std::string GetFeatureName(const Point& point, + const std::vector& feature_list) { + for (const Feature& f : feature_list) { + if (f.location().latitude() == point.latitude() && + f.location().longitude() == point.longitude()) { + return f.name(); + } + } + return ""; +} + +class RouteGuideImpl final : public RouteGuide::Service { + public: + explicit RouteGuideImpl(const std::string& db) { + routeguide::ParseDb(db, &feature_list_); + } + + Status GetFeature(ServerContext* context, const Point* point, + Feature* feature) override { + feature->set_name(GetFeatureName(*point, feature_list_)); + feature->mutable_location()->CopyFrom(*point); + return Status::OK; + } + + Status ListFeatures(ServerContext* context, + const routeguide::Rectangle* rectangle, + ServerWriter* writer) override { + auto lo = rectangle->lo(); + auto hi = rectangle->hi(); + long left = (std::min)(lo.longitude(), hi.longitude()); + long right = (std::max)(lo.longitude(), hi.longitude()); + long top = (std::max)(lo.latitude(), hi.latitude()); + long bottom = (std::min)(lo.latitude(), hi.latitude()); + for (const Feature& f : feature_list_) { + if (f.location().longitude() >= left && + f.location().longitude() <= right && + f.location().latitude() >= bottom && + f.location().latitude() <= top) { + writer->Write(f); + } + } + return Status::OK; + } + + Status RecordRoute(ServerContext* context, ServerReader* reader, + RouteSummary* summary) override { + Point point; + int point_count = 0; + int feature_count = 0; + float distance = 0.0; + Point previous; + + system_clock::time_point start_time = system_clock::now(); + while (reader->Read(&point)) { + point_count++; + if (!GetFeatureName(point, feature_list_).empty()) { + feature_count++; + } + if (point_count != 1) { + distance += GetDistance(previous, point); + } + previous = point; + } + system_clock::time_point end_time = system_clock::now(); + summary->set_point_count(point_count); + summary->set_feature_count(feature_count); + summary->set_distance(static_cast(distance)); + auto secs = std::chrono::duration_cast( + end_time - start_time); + summary->set_elapsed_time(secs.count()); + + return Status::OK; + } + + Status RouteChat(ServerContext* context, + ServerReaderWriter* stream) override { + std::vector received_notes; + RouteNote note; + while (stream->Read(¬e)) { + for (const RouteNote& n : received_notes) { + if (n.location().latitude() == note.location().latitude() && + n.location().longitude() == note.location().longitude()) { + stream->Write(n); + } + } + received_notes.push_back(note); + } + + return Status::OK; + } + + private: + + std::vector feature_list_; +}; + +void RunServer(const std::string& db_path) { + std::string server_address("0.0.0.0:50051"); + RouteGuideImpl service(db_path); + + ServerBuilder builder; + builder.AddListeningPort(server_address, grpc::InsecureServerCredentials()); + builder.RegisterService(&service); + std::unique_ptr server(builder.BuildAndStart()); + std::cout << "Server listening on " << server_address << std::endl; + server->Wait(); +} + +int main(int argc, char** argv) { + // Expect only arg: --db_path=path/to/route_guide_db.json. + std::string db = routeguide::GetDbFileContent(argc, argv); + RunServer(db); + + return 0; +} diff --git a/examples/csharp/.gitignore b/examples/csharp/.gitignore new file mode 100644 index 00000000..585000ea --- /dev/null +++ b/examples/csharp/.gitignore @@ -0,0 +1,5 @@ +bin/ +obj/ +packages/ +*.suo +*.userprefs diff --git a/examples/csharp/.nuget/packages.config b/examples/csharp/.nuget/packages.config new file mode 100644 index 00000000..b1437306 --- /dev/null +++ b/examples/csharp/.nuget/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/examples/csharp/helloworld/.nuget/packages.config b/examples/csharp/helloworld/.nuget/packages.config new file mode 100644 index 00000000..e2879c18 --- /dev/null +++ b/examples/csharp/helloworld/.nuget/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/examples/csharp/helloworld/Greeter.sln b/examples/csharp/helloworld/Greeter.sln new file mode 100644 index 00000000..9430e94d --- /dev/null +++ b/examples/csharp/helloworld/Greeter.sln @@ -0,0 +1,42 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.31101.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Greeter", "Greeter\Greeter.csproj", "{724DFC8C-4B57-4C3F-811C-0463BE2A2829}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GreeterServer", "GreeterServer\GreeterServer.csproj", "{A7706C84-92D2-4B7A-B779-76B64D2950EC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GreeterClient", "GreeterClient\GreeterClient.csproj", "{ACCF4597-3748-4117-8633-1CB767F8CCC3}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{FF1EBE95-F20D-4C27-8A61-D0125F3C8152}" + ProjectSection(SolutionItems) = preProject + .nuget\packages.config = .nuget\packages.config + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {724DFC8C-4B57-4C3F-811C-0463BE2A2829}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {724DFC8C-4B57-4C3F-811C-0463BE2A2829}.Debug|Any CPU.Build.0 = Debug|Any CPU + {724DFC8C-4B57-4C3F-811C-0463BE2A2829}.Release|Any CPU.ActiveCfg = Release|Any CPU + {724DFC8C-4B57-4C3F-811C-0463BE2A2829}.Release|Any CPU.Build.0 = Release|Any CPU + {A7706C84-92D2-4B7A-B779-76B64D2950EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A7706C84-92D2-4B7A-B779-76B64D2950EC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A7706C84-92D2-4B7A-B779-76B64D2950EC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A7706C84-92D2-4B7A-B779-76B64D2950EC}.Release|Any CPU.Build.0 = Release|Any CPU + {ACCF4597-3748-4117-8633-1CB767F8CCC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ACCF4597-3748-4117-8633-1CB767F8CCC3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ACCF4597-3748-4117-8633-1CB767F8CCC3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ACCF4597-3748-4117-8633-1CB767F8CCC3}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + StartupItem = Greeter\Greeter.csproj + EndGlobalSection +EndGlobal diff --git a/examples/csharp/helloworld/Greeter/.gitignore b/examples/csharp/helloworld/Greeter/.gitignore new file mode 100644 index 00000000..1746e326 --- /dev/null +++ b/examples/csharp/helloworld/Greeter/.gitignore @@ -0,0 +1,2 @@ +bin +obj diff --git a/examples/csharp/helloworld/Greeter/Greeter.csproj b/examples/csharp/helloworld/Greeter/Greeter.csproj new file mode 100644 index 00000000..3d92c4bf --- /dev/null +++ b/examples/csharp/helloworld/Greeter/Greeter.csproj @@ -0,0 +1,74 @@ + + + + + + + Debug + AnyCPU + 10.0.0 + 2.0 + {724DFC8C-4B57-4C3F-811C-0463BE2A2829} + Library + Greeter + Greeter + v4.5 + 3ef64a6a + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + false + + + full + true + bin\Release + prompt + 4 + false + + + + ..\packages\Google.Protobuf.3.0.0-alpha4\lib\portable-net45+netcore45+wpa81+wp8\Google.Protobuf.dll + + + False + ..\packages\Grpc.Core.0.7.0\lib\net45\Grpc.Core.dll + + + + ..\packages\Ix-Async.1.2.3\lib\net45\System.Interactive.Async.dll + + + + + + + + + + + + + + + + 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}. + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/csharp/helloworld/Greeter/Helloworld.cs b/examples/csharp/helloworld/Greeter/Helloworld.cs new file mode 100644 index 00000000..668165a6 --- /dev/null +++ b/examples/csharp/helloworld/Greeter/Helloworld.cs @@ -0,0 +1,248 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: helloworld.proto +#pragma warning disable 1591, 0612, 3021 +#region Designer generated code + +using pb = global::Google.Protobuf; +using pbc = global::Google.Protobuf.Collections; +using pbr = global::Google.Protobuf.Reflection; +using scg = global::System.Collections.Generic; +namespace Helloworld { + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public static partial class Helloworld { + + #region Descriptor + public static pbr::FileDescriptor Descriptor { + get { return descriptor; } + } + private static pbr::FileDescriptor descriptor; + + static Helloworld() { + byte[] descriptorData = global::System.Convert.FromBase64String( + string.Concat( + "ChBoZWxsb3dvcmxkLnByb3RvEgpoZWxsb3dvcmxkIhwKDEhlbGxvUmVxdWVz", + "dBIMCgRuYW1lGAEgASgJIh0KCkhlbGxvUmVwbHkSDwoHbWVzc2FnZRgBIAEo", + "CTJJCgdHcmVldGVyEj4KCFNheUhlbGxvEhguaGVsbG93b3JsZC5IZWxsb1Jl", + "cXVlc3QaFi5oZWxsb3dvcmxkLkhlbGxvUmVwbHkiAEIYChBpby5ncnBjLmV4", + "YW1wbGVzogIDSExXYgZwcm90bzM=")); + descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData, + new pbr::FileDescriptor[] { }, + new pbr::GeneratedCodeInfo(null, new pbr::GeneratedCodeInfo[] { + new pbr::GeneratedCodeInfo(typeof(global::Helloworld.HelloRequest), new[]{ "Name" }, null, null, null), + new pbr::GeneratedCodeInfo(typeof(global::Helloworld.HelloReply), new[]{ "Message" }, null, null, null) + })); + } + #endregion + + } + #region Messages + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class HelloRequest : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new HelloRequest()); + public static pb::MessageParser Parser { get { return _parser; } } + + public static pbr::MessageDescriptor Descriptor { + get { return global::Helloworld.Helloworld.Descriptor.MessageTypes[0]; } + } + + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + public HelloRequest() { + OnConstruction(); + } + + partial void OnConstruction(); + + public HelloRequest(HelloRequest other) : this() { + name_ = other.name_; + } + + public HelloRequest Clone() { + return new HelloRequest(this); + } + + public const int NameFieldNumber = 1; + private string name_ = ""; + public string Name { + get { return name_; } + set { + name_ = pb::Preconditions.CheckNotNull(value, "value"); + } + } + + public override bool Equals(object other) { + return Equals(other as HelloRequest); + } + + public bool Equals(HelloRequest other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Name != other.Name) return false; + return true; + } + + public override int GetHashCode() { + int hash = 1; + if (Name.Length != 0) hash ^= Name.GetHashCode(); + return hash; + } + + public override string ToString() { + return pb::JsonFormatter.Default.Format(this); + } + + public void WriteTo(pb::CodedOutputStream output) { + if (Name.Length != 0) { + output.WriteRawTag(10); + output.WriteString(Name); + } + } + + public int CalculateSize() { + int size = 0; + if (Name.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Name); + } + return size; + } + + public void MergeFrom(HelloRequest other) { + if (other == null) { + return; + } + if (other.Name.Length != 0) { + Name = other.Name; + } + } + + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + input.SkipLastField(); + break; + case 10: { + Name = input.ReadString(); + break; + } + } + } + } + + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class HelloReply : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new HelloReply()); + public static pb::MessageParser Parser { get { return _parser; } } + + public static pbr::MessageDescriptor Descriptor { + get { return global::Helloworld.Helloworld.Descriptor.MessageTypes[1]; } + } + + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + public HelloReply() { + OnConstruction(); + } + + partial void OnConstruction(); + + public HelloReply(HelloReply other) : this() { + message_ = other.message_; + } + + public HelloReply Clone() { + return new HelloReply(this); + } + + public const int MessageFieldNumber = 1; + private string message_ = ""; + public string Message { + get { return message_; } + set { + message_ = pb::Preconditions.CheckNotNull(value, "value"); + } + } + + public override bool Equals(object other) { + return Equals(other as HelloReply); + } + + public bool Equals(HelloReply other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Message != other.Message) return false; + return true; + } + + public override int GetHashCode() { + int hash = 1; + if (Message.Length != 0) hash ^= Message.GetHashCode(); + return hash; + } + + public override string ToString() { + return pb::JsonFormatter.Default.Format(this); + } + + public void WriteTo(pb::CodedOutputStream output) { + if (Message.Length != 0) { + output.WriteRawTag(10); + output.WriteString(Message); + } + } + + public int CalculateSize() { + int size = 0; + if (Message.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Message); + } + return size; + } + + public void MergeFrom(HelloReply other) { + if (other == null) { + return; + } + if (other.Message.Length != 0) { + Message = other.Message; + } + } + + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + input.SkipLastField(); + break; + case 10: { + Message = input.ReadString(); + break; + } + } + } + } + + } + + #endregion + +} + +#endregion Designer generated code diff --git a/examples/csharp/helloworld/Greeter/HelloworldGrpc.cs b/examples/csharp/helloworld/Greeter/HelloworldGrpc.cs new file mode 100644 index 00000000..edfe4d22 --- /dev/null +++ b/examples/csharp/helloworld/Greeter/HelloworldGrpc.cs @@ -0,0 +1,89 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: helloworld.proto +#region Designer generated code + +using System; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core; + +namespace Helloworld { + public static class Greeter + { + static readonly string __ServiceName = "helloworld.Greeter"; + + static readonly Marshaller __Marshaller_HelloRequest = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Helloworld.HelloRequest.Parser.ParseFrom); + static readonly Marshaller __Marshaller_HelloReply = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Helloworld.HelloReply.Parser.ParseFrom); + + static readonly Method __Method_SayHello = new Method( + MethodType.Unary, + __ServiceName, + "SayHello", + __Marshaller_HelloRequest, + __Marshaller_HelloReply); + + // service descriptor + public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor + { + get { return global::Helloworld.Helloworld.Descriptor.Services[0]; } + } + + // client interface + public interface IGreeterClient + { + global::Helloworld.HelloReply SayHello(global::Helloworld.HelloRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + global::Helloworld.HelloReply SayHello(global::Helloworld.HelloRequest request, CallOptions options); + AsyncUnaryCall SayHelloAsync(global::Helloworld.HelloRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + AsyncUnaryCall SayHelloAsync(global::Helloworld.HelloRequest request, CallOptions options); + } + + // server-side interface + public interface IGreeter + { + Task SayHello(global::Helloworld.HelloRequest request, ServerCallContext context); + } + + // client stub + public class GreeterClient : ClientBase, IGreeterClient + { + public GreeterClient(Channel channel) : base(channel) + { + } + public global::Helloworld.HelloReply SayHello(global::Helloworld.HelloRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var call = CreateCall(__Method_SayHello, new CallOptions(headers, deadline, cancellationToken)); + return Calls.BlockingUnaryCall(call, request); + } + public global::Helloworld.HelloReply SayHello(global::Helloworld.HelloRequest request, CallOptions options) + { + var call = CreateCall(__Method_SayHello, options); + return Calls.BlockingUnaryCall(call, request); + } + public AsyncUnaryCall SayHelloAsync(global::Helloworld.HelloRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var call = CreateCall(__Method_SayHello, new CallOptions(headers, deadline, cancellationToken)); + return Calls.AsyncUnaryCall(call, request); + } + public AsyncUnaryCall SayHelloAsync(global::Helloworld.HelloRequest request, CallOptions options) + { + var call = CreateCall(__Method_SayHello, options); + return Calls.AsyncUnaryCall(call, request); + } + } + + // creates service definition that can be registered with a server + public static ServerServiceDefinition BindService(IGreeter serviceImpl) + { + return ServerServiceDefinition.CreateBuilder(__ServiceName) + .AddMethod(__Method_SayHello, serviceImpl.SayHello).Build(); + } + + // creates a new client + public static GreeterClient NewClient(Channel channel) + { + return new GreeterClient(channel); + } + + } +} +#endregion diff --git a/examples/csharp/helloworld/Greeter/Properties/AssemblyInfo.cs b/examples/csharp/helloworld/Greeter/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..b9d0dc26 --- /dev/null +++ b/examples/csharp/helloworld/Greeter/Properties/AssemblyInfo.cs @@ -0,0 +1,22 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. +[assembly: AssemblyTitle("Greeter")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("jtattermusch")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. +[assembly: AssemblyVersion("1.0.*")] +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + diff --git a/examples/csharp/helloworld/Greeter/packages.config b/examples/csharp/helloworld/Greeter/packages.config new file mode 100644 index 00000000..1273624c --- /dev/null +++ b/examples/csharp/helloworld/Greeter/packages.config @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/examples/csharp/helloworld/GreeterClient/.gitignore b/examples/csharp/helloworld/GreeterClient/.gitignore new file mode 100644 index 00000000..1746e326 --- /dev/null +++ b/examples/csharp/helloworld/GreeterClient/.gitignore @@ -0,0 +1,2 @@ +bin +obj diff --git a/examples/csharp/helloworld/GreeterClient/GreeterClient.csproj b/examples/csharp/helloworld/GreeterClient/GreeterClient.csproj new file mode 100644 index 00000000..7874172b --- /dev/null +++ b/examples/csharp/helloworld/GreeterClient/GreeterClient.csproj @@ -0,0 +1,77 @@ + + + + + + + Debug + AnyCPU + 10.0.0 + 2.0 + {ACCF4597-3748-4117-8633-1CB767F8CCC3} + Exe + GreeterClient + GreeterClient + v4.5 + c4057b0a + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + true + + + full + true + bin\Release + prompt + 4 + true + + + + ..\packages\Google.Protobuf.3.0.0-alpha4\lib\portable-net45+netcore45+wpa81+wp8\Google.Protobuf.dll + + + False + ..\packages\Grpc.Core.0.7.0\lib\net45\Grpc.Core.dll + + + + ..\packages\Ix-Async.1.2.3\lib\net45\System.Interactive.Async.dll + + + + + + + + + + {724DFC8C-4B57-4C3F-811C-0463BE2A2829} + Greeter + + + + + + + + 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}. + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/csharp/helloworld/GreeterClient/Program.cs b/examples/csharp/helloworld/GreeterClient/Program.cs new file mode 100644 index 00000000..279cee07 --- /dev/null +++ b/examples/csharp/helloworld/GreeterClient/Program.cs @@ -0,0 +1,24 @@ +using System; +using Grpc.Core; +using Helloworld; + +namespace GreeterClient +{ + class Program + { + public static void Main(string[] args) + { + Channel channel = new Channel("127.0.0.1:50051", Credentials.Insecure); + + var client = Greeter.NewClient(channel); + String user = "you"; + + var reply = client.SayHello(new HelloRequest { Name = user }); + Console.WriteLine("Greeting: " + reply.Message); + + channel.ShutdownAsync().Wait(); + Console.WriteLine("Press any key to exit..."); + Console.ReadKey(); + } + } +} diff --git a/examples/csharp/helloworld/GreeterClient/Properties/AssemblyInfo.cs b/examples/csharp/helloworld/GreeterClient/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..1422e952 --- /dev/null +++ b/examples/csharp/helloworld/GreeterClient/Properties/AssemblyInfo.cs @@ -0,0 +1,22 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. +[assembly: AssemblyTitle("GreeterClient")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("jtattermusch")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. +[assembly: AssemblyVersion("1.0.*")] +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + diff --git a/examples/csharp/helloworld/GreeterClient/packages.config b/examples/csharp/helloworld/GreeterClient/packages.config new file mode 100644 index 00000000..1273624c --- /dev/null +++ b/examples/csharp/helloworld/GreeterClient/packages.config @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/examples/csharp/helloworld/GreeterServer/.gitignore b/examples/csharp/helloworld/GreeterServer/.gitignore new file mode 100644 index 00000000..1746e326 --- /dev/null +++ b/examples/csharp/helloworld/GreeterServer/.gitignore @@ -0,0 +1,2 @@ +bin +obj diff --git a/examples/csharp/helloworld/GreeterServer/GreeterServer.csproj b/examples/csharp/helloworld/GreeterServer/GreeterServer.csproj new file mode 100644 index 00000000..d1db2f47 --- /dev/null +++ b/examples/csharp/helloworld/GreeterServer/GreeterServer.csproj @@ -0,0 +1,77 @@ + + + + + + + Debug + AnyCPU + 10.0.0 + 2.0 + {A7706C84-92D2-4B7A-B779-76B64D2950EC} + Exe + GreeterServer + GreeterServer + v4.5 + cc15afe5 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + true + + + full + true + bin\Release + prompt + 4 + true + + + + ..\packages\Google.Protobuf.3.0.0-alpha4\lib\portable-net45+netcore45+wpa81+wp8\Google.Protobuf.dll + + + False + ..\packages\Grpc.Core.0.7.0\lib\net45\Grpc.Core.dll + + + + ..\packages\Ix-Async.1.2.3\lib\net45\System.Interactive.Async.dll + + + + + + + + + + {724DFC8C-4B57-4C3F-811C-0463BE2A2829} + Greeter + + + + + + + + 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}. + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/csharp/helloworld/GreeterServer/Program.cs b/examples/csharp/helloworld/GreeterServer/Program.cs new file mode 100644 index 00000000..0214b359 --- /dev/null +++ b/examples/csharp/helloworld/GreeterServer/Program.cs @@ -0,0 +1,37 @@ +using System; +using System.Threading.Tasks; +using Grpc.Core; +using Helloworld; + +namespace GreeterServer +{ + class GreeterImpl : Greeter.IGreeter + { + // Server side handler of the SayHello RPC + public Task SayHello(HelloRequest request, ServerCallContext context) + { + return Task.FromResult(new HelloReply { Message = "Hello " + request.Name }); + } + } + + class Program + { + const int Port = 50051; + + public static void Main(string[] args) + { + Server server = new Server + { + Services = { Greeter.BindService(new GreeterImpl()) }, + Ports = { new ServerPort("localhost", Port, ServerCredentials.Insecure) } + }; + server.Start(); + + Console.WriteLine("Greeter server listening on port " + Port); + Console.WriteLine("Press any key to stop the server..."); + Console.ReadKey(); + + server.ShutdownAsync().Wait(); + } + } +} diff --git a/examples/csharp/helloworld/GreeterServer/Properties/AssemblyInfo.cs b/examples/csharp/helloworld/GreeterServer/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..f8a8d521 --- /dev/null +++ b/examples/csharp/helloworld/GreeterServer/Properties/AssemblyInfo.cs @@ -0,0 +1,22 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. +[assembly: AssemblyTitle("GreeterServer")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("jtattermusch")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. +[assembly: AssemblyVersion("1.0.*")] +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + diff --git a/examples/csharp/helloworld/GreeterServer/packages.config b/examples/csharp/helloworld/GreeterServer/packages.config new file mode 100644 index 00000000..1273624c --- /dev/null +++ b/examples/csharp/helloworld/GreeterServer/packages.config @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/examples/csharp/helloworld/README.md b/examples/csharp/helloworld/README.md new file mode 100644 index 00000000..3719080f --- /dev/null +++ b/examples/csharp/helloworld/README.md @@ -0,0 +1,72 @@ +gRPC in 3 minutes (C#) +======================== + +BACKGROUND +------------- +For this sample, we've already generated the server and client stubs from `helloworld.proto`. +Example projects depend on NuGet packages `Grpc` and `Google.ProtocolBuffers` which have been already added to the project for you. + +PREREQUISITES +------------- +**Windows** +- .NET 4.5+ +- VS 2013 (with NuGet plugin installed) + +**Linux (Mono)** +- Mono +- Monodevelop 5.9 with NuGet Add-in installed (older versions might work) + +**MacOS (Mono)** +- Xamarin Studio (with NuGet plugin installed) + +BUILD +------- + +**Windows** +- Clone this repository. + +- Open solution `Greeter.sln` with Visual Studio + +- Build the solution (this will automatically download NuGet dependencies) + +**Linux (Mono)** +- Clone this repository. + +- Install gRPC C Core using instructions in https://github.com/grpc/homebrew-grpc + +- gRPC C# depends on native shared library `libgrpc_csharp_ext.so`. To make it visible + to Mono runtime, follow instructions in [Using gRPC C# on Linux](https://github.com/grpc/grpc/tree/master/src/csharp#usage-linux-mono) + +- Open solution `Greeter.sln` in MonoDevelop (you need to manually restore dependencies by using `mono nuget.exe restore` if you don't have NuGet add-in) + +- Build the solution. + +**MacOS (Mono)** +- See [Using gRPC C# on MacOS](https://github.com/grpc/grpc/tree/master/src/csharp#usage-macos-mono) for more info + on MacOS support. + +Try it! +------- + +- Run the server + + ``` + > cd GreeterServer/bin/Debug + > GreeterServer.exe + ``` + +- Run the client + + ``` + > cd GreeterClient/bin/Debug + > GreeterClient.exe + ``` + +You can also run the server and client directly from Visual Studio. + +On Linux or Mac, use `mono GreeterServer.exe` and `mono GreeterClient.exe` to run the server and client. + +Tutorial +-------- + +You can find a more detailed tutorial in [gRPC Basics: C#](route_guide/README.md) diff --git a/examples/csharp/helloworld/generate_protos.bat b/examples/csharp/helloworld/generate_protos.bat new file mode 100644 index 00000000..9a5a9d3e --- /dev/null +++ b/examples/csharp/helloworld/generate_protos.bat @@ -0,0 +1,10 @@ +@rem Generate the C# code for .proto files + +setlocal + +@rem enter this directory +cd /d %~dp0 + +packages\Google.Protobuf.3.0.0-alpha4\tools\protoc.exe -I../../protos --csharp_out Greeter ../../protos/helloworld.proto --grpc_out Greeter --plugin=protoc-gen-grpc=packages\Grpc.Tools.0.7.0\tools\grpc_csharp_plugin.exe + +endlocal \ No newline at end of file diff --git a/examples/csharp/route_guide/.gitignore b/examples/csharp/route_guide/.gitignore new file mode 100644 index 00000000..585000ea --- /dev/null +++ b/examples/csharp/route_guide/.gitignore @@ -0,0 +1,5 @@ +bin/ +obj/ +packages/ +*.suo +*.userprefs diff --git a/examples/csharp/route_guide/.nuget/packages.config b/examples/csharp/route_guide/.nuget/packages.config new file mode 100644 index 00000000..e2879c18 --- /dev/null +++ b/examples/csharp/route_guide/.nuget/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/examples/csharp/route_guide/README.md b/examples/csharp/route_guide/README.md new file mode 100644 index 00000000..155877e6 --- /dev/null +++ b/examples/csharp/route_guide/README.md @@ -0,0 +1,386 @@ +#gRPC Basics: C# # + +This tutorial provides a basic C# programmer's introduction to working with gRPC. By walking through this example you'll learn how to: + +- Define a service in a .proto file. +- Generate server and client code using the protocol buffer compiler. +- Use the C# gRPC API to write a simple client and server for your service. + +It assumes that you have read the [Getting started](https://github.com/grpc/grpc/tree/master/examples) guide and are familiar with [protocol buffers] (https://developers.google.com/protocol-buffers/docs/overview). Note that the example in this tutorial only uses the proto2 version of the protocol buffers language, as proto3 support for C# is not ready yet (see [protobuf C# README](https://github.com/google/protobuf/tree/master/csharp#proto2--proto3)). + +This isn't a comprehensive guide to using gRPC in C#: more reference documentation is coming soon. + +## Why use gRPC? + +Our example is a simple route mapping application that lets clients get information about features on their route, create a summary of their route, and exchange route information such as traffic updates with the server and other clients. + +With gRPC we can define our service once in a .proto file and implement clients and servers in any of gRPC's supported languages, which in turn can be run in environments ranging from servers inside Google to your own tablet - all the complexity of communication between different languages and environments is handled for you by gRPC. We also get all the advantages of working with protocol buffers, including efficient serialization, a simple IDL, and easy interface updating. + +## Example code and setup + +The example code for our tutorial is in [examples/csharp/route_guide](.). To download the example, clone this repository by running the following command: +```shell +$ git clone https://github.com/grpc/grpc.git +``` + +All the files for this tutorial are in the directory `examples/csharp/route_guide`. +Open the solution `examples/csharp/route_guide/RouteGuide.sln` from Visual Studio (or Monodevelop on Linux). + +On Windows, you should not need to do anything besides opening the solution. All the needed dependencies will be restored +for you automatically by the `Grpc` NuGet package upon building the solution. + +On Linux (or MacOS), you will first need to install protobuf and gRPC C Core using Linuxbrew (or Homebrew) tool in order to be +able to generate the server and client interface code and run the examples. Follow the instructions for [Linux](https://github.com/grpc/grpc/tree/master/src/csharp#usage-linux-mono) or [MacOS](https://github.com/grpc/grpc/tree/master/src/csharp#usage-macos-mono). + +## Defining the service + +Our first step (as you'll know from [Getting started](https://github.com/grpc/grpc/tree/master/examples)) is to define the gRPC *service* and the method *request* and *response* types using [protocol buffers] (https://developers.google.com/protocol-buffers/docs/overview). You can see the complete .proto file in [`RouteGuide/protos/route_guide.proto`](RouteGuide/protos/route_guide.proto). + +To define a service, you specify a named `service` in your .proto file: + +```protobuf +service RouteGuide { + ... +} +``` + +Then you define `rpc` methods inside your service definition, specifying their request and response types. gRPC lets you define four kinds of service method, all of which are used in the `RouteGuide` service: + +- A *simple RPC* where the client sends a request to the server using the stub and waits for a response to come back, just like a normal function call. +```protobuf + // Obtains the feature at a given position. + rpc GetFeature(Point) returns (Feature) {} +``` + +- A *server-side streaming RPC* where the client sends a request to the server and gets a stream to read a sequence of messages back. The client reads from the returned stream until there are no more messages. As you can see in our example, you specify a server-side streaming method by placing the `stream` keyword before the *response* type. +```protobuf + // Obtains the Features available within the given Rectangle. Results are + // streamed rather than returned at once (e.g. in a response message with a + // repeated field), as the rectangle may cover a large area and contain a + // huge number of features. + rpc ListFeatures(Rectangle) returns (stream Feature) {} +``` + +- A *client-side streaming RPC* where the client writes a sequence of messages and sends them to the server, again using a provided stream. Once the client has finished writing the messages, it waits for the server to read them all and return its response. You specify a server-side streaming method by placing the `stream` keyword before the *request* type. +```protobuf + // Accepts a stream of Points on a route being traversed, returning a + // RouteSummary when traversal is completed. + rpc RecordRoute(stream Point) returns (RouteSummary) {} +``` + +- A *bidirectional streaming RPC* where both sides send a sequence of messages using a read-write stream. The two streams operate independently, so clients and servers can read and write in whatever order they like: for example, the server could wait to receive all the client messages before writing its responses, or it could alternately read a message then write a message, or some other combination of reads and writes. The order of messages in each stream is preserved. You specify this type of method by placing the `stream` keyword before both the request and the response. +```protobuf + // Accepts a stream of RouteNotes sent while a route is being traversed, + // while receiving other RouteNotes (e.g. from other users). + rpc RouteChat(stream RouteNote) returns (stream RouteNote) {} +``` + +Our .proto file also contains protocol buffer message type definitions for all the request and response types used in our service methods - for example, here's the `Point` message type: +```protobuf +// Points are represented as latitude-longitude pairs in the E7 representation +// (degrees multiplied by 10**7 and rounded to the nearest integer). +// Latitudes should be in the range +/- 90 degrees and longitude should be in +// the range +/- 180 degrees (inclusive). +message Point { + int32 latitude = 1; + int32 longitude = 2; +} +``` + + +## Generating client and server code + +Next we need to generate the gRPC client and server interfaces from our .proto service definition. We do this using the protocol buffer compiler `protoc` with a special gRPC C# plugin. + +If you want to run this yourself, make sure you've installed protoc and gRPC C# plugin. The instructions vary based on your OS: +- For Windows, the `Grpc.Tools` and `Google.Protobuf` NuGet packages contain the binaries you will need to generate the code. +- For Linux, make sure you've [installed gRPC C Core using Linuxbrew](https://github.com/grpc/grpc/tree/master/src/csharp#usage-linux-mono) +- For MacOS, make sure you've [installed gRPC C Core using Homebrew](https://github.com/grpc/grpc/tree/master/src/csharp#usage-macos-mono) + +Once that's done, the following command can be used to generate the C# code. + +To generate the code on Windows, we use `protoc.exe` from the `Google.Protobuf` NuGet package and `grpc_csharp_plugin.exe` from the `Grpc.Tools` NuGet package (both under the `tools` directory). +Normally you would need to add the `Grpc.Tools` package to the solution yourself, but in this tutorial it has been already done for you. Following command should be run from the `csharp/route_guide` directory: +``` +> packages\Google.Protobuf.3.0.0-alpha4\tools\protoc -I RouteGuide/protos --csharp_out=RouteGuide --grpc_out=RouteGuide --plugin=protoc-gen-grpc=packages\Grpc.Tools.0.7.0\tools\grpc_csharp_plugin.exe RouteGuide/protos/route_guide.proto +``` + +On Linux/MacOS, we rely on `protoc` and `grpc_csharp_plugin` being installed by Linuxbrew/Homebrew. Run this command from the route_guide directory: +```shell +$ protoc -I RouteGuide/protos --csharp_out=RouteGuide --grpc_out=RouteGuide --plugin=protoc-gen-grpc=`which grpc_csharp_plugin` RouteGuide/protos/route_guide.proto +``` + +Running one of the previous commands regenerates the following files in the RouteGuide directory: +- `RouteGuide/RouteGuide.cs` defines a namespace `examples` + - This contains all the protocol buffer code to populate, serialize, and retrieve our request and response message types +- `RouteGuide/RouteGuideGrpc.cs`, provides stub and service classes + - an interface `RouteGuide.IRouteGuide` to inherit from when defining RouteGuide service implementations + - a class `RouteGuide.RouteGuideClient` that can be used to access remote RouteGuide instances + + + +## Creating the server + +First let's look at how we create a `RouteGuide` server. If you're only interested in creating gRPC clients, you can skip this section and go straight to [Creating the client](#client) (though you might find it interesting anyway!). + +There are two parts to making our `RouteGuide` service do its job: +- Implementing the service interface generated from our service definition: doing the actual "work" of our service. +- Running a gRPC server to listen for requests from clients and return the service responses. + +You can find our example `RouteGuide` server in [RouteGuideServer/RouteGuideImpl.cs](RouteGuideServer/RouteGuideServerImpl.cs). Let's take a closer look at how it works. + +### Implementing RouteGuide + +As you can see, our server has a `RouteGuideImpl` class that implements the generated `RouteGuide.IRouteGuide`: + +```csharp +// RouteGuideImpl provides an implementation of the RouteGuide service. +public class RouteGuideImpl : RouteGuide.IRouteGuide +``` + +#### Simple RPC + +`RouteGuideImpl` implements all our service methods. Let's look at the simplest type first, `GetFeature`, which just gets a `Point` from the client and returns the corresponding feature information from its database in a `Feature`. + +```csharp + public Task GetFeature(Point request, Grpc.Core.ServerCallContext context) + { + return Task.FromResult(CheckFeature(request)); + } +``` + +The method is passed a context for the RPC (which is empty in the alpha release), the client's `Point` protocol buffer request, and returns a `Feature` protocol buffer. In the method we create the `Feature` with the appropriate information, and then return it. To allow asynchronous +implementation, the method returns `Task` rather than just `Feature`. You are free to perform your computations synchronously and return +the result once you've finished, just as we do in the example. + +#### Server-side streaming RPC + +Now let's look at something a bit more complicated - a streaming RPC. `ListFeatures` is a server-side streaming RPC, so we need to send back multiple `Feature` protocol buffers to our client. + +```csharp + // in RouteGuideImpl + public async Task ListFeatures(Rectangle request, + Grpc.Core.IServerStreamWriter responseStream, + Grpc.Core.ServerCallContext context) + { + var responses = features.FindAll( (feature) => feature.Exists() && request.Contains(feature.Location) ); + foreach (var response in responses) + { + await responseStream.WriteAsync(response); + } + } +``` + +As you can see, here the request object is a `Rectangle` in which our client wants to find `Feature`s, but instead of returning a simple response we need to write responses to an asynchronous stream `IServerStreamWriter` using async method `WriteAsync`. + +#### Client-side streaming RPC + +Similarly, the client-side streaming method `RecordRoute` uses an [IAsyncEnumerator](https://github.com/Reactive-Extensions/Rx.NET/blob/master/Ix.NET/Source/System.Interactive.Async/IAsyncEnumerator.cs), to read the stream of requests using the async method `MoveNext` and the `Current` property. + +```csharp + public async Task RecordRoute(Grpc.Core.IAsyncStreamReader requestStream, + Grpc.Core.ServerCallContext context) + { + int pointCount = 0; + int featureCount = 0; + int distance = 0; + Point previous = null; + var stopwatch = new Stopwatch(); + stopwatch.Start(); + + while (await requestStream.MoveNext()) + { + var point = requestStream.Current; + pointCount++; + if (CheckFeature(point).Exists()) + { + featureCount++; + } + if (previous != null) + { + distance += (int) previous.GetDistance(point); + } + previous = point; + } + + stopwatch.Stop(); + + return new RouteSummary + { + PointCount = pointCount, + FeatureCount = featureCount, + Distance = distance, + ElapsedTime = (int)(stopwatch.ElapsedMilliseconds / 1000) + }; + } +``` + +#### Bidirectional streaming RPC + +Finally, let's look at our bidirectional streaming RPC `RouteChat`. + +```csharp + public async Task RouteChat(Grpc.Core.IAsyncStreamReader requestStream, + Grpc.Core.IServerStreamWriter responseStream, + Grpc.Core.ServerCallContext context,) + { + while (await requestStream.MoveNext()) + { + var note = requestStream.Current; + List prevNotes = AddNoteForLocation(note.Location, note); + foreach (var prevNote in prevNotes) + { + await responseStream.WriteAsync(prevNote); + } + } + } +``` + +Here the method receives both `requestStream` and `responseStream` arguments. Reading the requests is done the same way as in the client-side streaming method `RecordRoute`. Writing the responses is done the same way as in the server-side streaming method `ListFeatures`. + +### Starting the server + +Once we've implemented all our methods, we also need to start up a gRPC server so that clients can actually use our service. The following snippet shows how we do this for our `RouteGuide` service: + +```csharp +var features = RouteGuideUtil.ParseFeatures(RouteGuideUtil.DefaultFeaturesFile); + +Server server = new Server +{ + Services = { RouteGuide.BindService(new RouteGuideImpl(features)) }, + Ports = { new ServerPort("localhost", Port, ServerCredentials.Insecure) } +}; +server.Start(); + +Console.WriteLine("RouteGuide server listening on port " + port); +Console.WriteLine("Press any key to stop the server..."); +Console.ReadKey(); + +server.ShutdownAsync().Wait(); +``` +As you can see, we build and start our server using `Grpc.Core.Server` class. To do this, we: + +1. Create an instance of `Grpc.Core.Server`. +1. Create an instance of our service implementation class `RouteGuideImpl`. +3. Register our service implementation by adding its service definition to `Services` collection (We obtain the service definition from the generated `RouteGuide.BindService` method). +2. Specify the address and port we want to use to listen for client requests. This is done by adding `ServerPort` to `Ports` collection. +4. Call `Start` on the server instance to start an RPC server for our service. + + +## Creating the client + +In this section, we'll look at creating a C# client for our `RouteGuide` service. You can see our complete example client code in [RouteGuideClient/Program.cs](RouteGuideClient/Program.cs). + +### Creating a stub + +To call service methods, we first need to create a *stub*. + +First, we need to create a gRPC client channel that will connect to gRPC server. Then, we use the `RouteGuide.NewClient` method of the `RouteGuide` class generated from our .proto. + +```csharp +Channel channel = new Channel("127.0.0.1:50052", Credentials.Insecure) +var client = new RouteGuideClient(RouteGuide.NewClient(channel)); + +// YOUR CODE GOES HERE + +channel.ShutdownAsync().Wait(); +``` + +### Calling service methods + +Now let's look at how we call our service methods. gRPC C# provides asynchronous versions of each of the supported method types. For convenience, +gRPC C# also provides a synchronous method stub, but only for simple (single request/single response) RPCs. + +#### Simple RPC + +Calling the simple RPC `GetFeature` in a synchronous way is nearly as straightforward as calling a local method. + +```csharp +Point request = new Point { Latitude = 409146138, Longitude = -746188906 }; +Feature feature = client.GetFeature(request); +``` + +As you can see, we create and populate a request protocol buffer object (in our case `Point`), and call the desired method on the client object, passing it the request. If the RPC finishes with success, the response protocol buffer (in our case `Feature`) will be returned. Otherwise, an exception of type `RpcException` will be thrown, indicating the status code of the problem. + +Alternatively, if you are in async context, you can call an asynchronous version of the method (and use `await` keyword to await the result): +```csharp +Point request = new Point { Latitude = 409146138, Longitude = -746188906 }; +Feature feature = await client.GetFeatureAsync(request); +``` + +#### Streaming RPCs + +Now let's look at our streaming methods. If you've already read [Creating the server](#server) some of this may look very familiar - streaming RPCs are implemented in a similar way on both sides. The difference with respect to simple call is that the client methods return an instance of a call object, that provides access to request/response streams and/or asynchronous result (depending on the streaming type you are using). + +Here's where we call the server-side streaming method `ListFeatures`, which has property `ReponseStream` of type `IAsyncEnumerator` + +```csharp +using (var call = client.ListFeatures(request)) +{ + while (await call.ResponseStream.MoveNext()) + { + Feature feature = call.ResponseStream.Current; + Console.WriteLine("Received " + feature.ToString()); + } +} +``` + +The client-side streaming method `RecordRoute` is similar, except we use the property `RequestStream` to write the requests one by one using `WriteAsync` and eventually signal that no more request will be send using `CompleteAsync`. The method result can be obtained through the property +`ResponseAsync`. +```csharp +using (var call = client.RecordRoute()) +{ + foreach (var point in points) + { + await call.RequestStream.WriteAsync(point); + } + await call.RequestStream.CompleteAsync(); + + RouteSummary summary = await call.ResponseAsync; +} +``` + +Finally, let's look at our bidirectional streaming RPC `RouteChat`. In this case, we write the request to `RequestStream` and receive the responses from `ResponseStream`. As you can see from the example, the streams are independent of each other. + +```csharp + using (var call = client.RouteChat()) + { + var responseReaderTask = Task.Run(async () => + { + while (await call.ResponseStream.MoveNext()) + { + var note = call.ResponseStream.Current; + Console.WriteLine("Received " + note); + } + }); + + foreach (RouteNote request in requests) + { + await call.RequestStream.WriteAsync(request); + } + await call.RequestStream.CompleteAsync(); + await responseReaderTask; +} +``` + +## Try it out! + +Build client and server: + +Open the solution `examples/csharp/route_guide/RouteGuide.sln` from Visual Studio (or Monodevelop on Linux) and hit "Build". + +Run the server, which will listen on port 50052: +``` +> cd RouteGuideServer/bin/Debug +> RouteGuideServer.exe +``` + +Run the client (in a different terminal): +``` +> cd RouteGuideClient/bin/Debug +> RouteGuideClient.exe +``` + +You can also run the server and client directly from Visual Studio. + +On Linux or Mac, use `mono RouteGuideServer.exe` and `mono RouteGuideClient.exe` to run the server and client. diff --git a/examples/csharp/route_guide/RouteGuide.sln b/examples/csharp/route_guide/RouteGuide.sln new file mode 100644 index 00000000..0b79fdc5 --- /dev/null +++ b/examples/csharp/route_guide/RouteGuide.sln @@ -0,0 +1,39 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.31101.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RouteGuide", "RouteGuide\RouteGuide.csproj", "{49954D9C-5F17-4662-96B2-73BE833DD81A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RouteGuideClient", "RouteGuideClient\RouteGuideClient.csproj", "{D47BE663-4DE3-4206-B7A8-EA3FA066DADC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RouteGuideServer", "RouteGuideServer\RouteGuideServer.csproj", "{4B7C7794-BE24-4477-ACE7-18259EB73D27}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{2F6B184B-A576-4F21-AF2E-27E73D1FC96E}" + ProjectSection(SolutionItems) = preProject + .nuget\packages.config = .nuget\packages.config + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {49954D9C-5F17-4662-96B2-73BE833DD81A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {49954D9C-5F17-4662-96B2-73BE833DD81A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {49954D9C-5F17-4662-96B2-73BE833DD81A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {49954D9C-5F17-4662-96B2-73BE833DD81A}.Release|Any CPU.Build.0 = Release|Any CPU + {D47BE663-4DE3-4206-B7A8-EA3FA066DADC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D47BE663-4DE3-4206-B7A8-EA3FA066DADC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D47BE663-4DE3-4206-B7A8-EA3FA066DADC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D47BE663-4DE3-4206-B7A8-EA3FA066DADC}.Release|Any CPU.Build.0 = Release|Any CPU + {4B7C7794-BE24-4477-ACE7-18259EB73D27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4B7C7794-BE24-4477-ACE7-18259EB73D27}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4B7C7794-BE24-4477-ACE7-18259EB73D27}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4B7C7794-BE24-4477-ACE7-18259EB73D27}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/examples/csharp/route_guide/RouteGuide/Properties/AssemblyInfo.cs b/examples/csharp/route_guide/RouteGuide/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..9c3441e4 --- /dev/null +++ b/examples/csharp/route_guide/RouteGuide/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("RouteGuide")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("RouteGuide")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("ef6b85bc-ac27-46de-8714-a658236cc6fb")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/examples/csharp/route_guide/RouteGuide/RouteGuide.cs b/examples/csharp/route_guide/RouteGuide/RouteGuide.cs new file mode 100644 index 00000000..deb97e1b --- /dev/null +++ b/examples/csharp/route_guide/RouteGuide/RouteGuide.cs @@ -0,0 +1,776 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: route_guide.proto +#pragma warning disable 1591, 0612, 3021 +#region Designer generated code + +using pb = global::Google.Protobuf; +using pbc = global::Google.Protobuf.Collections; +using pbr = global::Google.Protobuf.Reflection; +using scg = global::System.Collections.Generic; +namespace Routeguide { + + namespace Proto { + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public static partial class RouteGuide { + + #region Descriptor + public static pbr::FileDescriptor Descriptor { + get { return descriptor; } + } + private static pbr::FileDescriptor descriptor; + + static RouteGuide() { + byte[] descriptorData = global::System.Convert.FromBase64String( + string.Concat( + "ChFyb3V0ZV9ndWlkZS5wcm90bxIKcm91dGVndWlkZSIsCgVQb2ludBIQCghs", + "YXRpdHVkZRgBIAEoBRIRCglsb25naXR1ZGUYAiABKAUiSQoJUmVjdGFuZ2xl", + "Eh0KAmxvGAEgASgLMhEucm91dGVndWlkZS5Qb2ludBIdCgJoaRgCIAEoCzIR", + "LnJvdXRlZ3VpZGUuUG9pbnQiPAoHRmVhdHVyZRIMCgRuYW1lGAEgASgJEiMK", + "CGxvY2F0aW9uGAIgASgLMhEucm91dGVndWlkZS5Qb2ludCJBCglSb3V0ZU5v", + "dGUSIwoIbG9jYXRpb24YASABKAsyES5yb3V0ZWd1aWRlLlBvaW50Eg8KB21l", + "c3NhZ2UYAiABKAkiYgoMUm91dGVTdW1tYXJ5EhMKC3BvaW50X2NvdW50GAEg", + "ASgFEhUKDWZlYXR1cmVfY291bnQYAiABKAUSEAoIZGlzdGFuY2UYAyABKAUS", + "FAoMZWxhcHNlZF90aW1lGAQgASgFMoUCCgpSb3V0ZUd1aWRlEjYKCkdldEZl", + "YXR1cmUSES5yb3V0ZWd1aWRlLlBvaW50GhMucm91dGVndWlkZS5GZWF0dXJl", + "IgASPgoMTGlzdEZlYXR1cmVzEhUucm91dGVndWlkZS5SZWN0YW5nbGUaEy5y", + "b3V0ZWd1aWRlLkZlYXR1cmUiADABEj4KC1JlY29yZFJvdXRlEhEucm91dGVn", + "dWlkZS5Qb2ludBoYLnJvdXRlZ3VpZGUuUm91dGVTdW1tYXJ5IgAoARI/CglS", + "b3V0ZUNoYXQSFS5yb3V0ZWd1aWRlLlJvdXRlTm90ZRoVLnJvdXRlZ3VpZGUu", + "Um91dGVOb3RlIgAoATABQg8KB2V4LmdycGOiAgNSVEdiBnByb3RvMw==")); + descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData, + new pbr::FileDescriptor[] { }, + new pbr::GeneratedCodeInfo(null, new pbr::GeneratedCodeInfo[] { + new pbr::GeneratedCodeInfo(typeof(global::Routeguide.Point), new[]{ "Latitude", "Longitude" }, null, null, null), + new pbr::GeneratedCodeInfo(typeof(global::Routeguide.Rectangle), new[]{ "Lo", "Hi" }, null, null, null), + new pbr::GeneratedCodeInfo(typeof(global::Routeguide.Feature), new[]{ "Name", "Location" }, null, null, null), + new pbr::GeneratedCodeInfo(typeof(global::Routeguide.RouteNote), new[]{ "Location", "Message" }, null, null, null), + new pbr::GeneratedCodeInfo(typeof(global::Routeguide.RouteSummary), new[]{ "PointCount", "FeatureCount", "Distance", "ElapsedTime" }, null, null, null) + })); + } + #endregion + + } + } + #region Messages + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class Point : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new Point()); + public static pb::MessageParser Parser { get { return _parser; } } + + public static pbr::MessageDescriptor Descriptor { + get { return global::Routeguide.Proto.RouteGuide.Descriptor.MessageTypes[0]; } + } + + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + public Point() { + OnConstruction(); + } + + partial void OnConstruction(); + + public Point(Point other) : this() { + latitude_ = other.latitude_; + longitude_ = other.longitude_; + } + + public Point Clone() { + return new Point(this); + } + + public const int LatitudeFieldNumber = 1; + private int latitude_; + public int Latitude { + get { return latitude_; } + set { + latitude_ = value; + } + } + + public const int LongitudeFieldNumber = 2; + private int longitude_; + public int Longitude { + get { return longitude_; } + set { + longitude_ = value; + } + } + + public override bool Equals(object other) { + return Equals(other as Point); + } + + public bool Equals(Point other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Latitude != other.Latitude) return false; + if (Longitude != other.Longitude) return false; + return true; + } + + public override int GetHashCode() { + int hash = 1; + if (Latitude != 0) hash ^= Latitude.GetHashCode(); + if (Longitude != 0) hash ^= Longitude.GetHashCode(); + return hash; + } + + public override string ToString() { + return pb::JsonFormatter.Default.Format(this); + } + + public void WriteTo(pb::CodedOutputStream output) { + if (Latitude != 0) { + output.WriteRawTag(8); + output.WriteInt32(Latitude); + } + if (Longitude != 0) { + output.WriteRawTag(16); + output.WriteInt32(Longitude); + } + } + + public int CalculateSize() { + int size = 0; + if (Latitude != 0) { + size += 1 + pb::CodedOutputStream.ComputeInt32Size(Latitude); + } + if (Longitude != 0) { + size += 1 + pb::CodedOutputStream.ComputeInt32Size(Longitude); + } + return size; + } + + public void MergeFrom(Point other) { + if (other == null) { + return; + } + if (other.Latitude != 0) { + Latitude = other.Latitude; + } + if (other.Longitude != 0) { + Longitude = other.Longitude; + } + } + + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + input.SkipLastField(); + break; + case 8: { + Latitude = input.ReadInt32(); + break; + } + case 16: { + Longitude = input.ReadInt32(); + break; + } + } + } + } + + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class Rectangle : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new Rectangle()); + public static pb::MessageParser Parser { get { return _parser; } } + + public static pbr::MessageDescriptor Descriptor { + get { return global::Routeguide.Proto.RouteGuide.Descriptor.MessageTypes[1]; } + } + + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + public Rectangle() { + OnConstruction(); + } + + partial void OnConstruction(); + + public Rectangle(Rectangle other) : this() { + Lo = other.lo_ != null ? other.Lo.Clone() : null; + Hi = other.hi_ != null ? other.Hi.Clone() : null; + } + + public Rectangle Clone() { + return new Rectangle(this); + } + + public const int LoFieldNumber = 1; + private global::Routeguide.Point lo_; + public global::Routeguide.Point Lo { + get { return lo_; } + set { + lo_ = value; + } + } + + public const int HiFieldNumber = 2; + private global::Routeguide.Point hi_; + public global::Routeguide.Point Hi { + get { return hi_; } + set { + hi_ = value; + } + } + + public override bool Equals(object other) { + return Equals(other as Rectangle); + } + + public bool Equals(Rectangle other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (!object.Equals(Lo, other.Lo)) return false; + if (!object.Equals(Hi, other.Hi)) return false; + return true; + } + + public override int GetHashCode() { + int hash = 1; + if (lo_ != null) hash ^= Lo.GetHashCode(); + if (hi_ != null) hash ^= Hi.GetHashCode(); + return hash; + } + + public override string ToString() { + return pb::JsonFormatter.Default.Format(this); + } + + public void WriteTo(pb::CodedOutputStream output) { + if (lo_ != null) { + output.WriteRawTag(10); + output.WriteMessage(Lo); + } + if (hi_ != null) { + output.WriteRawTag(18); + output.WriteMessage(Hi); + } + } + + public int CalculateSize() { + int size = 0; + if (lo_ != null) { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(Lo); + } + if (hi_ != null) { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(Hi); + } + return size; + } + + public void MergeFrom(Rectangle other) { + if (other == null) { + return; + } + if (other.lo_ != null) { + if (lo_ == null) { + lo_ = new global::Routeguide.Point(); + } + Lo.MergeFrom(other.Lo); + } + if (other.hi_ != null) { + if (hi_ == null) { + hi_ = new global::Routeguide.Point(); + } + Hi.MergeFrom(other.Hi); + } + } + + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + input.SkipLastField(); + break; + case 10: { + if (lo_ == null) { + lo_ = new global::Routeguide.Point(); + } + input.ReadMessage(lo_); + break; + } + case 18: { + if (hi_ == null) { + hi_ = new global::Routeguide.Point(); + } + input.ReadMessage(hi_); + break; + } + } + } + } + + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class Feature : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new Feature()); + public static pb::MessageParser Parser { get { return _parser; } } + + public static pbr::MessageDescriptor Descriptor { + get { return global::Routeguide.Proto.RouteGuide.Descriptor.MessageTypes[2]; } + } + + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + public Feature() { + OnConstruction(); + } + + partial void OnConstruction(); + + public Feature(Feature other) : this() { + name_ = other.name_; + Location = other.location_ != null ? other.Location.Clone() : null; + } + + public Feature Clone() { + return new Feature(this); + } + + public const int NameFieldNumber = 1; + private string name_ = ""; + public string Name { + get { return name_; } + set { + name_ = pb::Preconditions.CheckNotNull(value, "value"); + } + } + + public const int LocationFieldNumber = 2; + private global::Routeguide.Point location_; + public global::Routeguide.Point Location { + get { return location_; } + set { + location_ = value; + } + } + + public override bool Equals(object other) { + return Equals(other as Feature); + } + + public bool Equals(Feature other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Name != other.Name) return false; + if (!object.Equals(Location, other.Location)) return false; + return true; + } + + public override int GetHashCode() { + int hash = 1; + if (Name.Length != 0) hash ^= Name.GetHashCode(); + if (location_ != null) hash ^= Location.GetHashCode(); + return hash; + } + + public override string ToString() { + return pb::JsonFormatter.Default.Format(this); + } + + public void WriteTo(pb::CodedOutputStream output) { + if (Name.Length != 0) { + output.WriteRawTag(10); + output.WriteString(Name); + } + if (location_ != null) { + output.WriteRawTag(18); + output.WriteMessage(Location); + } + } + + public int CalculateSize() { + int size = 0; + if (Name.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Name); + } + if (location_ != null) { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(Location); + } + return size; + } + + public void MergeFrom(Feature other) { + if (other == null) { + return; + } + if (other.Name.Length != 0) { + Name = other.Name; + } + if (other.location_ != null) { + if (location_ == null) { + location_ = new global::Routeguide.Point(); + } + Location.MergeFrom(other.Location); + } + } + + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + input.SkipLastField(); + break; + case 10: { + Name = input.ReadString(); + break; + } + case 18: { + if (location_ == null) { + location_ = new global::Routeguide.Point(); + } + input.ReadMessage(location_); + break; + } + } + } + } + + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class RouteNote : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new RouteNote()); + public static pb::MessageParser Parser { get { return _parser; } } + + public static pbr::MessageDescriptor Descriptor { + get { return global::Routeguide.Proto.RouteGuide.Descriptor.MessageTypes[3]; } + } + + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + public RouteNote() { + OnConstruction(); + } + + partial void OnConstruction(); + + public RouteNote(RouteNote other) : this() { + Location = other.location_ != null ? other.Location.Clone() : null; + message_ = other.message_; + } + + public RouteNote Clone() { + return new RouteNote(this); + } + + public const int LocationFieldNumber = 1; + private global::Routeguide.Point location_; + public global::Routeguide.Point Location { + get { return location_; } + set { + location_ = value; + } + } + + public const int MessageFieldNumber = 2; + private string message_ = ""; + public string Message { + get { return message_; } + set { + message_ = pb::Preconditions.CheckNotNull(value, "value"); + } + } + + public override bool Equals(object other) { + return Equals(other as RouteNote); + } + + public bool Equals(RouteNote other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (!object.Equals(Location, other.Location)) return false; + if (Message != other.Message) return false; + return true; + } + + public override int GetHashCode() { + int hash = 1; + if (location_ != null) hash ^= Location.GetHashCode(); + if (Message.Length != 0) hash ^= Message.GetHashCode(); + return hash; + } + + public override string ToString() { + return pb::JsonFormatter.Default.Format(this); + } + + public void WriteTo(pb::CodedOutputStream output) { + if (location_ != null) { + output.WriteRawTag(10); + output.WriteMessage(Location); + } + if (Message.Length != 0) { + output.WriteRawTag(18); + output.WriteString(Message); + } + } + + public int CalculateSize() { + int size = 0; + if (location_ != null) { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(Location); + } + if (Message.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Message); + } + return size; + } + + public void MergeFrom(RouteNote other) { + if (other == null) { + return; + } + if (other.location_ != null) { + if (location_ == null) { + location_ = new global::Routeguide.Point(); + } + Location.MergeFrom(other.Location); + } + if (other.Message.Length != 0) { + Message = other.Message; + } + } + + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + input.SkipLastField(); + break; + case 10: { + if (location_ == null) { + location_ = new global::Routeguide.Point(); + } + input.ReadMessage(location_); + break; + } + case 18: { + Message = input.ReadString(); + break; + } + } + } + } + + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class RouteSummary : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new RouteSummary()); + public static pb::MessageParser Parser { get { return _parser; } } + + public static pbr::MessageDescriptor Descriptor { + get { return global::Routeguide.Proto.RouteGuide.Descriptor.MessageTypes[4]; } + } + + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + public RouteSummary() { + OnConstruction(); + } + + partial void OnConstruction(); + + public RouteSummary(RouteSummary other) : this() { + pointCount_ = other.pointCount_; + featureCount_ = other.featureCount_; + distance_ = other.distance_; + elapsedTime_ = other.elapsedTime_; + } + + public RouteSummary Clone() { + return new RouteSummary(this); + } + + public const int PointCountFieldNumber = 1; + private int pointCount_; + public int PointCount { + get { return pointCount_; } + set { + pointCount_ = value; + } + } + + public const int FeatureCountFieldNumber = 2; + private int featureCount_; + public int FeatureCount { + get { return featureCount_; } + set { + featureCount_ = value; + } + } + + public const int DistanceFieldNumber = 3; + private int distance_; + public int Distance { + get { return distance_; } + set { + distance_ = value; + } + } + + public const int ElapsedTimeFieldNumber = 4; + private int elapsedTime_; + public int ElapsedTime { + get { return elapsedTime_; } + set { + elapsedTime_ = value; + } + } + + public override bool Equals(object other) { + return Equals(other as RouteSummary); + } + + public bool Equals(RouteSummary other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (PointCount != other.PointCount) return false; + if (FeatureCount != other.FeatureCount) return false; + if (Distance != other.Distance) return false; + if (ElapsedTime != other.ElapsedTime) return false; + return true; + } + + public override int GetHashCode() { + int hash = 1; + if (PointCount != 0) hash ^= PointCount.GetHashCode(); + if (FeatureCount != 0) hash ^= FeatureCount.GetHashCode(); + if (Distance != 0) hash ^= Distance.GetHashCode(); + if (ElapsedTime != 0) hash ^= ElapsedTime.GetHashCode(); + return hash; + } + + public override string ToString() { + return pb::JsonFormatter.Default.Format(this); + } + + public void WriteTo(pb::CodedOutputStream output) { + if (PointCount != 0) { + output.WriteRawTag(8); + output.WriteInt32(PointCount); + } + if (FeatureCount != 0) { + output.WriteRawTag(16); + output.WriteInt32(FeatureCount); + } + if (Distance != 0) { + output.WriteRawTag(24); + output.WriteInt32(Distance); + } + if (ElapsedTime != 0) { + output.WriteRawTag(32); + output.WriteInt32(ElapsedTime); + } + } + + public int CalculateSize() { + int size = 0; + if (PointCount != 0) { + size += 1 + pb::CodedOutputStream.ComputeInt32Size(PointCount); + } + if (FeatureCount != 0) { + size += 1 + pb::CodedOutputStream.ComputeInt32Size(FeatureCount); + } + if (Distance != 0) { + size += 1 + pb::CodedOutputStream.ComputeInt32Size(Distance); + } + if (ElapsedTime != 0) { + size += 1 + pb::CodedOutputStream.ComputeInt32Size(ElapsedTime); + } + return size; + } + + public void MergeFrom(RouteSummary other) { + if (other == null) { + return; + } + if (other.PointCount != 0) { + PointCount = other.PointCount; + } + if (other.FeatureCount != 0) { + FeatureCount = other.FeatureCount; + } + if (other.Distance != 0) { + Distance = other.Distance; + } + if (other.ElapsedTime != 0) { + ElapsedTime = other.ElapsedTime; + } + } + + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + input.SkipLastField(); + break; + case 8: { + PointCount = input.ReadInt32(); + break; + } + case 16: { + FeatureCount = input.ReadInt32(); + break; + } + case 24: { + Distance = input.ReadInt32(); + break; + } + case 32: { + ElapsedTime = input.ReadInt32(); + break; + } + } + } + } + + } + + #endregion + +} + +#endregion Designer generated code diff --git a/examples/csharp/route_guide/RouteGuide/RouteGuide.csproj b/examples/csharp/route_guide/RouteGuide/RouteGuide.csproj new file mode 100644 index 00000000..302a783a --- /dev/null +++ b/examples/csharp/route_guide/RouteGuide/RouteGuide.csproj @@ -0,0 +1,93 @@ + + + + + + + + Debug + AnyCPU + {49954D9C-5F17-4662-96B2-73BE833DD81A} + Library + Properties + RouteGuide + RouteGuide + v4.5 + 512 + 443bbc38 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Google.Protobuf.3.0.0-alpha4\lib\portable-net45+netcore45+wpa81+wp8\Google.Protobuf.dll + + + False + ..\packages\Grpc.Core.0.7.0\lib\net45\Grpc.Core.dll + + + False + ..\packages\Newtonsoft.Json.7.0.1-beta2\lib\net45\Newtonsoft.Json.dll + + + + + ..\packages\Ix-Async.1.2.3\lib\net45\System.Interactive.Async.dll + + + + + + + + + + + + + + + + + PreserveNewest + + + + + + 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}. + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/csharp/route_guide/RouteGuide/RouteGuideGrpc.cs b/examples/csharp/route_guide/RouteGuide/RouteGuideGrpc.cs new file mode 100644 index 00000000..d60256ff --- /dev/null +++ b/examples/csharp/route_guide/RouteGuide/RouteGuideGrpc.cs @@ -0,0 +1,155 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: route_guide.proto +#region Designer generated code + +using System; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core; + +namespace Routeguide { + public static class RouteGuide + { + static readonly string __ServiceName = "routeguide.RouteGuide"; + + static readonly Marshaller __Marshaller_Point = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Routeguide.Point.Parser.ParseFrom); + static readonly Marshaller __Marshaller_Feature = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Routeguide.Feature.Parser.ParseFrom); + static readonly Marshaller __Marshaller_Rectangle = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Routeguide.Rectangle.Parser.ParseFrom); + static readonly Marshaller __Marshaller_RouteSummary = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Routeguide.RouteSummary.Parser.ParseFrom); + static readonly Marshaller __Marshaller_RouteNote = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Routeguide.RouteNote.Parser.ParseFrom); + + static readonly Method __Method_GetFeature = new Method( + MethodType.Unary, + __ServiceName, + "GetFeature", + __Marshaller_Point, + __Marshaller_Feature); + + static readonly Method __Method_ListFeatures = new Method( + MethodType.ServerStreaming, + __ServiceName, + "ListFeatures", + __Marshaller_Rectangle, + __Marshaller_Feature); + + static readonly Method __Method_RecordRoute = new Method( + MethodType.ClientStreaming, + __ServiceName, + "RecordRoute", + __Marshaller_Point, + __Marshaller_RouteSummary); + + static readonly Method __Method_RouteChat = new Method( + MethodType.DuplexStreaming, + __ServiceName, + "RouteChat", + __Marshaller_RouteNote, + __Marshaller_RouteNote); + + // service descriptor + public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor + { + get { return global::Routeguide.Proto.RouteGuide.Descriptor.Services[0]; } + } + + // client interface + public interface IRouteGuideClient + { + global::Routeguide.Feature GetFeature(global::Routeguide.Point request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + global::Routeguide.Feature GetFeature(global::Routeguide.Point request, CallOptions options); + AsyncUnaryCall GetFeatureAsync(global::Routeguide.Point request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + AsyncUnaryCall GetFeatureAsync(global::Routeguide.Point request, CallOptions options); + AsyncServerStreamingCall ListFeatures(global::Routeguide.Rectangle request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + AsyncServerStreamingCall ListFeatures(global::Routeguide.Rectangle request, CallOptions options); + AsyncClientStreamingCall RecordRoute(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + AsyncClientStreamingCall RecordRoute(CallOptions options); + AsyncDuplexStreamingCall RouteChat(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + AsyncDuplexStreamingCall RouteChat(CallOptions options); + } + + // server-side interface + public interface IRouteGuide + { + Task GetFeature(global::Routeguide.Point request, ServerCallContext context); + Task ListFeatures(global::Routeguide.Rectangle request, IServerStreamWriter responseStream, ServerCallContext context); + Task RecordRoute(IAsyncStreamReader requestStream, ServerCallContext context); + Task RouteChat(IAsyncStreamReader requestStream, IServerStreamWriter responseStream, ServerCallContext context); + } + + // client stub + public class RouteGuideClient : ClientBase, IRouteGuideClient + { + public RouteGuideClient(Channel channel) : base(channel) + { + } + public global::Routeguide.Feature GetFeature(global::Routeguide.Point request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var call = CreateCall(__Method_GetFeature, new CallOptions(headers, deadline, cancellationToken)); + return Calls.BlockingUnaryCall(call, request); + } + public global::Routeguide.Feature GetFeature(global::Routeguide.Point request, CallOptions options) + { + var call = CreateCall(__Method_GetFeature, options); + return Calls.BlockingUnaryCall(call, request); + } + public AsyncUnaryCall GetFeatureAsync(global::Routeguide.Point request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var call = CreateCall(__Method_GetFeature, new CallOptions(headers, deadline, cancellationToken)); + return Calls.AsyncUnaryCall(call, request); + } + public AsyncUnaryCall GetFeatureAsync(global::Routeguide.Point request, CallOptions options) + { + var call = CreateCall(__Method_GetFeature, options); + return Calls.AsyncUnaryCall(call, request); + } + public AsyncServerStreamingCall ListFeatures(global::Routeguide.Rectangle request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var call = CreateCall(__Method_ListFeatures, new CallOptions(headers, deadline, cancellationToken)); + return Calls.AsyncServerStreamingCall(call, request); + } + public AsyncServerStreamingCall ListFeatures(global::Routeguide.Rectangle request, CallOptions options) + { + var call = CreateCall(__Method_ListFeatures, options); + return Calls.AsyncServerStreamingCall(call, request); + } + public AsyncClientStreamingCall RecordRoute(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var call = CreateCall(__Method_RecordRoute, new CallOptions(headers, deadline, cancellationToken)); + return Calls.AsyncClientStreamingCall(call); + } + public AsyncClientStreamingCall RecordRoute(CallOptions options) + { + var call = CreateCall(__Method_RecordRoute, options); + return Calls.AsyncClientStreamingCall(call); + } + public AsyncDuplexStreamingCall RouteChat(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var call = CreateCall(__Method_RouteChat, new CallOptions(headers, deadline, cancellationToken)); + return Calls.AsyncDuplexStreamingCall(call); + } + public AsyncDuplexStreamingCall RouteChat(CallOptions options) + { + var call = CreateCall(__Method_RouteChat, options); + return Calls.AsyncDuplexStreamingCall(call); + } + } + + // creates service definition that can be registered with a server + public static ServerServiceDefinition BindService(IRouteGuide serviceImpl) + { + return ServerServiceDefinition.CreateBuilder(__ServiceName) + .AddMethod(__Method_GetFeature, serviceImpl.GetFeature) + .AddMethod(__Method_ListFeatures, serviceImpl.ListFeatures) + .AddMethod(__Method_RecordRoute, serviceImpl.RecordRoute) + .AddMethod(__Method_RouteChat, serviceImpl.RouteChat).Build(); + } + + // creates a new client + public static RouteGuideClient NewClient(Channel channel) + { + return new RouteGuideClient(channel); + } + + } +} +#endregion diff --git a/examples/csharp/route_guide/RouteGuide/RouteGuideUtil.cs b/examples/csharp/route_guide/RouteGuide/RouteGuideUtil.cs new file mode 100644 index 00000000..e898738c --- /dev/null +++ b/examples/csharp/route_guide/RouteGuide/RouteGuideUtil.cs @@ -0,0 +1,112 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Routeguide +{ + /// + /// Utility methods for the route guide example. + /// + public static class RouteGuideUtil + { + public const string DefaultFeaturesFile = "route_guide_db.json"; + + private const double CoordFactor = 1e7; + + /// + /// Indicates whether the given feature exists (i.e. has a valid name). + /// + public static bool Exists(this Feature feature) + { + return feature != null && (feature.Name.Length != 0); + } + + public static double GetLatitude(this Point point) + { + return point.Latitude / CoordFactor; + } + + public static double GetLongitude(this Point point) + { + return point.Longitude / CoordFactor; + } + + /// + /// Calculate the distance between two points using the "haversine" formula. + /// This code was taken from http://www.movable-type.co.uk/scripts/latlong.html. + /// + /// the starting point + /// the end point + /// the distance between the points in meters + public static double GetDistance(this Point start, Point end) + { + double lat1 = start.GetLatitude(); + double lat2 = end.GetLatitude(); + double lon1 = start.GetLongitude(); + double lon2 = end.GetLongitude(); + int r = 6371000; // metres + double phi1 = ToRadians(lat1); + double phi2 = ToRadians(lat2); + double deltaPhi = ToRadians(lat2 - lat1); + double deltaLambda = ToRadians(lon2 - lon1); + + double a = Math.Sin(deltaPhi / 2) * Math.Sin(deltaPhi / 2) + Math.Cos(phi1) * Math.Cos(phi2) * Math.Sin(deltaLambda / 2) * Math.Sin(deltaLambda / 2); + double c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a)); + + return r * c; + } + + /// + /// Returns true if rectangular area contains given point. + /// + public static bool Contains(this Rectangle rectangle, Point point) + { + int left = Math.Min(rectangle.Lo.Longitude, rectangle.Hi.Longitude); + int right = Math.Max(rectangle.Lo.Longitude, rectangle.Hi.Longitude); + int top = Math.Max(rectangle.Lo.Latitude, rectangle.Hi.Latitude); + int bottom = Math.Min(rectangle.Lo.Latitude, rectangle.Hi.Latitude); + return (point.Longitude >= left && point.Longitude <= right && point.Latitude >= bottom && point.Latitude <= top); + } + + private static double ToRadians(double val) + { + return (Math.PI / 180) * val; + } + + /// + /// Parses features from a JSON file. + /// + public static List ParseFeatures(string filename) + { + var features = new List(); + var jsonFeatures = JsonConvert.DeserializeObject>(File.ReadAllText(filename)); + + foreach(var jsonFeature in jsonFeatures) + { + features.Add(new Feature + { + Name = jsonFeature.name, + Location = new Point { Longitude = jsonFeature.location.longitude, Latitude = jsonFeature.location.latitude} + }); + } + return features; + } + + private class JsonFeature + { + public string name; + public JsonLocation location; + } + + private class JsonLocation + { + public int longitude; + public int latitude; + } + } +} diff --git a/examples/csharp/route_guide/RouteGuide/packages.config b/examples/csharp/route_guide/RouteGuide/packages.config new file mode 100644 index 00000000..8cd2ee75 --- /dev/null +++ b/examples/csharp/route_guide/RouteGuide/packages.config @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/examples/csharp/route_guide/RouteGuide/route_guide_db.json b/examples/csharp/route_guide/RouteGuide/route_guide_db.json new file mode 100644 index 00000000..209f0162 --- /dev/null +++ b/examples/csharp/route_guide/RouteGuide/route_guide_db.json @@ -0,0 +1,601 @@ +[{ + "location": { + "latitude": 407838351, + "longitude": -746143763 + }, + "name": "Patriots Path, Mendham, NJ 07945, USA" +}, { + "location": { + "latitude": 408122808, + "longitude": -743999179 + }, + "name": "101 New Jersey 10, Whippany, NJ 07981, USA" +}, { + "location": { + "latitude": 413628156, + "longitude": -749015468 + }, + "name": "U.S. 6, Shohola, PA 18458, USA" +}, { + "location": { + "latitude": 419999544, + "longitude": -740371136 + }, + "name": "5 Conners Road, Kingston, NY 12401, USA" +}, { + "location": { + "latitude": 414008389, + "longitude": -743951297 + }, + "name": "Mid Hudson Psychiatric Center, New Hampton, NY 10958, USA" +}, { + "location": { + "latitude": 419611318, + "longitude": -746524769 + }, + "name": "287 Flugertown Road, Livingston Manor, NY 12758, USA" +}, { + "location": { + "latitude": 406109563, + "longitude": -742186778 + }, + "name": "4001 Tremley Point Road, Linden, NJ 07036, USA" +}, { + "location": { + "latitude": 416802456, + "longitude": -742370183 + }, + "name": "352 South Mountain Road, Wallkill, NY 12589, USA" +}, { + "location": { + "latitude": 412950425, + "longitude": -741077389 + }, + "name": "Bailey Turn Road, Harriman, NY 10926, USA" +}, { + "location": { + "latitude": 412144655, + "longitude": -743949739 + }, + "name": "193-199 Wawayanda Road, Hewitt, NJ 07421, USA" +}, { + "location": { + "latitude": 415736605, + "longitude": -742847522 + }, + "name": "406-496 Ward Avenue, Pine Bush, NY 12566, USA" +}, { + "location": { + "latitude": 413843930, + "longitude": -740501726 + }, + "name": "162 Merrill Road, Highland Mills, NY 10930, USA" +}, { + "location": { + "latitude": 410873075, + "longitude": -744459023 + }, + "name": "Clinton Road, West Milford, NJ 07480, USA" +}, { + "location": { + "latitude": 412346009, + "longitude": -744026814 + }, + "name": "16 Old Brook Lane, Warwick, NY 10990, USA" +}, { + "location": { + "latitude": 402948455, + "longitude": -747903913 + }, + "name": "3 Drake Lane, Pennington, NJ 08534, USA" +}, { + "location": { + "latitude": 406337092, + "longitude": -740122226 + }, + "name": "6324 8th Avenue, Brooklyn, NY 11220, USA" +}, { + "location": { + "latitude": 406421967, + "longitude": -747727624 + }, + "name": "1 Merck Access Road, Whitehouse Station, NJ 08889, USA" +}, { + "location": { + "latitude": 416318082, + "longitude": -749677716 + }, + "name": "78-98 Schalck Road, Narrowsburg, NY 12764, USA" +}, { + "location": { + "latitude": 415301720, + "longitude": -748416257 + }, + "name": "282 Lakeview Drive Road, Highland Lake, NY 12743, USA" +}, { + "location": { + "latitude": 402647019, + "longitude": -747071791 + }, + "name": "330 Evelyn Avenue, Hamilton Township, NJ 08619, USA" +}, { + "location": { + "latitude": 412567807, + "longitude": -741058078 + }, + "name": "New York State Reference Route 987E, Southfields, NY 10975, USA" +}, { + "location": { + "latitude": 416855156, + "longitude": -744420597 + }, + "name": "103-271 Tempaloni Road, Ellenville, NY 12428, USA" +}, { + "location": { + "latitude": 404663628, + "longitude": -744820157 + }, + "name": "1300 Airport Road, North Brunswick Township, NJ 08902, USA" +}, { + "location": { + "latitude": 407113723, + "longitude": -749746483 + }, + "name": "" +}, { + "location": { + "latitude": 402133926, + "longitude": -743613249 + }, + "name": "" +}, { + "location": { + "latitude": 400273442, + "longitude": -741220915 + }, + "name": "" +}, { + "location": { + "latitude": 411236786, + "longitude": -744070769 + }, + "name": "" +}, { + "location": { + "latitude": 411633782, + "longitude": -746784970 + }, + "name": "211-225 Plains Road, Augusta, NJ 07822, USA" +}, { + "location": { + "latitude": 415830701, + "longitude": -742952812 + }, + "name": "" +}, { + "location": { + "latitude": 413447164, + "longitude": -748712898 + }, + "name": "165 Pedersen Ridge Road, Milford, PA 18337, USA" +}, { + "location": { + "latitude": 405047245, + "longitude": -749800722 + }, + "name": "100-122 Locktown Road, Frenchtown, NJ 08825, USA" +}, { + "location": { + "latitude": 418858923, + "longitude": -746156790 + }, + "name": "" +}, { + "location": { + "latitude": 417951888, + "longitude": -748484944 + }, + "name": "650-652 Willi Hill Road, Swan Lake, NY 12783, USA" +}, { + "location": { + "latitude": 407033786, + "longitude": -743977337 + }, + "name": "26 East 3rd Street, New Providence, NJ 07974, USA" +}, { + "location": { + "latitude": 417548014, + "longitude": -740075041 + }, + "name": "" +}, { + "location": { + "latitude": 410395868, + "longitude": -744972325 + }, + "name": "" +}, { + "location": { + "latitude": 404615353, + "longitude": -745129803 + }, + "name": "" +}, { + "location": { + "latitude": 406589790, + "longitude": -743560121 + }, + "name": "611 Lawrence Avenue, Westfield, NJ 07090, USA" +}, { + "location": { + "latitude": 414653148, + "longitude": -740477477 + }, + "name": "18 Lannis Avenue, New Windsor, NY 12553, USA" +}, { + "location": { + "latitude": 405957808, + "longitude": -743255336 + }, + "name": "82-104 Amherst Avenue, Colonia, NJ 07067, USA" +}, { + "location": { + "latitude": 411733589, + "longitude": -741648093 + }, + "name": "170 Seven Lakes Drive, Sloatsburg, NY 10974, USA" +}, { + "location": { + "latitude": 412676291, + "longitude": -742606606 + }, + "name": "1270 Lakes Road, Monroe, NY 10950, USA" +}, { + "location": { + "latitude": 409224445, + "longitude": -748286738 + }, + "name": "509-535 Alphano Road, Great Meadows, NJ 07838, USA" +}, { + "location": { + "latitude": 406523420, + "longitude": -742135517 + }, + "name": "652 Garden Street, Elizabeth, NJ 07202, USA" +}, { + "location": { + "latitude": 401827388, + "longitude": -740294537 + }, + "name": "349 Sea Spray Court, Neptune City, NJ 07753, USA" +}, { + "location": { + "latitude": 410564152, + "longitude": -743685054 + }, + "name": "13-17 Stanley Street, West Milford, NJ 07480, USA" +}, { + "location": { + "latitude": 408472324, + "longitude": -740726046 + }, + "name": "47 Industrial Avenue, Teterboro, NJ 07608, USA" +}, { + "location": { + "latitude": 412452168, + "longitude": -740214052 + }, + "name": "5 White Oak Lane, Stony Point, NY 10980, USA" +}, { + "location": { + "latitude": 409146138, + "longitude": -746188906 + }, + "name": "Berkshire Valley Management Area Trail, Jefferson, NJ, USA" +}, { + "location": { + "latitude": 404701380, + "longitude": -744781745 + }, + "name": "1007 Jersey Avenue, New Brunswick, NJ 08901, USA" +}, { + "location": { + "latitude": 409642566, + "longitude": -746017679 + }, + "name": "6 East Emerald Isle Drive, Lake Hopatcong, NJ 07849, USA" +}, { + "location": { + "latitude": 408031728, + "longitude": -748645385 + }, + "name": "1358-1474 New Jersey 57, Port Murray, NJ 07865, USA" +}, { + "location": { + "latitude": 413700272, + "longitude": -742135189 + }, + "name": "367 Prospect Road, Chester, NY 10918, USA" +}, { + "location": { + "latitude": 404310607, + "longitude": -740282632 + }, + "name": "10 Simon Lake Drive, Atlantic Highlands, NJ 07716, USA" +}, { + "location": { + "latitude": 409319800, + "longitude": -746201391 + }, + "name": "11 Ward Street, Mount Arlington, NJ 07856, USA" +}, { + "location": { + "latitude": 406685311, + "longitude": -742108603 + }, + "name": "300-398 Jefferson Avenue, Elizabeth, NJ 07201, USA" +}, { + "location": { + "latitude": 419018117, + "longitude": -749142781 + }, + "name": "43 Dreher Road, Roscoe, NY 12776, USA" +}, { + "location": { + "latitude": 412856162, + "longitude": -745148837 + }, + "name": "Swan Street, Pine Island, NY 10969, USA" +}, { + "location": { + "latitude": 416560744, + "longitude": -746721964 + }, + "name": "66 Pleasantview Avenue, Monticello, NY 12701, USA" +}, { + "location": { + "latitude": 405314270, + "longitude": -749836354 + }, + "name": "" +}, { + "location": { + "latitude": 414219548, + "longitude": -743327440 + }, + "name": "" +}, { + "location": { + "latitude": 415534177, + "longitude": -742900616 + }, + "name": "565 Winding Hills Road, Montgomery, NY 12549, USA" +}, { + "location": { + "latitude": 406898530, + "longitude": -749127080 + }, + "name": "231 Rocky Run Road, Glen Gardner, NJ 08826, USA" +}, { + "location": { + "latitude": 407586880, + "longitude": -741670168 + }, + "name": "100 Mount Pleasant Avenue, Newark, NJ 07104, USA" +}, { + "location": { + "latitude": 400106455, + "longitude": -742870190 + }, + "name": "517-521 Huntington Drive, Manchester Township, NJ 08759, USA" +}, { + "location": { + "latitude": 400066188, + "longitude": -746793294 + }, + "name": "" +}, { + "location": { + "latitude": 418803880, + "longitude": -744102673 + }, + "name": "40 Mountain Road, Napanoch, NY 12458, USA" +}, { + "location": { + "latitude": 414204288, + "longitude": -747895140 + }, + "name": "" +}, { + "location": { + "latitude": 414777405, + "longitude": -740615601 + }, + "name": "" +}, { + "location": { + "latitude": 415464475, + "longitude": -747175374 + }, + "name": "48 North Road, Forestburgh, NY 12777, USA" +}, { + "location": { + "latitude": 404062378, + "longitude": -746376177 + }, + "name": "" +}, { + "location": { + "latitude": 405688272, + "longitude": -749285130 + }, + "name": "" +}, { + "location": { + "latitude": 400342070, + "longitude": -748788996 + }, + "name": "" +}, { + "location": { + "latitude": 401809022, + "longitude": -744157964 + }, + "name": "" +}, { + "location": { + "latitude": 404226644, + "longitude": -740517141 + }, + "name": "9 Thompson Avenue, Leonardo, NJ 07737, USA" +}, { + "location": { + "latitude": 410322033, + "longitude": -747871659 + }, + "name": "" +}, { + "location": { + "latitude": 407100674, + "longitude": -747742727 + }, + "name": "" +}, { + "location": { + "latitude": 418811433, + "longitude": -741718005 + }, + "name": "213 Bush Road, Stone Ridge, NY 12484, USA" +}, { + "location": { + "latitude": 415034302, + "longitude": -743850945 + }, + "name": "" +}, { + "location": { + "latitude": 411349992, + "longitude": -743694161 + }, + "name": "" +}, { + "location": { + "latitude": 404839914, + "longitude": -744759616 + }, + "name": "1-17 Bergen Court, New Brunswick, NJ 08901, USA" +}, { + "location": { + "latitude": 414638017, + "longitude": -745957854 + }, + "name": "35 Oakland Valley Road, Cuddebackville, NY 12729, USA" +}, { + "location": { + "latitude": 412127800, + "longitude": -740173578 + }, + "name": "" +}, { + "location": { + "latitude": 401263460, + "longitude": -747964303 + }, + "name": "" +}, { + "location": { + "latitude": 412843391, + "longitude": -749086026 + }, + "name": "" +}, { + "location": { + "latitude": 418512773, + "longitude": -743067823 + }, + "name": "" +}, { + "location": { + "latitude": 404318328, + "longitude": -740835638 + }, + "name": "42-102 Main Street, Belford, NJ 07718, USA" +}, { + "location": { + "latitude": 419020746, + "longitude": -741172328 + }, + "name": "" +}, { + "location": { + "latitude": 404080723, + "longitude": -746119569 + }, + "name": "" +}, { + "location": { + "latitude": 401012643, + "longitude": -744035134 + }, + "name": "" +}, { + "location": { + "latitude": 404306372, + "longitude": -741079661 + }, + "name": "" +}, { + "location": { + "latitude": 403966326, + "longitude": -748519297 + }, + "name": "" +}, { + "location": { + "latitude": 405002031, + "longitude": -748407866 + }, + "name": "" +}, { + "location": { + "latitude": 409532885, + "longitude": -742200683 + }, + "name": "" +}, { + "location": { + "latitude": 416851321, + "longitude": -742674555 + }, + "name": "" +}, { + "location": { + "latitude": 406411633, + "longitude": -741722051 + }, + "name": "3387 Richmond Terrace, Staten Island, NY 10303, USA" +}, { + "location": { + "latitude": 413069058, + "longitude": -744597778 + }, + "name": "261 Van Sickle Road, Goshen, NY 10924, USA" +}, { + "location": { + "latitude": 418465462, + "longitude": -746859398 + }, + "name": "" +}, { + "location": { + "latitude": 411733222, + "longitude": -744228360 + }, + "name": "" +}, { + "location": { + "latitude": 410248224, + "longitude": -747127767 + }, + "name": "3 Hasta Way, Newton, NJ 07860, USA" +}] \ No newline at end of file diff --git a/examples/csharp/route_guide/RouteGuideClient/App.config b/examples/csharp/route_guide/RouteGuideClient/App.config new file mode 100644 index 00000000..8e156463 --- /dev/null +++ b/examples/csharp/route_guide/RouteGuideClient/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/examples/csharp/route_guide/RouteGuideClient/Program.cs b/examples/csharp/route_guide/RouteGuideClient/Program.cs new file mode 100644 index 00000000..be65fc38 --- /dev/null +++ b/examples/csharp/route_guide/RouteGuideClient/Program.cs @@ -0,0 +1,227 @@ +using Grpc.Core; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Routeguide +{ + class Program + { + /// + /// Sample client code that makes gRPC calls to the server. + /// + public class RouteGuideClient + { + readonly RouteGuide.IRouteGuideClient client; + + public RouteGuideClient(RouteGuide.IRouteGuideClient client) + { + this.client = client; + } + + /// + /// Blocking unary call example. Calls GetFeature and prints the response. + /// + public void GetFeature(int lat, int lon) + { + try + { + Log("*** GetFeature: lat={0} lon={1}", lat, lon); + + Point request = new Point { Latitude = lat, Longitude = lon }; + + Feature feature = client.GetFeature(request); + if (feature.Exists()) + { + Log("Found feature called \"{0}\" at {1}, {2}", + feature.Name, feature.Location.GetLatitude(), feature.Location.GetLongitude()); + } + else + { + Log("Found no feature at {0}, {1}", + feature.Location.GetLatitude(), feature.Location.GetLongitude()); + } + } + catch (RpcException e) + { + Log("RPC failed " + e); + throw; + } + } + + + /// + /// Server-streaming example. Calls listFeatures with a rectangle of interest. Prints each response feature as it arrives. + /// + public async Task ListFeatures(int lowLat, int lowLon, int hiLat, int hiLon) + { + try + { + Log("*** ListFeatures: lowLat={0} lowLon={1} hiLat={2} hiLon={3}", lowLat, lowLon, hiLat, + hiLon); + + Rectangle request = new Rectangle + { + Lo = new Point { Latitude = lowLat, Longitude = lowLon }, + Hi = new Point { Latitude = hiLat, Longitude = hiLon } + }; + + using (var call = client.ListFeatures(request)) + { + var responseStream = call.ResponseStream; + StringBuilder responseLog = new StringBuilder("Result: "); + + while (await responseStream.MoveNext()) + { + Feature feature = responseStream.Current; + responseLog.Append(feature.ToString()); + } + Log(responseLog.ToString()); + } + } + catch (RpcException e) + { + Log("RPC failed " + e); + throw; + } + } + + /// + /// Client-streaming example. Sends numPoints randomly chosen points from features + /// with a variable delay in between. Prints the statistics when they are sent from the server. + /// + public async Task RecordRoute(List features, int numPoints) + { + try + { + Log("*** RecordRoute"); + using (var call = client.RecordRoute()) + { + // Send numPoints points randomly selected from the features list. + StringBuilder numMsg = new StringBuilder(); + Random rand = new Random(); + for (int i = 0; i < numPoints; ++i) + { + int index = rand.Next(features.Count); + Point point = features[index].Location; + Log("Visiting point {0}, {1}", point.GetLatitude(), point.GetLongitude()); + + await call.RequestStream.WriteAsync(point); + + // A bit of delay before sending the next one. + await Task.Delay(rand.Next(1000) + 500); + } + await call.RequestStream.CompleteAsync(); + + RouteSummary summary = await call.ResponseAsync; + Log("Finished trip with {0} points. Passed {1} features. " + + "Travelled {2} meters. It took {3} seconds.", summary.PointCount, + summary.FeatureCount, summary.Distance, summary.ElapsedTime); + + Log("Finished RecordRoute"); + } + } + catch (RpcException e) + { + Log("RPC failed", e); + throw; + } + } + + /// + /// Bi-directional streaming example. Send some chat messages, and print any + /// chat messages that are sent from the server. + /// + public async Task RouteChat() + { + try + { + Log("*** RouteChat"); + var requests = new List + { + NewNote("First message", 0, 0), + NewNote("Second message", 0, 1), + NewNote("Third message", 1, 0), + NewNote("Fourth message", 0, 0) + }; + + using (var call = client.RouteChat()) + { + var responseReaderTask = Task.Run(async () => + { + while (await call.ResponseStream.MoveNext()) + { + var note = call.ResponseStream.Current; + Log("Got message \"{0}\" at {1}, {2}", note.Message, + note.Location.Latitude, note.Location.Longitude); + } + }); + + foreach (RouteNote request in requests) + { + Log("Sending message \"{0}\" at {1}, {2}", request.Message, + request.Location.Latitude, request.Location.Longitude); + + await call.RequestStream.WriteAsync(request); + } + await call.RequestStream.CompleteAsync(); + await responseReaderTask; + + Log("Finished RouteChat"); + } + } + catch (RpcException e) + { + Log("RPC failed", e); + throw; + } + } + + private void Log(string s, params object[] args) + { + Console.WriteLine(string.Format(s, args)); + } + + private void Log(string s) + { + Console.WriteLine(s); + } + + private RouteNote NewNote(string message, int lat, int lon) + { + return new RouteNote + { + Message = message, + Location = new Point { Latitude = lat, Longitude = lon } + }; + } + } + + static void Main(string[] args) + { + var channel = new Channel("127.0.0.1:50052", Credentials.Insecure); + var client = new RouteGuideClient(RouteGuide.NewClient(channel)); + + // Looking for a valid feature + client.GetFeature(409146138, -746188906); + + // Feature missing. + client.GetFeature(0, 0); + + // Looking for features between 40, -75 and 42, -73. + client.ListFeatures(400000000, -750000000, 420000000, -730000000).Wait(); + + // Record a few randomly selected points from the features file. + client.RecordRoute(RouteGuideUtil.ParseFeatures(RouteGuideUtil.DefaultFeaturesFile), 10).Wait(); + + // Send and receive some notes. + client.RouteChat().Wait(); + + channel.ShutdownAsync().Wait(); + Console.WriteLine("Press any key to exit..."); + Console.ReadKey(); + } + } +} diff --git a/examples/csharp/route_guide/RouteGuideClient/Properties/AssemblyInfo.cs b/examples/csharp/route_guide/RouteGuideClient/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..a17e164a --- /dev/null +++ b/examples/csharp/route_guide/RouteGuideClient/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("RouteGuideClient")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("RouteGuideClient")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("914644eb-47cd-4a37-9fba-5e62dd432333")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/examples/csharp/route_guide/RouteGuideClient/RouteGuideClient.csproj b/examples/csharp/route_guide/RouteGuideClient/RouteGuideClient.csproj new file mode 100644 index 00000000..cf9e567f --- /dev/null +++ b/examples/csharp/route_guide/RouteGuideClient/RouteGuideClient.csproj @@ -0,0 +1,93 @@ + + + + + + + + Debug + AnyCPU + {D47BE663-4DE3-4206-B7A8-EA3FA066DADC} + Exe + Properties + RouteGuideClient + RouteGuideClient + v4.5 + 512 + 77622de6 + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Google.Protobuf.3.0.0-alpha4\lib\portable-net45+netcore45+wpa81+wp8\Google.Protobuf.dll + + + False + ..\packages\Grpc.Core.0.7.0\lib\net45\Grpc.Core.dll + + + + + ..\packages\Ix-Async.1.2.3\lib\net45\System.Interactive.Async.dll + + + + + + + + + + + + + + + + + + {49954d9c-5f17-4662-96b2-73be833dd81a} + RouteGuide + + + + + + 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}. + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/csharp/route_guide/RouteGuideClient/packages.config b/examples/csharp/route_guide/RouteGuideClient/packages.config new file mode 100644 index 00000000..1273624c --- /dev/null +++ b/examples/csharp/route_guide/RouteGuideClient/packages.config @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/examples/csharp/route_guide/RouteGuideServer/App.config b/examples/csharp/route_guide/RouteGuideServer/App.config new file mode 100644 index 00000000..8e156463 --- /dev/null +++ b/examples/csharp/route_guide/RouteGuideServer/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/examples/csharp/route_guide/RouteGuideServer/Program.cs b/examples/csharp/route_guide/RouteGuideServer/Program.cs new file mode 100644 index 00000000..baced0b1 --- /dev/null +++ b/examples/csharp/route_guide/RouteGuideServer/Program.cs @@ -0,0 +1,32 @@ +using Grpc.Core; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Routeguide +{ + class Program + { + static void Main(string[] args) + { + const int Port = 50052; + + var features = RouteGuideUtil.ParseFeatures(RouteGuideUtil.DefaultFeaturesFile); + + Server server = new Server + { + Services = { RouteGuide.BindService(new RouteGuideImpl(features)) }, + Ports = { new ServerPort("localhost", Port, ServerCredentials.Insecure) } + }; + server.Start(); + + Console.WriteLine("RouteGuide server listening on port " + Port); + Console.WriteLine("Press any key to stop the server..."); + Console.ReadKey(); + + server.ShutdownAsync().Wait(); + } + } +} diff --git a/examples/csharp/route_guide/RouteGuideServer/Properties/AssemblyInfo.cs b/examples/csharp/route_guide/RouteGuideServer/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..a161b1d6 --- /dev/null +++ b/examples/csharp/route_guide/RouteGuideServer/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("RouteGuideServer")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("RouteGuideServer")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("908bdeef-05cc-42bf-9498-c4c573df8925")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/examples/csharp/route_guide/RouteGuideServer/RouteGuideImpl.cs b/examples/csharp/route_guide/RouteGuideServer/RouteGuideImpl.cs new file mode 100644 index 00000000..b8282943 --- /dev/null +++ b/examples/csharp/route_guide/RouteGuideServer/RouteGuideImpl.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Grpc.Core.Utils; + +namespace Routeguide +{ + /// + /// Example implementation of RouteGuide server. + /// + public class RouteGuideImpl : RouteGuide.IRouteGuide + { + readonly List features; + readonly object myLock = new object(); + readonly Dictionary> routeNotes = new Dictionary>(); + + public RouteGuideImpl(List features) + { + this.features = features; + } + + /// + /// Gets the feature at the requested point. If no feature at that location + /// exists, an unnammed feature is returned at the provided location. + /// + public Task GetFeature(Point request, Grpc.Core.ServerCallContext context) + { + return Task.FromResult(CheckFeature(request)); + } + + /// + /// Gets all features contained within the given bounding rectangle. + /// + public async Task ListFeatures(Rectangle request, Grpc.Core.IServerStreamWriter responseStream, Grpc.Core.ServerCallContext context) + { + var responses = features.FindAll( (feature) => feature.Exists() && request.Contains(feature.Location) ); + foreach (var response in responses) + { + await responseStream.WriteAsync(response); + } + } + + /// + /// Gets a stream of points, and responds with statistics about the "trip": number of points, + /// number of known features visited, total distance traveled, and total time spent. + /// + public async Task RecordRoute(Grpc.Core.IAsyncStreamReader requestStream, Grpc.Core.ServerCallContext context) + { + int pointCount = 0; + int featureCount = 0; + int distance = 0; + Point previous = null; + var stopwatch = new Stopwatch(); + stopwatch.Start(); + + while (await requestStream.MoveNext()) + { + var point = requestStream.Current; + pointCount++; + if (CheckFeature(point).Exists()) + { + featureCount++; + } + if (previous != null) + { + distance += (int) previous.GetDistance(point); + } + previous = point; + } + + stopwatch.Stop(); + + return new RouteSummary + { + PointCount = pointCount, + FeatureCount = featureCount, + Distance = distance, + ElapsedTime = (int)(stopwatch.ElapsedMilliseconds / 1000) + }; + } + + /// + /// Receives a stream of message/location pairs, and responds with a stream of all previous + /// messages at each of those locations. + /// + public async Task RouteChat(Grpc.Core.IAsyncStreamReader requestStream, Grpc.Core.IServerStreamWriter responseStream, Grpc.Core.ServerCallContext context) + { + while (await requestStream.MoveNext()) + { + var note = requestStream.Current; + List prevNotes = AddNoteForLocation(note.Location, note); + foreach (var prevNote in prevNotes) + { + await responseStream.WriteAsync(prevNote); + } + } + } + + /// + /// Adds a note for location and returns a list of pre-existing notes for that location (not containing the newly added note). + /// + private List AddNoteForLocation(Point location, RouteNote note) + { + lock (myLock) + { + List notes; + if (!routeNotes.TryGetValue(location, out notes)) { + notes = new List(); + routeNotes.Add(location, notes); + } + var preexistingNotes = new List(notes); + notes.Add(note); + return preexistingNotes; + } + } + + /// + /// Gets the feature at the given point. + /// + /// the location to check + /// The feature object at the point Note that an empty name indicates no feature. + private Feature CheckFeature(Point location) + { + var result = features.FirstOrDefault((feature) => feature.Location.Equals(location)); + if (result == null) + { + // No feature was found, return an unnamed feature. + return new Feature { Name = "", Location = location }; + } + return result; + } + } +} diff --git a/examples/csharp/route_guide/RouteGuideServer/RouteGuideServer.csproj b/examples/csharp/route_guide/RouteGuideServer/RouteGuideServer.csproj new file mode 100644 index 00000000..55e1331c --- /dev/null +++ b/examples/csharp/route_guide/RouteGuideServer/RouteGuideServer.csproj @@ -0,0 +1,94 @@ + + + + + + + + Debug + AnyCPU + {4B7C7794-BE24-4477-ACE7-18259EB73D27} + Exe + Properties + RouteGuideServer + RouteGuideServer + v4.5 + 512 + 568005e2 + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Google.Protobuf.3.0.0-alpha4\lib\portable-net45+netcore45+wpa81+wp8\Google.Protobuf.dll + + + False + ..\packages\Grpc.Core.0.7.0\lib\net45\Grpc.Core.dll + + + + + ..\packages\Ix-Async.1.2.3\lib\net45\System.Interactive.Async.dll + + + + + + + + + + + + + + + + + + + {49954d9c-5f17-4662-96b2-73be833dd81a} + RouteGuide + + + + + + 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}. + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/csharp/route_guide/RouteGuideServer/packages.config b/examples/csharp/route_guide/RouteGuideServer/packages.config new file mode 100644 index 00000000..1273624c --- /dev/null +++ b/examples/csharp/route_guide/RouteGuideServer/packages.config @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/examples/csharp/route_guide/generate_protos.bat b/examples/csharp/route_guide/generate_protos.bat new file mode 100644 index 00000000..fb0dc551 --- /dev/null +++ b/examples/csharp/route_guide/generate_protos.bat @@ -0,0 +1,10 @@ +@rem Generate the C# code for .proto files + +setlocal + +@rem enter this directory +cd /d %~dp0 + +packages\Google.Protobuf.3.0.0-alpha4\tools\protoc.exe -I../../protos --csharp_out RouteGuide ../../protos/route_guide.proto --grpc_out RouteGuide --plugin=protoc-gen-grpc=packages\Grpc.Tools.0.7.0\tools\grpc_csharp_plugin.exe + +endlocal \ No newline at end of file diff --git a/examples/node/.gitignore b/examples/node/.gitignore new file mode 100644 index 00000000..3d06f5db --- /dev/null +++ b/examples/node/.gitignore @@ -0,0 +1,3 @@ +*~ +node_modules +npm-debug.log \ No newline at end of file diff --git a/examples/node/README.md b/examples/node/README.md new file mode 100644 index 00000000..045fe51e --- /dev/null +++ b/examples/node/README.md @@ -0,0 +1,60 @@ +gRPC in 3 minutes (Node.js) +=========================== + +PREREQUISITES +------------- + +- `node`: This requires Node 10.x or greater. +- [homebrew][] on Mac OS X, [linuxbrew][] on Linux. These simplify the installation of the gRPC C core. + +INSTALL +------- + - On Mac OS X, install [homebrew][]. On Linux, install [linuxbrew][]. Run the following command to install gRPC Node.js. + + ```sh + $ curl -fsSL https://goo.gl/getgrpc | bash -s nodejs + ``` + This will download and run the [gRPC install script][], then install the latest version of gRPC Nodejs npm package. + - Clone this repository + + ```sh + $ git clone https://github.com/grpc/grpc.git + ``` + + - Install this package's dependencies + + ```sh + $ cd examples/node + $ npm install + ``` + +TRY IT! +------- + + - Run the server + + ```sh + $ # from this directory (grpc_common/node). + $ node ./greeter_server.js & + ``` + + - Run the client + + ```sh + $ # from this directory + $ node ./greeter_client.js + ``` + +NOTE +---- +This directory has a copy of `helloworld.proto` because it currently depends on +some Protocol Buffer 2.0 syntax that is deprecated in Protocol Buffer 3.0. + +TUTORIAL +-------- +You can find a more detailed tutorial in [gRPC Basics: Node.js][] + +[homebrew]:http://brew.sh +[linuxbrew]:https://github.com/Homebrew/linuxbrew#installation +[gRPC install script]:https://raw.githubusercontent.com/grpc/homebrew-grpc/master/scripts/install +[gRPC Basics: Node.js]:https://github.com/grpc/grpc/blob/master/examples/node/route_guide/README.md diff --git a/examples/node/greeter_client.js b/examples/node/greeter_client.js new file mode 100644 index 00000000..ddc8abbb --- /dev/null +++ b/examples/node/greeter_client.js @@ -0,0 +1,53 @@ +/* + * + * Copyright 2015, 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. + * + */ + +var PROTO_PATH = __dirname + '/helloworld.proto'; + +var grpc = require('grpc'); +var hello_proto = grpc.load(PROTO_PATH).helloworld; + +function main() { + var client = new hello_proto.Greeter('localhost:50051', + grpc.Credentials.createInsecure()); + var user; + if (process.argv.length >= 3) { + user = process.argv[2]; + } else { + user = 'world'; + } + client.sayHello({name: user}, function(err, response) { + console.log('Greeting:', response.message); + }); +} + +main(); diff --git a/examples/node/greeter_server.js b/examples/node/greeter_server.js new file mode 100644 index 00000000..44b44afa --- /dev/null +++ b/examples/node/greeter_server.js @@ -0,0 +1,57 @@ +/* + * + * Copyright 2015, 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. + * + */ + +var PROTO_PATH = __dirname + '/helloworld.proto'; + +var grpc = require('grpc'); +var hello_proto = grpc.load(PROTO_PATH).helloworld; + +/** + * Implements the SayHello RPC method. + */ +function sayHello(call, callback) { + callback(null, {message: 'Hello ' + call.request.name}); +} + +/** + * Starts an RPC server that receives requests for the Greeter service at the + * sample server port + */ +function main() { + var server = new grpc.Server(); + server.addProtoService(hello_proto.Greeter.service, {sayHello: sayHello}); + server.bind('0.0.0.0:50051', grpc.ServerCredentials.createInsecure()); + server.start(); +} + +main(); diff --git a/examples/node/helloworld.proto b/examples/node/helloworld.proto new file mode 100644 index 00000000..a52c947f --- /dev/null +++ b/examples/node/helloworld.proto @@ -0,0 +1,50 @@ +// Copyright 2015, 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. + +syntax = "proto3"; + +option java_package = "ex.grpc"; + +package helloworld; + +// The greeting service definition. +service Greeter { + // Sends a greeting + rpc SayHello (HelloRequest) returns (HelloReply) {} +} + +// The request message containing the user's name. +message HelloRequest { + optional string name = 1; +} + +// The response message containing the greetings +message HelloReply { + optional string message = 1; +} diff --git a/examples/node/package.json b/examples/node/package.json new file mode 100644 index 00000000..6c4f95b1 --- /dev/null +++ b/examples/node/package.json @@ -0,0 +1,10 @@ +{ + "name": "grpc-demo", + "version": "0.5.0", + "dependencies": { + "async": "^0.9.0", + "grpc": "~0.11.0", + "minimist": "^1.1.0", + "underscore": "^1.8.2" + } +} diff --git a/examples/node/route_guide/README.md b/examples/node/route_guide/README.md new file mode 100644 index 00000000..10486065 --- /dev/null +++ b/examples/node/route_guide/README.md @@ -0,0 +1,363 @@ +#gRPC Basics: Node.js + +This tutorial provides a basic Node.js programmer's introduction to working with gRPC. By walking through this example you'll learn how to: + +- Define a service in a .proto file. +- Use the Node.js gRPC API to write a simple client and server for your service. + +It assumes that you have read the [Getting started](https://github.com/grpc/grpc/tree/master/examples) guide and are familiar with [protocol buffers] (https://developers.google.com/protocol-buffers/docs/overview). Note that the example in this tutorial uses the proto3 version of the protocol buffers language, which is currently in alpha release:you can find out more in the [proto3 language guide](https://developers.google.com/protocol-buffers/docs/proto3) and see the [release notes](https://github.com/google/protobuf/releases) for the new version in the protocol buffers Github repository. + +This isn't a comprehensive guide to using gRPC in Node.js: more reference documentation is coming soon. + +## Why use gRPC? + +Our example is a simple route mapping application that lets clients get information about features on their route, create a summary of their route, and exchange route information such as traffic updates with the server and other clients. + +With gRPC we can define our service once in a .proto file and implement clients and servers in any of gRPC's supported languages, which in turn can be run in environments ranging from servers inside Google to your own tablet - all the complexity of communication between different languages and environments is handled for you by gRPC. We also get all the advantages of working with protocol buffers, including efficient serialization, a simple IDL, and easy interface updating. + +## Example code and setup + +The example code for our tutorial is in [examples/node/route_guide](.). To download the example, clone this repository by running the following command: +```shell +$ git clone https://github.com/grpc/grpc.git +``` + +Then change your current directory to `examples/node/route_guide`: +```shell +$ cd examples/node/route_guide +``` + +You also should have the relevant tools installed to generate the server and client interface code - if you don't already, follow the setup instructions in [the Node.js quick start guide](..). + + +## Defining the service + +Our first step (as you'll know from [Getting started](https://github.com/grpc/grpc/tree/master/examples)) is to define the gRPC *service* and the method *request* and *response* types using [protocol buffers] (https://developers.google.com/protocol-buffers/docs/overview). You can see the complete .proto file in [`examples/protos/route_guide.proto`](../../route_guide.proto). + +To define a service, you specify a named `service` in your .proto file: + +```protobuf +service RouteGuide { + ... +} +``` + +Then you define `rpc` methods inside your service definition, specifying their request and response types. gRPC lets you define four kinds of service method, all of which are used in the `RouteGuide` service: + +- A *simple RPC* where the client sends a request to the server using the stub and waits for a response to come back, just like a normal function call. +```protobuf + // Obtains the feature at a given position. + rpc GetFeature(Point) returns (Feature) {} +``` + +- A *server-side streaming RPC* where the client sends a request to the server and gets a stream to read a sequence of messages back. The client reads from the returned stream until there are no more messages. As you can see in our example, you specify a server-side streaming method by placing the `stream` keyword before the *response* type. +```protobuf + // Obtains the Features available within the given Rectangle. Results are + // streamed rather than returned at once (e.g. in a response message with a + // repeated field), as the rectangle may cover a large area and contain a + // huge number of features. + rpc ListFeatures(Rectangle) returns (stream Feature) {} +``` + +- A *client-side streaming RPC* where the client writes a sequence of messages and sends them to the server, again using a provided stream. Once the client has finished writing the messages, it waits for the server to read them all and return its response. You specify a server-side streaming method by placing the `stream` keyword before the *request* type. +```protobuf + // Accepts a stream of Points on a route being traversed, returning a + // RouteSummary when traversal is completed. + rpc RecordRoute(stream Point) returns (RouteSummary) {} +``` + +- A *bidirectional streaming RPC* where both sides send a sequence of messages using a read-write stream. The two streams operate independently, so clients and servers can read and write in whatever order they like: for example, the server could wait to receive all the client messages before writing its responses, or it could alternately read a message then write a message, or some other combination of reads and writes. The order of messages in each stream is preserved. You specify this type of method by placing the `stream` keyword before both the request and the response. +```protobuf + // Accepts a stream of RouteNotes sent while a route is being traversed, + // while receiving other RouteNotes (e.g. from other users). + rpc RouteChat(stream RouteNote) returns (stream RouteNote) {} +``` + +Our .proto file also contains protocol buffer message type definitions for all the request and response types used in our service methods - for example, here's the `Point` message type: +```protobuf +// Points are represented as latitude-longitude pairs in the E7 representation +// (degrees multiplied by 10**7 and rounded to the nearest integer). +// Latitudes should be in the range +/- 90 degrees and longitude should be in +// the range +/- 180 degrees (inclusive). +message Point { + int32 latitude = 1; + int32 longitude = 2; +} +``` + + +## Loading service descriptors from proto files + +The Node.js library dynamically generates service descriptors and client stub definitions from `.proto` files loaded at runtime. + +To load a `.proto` file, simply `require` the gRPC library, then use its `load()` method: + +```node +var grpc = require('grpc'); +var protoDescriptor = grpc.load(__dirname + '/route_guide.proto'); +// The protoDescriptor object has the full package hierarchy +var example = protoDescriptor.routeguide; +``` + +Once you've done this, the stub constructor is in the `routeguide` namespace (`protoDescriptor.routeguide.RouteGuide`) and the service descriptor (which is used to create a server) is a property of the stub (`protoDescriptor.routeguide.RouteGuide.service`); + + +## Creating the server + +First let's look at how we create a `RouteGuide` server. If you're only interested in creating gRPC clients, you can skip this section and go straight to [Creating the client](#client) (though you might find it interesting anyway!). + +There are two parts to making our `RouteGuide` service do its job: +- Implementing the service interface generated from our service definition: doing the actual "work" of our service. +- Running a gRPC server to listen for requests from clients and return the service responses. + +You can find our example `RouteGuide` server in [route_guide_server.js](route_guide_server.js). Let's take a closer look at how it works. + +### Implementing RouteGuide + +As you can see, our server has a `Server` constructor generated from the `RouteGuide.service` descriptor object + +```node +var Server = grpc.buildServer([routeguide.RouteGuide.service]); +``` +In this case we're implementing the *asynchronous* version of `RouteGuide`, which provides our default gRPC server behaviour. + +The functions in `route_guide_server.js` implement all our service methods. Let's look at the simplest type first, `getFeature`, which just gets a `Point` from the client and returns the corresponding feature information from its database in a `Feature`. + +```node +function checkFeature(point) { + var feature; + // Check if there is already a feature object for the given point + for (var i = 0; i < feature_list.length; i++) { + feature = feature_list[i]; + if (feature.location.latitude === point.latitude && + feature.location.longitude === point.longitude) { + return feature; + } + } + var name = ''; + feature = { + name: name, + location: point + }; + return feature; +} +function getFeature(call, callback) { + callback(null, checkFeature(call.request)); +} +``` + +The method is passed a call object for the RPC, which has the `Point` parameter as a property, and a callback to which we can pass our returned `Feature`. In the method body we populate a `Feature` corresponding to the given point and pass it to the callback, with a null first parameter to indicate that there is no error. + +Now let's look at something a bit more complicated - a streaming RPC. `listFeatures` is a server-side streaming RPC, so we need to send back multiple `Feature`s to our client. + +```node +function listFeatures(call) { + var lo = call.request.lo; + var hi = call.request.hi; + var left = _.min([lo.longitude, hi.longitude]); + var right = _.max([lo.longitude, hi.longitude]); + var top = _.max([lo.latitude, hi.latitude]); + var bottom = _.min([lo.latitude, hi.latitude]); + // For each feature, check if it is in the given bounding box + _.each(feature_list, function(feature) { + if (feature.name === '') { + return; + } + if (feature.location.longitude >= left && + feature.location.longitude <= right && + feature.location.latitude >= bottom && + feature.location.latitude <= top) { + call.write(feature); + } + }); + call.end(); +} +``` + +As you can see, instead of getting the call object and callback in our method parameters, this time we get a `call` object that implements the `Writable` interface. In the method, we create as many `Feature` objects as we need to return, writing them to the `call` using its `write()` method. Finally, we call `call.end()` to indicate that we have sent all messages. + +If you look at the client-side streaming method `RecordRoute` you'll see it's quite similar to the unary call, except this time the `call` parameter implements the `Reader` interface. The `call`'s `'data'` event fires every time there is new data, and the `'end'` event fires when all data has been read. Like the unary case, we respond by calling the callback + +```node +call.on('data', function(point) { + // Process user data +}); +call.on('end', function() { + callback(null, result); +}); +``` + +Finally, let's look at our bidirectional streaming RPC `RouteChat()`. + +```node +function routeChat(call) { + call.on('data', function(note) { + var key = pointKey(note.location); + /* For each note sent, respond with all previous notes that correspond to + * the same point */ + if (route_notes.hasOwnProperty(key)) { + _.each(route_notes[key], function(note) { + call.write(note); + }); + } else { + route_notes[key] = []; + } + // Then add the new note to the list + route_notes[key].push(JSON.parse(JSON.stringify(note))); + }); + call.on('end', function() { + call.end(); + }); +} +``` + +This time we get a `call` implementing `Duplex` that can be used to read *and* write messages. The syntax for reading and writing here is exactly the same as for our client-streaming and server-streaming methods. Although each side will always get the other's messages in the order they were written, both the client and server can read and write in any order — the streams operate completely independently. + +### Starting the server + +Once we've implemented all our methods, we also need to start up a gRPC server so that clients can actually use our service. The following snippet shows how we do this for our `RouteGuide` service: + +```node +function getServer() { + var server = new grpc.Server(); + server.addProtoService(routeguide.RouteGuide.service, { + getFeature: getFeature, + listFeatures: listFeatures, + recordRoute: recordRoute, + routeChat: routeChat + }); + return server; +} +var routeServer = getServer(); +routeServer.bind('0.0.0.0:50051', grpc.ServerCredentials.createInsecure()); +routeServer.start(); +``` + +As you can see, we build and start our server with the following steps: + + 1. Create a `Server` constructor from the `RouteGuide` service descriptor. + 2. Implement the service methods. + 3. Create an instance of the server by calling the `Server` constructor with the method implementations. + 4. Specify the address and port we want to use to listen for client requests using the instance's `bind()` method. + 5. Call `listen()` on the instance to start the RPC server. + + +## Creating the client + +In this section, we'll look at creating a Node.js client for our `RouteGuide` service. You can see our complete example client code in [route_guide_client.js](route_guide_client.js). + +### Creating a stub + +To call service methods, we first need to create a *stub*. To do this, we just need to call the RouteGuide stub constructor, specifying the server address and port. + +```node +var client = new routeguide.RouteGuide('localhost:50051', + grpc.Credentials.createInsecure()); +``` + +### Calling service methods + +Now let's look at how we call our service methods. Note that all of these methods are asynchronous: they use either events or callbacks to retrieve results. + +#### Simple RPC + +Calling the simple RPC `GetFeature` is nearly as straightforward as calling a local asynchronous method. + +```node +var point = {latitude: 409146138, longitude: -746188906}; +stub.getFeature(point, function(err, feature) { + if (err) { + // process error + } else { + // process feature + } +}); +``` + +As you can see, we create and populate a request object. Finally, we call the method on the stub, passing it the request and callback. If there is no error, then we can read the response information from the server from our response object. + +```node + console.log('Found feature called "' + feature.name + '" at ' + + feature.location.latitude/COORD_FACTOR + ', ' + + feature.location.longitude/COORD_FACTOR); +``` + +#### Streaming RPCs + +Now let's look at our streaming methods. If you've already read [Creating the server](#server) some of this may look very familiar - streaming RPCs are implemented in a similar way on both sides. Here's where we call the server-side streaming method `ListFeatures`, which returns a stream of geographical `Feature`s: + +```node +var call = client.listFeatures(rectangle); + call.on('data', function(feature) { + console.log('Found feature called "' + feature.name + '" at ' + + feature.location.latitude/COORD_FACTOR + ', ' + + feature.location.longitude/COORD_FACTOR); + }); + call.on('end', function() { + // The server has finished sending + }); + call.on('status', function(status) { + // process status + }); +``` + +Instead of passing the method a request and callback, we pass it a request and get a `Readable` stream object back. The client can use the `Readable`'s `'data'` event to read the server's responses. This event fires with each `Feature` message object until there are no more messages: the `'end'` event indicates that the call is done. Finally, the status event fires when the server sends the status. + +The client-side streaming method `RecordRoute` is similar, except there we pass the method a callback and get back a `Writable`. + +```node + var call = client.recordRoute(function(error, stats) { + if (error) { + callback(error); + } + console.log('Finished trip with', stats.point_count, 'points'); + console.log('Passed', stats.feature_count, 'features'); + console.log('Travelled', stats.distance, 'meters'); + console.log('It took', stats.elapsed_time, 'seconds'); + }); + function pointSender(lat, lng) { + return function(callback) { + console.log('Visiting point ' + lat/COORD_FACTOR + ', ' + + lng/COORD_FACTOR); + call.write({ + latitude: lat, + longitude: lng + }); + _.delay(callback, _.random(500, 1500)); + }; + } + var point_senders = []; + for (var i = 0; i < num_points; i++) { + var rand_point = feature_list[_.random(0, feature_list.length - 1)]; + point_senders[i] = pointSender(rand_point.location.latitude, + rand_point.location.longitude); + } + async.series(point_senders, function() { + call.end(); + }); +``` + +Once we've finished writing our client's requests to the stream using `write()`, we need to call `end()` on the stream to let gRPC know that we've finished writing. If the status is `OK`, the `stats` object will be populated with the server's response. + +Finally, let's look at our bidirectional streaming RPC `routeChat()`. In this case, we just pass a context to the method and get back a `Duplex` stream object, which we can use to both write and read messages. + +```node +var call = client.routeChat(); +``` + +The syntax for reading and writing here is exactly the same as for our client-streaming and server-streaming methods. Although each side will always get the other's messages in the order they were written, both the client and server can read and write in any order — the streams operate completely independently. + +## Try it out! + +Build client and server: +```shell +$ npm install +``` +Run the server, which will listen on port 50051: +```shell +$ node ./route_guide_server.js --db_path=route_guide_db.json +``` +Run the client (in a different terminal): +```shell +$ node ./route_guide_client.js --db_path=route_guide_db.json +``` diff --git a/examples/node/route_guide/route_guide.proto b/examples/node/route_guide/route_guide.proto new file mode 100644 index 00000000..38daa933 --- /dev/null +++ b/examples/node/route_guide/route_guide.proto @@ -0,0 +1,120 @@ +// Copyright 2015, 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. + +syntax = "proto3"; + +option java_package = "io.grpc.routeguide"; + +package routeguide; + +// Interface exported by the server. +service RouteGuide { + // A simple RPC. + // + // Obtains the feature at a given position. + rpc GetFeature(Point) returns (Feature) {} + + // A server-to-client streaming RPC. + // + // Obtains the Features available within the given Rectangle. Results are + // streamed rather than returned at once (e.g. in a response message with a + // repeated field), as the rectangle may cover a large area and contain a + // huge number of features. + rpc ListFeatures(Rectangle) returns (stream Feature) {} + + // A client-to-server streaming RPC. + // + // Accepts a stream of Points on a route being traversed, returning a + // RouteSummary when traversal is completed. + rpc RecordRoute(stream Point) returns (RouteSummary) {} + + // A Bidirectional streaming RPC. + // + // Accepts a stream of RouteNotes sent while a route is being traversed, + // while receiving other RouteNotes (e.g. from other users). + rpc RouteChat(stream RouteNote) returns (stream RouteNote) {} +} + +// Points are represented as latitude-longitude pairs in the E7 representation +// (degrees multiplied by 10**7 and rounded to the nearest integer). +// Latitudes should be in the range +/- 90 degrees and longitude should be in +// the range +/- 180 degrees (inclusive). +message Point { + optional int32 latitude = 1; + optional int32 longitude = 2; +} + +// A latitude-longitude rectangle, represented as two diagonally opposite +// points "lo" and "hi". +message Rectangle { + // One corner of the rectangle. + optional Point lo = 1; + + // The other corner of the rectangle. + optional Point hi = 2; +} + +// A feature names something at a given point. +// +// If a feature could not be named, the name is empty. +message Feature { + // The name of the feature. + optional string name = 1; + + // The point where the feature is detected. + optional Point location = 2; +} + +// A RouteNote is a message sent while at a given point. +message RouteNote { + // The location from which the message is sent. + optional Point location = 1; + + // The message to be sent. + optional string message = 2; +} + +// A RouteSummary is received in response to a RecordRoute rpc. +// +// It contains the number of individual points received, the number of +// detected features, and the total distance covered as the cumulative sum of +// the distance between each point. +message RouteSummary { + // The number of points received. + optional int32 point_count = 1; + + // The number of known features passed while traversing the route. + optional int32 feature_count = 2; + + // The distance covered in metres. + optional int32 distance = 3; + + // The duration of the traversal in seconds. + optional int32 elapsed_time = 4; +} diff --git a/examples/node/route_guide/route_guide_client.js b/examples/node/route_guide/route_guide_client.js new file mode 100644 index 00000000..1da6c690 --- /dev/null +++ b/examples/node/route_guide/route_guide_client.js @@ -0,0 +1,232 @@ +// Copyright 2015, 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. + +var async = require('async'); +var fs = require('fs'); +var parseArgs = require('minimist'); +var path = require('path'); +var _ = require('underscore'); +var grpc = require('grpc'); +var routeguide = grpc.load(__dirname + '/route_guide.proto').routeguide; +var client = new routeguide.RouteGuide('localhost:50051', + grpc.Credentials.createInsecure()); + +var COORD_FACTOR = 1e7; + +/** + * Run the getFeature demo. Calls getFeature with a point known to have a + * feature and a point known not to have a feature. + * @param {function} callback Called when this demo is complete + */ +function runGetFeature(callback) { + var next = _.after(2, callback); + function featureCallback(error, feature) { + if (error) { + callback(error); + } + if (feature.name === '') { + console.log('Found no feature at ' + + feature.location.latitude/COORD_FACTOR + ', ' + + feature.location.longitude/COORD_FACTOR); + } else { + console.log('Found feature called "' + feature.name + '" at ' + + feature.location.latitude/COORD_FACTOR + ', ' + + feature.location.longitude/COORD_FACTOR); + } + next(); + } + var point1 = { + latitude: 409146138, + longitude: -746188906 + }; + var point2 = { + latitude: 0, + longitude: 0 + }; + client.getFeature(point1, featureCallback); + client.getFeature(point2, featureCallback); +} + +/** + * Run the listFeatures demo. Calls listFeatures with a rectangle containing all + * of the features in the pre-generated database. Prints each response as it + * comes in. + * @param {function} callback Called when this demo is complete + */ +function runListFeatures(callback) { + var rectangle = { + lo: { + latitude: 400000000, + longitude: -750000000 + }, + hi: { + latitude: 420000000, + longitude: -730000000 + } + }; + console.log('Looking for features between 40, -75 and 42, -73'); + var call = client.listFeatures(rectangle); + call.on('data', function(feature) { + console.log('Found feature called "' + feature.name + '" at ' + + feature.location.latitude/COORD_FACTOR + ', ' + + feature.location.longitude/COORD_FACTOR); + }); + call.on('end', callback); +} + +/** + * Run the recordRoute demo. Sends several randomly chosen points from the + * pre-generated feature database with a variable delay in between. Prints the + * statistics when they are sent from the server. + * @param {function} callback Called when this demo is complete + */ +function runRecordRoute(callback) { + var argv = parseArgs(process.argv, { + string: 'db_path' + }); + fs.readFile(path.resolve(argv.db_path), function(err, data) { + if (err) callback(err); + var feature_list = JSON.parse(data); + + var num_points = 10; + var call = client.recordRoute(function(error, stats) { + if (error) { + callback(error); + } + console.log('Finished trip with', stats.point_count, 'points'); + console.log('Passed', stats.feature_count, 'features'); + console.log('Travelled', stats.distance, 'meters'); + console.log('It took', stats.elapsed_time, 'seconds'); + callback(); + }); + /** + * Constructs a function that asynchronously sends the given point and then + * delays sending its callback + * @param {number} lat The latitude to send + * @param {number} lng The longitude to send + * @return {function(function)} The function that sends the point + */ + function pointSender(lat, lng) { + /** + * Sends the point, then calls the callback after a delay + * @param {function} callback Called when complete + */ + return function(callback) { + console.log('Visiting point ' + lat/COORD_FACTOR + ', ' + + lng/COORD_FACTOR); + call.write({ + latitude: lat, + longitude: lng + }); + _.delay(callback, _.random(500, 1500)); + }; + } + var point_senders = []; + for (var i = 0; i < num_points; i++) { + var rand_point = feature_list[_.random(0, feature_list.length - 1)]; + point_senders[i] = pointSender(rand_point.location.latitude, + rand_point.location.longitude); + } + async.series(point_senders, function() { + call.end(); + }); + }); +} + +/** + * Run the routeChat demo. Send some chat messages, and print any chat messages + * that are sent from the server. + * @param {function} callback Called when the demo is complete + */ +function runRouteChat(callback) { + var call = client.routeChat(); + call.on('data', function(note) { + console.log('Got message "' + note.message + '" at ' + + note.location.latitude + ', ' + note.location.longitude); + }); + + call.on('end', callback); + + var notes = [{ + location: { + latitude: 0, + longitude: 0 + }, + message: 'First message' + }, { + location: { + latitude: 0, + longitude: 1 + }, + message: 'Second message' + }, { + location: { + latitude: 1, + longitude: 0 + }, + message: 'Third message' + }, { + location: { + latitude: 0, + longitude: 0 + }, + message: 'Fourth message' + }]; + for (var i = 0; i < notes.length; i++) { + var note = notes[i]; + console.log('Sending message "' + note.message + '" at ' + + note.location.latitude + ', ' + note.location.longitude); + call.write(note); + } + call.end(); +} + +/** + * Run all of the demos in order + */ +function main() { + async.series([ + runGetFeature, + runListFeatures, + runRecordRoute, + runRouteChat + ]); +} + +if (require.main === module) { + main(); +} + +exports.runGetFeature = runGetFeature; + +exports.runListFeatures = runListFeatures; + +exports.runRecordRoute = runRecordRoute; + +exports.runRouteChat = runRouteChat; diff --git a/examples/node/route_guide/route_guide_db.json b/examples/node/route_guide/route_guide_db.json new file mode 100644 index 00000000..9d6a980a --- /dev/null +++ b/examples/node/route_guide/route_guide_db.json @@ -0,0 +1,601 @@ +[{ + "location": { + "latitude": 407838351, + "longitude": -746143763 + }, + "name": "Patriots Path, Mendham, NJ 07945, USA" +}, { + "location": { + "latitude": 408122808, + "longitude": -743999179 + }, + "name": "101 New Jersey 10, Whippany, NJ 07981, USA" +}, { + "location": { + "latitude": 413628156, + "longitude": -749015468 + }, + "name": "U.S. 6, Shohola, PA 18458, USA" +}, { + "location": { + "latitude": 419999544, + "longitude": -740371136 + }, + "name": "5 Conners Road, Kingston, NY 12401, USA" +}, { + "location": { + "latitude": 414008389, + "longitude": -743951297 + }, + "name": "Mid Hudson Psychiatric Center, New Hampton, NY 10958, USA" +}, { + "location": { + "latitude": 419611318, + "longitude": -746524769 + }, + "name": "287 Flugertown Road, Livingston Manor, NY 12758, USA" +}, { + "location": { + "latitude": 406109563, + "longitude": -742186778 + }, + "name": "4001 Tremley Point Road, Linden, NJ 07036, USA" +}, { + "location": { + "latitude": 416802456, + "longitude": -742370183 + }, + "name": "352 South Mountain Road, Wallkill, NY 12589, USA" +}, { + "location": { + "latitude": 412950425, + "longitude": -741077389 + }, + "name": "Bailey Turn Road, Harriman, NY 10926, USA" +}, { + "location": { + "latitude": 412144655, + "longitude": -743949739 + }, + "name": "193-199 Wawayanda Road, Hewitt, NJ 07421, USA" +}, { + "location": { + "latitude": 415736605, + "longitude": -742847522 + }, + "name": "406-496 Ward Avenue, Pine Bush, NY 12566, USA" +}, { + "location": { + "latitude": 413843930, + "longitude": -740501726 + }, + "name": "162 Merrill Road, Highland Mills, NY 10930, USA" +}, { + "location": { + "latitude": 410873075, + "longitude": -744459023 + }, + "name": "Clinton Road, West Milford, NJ 07480, USA" +}, { + "location": { + "latitude": 412346009, + "longitude": -744026814 + }, + "name": "16 Old Brook Lane, Warwick, NY 10990, USA" +}, { + "location": { + "latitude": 402948455, + "longitude": -747903913 + }, + "name": "3 Drake Lane, Pennington, NJ 08534, USA" +}, { + "location": { + "latitude": 406337092, + "longitude": -740122226 + }, + "name": "6324 8th Avenue, Brooklyn, NY 11220, USA" +}, { + "location": { + "latitude": 406421967, + "longitude": -747727624 + }, + "name": "1 Merck Access Road, Whitehouse Station, NJ 08889, USA" +}, { + "location": { + "latitude": 416318082, + "longitude": -749677716 + }, + "name": "78-98 Schalck Road, Narrowsburg, NY 12764, USA" +}, { + "location": { + "latitude": 415301720, + "longitude": -748416257 + }, + "name": "282 Lakeview Drive Road, Highland Lake, NY 12743, USA" +}, { + "location": { + "latitude": 402647019, + "longitude": -747071791 + }, + "name": "330 Evelyn Avenue, Hamilton Township, NJ 08619, USA" +}, { + "location": { + "latitude": 412567807, + "longitude": -741058078 + }, + "name": "New York State Reference Route 987E, Southfields, NY 10975, USA" +}, { + "location": { + "latitude": 416855156, + "longitude": -744420597 + }, + "name": "103-271 Tempaloni Road, Ellenville, NY 12428, USA" +}, { + "location": { + "latitude": 404663628, + "longitude": -744820157 + }, + "name": "1300 Airport Road, North Brunswick Township, NJ 08902, USA" +}, { + "location": { + "latitude": 407113723, + "longitude": -749746483 + }, + "name": "" +}, { + "location": { + "latitude": 402133926, + "longitude": -743613249 + }, + "name": "" +}, { + "location": { + "latitude": 400273442, + "longitude": -741220915 + }, + "name": "" +}, { + "location": { + "latitude": 411236786, + "longitude": -744070769 + }, + "name": "" +}, { + "location": { + "latitude": 411633782, + "longitude": -746784970 + }, + "name": "211-225 Plains Road, Augusta, NJ 07822, USA" +}, { + "location": { + "latitude": 415830701, + "longitude": -742952812 + }, + "name": "" +}, { + "location": { + "latitude": 413447164, + "longitude": -748712898 + }, + "name": "165 Pedersen Ridge Road, Milford, PA 18337, USA" +}, { + "location": { + "latitude": 405047245, + "longitude": -749800722 + }, + "name": "100-122 Locktown Road, Frenchtown, NJ 08825, USA" +}, { + "location": { + "latitude": 418858923, + "longitude": -746156790 + }, + "name": "" +}, { + "location": { + "latitude": 417951888, + "longitude": -748484944 + }, + "name": "650-652 Willi Hill Road, Swan Lake, NY 12783, USA" +}, { + "location": { + "latitude": 407033786, + "longitude": -743977337 + }, + "name": "26 East 3rd Street, New Providence, NJ 07974, USA" +}, { + "location": { + "latitude": 417548014, + "longitude": -740075041 + }, + "name": "" +}, { + "location": { + "latitude": 410395868, + "longitude": -744972325 + }, + "name": "" +}, { + "location": { + "latitude": 404615353, + "longitude": -745129803 + }, + "name": "" +}, { + "location": { + "latitude": 406589790, + "longitude": -743560121 + }, + "name": "611 Lawrence Avenue, Westfield, NJ 07090, USA" +}, { + "location": { + "latitude": 414653148, + "longitude": -740477477 + }, + "name": "18 Lannis Avenue, New Windsor, NY 12553, USA" +}, { + "location": { + "latitude": 405957808, + "longitude": -743255336 + }, + "name": "82-104 Amherst Avenue, Colonia, NJ 07067, USA" +}, { + "location": { + "latitude": 411733589, + "longitude": -741648093 + }, + "name": "170 Seven Lakes Drive, Sloatsburg, NY 10974, USA" +}, { + "location": { + "latitude": 412676291, + "longitude": -742606606 + }, + "name": "1270 Lakes Road, Monroe, NY 10950, USA" +}, { + "location": { + "latitude": 409224445, + "longitude": -748286738 + }, + "name": "509-535 Alphano Road, Great Meadows, NJ 07838, USA" +}, { + "location": { + "latitude": 406523420, + "longitude": -742135517 + }, + "name": "652 Garden Street, Elizabeth, NJ 07202, USA" +}, { + "location": { + "latitude": 401827388, + "longitude": -740294537 + }, + "name": "349 Sea Spray Court, Neptune City, NJ 07753, USA" +}, { + "location": { + "latitude": 410564152, + "longitude": -743685054 + }, + "name": "13-17 Stanley Street, West Milford, NJ 07480, USA" +}, { + "location": { + "latitude": 408472324, + "longitude": -740726046 + }, + "name": "47 Industrial Avenue, Teterboro, NJ 07608, USA" +}, { + "location": { + "latitude": 412452168, + "longitude": -740214052 + }, + "name": "5 White Oak Lane, Stony Point, NY 10980, USA" +}, { + "location": { + "latitude": 409146138, + "longitude": -746188906 + }, + "name": "Berkshire Valley Management Area Trail, Jefferson, NJ, USA" +}, { + "location": { + "latitude": 404701380, + "longitude": -744781745 + }, + "name": "1007 Jersey Avenue, New Brunswick, NJ 08901, USA" +}, { + "location": { + "latitude": 409642566, + "longitude": -746017679 + }, + "name": "6 East Emerald Isle Drive, Lake Hopatcong, NJ 07849, USA" +}, { + "location": { + "latitude": 408031728, + "longitude": -748645385 + }, + "name": "1358-1474 New Jersey 57, Port Murray, NJ 07865, USA" +}, { + "location": { + "latitude": 413700272, + "longitude": -742135189 + }, + "name": "367 Prospect Road, Chester, NY 10918, USA" +}, { + "location": { + "latitude": 404310607, + "longitude": -740282632 + }, + "name": "10 Simon Lake Drive, Atlantic Highlands, NJ 07716, USA" +}, { + "location": { + "latitude": 409319800, + "longitude": -746201391 + }, + "name": "11 Ward Street, Mount Arlington, NJ 07856, USA" +}, { + "location": { + "latitude": 406685311, + "longitude": -742108603 + }, + "name": "300-398 Jefferson Avenue, Elizabeth, NJ 07201, USA" +}, { + "location": { + "latitude": 419018117, + "longitude": -749142781 + }, + "name": "43 Dreher Road, Roscoe, NY 12776, USA" +}, { + "location": { + "latitude": 412856162, + "longitude": -745148837 + }, + "name": "Swan Street, Pine Island, NY 10969, USA" +}, { + "location": { + "latitude": 416560744, + "longitude": -746721964 + }, + "name": "66 Pleasantview Avenue, Monticello, NY 12701, USA" +}, { + "location": { + "latitude": 405314270, + "longitude": -749836354 + }, + "name": "" +}, { + "location": { + "latitude": 414219548, + "longitude": -743327440 + }, + "name": "" +}, { + "location": { + "latitude": 415534177, + "longitude": -742900616 + }, + "name": "565 Winding Hills Road, Montgomery, NY 12549, USA" +}, { + "location": { + "latitude": 406898530, + "longitude": -749127080 + }, + "name": "231 Rocky Run Road, Glen Gardner, NJ 08826, USA" +}, { + "location": { + "latitude": 407586880, + "longitude": -741670168 + }, + "name": "100 Mount Pleasant Avenue, Newark, NJ 07104, USA" +}, { + "location": { + "latitude": 400106455, + "longitude": -742870190 + }, + "name": "517-521 Huntington Drive, Manchester Township, NJ 08759, USA" +}, { + "location": { + "latitude": 400066188, + "longitude": -746793294 + }, + "name": "" +}, { + "location": { + "latitude": 418803880, + "longitude": -744102673 + }, + "name": "40 Mountain Road, Napanoch, NY 12458, USA" +}, { + "location": { + "latitude": 414204288, + "longitude": -747895140 + }, + "name": "" +}, { + "location": { + "latitude": 414777405, + "longitude": -740615601 + }, + "name": "" +}, { + "location": { + "latitude": 415464475, + "longitude": -747175374 + }, + "name": "48 North Road, Forestburgh, NY 12777, USA" +}, { + "location": { + "latitude": 404062378, + "longitude": -746376177 + }, + "name": "" +}, { + "location": { + "latitude": 405688272, + "longitude": -749285130 + }, + "name": "" +}, { + "location": { + "latitude": 400342070, + "longitude": -748788996 + }, + "name": "" +}, { + "location": { + "latitude": 401809022, + "longitude": -744157964 + }, + "name": "" +}, { + "location": { + "latitude": 404226644, + "longitude": -740517141 + }, + "name": "9 Thompson Avenue, Leonardo, NJ 07737, USA" +}, { + "location": { + "latitude": 410322033, + "longitude": -747871659 + }, + "name": "" +}, { + "location": { + "latitude": 407100674, + "longitude": -747742727 + }, + "name": "" +}, { + "location": { + "latitude": 418811433, + "longitude": -741718005 + }, + "name": "213 Bush Road, Stone Ridge, NY 12484, USA" +}, { + "location": { + "latitude": 415034302, + "longitude": -743850945 + }, + "name": "" +}, { + "location": { + "latitude": 411349992, + "longitude": -743694161 + }, + "name": "" +}, { + "location": { + "latitude": 404839914, + "longitude": -744759616 + }, + "name": "1-17 Bergen Court, New Brunswick, NJ 08901, USA" +}, { + "location": { + "latitude": 414638017, + "longitude": -745957854 + }, + "name": "35 Oakland Valley Road, Cuddebackville, NY 12729, USA" +}, { + "location": { + "latitude": 412127800, + "longitude": -740173578 + }, + "name": "" +}, { + "location": { + "latitude": 401263460, + "longitude": -747964303 + }, + "name": "" +}, { + "location": { + "latitude": 412843391, + "longitude": -749086026 + }, + "name": "" +}, { + "location": { + "latitude": 418512773, + "longitude": -743067823 + }, + "name": "" +}, { + "location": { + "latitude": 404318328, + "longitude": -740835638 + }, + "name": "42-102 Main Street, Belford, NJ 07718, USA" +}, { + "location": { + "latitude": 419020746, + "longitude": -741172328 + }, + "name": "" +}, { + "location": { + "latitude": 404080723, + "longitude": -746119569 + }, + "name": "" +}, { + "location": { + "latitude": 401012643, + "longitude": -744035134 + }, + "name": "" +}, { + "location": { + "latitude": 404306372, + "longitude": -741079661 + }, + "name": "" +}, { + "location": { + "latitude": 403966326, + "longitude": -748519297 + }, + "name": "" +}, { + "location": { + "latitude": 405002031, + "longitude": -748407866 + }, + "name": "" +}, { + "location": { + "latitude": 409532885, + "longitude": -742200683 + }, + "name": "" +}, { + "location": { + "latitude": 416851321, + "longitude": -742674555 + }, + "name": "" +}, { + "location": { + "latitude": 406411633, + "longitude": -741722051 + }, + "name": "3387 Richmond Terrace, Staten Island, NY 10303, USA" +}, { + "location": { + "latitude": 413069058, + "longitude": -744597778 + }, + "name": "261 Van Sickle Road, Goshen, NY 10924, USA" +}, { + "location": { + "latitude": 418465462, + "longitude": -746859398 + }, + "name": "" +}, { + "location": { + "latitude": 411733222, + "longitude": -744228360 + }, + "name": "" +}, { + "location": { + "latitude": 410248224, + "longitude": -747127767 + }, + "name": "3 Hasta Way, Newton, NJ 07860, USA" +}] diff --git a/examples/node/route_guide/route_guide_server.js b/examples/node/route_guide/route_guide_server.js new file mode 100644 index 00000000..faae3087 --- /dev/null +++ b/examples/node/route_guide/route_guide_server.js @@ -0,0 +1,247 @@ +// Copyright 2015, 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. + +var fs = require('fs'); +var parseArgs = require('minimist'); +var path = require('path'); +var _ = require('underscore'); +var grpc = require('grpc'); +var routeguide = grpc.load(__dirname + '/route_guide.proto').routeguide; + +var COORD_FACTOR = 1e7; + +/** + * For simplicity, a point is a record type that looks like + * {latitude: number, longitude: number}, and a feature is a record type that + * looks like {name: string, location: point}. feature objects with name==='' + * are points with no feature. + */ + +/** + * List of feature objects at points that have been requested so far. + */ +var feature_list = []; + +/** + * Get a feature object at the given point, or creates one if it does not exist. + * @param {point} point The point to check + * @return {feature} The feature object at the point. Note that an empty name + * indicates no feature + */ +function checkFeature(point) { + var feature; + // Check if there is already a feature object for the given point + for (var i = 0; i < feature_list.length; i++) { + feature = feature_list[i]; + if (feature.location.latitude === point.latitude && + feature.location.longitude === point.longitude) { + return feature; + } + } + var name = ''; + feature = { + name: name, + location: point + }; + return feature; +} + +/** + * getFeature request handler. Gets a request with a point, and responds with a + * feature object indicating whether there is a feature at that point. + * @param {EventEmitter} call Call object for the handler to process + * @param {function(Error, feature)} callback Response callback + */ +function getFeature(call, callback) { + callback(null, checkFeature(call.request)); +} + +/** + * listFeatures request handler. Gets a request with two points, and responds + * with a stream of all features in the bounding box defined by those points. + * @param {Writable} call Writable stream for responses with an additional + * request property for the request value. + */ +function listFeatures(call) { + var lo = call.request.lo; + var hi = call.request.hi; + var left = _.min([lo.longitude, hi.longitude]); + var right = _.max([lo.longitude, hi.longitude]); + var top = _.max([lo.latitude, hi.latitude]); + var bottom = _.min([lo.latitude, hi.latitude]); + // For each feature, check if it is in the given bounding box + _.each(feature_list, function(feature) { + if (feature.name === '') { + return; + } + if (feature.location.longitude >= left && + feature.location.longitude <= right && + feature.location.latitude >= bottom && + feature.location.latitude <= top) { + call.write(feature); + } + }); + call.end(); +} + +/** + * Calculate the distance between two points using the "haversine" formula. + * This code was taken from http://www.movable-type.co.uk/scripts/latlong.html. + * @param start The starting point + * @param end The end point + * @return The distance between the points in meters + */ +function getDistance(start, end) { + function toRadians(num) { + return num * Math.PI / 180; + } + var lat1 = start.latitude / COORD_FACTOR; + var lat2 = end.latitude / COORD_FACTOR; + var lon1 = start.longitude / COORD_FACTOR; + var lon2 = end.longitude / COORD_FACTOR; + var R = 6371000; // metres + var φ1 = toRadians(lat1); + var φ2 = toRadians(lat2); + var Δφ = toRadians(lat2-lat1); + var Δλ = toRadians(lon2-lon1); + + var a = Math.sin(Δφ/2) * Math.sin(Δφ/2) + + Math.cos(φ1) * Math.cos(φ2) * + Math.sin(Δλ/2) * Math.sin(Δλ/2); + var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); + + return R * c; +} + +/** + * recordRoute handler. Gets a stream of points, and responds with statistics + * about the "trip": number of points, number of known features visited, total + * distance traveled, and total time spent. + * @param {Readable} call The request point stream. + * @param {function(Error, routeSummary)} callback The callback to pass the + * response to + */ +function recordRoute(call, callback) { + var point_count = 0; + var feature_count = 0; + var distance = 0; + var previous = null; + // Start a timer + var start_time = process.hrtime(); + call.on('data', function(point) { + point_count += 1; + if (checkFeature(point).name !== '') { + feature_count += 1; + } + /* For each point after the first, add the incremental distance from the + * previous point to the total distance value */ + if (previous != null) { + distance += getDistance(previous, point); + } + previous = point; + }); + call.on('end', function() { + callback(null, { + point_count: point_count, + feature_count: feature_count, + // Cast the distance to an integer + distance: distance|0, + // End the timer + elapsed_time: process.hrtime(start_time)[0] + }); + }); +} + +var route_notes = {}; + +/** + * Turn the point into a dictionary key. + * @param {point} point The point to use + * @return {string} The key for an object + */ +function pointKey(point) { + return point.latitude + ' ' + point.longitude; +} + +/** + * routeChat handler. Receives a stream of message/location pairs, and responds + * with a stream of all previous messages at each of those locations. + * @param {Duplex} call The stream for incoming and outgoing messages + */ +function routeChat(call) { + call.on('data', function(note) { + var key = pointKey(note.location); + /* For each note sent, respond with all previous notes that correspond to + * the same point */ + if (route_notes.hasOwnProperty(key)) { + _.each(route_notes[key], function(note) { + call.write(note); + }); + } else { + route_notes[key] = []; + } + // Then add the new note to the list + route_notes[key].push(JSON.parse(JSON.stringify(note))); + }); + call.on('end', function() { + call.end(); + }); +} + +/** + * Get a new server with the handler functions in this file bound to the methods + * it serves. + * @return {Server} The new server object + */ +function getServer() { + var server = new grpc.Server(); + server.addProtoService(routeguide.RouteGuide.service, { + getFeature: getFeature, + listFeatures: listFeatures, + recordRoute: recordRoute, + routeChat: routeChat + }); + return server; +} + +if (require.main === module) { + // If this is run as a script, start a server on an unused port + var routeServer = getServer(); + routeServer.bind('0.0.0.0:50051', grpc.ServerCredentials.createInsecure()); + var argv = parseArgs(process.argv, { + string: 'db_path' + }); + fs.readFile(path.resolve(argv.db_path), function(err, data) { + if (err) throw err; + feature_list = JSON.parse(data); + routeServer.start(); + }); +} + +exports.getServer = getServer; diff --git a/examples/objective-c/auth_sample/AuthSample.xcodeproj/project.pbxproj b/examples/objective-c/auth_sample/AuthSample.xcodeproj/project.pbxproj new file mode 100644 index 00000000..51a39c57 --- /dev/null +++ b/examples/objective-c/auth_sample/AuthSample.xcodeproj/project.pbxproj @@ -0,0 +1,366 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 63E1E9821B28CB2100EF0978 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 63E1E9811B28CB2100EF0978 /* main.m */; }; + 63E1E9851B28CB2100EF0978 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 63E1E9841B28CB2100EF0978 /* AppDelegate.m */; }; + 63E1E9881B28CB2100EF0978 /* SelectUserViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 63E1E9871B28CB2100EF0978 /* SelectUserViewController.m */; }; + 63E1E98B1B28CB2100EF0978 /* MakeRPCViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 63E1E98A1B28CB2100EF0978 /* MakeRPCViewController.m */; }; + 63E1E98E1B28CB2100EF0978 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 63E1E98C1B28CB2100EF0978 /* Main.storyboard */; }; + 63E1E9901B28CB2100EF0978 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 63E1E98F1B28CB2100EF0978 /* Images.xcassets */; }; + 63F5DE481B28F5C100CDD07E /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 63F5DE471B28F5C100CDD07E /* GoogleService-Info.plist */; }; + 832213142AB24DB816D02635 /* libPods-AuthSample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F217A6ECA7F5BD1D5FB5071B /* libPods-AuthSample.a */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 63E1E97C1B28CB2100EF0978 /* AuthSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AuthSample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 63E1E9801B28CB2100EF0978 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 63E1E9811B28CB2100EF0978 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 63E1E9831B28CB2100EF0978 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 63E1E9841B28CB2100EF0978 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 63E1E9861B28CB2100EF0978 /* SelectUserViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SelectUserViewController.h; sourceTree = ""; }; + 63E1E9871B28CB2100EF0978 /* SelectUserViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SelectUserViewController.m; sourceTree = ""; }; + 63E1E9891B28CB2100EF0978 /* MakeRPCViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MakeRPCViewController.h; sourceTree = ""; }; + 63E1E98A1B28CB2100EF0978 /* MakeRPCViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MakeRPCViewController.m; sourceTree = ""; }; + 63E1E98D1B28CB2100EF0978 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 63E1E98F1B28CB2100EF0978 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + 63F5DE471B28F5C100CDD07E /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + A387D6CECBCF2EAF2983033A /* Pods-AuthSample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AuthSample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-AuthSample/Pods-AuthSample.debug.xcconfig"; sourceTree = ""; }; + B444176735DA81FBE4B8B80C /* Pods-AuthSample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AuthSample.release.xcconfig"; path = "Pods/Target Support Files/Pods-AuthSample/Pods-AuthSample.release.xcconfig"; sourceTree = ""; }; + F217A6ECA7F5BD1D5FB5071B /* libPods-AuthSample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-AuthSample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 63E1E9791B28CB2000EF0978 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 832213142AB24DB816D02635 /* libPods-AuthSample.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 021FA0E0B3F5D3D477DDAC10 /* Pods */ = { + isa = PBXGroup; + children = ( + A387D6CECBCF2EAF2983033A /* Pods-AuthSample.debug.xcconfig */, + B444176735DA81FBE4B8B80C /* Pods-AuthSample.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; + 4F443572636F3D60F26E870D /* Frameworks */ = { + isa = PBXGroup; + children = ( + F217A6ECA7F5BD1D5FB5071B /* libPods-AuthSample.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + 63E1E9731B28CB2000EF0978 = { + isa = PBXGroup; + children = ( + 63E1E97E1B28CB2100EF0978 /* AuthSample */, + 63E1E97D1B28CB2100EF0978 /* Products */, + 021FA0E0B3F5D3D477DDAC10 /* Pods */, + 4F443572636F3D60F26E870D /* Frameworks */, + ); + sourceTree = ""; + }; + 63E1E97D1B28CB2100EF0978 /* Products */ = { + isa = PBXGroup; + children = ( + 63E1E97C1B28CB2100EF0978 /* AuthSample.app */, + ); + name = Products; + sourceTree = ""; + }; + 63E1E97E1B28CB2100EF0978 /* AuthSample */ = { + isa = PBXGroup; + children = ( + 63E1E9861B28CB2100EF0978 /* SelectUserViewController.h */, + 63E1E9871B28CB2100EF0978 /* SelectUserViewController.m */, + 63E1E9891B28CB2100EF0978 /* MakeRPCViewController.h */, + 63E1E98A1B28CB2100EF0978 /* MakeRPCViewController.m */, + 63E1E97F1B28CB2100EF0978 /* Supporting Files */, + ); + name = AuthSample; + sourceTree = SOURCE_ROOT; + }; + 63E1E97F1B28CB2100EF0978 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 63E1E98C1B28CB2100EF0978 /* Main.storyboard */, + 63F5DE471B28F5C100CDD07E /* GoogleService-Info.plist */, + 63E1E98F1B28CB2100EF0978 /* Images.xcassets */, + 63E1E9801B28CB2100EF0978 /* Info.plist */, + 63E1E9831B28CB2100EF0978 /* AppDelegate.h */, + 63E1E9841B28CB2100EF0978 /* AppDelegate.m */, + 63E1E9811B28CB2100EF0978 /* main.m */, + ); + name = "Supporting Files"; + path = Misc; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 63E1E97B1B28CB2000EF0978 /* AuthSample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 63E1E9A21B28CB2100EF0978 /* Build configuration list for PBXNativeTarget "AuthSample" */; + buildPhases = ( + DAABBA7B5788A39108D7CA83 /* Check Pods Manifest.lock */, + 63E1E9781B28CB2000EF0978 /* Sources */, + 63E1E9791B28CB2000EF0978 /* Frameworks */, + 63E1E97A1B28CB2000EF0978 /* Resources */, + AEFCCC69DD59CE8F6EB769D7 /* Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = AuthSample; + productName = AuthSample; + productReference = 63E1E97C1B28CB2100EF0978 /* AuthSample.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 63E1E9741B28CB2000EF0978 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0630; + ORGANIZATIONNAME = gRPC; + TargetAttributes = { + 63E1E97B1B28CB2000EF0978 = { + CreatedOnToolsVersion = 6.3.1; + }; + }; + }; + buildConfigurationList = 63E1E9771B28CB2000EF0978 /* Build configuration list for PBXProject "AuthSample" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 63E1E9731B28CB2000EF0978; + productRefGroup = 63E1E97D1B28CB2100EF0978 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 63E1E97B1B28CB2000EF0978 /* AuthSample */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 63E1E97A1B28CB2000EF0978 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 63E1E98E1B28CB2100EF0978 /* Main.storyboard in Resources */, + 63E1E9901B28CB2100EF0978 /* Images.xcassets in Resources */, + 63F5DE481B28F5C100CDD07E /* GoogleService-Info.plist in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + AEFCCC69DD59CE8F6EB769D7 /* Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-AuthSample/Pods-AuthSample-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + DAABBA7B5788A39108D7CA83 /* Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 63E1E9781B28CB2000EF0978 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 63E1E98B1B28CB2100EF0978 /* MakeRPCViewController.m in Sources */, + 63E1E9851B28CB2100EF0978 /* AppDelegate.m in Sources */, + 63E1E9881B28CB2100EF0978 /* SelectUserViewController.m in Sources */, + 63E1E9821B28CB2100EF0978 /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 63E1E98C1B28CB2100EF0978 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 63E1E98D1B28CB2100EF0978 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 63E1E9A01B28CB2100EF0978 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.3; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 63E1E9A11B28CB2100EF0978 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.3; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 63E1E9A31B28CB2100EF0978 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A387D6CECBCF2EAF2983033A /* Pods-AuthSample.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = Misc/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 63E1E9A41B28CB2100EF0978 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = B444176735DA81FBE4B8B80C /* Pods-AuthSample.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = Misc/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 63E1E9771B28CB2000EF0978 /* Build configuration list for PBXProject "AuthSample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 63E1E9A01B28CB2100EF0978 /* Debug */, + 63E1E9A11B28CB2100EF0978 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 63E1E9A21B28CB2100EF0978 /* Build configuration list for PBXNativeTarget "AuthSample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 63E1E9A31B28CB2100EF0978 /* Debug */, + 63E1E9A41B28CB2100EF0978 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 63E1E9741B28CB2000EF0978 /* Project object */; +} diff --git a/examples/objective-c/auth_sample/AuthSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/examples/objective-c/auth_sample/AuthSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..39b57a4b --- /dev/null +++ b/examples/objective-c/auth_sample/AuthSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/examples/objective-c/auth_sample/AuthTestService.podspec b/examples/objective-c/auth_sample/AuthTestService.podspec new file mode 100644 index 00000000..9f2a2cc3 --- /dev/null +++ b/examples/objective-c/auth_sample/AuthTestService.podspec @@ -0,0 +1,35 @@ +Pod::Spec.new do |s| + s.name = "AuthTestService" + s.version = "0.0.1" + s.license = "New BSD" + + s.ios.deployment_target = "6.0" + s.osx.deployment_target = "10.8" + + # Base directory where the .proto files are. + src = "../../protos" + + # Directory where the generated files will be place. + dir = "Pods/" + s.name + + # Run protoc with the Objective-C and gRPC plugins to generate protocol messages and gRPC clients. + s.prepare_command = <<-CMD + mkdir -p #{dir} + protoc -I #{src} --objc_out=#{dir} --objcgrpc_out=#{dir} #{src}/auth_sample.proto + CMD + + s.subspec "Messages" do |ms| + ms.source_files = "#{dir}/*.pbobjc.{h,m}", "#{dir}/**/*.pbobjc.{h,m}" + ms.header_mappings_dir = dir + ms.requires_arc = false + ms.dependency "Protobuf", "~> 3.0.0-alpha-3" + end + + s.subspec "Services" do |ss| + ss.source_files = "#{dir}/*.pbrpc.{h,m}", "#{dir}/**/*.pbrpc.{h,m}" + ss.header_mappings_dir = dir + ss.requires_arc = true + ss.dependency "gRPC", "~> 0.6" + ss.dependency "#{s.name}/Messages" + end +end diff --git a/examples/objective-c/auth_sample/MakeRPCViewController.h b/examples/objective-c/auth_sample/MakeRPCViewController.h new file mode 100644 index 00000000..c75a8b31 --- /dev/null +++ b/examples/objective-c/auth_sample/MakeRPCViewController.h @@ -0,0 +1,40 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import + +extern NSString * const kTestScope; + +@interface MakeRPCViewController : UIViewController +@property(weak, nonatomic) IBOutlet UILabel *mainLabel; +@end diff --git a/examples/objective-c/auth_sample/MakeRPCViewController.m b/examples/objective-c/auth_sample/MakeRPCViewController.m new file mode 100644 index 00000000..366bc9de --- /dev/null +++ b/examples/objective-c/auth_sample/MakeRPCViewController.m @@ -0,0 +1,100 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "MakeRPCViewController.h" + +#import +#import +#include +#import + +NSString * const kTestScope = @"https://www.googleapis.com/auth/xapi.zoo"; + +static NSString * const kTestHostAddress = @"grpc-test.sandbox.google.com"; + +// Category for RPC errors to create the descriptions as we want them to appear on our view. +@interface NSError (AuthSample) +- (NSString *)UIDescription; +@end + +@implementation NSError (AuthSample) +- (NSString *)UIDescription { + if (self.code == GRPC_STATUS_UNAUTHENTICATED) { + // Authentication error. OAuth2 specifies we'll receive a challenge header. + // |userInfo[kGRPCStatusMetadataKey]| is the dictionary of response metadata. + NSString *challengeHeader = self.userInfo[kGRPCStatusMetadataKey][@"www-authenticate"] ?: @""; + return [@"Invalid credentials. Server challenge:\n" stringByAppendingString:challengeHeader]; + } else { + // Any other error. + return [NSString stringWithFormat:@"Unexpected RPC error %li: %@", + (long)self.code, self.localizedDescription]; + } +} +@end + +@implementation MakeRPCViewController + +- (void)viewWillAppear:(BOOL)animated { + + // Create a service client and a proto request as usual. + AUTHTestService *client = [[AUTHTestService alloc] initWithHost:kTestHostAddress]; + + AUTHRequest *request = [AUTHRequest message]; + request.fillUsername = YES; + request.fillOauthScope = YES; + + // Create a not-yet-started RPC. We want to set the request headers on this object before starting + // it. + ProtoRPC *call = + [client RPCToUnaryCallWithRequest:request handler:^(AUTHResponse *response, NSError *error) { + if (response) { + // This test server responds with the email and scope of the access token it receives. + self.mainLabel.text = [NSString stringWithFormat:@"Used scope: %@ on behalf of user %@", + response.oauthScope, response.username]; + + } else { + self.mainLabel.text = error.UIDescription; + } + }]; + + // Set the access token to be used. + NSString *accessToken = GIDSignIn.sharedInstance.currentUser.authentication.accessToken; + call.requestMetadata[@"Authorization"] = [@"Bearer " stringByAppendingString:accessToken]; + + // Start the RPC. + [call start]; + + self.mainLabel.text = @"Waiting for RPC to complete..."; +} + +@end diff --git a/examples/objective-c/auth_sample/Misc/AppDelegate.h b/examples/objective-c/auth_sample/Misc/AppDelegate.h new file mode 100644 index 00000000..102e7f3a --- /dev/null +++ b/examples/objective-c/auth_sample/Misc/AppDelegate.h @@ -0,0 +1,38 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import + +@interface AppDelegate : UIResponder +@property (strong, nonatomic) UIWindow *window; +@end diff --git a/examples/objective-c/auth_sample/Misc/AppDelegate.m b/examples/objective-c/auth_sample/Misc/AppDelegate.m new file mode 100644 index 00000000..798d3429 --- /dev/null +++ b/examples/objective-c/auth_sample/Misc/AppDelegate.m @@ -0,0 +1,61 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "AppDelegate.h" + +#import + +@implementation AppDelegate + +// As instructed in https://developers.google.com/identity/sign-in/ios/sign-in +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + NSError* configureError; + [GGLContext.sharedInstance configureWithError: &configureError]; + NSAssert(!configureError, @"Error configuring Google services: %@", configureError); + + return YES; +} + +// As instructed in https://developers.google.com/identity/sign-in/ios/sign-in +- (BOOL)application:(UIApplication *)application + openURL:(NSURL *)url + sourceApplication:(NSString *)sourceApplication + annotation:(id)annotation { + // This will properly handle the URL that the application receives at the end of the + // authentication process. + return [GIDSignIn.sharedInstance handleURL:url + sourceApplication:sourceApplication + annotation:annotation]; +} + +@end diff --git a/examples/objective-c/auth_sample/Misc/Base.lproj/Main.storyboard b/examples/objective-c/auth_sample/Misc/Base.lproj/Main.storyboard new file mode 100644 index 00000000..fb29c124 --- /dev/null +++ b/examples/objective-c/auth_sample/Misc/Base.lproj/Main.storyboard @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/objective-c/auth_sample/Misc/GoogleService-Info.plist b/examples/objective-c/auth_sample/Misc/GoogleService-Info.plist new file mode 100644 index 00000000..86909d84 --- /dev/null +++ b/examples/objective-c/auth_sample/Misc/GoogleService-Info.plist @@ -0,0 +1,10 @@ + + + + + CLIENT_ID + 15087385131-lh9bpkiai9nls53uadju0if6k7un3uih.apps.googleusercontent.com + REVERSED_CLIENT_ID + com.googleusercontent.apps.15087385131-lh9bpkiai9nls53uadju0if6k7un3uih + + \ No newline at end of file diff --git a/examples/objective-c/auth_sample/Misc/Images.xcassets/AppIcon.appiconset/Contents.json b/examples/objective-c/auth_sample/Misc/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..36d2c80d --- /dev/null +++ b/examples/objective-c/auth_sample/Misc/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/objective-c/auth_sample/Misc/Images.xcassets/first.imageset/Contents.json b/examples/objective-c/auth_sample/Misc/Images.xcassets/first.imageset/Contents.json new file mode 100644 index 00000000..33a74510 --- /dev/null +++ b/examples/objective-c/auth_sample/Misc/Images.xcassets/first.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "first.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/objective-c/auth_sample/Misc/Images.xcassets/first.imageset/first.pdf b/examples/objective-c/auth_sample/Misc/Images.xcassets/first.imageset/first.pdf new file mode 100644 index 0000000000000000000000000000000000000000..47d911dea647d55983671ead4d08b6f6b3600715 GIT binary patch literal 2465 zcmai03se(l7FLvisY+L#f-EBsK`11Xkc1=%MIujmB|xQ!>5vQ%APFP`Mlf5nSPOKE z2-^C>7DZkM!J;Da5{kfrC?Kvp3J41-3shFXvhuJqKo*oer*r0H{{P-P_x0cTzLzjD!bE z0qT;#Q7l4Gy%fMoXJaKT`@{5#R(MOqJPwQifv8iK6A%Ot9L14h2`38T!2s4PM=1!< zmL06}VYAA|ay#jZRs>HpA%X+eQW4rufWU%d1w5GTy!X#LeZsF_+~ccZmn3Fi)v^Z; zIG;?uU*yLLEYs61tjD>gXOFvSWsh{48xJvPNqKrIJtMdCz2cA2aC7TF?b@K`V!Lw- zE;zpH&ApqhoRjAHt}gK}>(qAc8dvrkD31*`<5<%C(&4_LqhQ3-GOf8~n^leG?+-@@^pjPa$J2gW@O)!b9hdTJ zTauyIJ&~rqeEZC1p9dWgx7{_WRc2=drMO=wcT7B{Zd58z`d)r_36r4V<6PGkCjpGfbnVMtm@;b}F!T?)Qav$_y7H5T;Mf!T}M zWxP9TNqrV?e5;b|pWd3^u2KAatk&3aDB@0 zZBaQNdCZ2#fblzYZnRCCjQ-GQWb-s8bX&<)?SxnUGdDYVFVk`xIf7@g3~$DX`H71_;r5OSZz1p`$_8y_9 z{;MJJ+n=>7Ewg;GnGHoz)&ID0z@F2!e$F7cWQ?d6s(!VY)_Gw})xCyMvsD={5i&H* zAIr_ACo8;Se6<*!-mm9Am79Iz^RVlc?%S5sg|E*SyIV{dd9{Mpf#d3cih5WKt=%ps zBEo)bt8EjmeCFYJRYU|b7d`p+-V|X2wOCYtyLP6t=!WH-kgdf0A};ytPfZiCwVPx{ z`g;zpe{8a4RQxQUwVU02<4X3w|9h;}XjhGWquqn{vVqq8g{*}BQF9=ybe_TM*!M=rFs1pN)yzctIXAoicsCe6>fit>wgZ#vp^hZOY0`J`rx zwdSK?GwR_xm9;5XjH|vf{O+Yg-)z;s*xt>;-vU`D-_SK<=~4eEVQkV~57{vyH6ebu>DqwE~Mx_A31Lg~$ zy`i52ew?GEluE%AN>ju*5z~dx2QQLP-G+E>o|En2+s&<^4*d zWD+<8PXs_Jp7_5Dpi(Gg3J?M;%Rn0{dcxQRh!ip+iHJ_e`!bM1MGxwI8Iedr+x>wI zq@&04fsADHfBccq^gpm8Q_u-~Uj|Z$YwSR(^_mzUb!}Xb2Cj`uqphg}v_Y%?Fs=;= zWq3s{hy)Tu!ji@RJsd$C+G{F6V2j1*Xk+7xo|LO7M2yb)(wc=%x-AclP9pLsbTF7m tf?*P!2EjJu5MD57P32Q4pdJ3tO_shCDT2=K;$ebx8ymcd$v!V9{2$sQiO>K5 literal 0 HcmV?d00001 diff --git a/examples/objective-c/auth_sample/Misc/Images.xcassets/second.imageset/Contents.json b/examples/objective-c/auth_sample/Misc/Images.xcassets/second.imageset/Contents.json new file mode 100644 index 00000000..03bd9c92 --- /dev/null +++ b/examples/objective-c/auth_sample/Misc/Images.xcassets/second.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "second.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/objective-c/auth_sample/Misc/Images.xcassets/second.imageset/second.pdf b/examples/objective-c/auth_sample/Misc/Images.xcassets/second.imageset/second.pdf new file mode 100644 index 0000000000000000000000000000000000000000..401614e288b4b160471c2776bed6f09762af3e1c GIT binary patch literal 2423 zcmai02~-nz8dqq7rWU#)x2z)&iMWtV?gK$2a)?|BP!TbkV}J-Gl1wmy+3G9S0=uOY zwCjai6uBOPMMdNiN`VDYKwLQr2#W^`R93*Ua=0^)EGYY4=grIf|KIohziYnv{elnR zdN_hk6db<(+3RPcxs!iut8c(j0TLh=9mY931H^rhEE0|aXo!dh5Iv+ZFr+}9F+v#P zLSnfD0$f~hN>~92V{z)WJA&2)5rW!|I?;o?oI*%zejJiuY>hFLQwKlAa9O+PnPh#% zJp~JAfs-De6u@;{cREMs}D-sGib_4>50m8_|Uj-zC0m@Qx zJV+^5Da4Qxpf3sBtKZs<_aI$Cb1T+h<->yL^rfiGNelYkqgC3#SqVP_iy*_RPcbjH)xYf zvYdC-)D_t8c{=5%;$VJQxs_#61J*kuXMC?|&PbPo-7uSxoNr~&J94|gKk+03JGXXM zx4w~=^VUzWqQveu_ilRgPdgNNdnLZCH`=;w91}cP5ihdm*B_jI+rk=Pz2Lk!)XUu| zkls3P5&YHJ!t0kF+MUVBHOAx)a zy|JxHNd37z*~b5#%*0w_F|#4?erNP;pG`Fevoq2%?9Y>%Cmzv1(ks;aPIGaiy1mhU zr{7lB^Mem&`QK{b{uiDC zLD8muY(FNk{YdV|zO#o_VB%);8|~Bh(Z4p?GM}g-HN2;q7~YNl-lD~HZ$taYw$Fzbn6c?c5eo=@`i>()el#qwB)hI-gp0 ztsw@dh-I5NJkk)RCoBC`L8ffKfY)=oG=#4LA9TO6eA}9~@a6dupB+=S{%s&NY}^0GY`hAqod)uxXHitrN%bWOJohV>t?!&Z#d4y^7J@=Oh`HV zee**4j>xRUd)|D0^I-B#<`m1-5PO{)!v(q?ecFYqr_8Q{+z7#n0uFbq%_?hZ-Ck(6NRn=vDS7Nb1Xxx_)9`!i- zGR>av*PJ+P_AGO*a_$FbtCCISg5PK44p&W#t$)+HVcxpR9LAL%_Pv$p`0tO*sYZD0 zCG3-ipr4WoVbjpw%Nf^r^rnWcDcjumgBPEjO;NM6QtZ8wU#0rjHx5zs%CahF4$W*U zxu!q8kYP+>v!BNPVIRJUGIWU@7qVvx2VZDw4gV8%z%bjpF#aF(W|PmliwlakuRq>= zhaCRc^R!7T)#Twb0{!Tns=CxGcGX|leErg`f3|v0eBXxHU&6Sj-_>zOL!!d+j5Xt* zPS;rWd^zEF{5d5%)nNK=)zFJ8Uc=aQKD%UK)_TNes1Gh{SD*Mb_@O` z7*zs9Uq~Vq?v|^8FcM-yLGMKzR3YFB0z@CFM2X-dio%Gu6&MJj=I?uS7sA3AdE^p8 zE49$PjJzPIJwfn;jso7X2#jynr3dO@p5QAK1O9wqKLC=PkQjj~p#n|-5O`ihbTzzu zL{}2QEe28pJYfVZRW56FL`LvO&|C^1Th{0}hQMW$Ng$QPpnzlwnMnf=kVy8(y==L% zLMa>r0gELdT + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + io.grpc.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + REVERSED_CLIENT_ID + CFBundleURLSchemes + + com.googleusercontent.apps.15087385131-lh9bpkiai9nls53uadju0if6k7un3uih + + + + CFBundleTypeRole + Editor + CFBundleURLName + BUNDLE_ID + CFBundleURLSchemes + + io.grpc.AuthSample + + + + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + Main + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UIStatusBarTintParameters + + UINavigationBar + + Style + UIBarStyleDefault + Translucent + + + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/examples/objective-c/auth_sample/Misc/main.m b/examples/objective-c/auth_sample/Misc/main.m new file mode 100644 index 00000000..81e9d44e --- /dev/null +++ b/examples/objective-c/auth_sample/Misc/main.m @@ -0,0 +1,41 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import +#import "AppDelegate.h" + +int main(int argc, char * argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/examples/objective-c/auth_sample/Podfile b/examples/objective-c/auth_sample/Podfile new file mode 100644 index 00000000..dd4fd558 --- /dev/null +++ b/examples/objective-c/auth_sample/Podfile @@ -0,0 +1,10 @@ +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '8.0' + +target 'AuthSample' do + # Depend on the generated AuthTestService library. + pod 'AuthTestService', :path => '.' + + # Depend on Google's OAuth2 library + pod 'Google/SignIn' +end diff --git a/examples/objective-c/auth_sample/README.md b/examples/objective-c/auth_sample/README.md new file mode 100644 index 00000000..5ae64526 --- /dev/null +++ b/examples/objective-c/auth_sample/README.md @@ -0,0 +1,189 @@ +#OAuth2 on gRPC: Objective-C + +This example application demostrates how to use OAuth2 on gRPC to make authenticated API calls on +behalf of a user. By walking through it you'll learn how to use the Objective-C gRPC API to: + +- Initialize and configure a remote call object before the RPC is started. +- Set request metadata elements on a call, which are semantically equivalent to HTTP request +headers. +- Read response metadata from a call, which is equivalent to HTTP response headers and trailers. + +It assumes you know the basics on how to make gRPC API calls using the Objective-C client library, +as shown in the [Hello World](../helloworld) +or [Route Guide](../route_guide) tutorials, +and are familiar with OAuth2 concepts like _access token_. + +- [Example code and setup](#setup) +- [Try it out!](#try) +- [Create an RPC object and start it later](#rpc-object) +- [Set request metadata of a call: Authorization header with an access token](#request-metadata) +- [Get response metadata of a call: Auth challenge header](#response-metadata) + + +## Example code and setup + +The example code for our tutorial is in [examples/objective-c/auth_sample](.). +To download the example, clone this repository by running the following command: +```shell +$ git clone https://github.com/grpc/grpc.git +``` + +Then change your current directory to `examples/objective-c/auth_sample`: +```shell +$ cd examples/objective-c/auth_sample +``` + +Our example is a simple application with two views. The first one lets a user sign in and out using +the OAuth2 flow of Google's [iOS SignIn library](https://developers.google.com/identity/sign-in/ios/). +(Google's library is used in this example because the test gRPC service we are going to call expects +Google account credentials, but neither gRPC nor the Objective-C client library is tied to any +specific OAuth2 provider). The second view makes a gRPC request to the test server, using the +access token obtained by the first view. + +Note: OAuth2 libraries need the application to register and obtain an ID from the identity provider +(in the case of this example app, Google). The app's XCode project is configured using that ID, so +you shouldn't copy this project "as is" for your own app: it would result in your app being +identified in the consent screen as "gRPC-AuthSample", and not having access to real Google +services. Instead, configure your own XCode project following the [instructions here](https://developers.google.com/identity/sign-in/ios/). + +As with the other examples, you also should have [Cocoapods](https://cocoapods.org/#install) +installed, as well as the relevant tools to generate the client library code. You can obtain the +latter by following [these setup instructions](https://github.com/grpc/homebrew-grpc). + + + +## Try it out! + +To try the sample app, first have Cocoapods generate and install the client library for our .proto +files: + +```shell +$ pod install +``` + +(This might have to compile OpenSSL, which takes around 15 minutes if Cocoapods doesn't have it yet +on your computer's cache). + +Finally, open the XCode workspace created by Cocoapods, and run the app. + +The first view, `SelectUserViewController.h/m`, asks you to sign in with your Google account, and to +give the "gRPC-AuthSample" app the following permissions: + +- View your email address. +- View your basic profile info. +- "Test scope for access to the Zoo service". + +This last permission, corresponding to the scope `https://www.googleapis.com/auth/xapi.zoo` doesn't +grant any real capability: it's only used for testing. You can log out at any time. + +The second view, `MakeRPCViewController.h/m`, makes a gRPC request to a test server at +https://grpc-test.sandbox.google.com, sending the access token along with the request. The test +service simply validates the token and writes in its response which user it belongs to, and which +scopes it gives access to. (The client application already knows those two values; it's a way to +verify that everything went as expected). + +The next sections guide you step-by-step through how the gRPC call in `MakeRPCViewController` is +performed. + + +## Create an RPC object and start it later + +The other basic tutorials show how to invoke an RPC by calling an asynchronous method in a generated +client object. This shows how to initialize an object that represents the RPC, and configure it +before starting the network request. + +Assume you have a proto service definition like this: + +```protobuf +option objc_class_prefix = "AUTH"; + +service TestService { + rpc UnaryCall(Request) returns (Response); +} +``` + +A `unaryCallWithRequest:handler:` method, with which you're already familiar, is generated for the +`AUTHTestService` class: + +```objective-c +[client unaryCallWithRequest:request handler:^(AUTHResponse *response, NSError *error) { + ... +}]; +``` + +In addition, an `RPCToUnaryCallWithRequest:handler:` method is generated, which returns a +not-yet-started RPC object: + +```objective-c +#import + +ProtoRPC *call = + [client RPCToUnaryCallWithRequest:request handler:^(AUTHResponse *response, NSError *error) { + ... + }]; +``` + +The RPC represented by this object can be started at any later time like this: + +```objective-c +[call start]; +``` + + +## Set request metadata of a call: Authorization header with an access token + +The `ProtoRPC` class has a `requestMetadata` property (inherited from `GRPCCall`) defined like this: + +```objective-c +- (NSMutableDictionary *)requestMetadata; // nonatomic +- (void)setRequestMetadata:(NSDictionary *)requestMetadata; // nonatomic, copy +``` + +Setting it to a dictionary of metadata keys and values will have them sent on the wire when the call +is started. gRPC metadata are pieces of information about the call sent by the client to the server +(and vice versa). They take the form of key-value pairs and are essentially opaque to gRPC itself. + +```objective-c +call.requestMetadata = @{@"My-Header": @"Value for this header", + @"Another-Header": @"Its value"}; +``` + +For convenience, the property is initialized with an empty `NSMutableDictionary`, so that request +metadata elements can be set like this: + +```objective-c +call.requestMetadata[@"My-Header"] = @"Value for this header"; +``` + +If you have an access token, OAuth2 specifies it is to be sent in this format: + +```objective-c +call.requestMetadata[@"Authorization"] = [@"Bearer " stringByAppendingString:accessToken]; +``` + + +## Get response metadata of a call: Auth challenge header + +The `ProtoRPC` class also inherits a `responseMetadata` property, analogous to the request metadata +we just looked at. It's defined like this: + +```objective-c +@property(atomic, readonly) NSDictionary *responseMetadata; +``` + +To access OAuth2's authentication challenge header you write: + +```objective-c +call.responseMetadata[@"www-authenticate"] +``` + +Note that, as gRPC metadata elements are mapped to HTTP/2 headers (or trailers), the keys of the +response metadata are always ASCII strings in lowercase. + +Many uses cases of response metadata are getting more details about an RPC error. For convenience, +when a `NSError` instance is passed to an RPC handler block, the response metadata dictionary can +also be accessed this way: + +```objective-c +error.userInfo[kGRPCStatusMetadataKey] +``` diff --git a/examples/objective-c/auth_sample/SelectUserViewController.h b/examples/objective-c/auth_sample/SelectUserViewController.h new file mode 100644 index 00000000..eb3c2cf5 --- /dev/null +++ b/examples/objective-c/auth_sample/SelectUserViewController.h @@ -0,0 +1,42 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import +#import + +@interface SelectUserViewController : UIViewController +@property(weak, nonatomic) IBOutlet GIDSignInButton *signInButton; +@property(weak, nonatomic) IBOutlet UIButton *signOutButton; +@property(weak, nonatomic) IBOutlet UILabel *mainLabel; +@property(weak, nonatomic) IBOutlet UILabel *subLabel; +@end diff --git a/examples/objective-c/auth_sample/SelectUserViewController.m b/examples/objective-c/auth_sample/SelectUserViewController.m new file mode 100644 index 00000000..954c531f --- /dev/null +++ b/examples/objective-c/auth_sample/SelectUserViewController.m @@ -0,0 +1,86 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "SelectUserViewController.h" + +#import "MakeRPCViewController.h" + +@implementation SelectUserViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + self.signOutButton.layer.cornerRadius = 5; + self.signOutButton.hidden = YES; + + // As instructed in https://developers.google.com/identity/sign-in/ios/sign-in + GIDSignIn *signIn = GIDSignIn.sharedInstance; + signIn.delegate = self; + signIn.uiDelegate = self; + + // As instructed in https://developers.google.com/identity/sign-in/ios/additional-scopes + if (![signIn.scopes containsObject:kTestScope]) { + signIn.scopes = [signIn.scopes arrayByAddingObject:kTestScope]; + } + + [signIn signInSilently]; +} + +- (void)signIn:(GIDSignIn *)signIn +didSignInForUser:(GIDGoogleUser *)user + withError:(NSError *)error { + if (error) { + // The user probably cancelled the sign-in flow. + return; + } + + self.mainLabel.text = [NSString stringWithFormat:@"User: %@", user.profile.email]; + NSString *scopes = [user.accessibleScopes componentsJoinedByString:@", "]; + scopes = scopes.length ? scopes : @"(none)"; + self.subLabel.text = [NSString stringWithFormat:@"Scopes: %@", scopes]; + + self.signInButton.hidden = YES; + self.signOutButton.hidden = NO; +} + +- (IBAction)didTapSignOut { + [GIDSignIn.sharedInstance signOut]; + + self.mainLabel.text = @"Please sign in."; + self.subLabel.text = @""; + + self.signInButton.hidden = NO; + self.signOutButton.hidden = YES; +} + +@end diff --git a/examples/objective-c/helloworld/HelloWorld.podspec b/examples/objective-c/helloworld/HelloWorld.podspec new file mode 100644 index 00000000..ae009a68 --- /dev/null +++ b/examples/objective-c/helloworld/HelloWorld.podspec @@ -0,0 +1,35 @@ +Pod::Spec.new do |s| + s.name = "HelloWorld" + s.version = "0.0.1" + s.license = "New BSD" + + s.ios.deployment_target = "6.0" + s.osx.deployment_target = "10.8" + + # Base directory where the .proto files are. + src = "../../protos" + + # Directory where the generated files will be place. + dir = "Pods/" + s.name + + # Run protoc with the Objective-C and gRPC plugins to generate protocol messages and gRPC clients. + s.prepare_command = <<-CMD + mkdir -p #{dir} + protoc -I #{src} --objc_out=#{dir} --objcgrpc_out=#{dir} #{src}/helloworld.proto + CMD + + s.subspec "Messages" do |ms| + ms.source_files = "#{dir}/*.pbobjc.{h,m}", "#{dir}/**/*.pbobjc.{h,m}" + ms.header_mappings_dir = dir + ms.requires_arc = false + ms.dependency "Protobuf", "~> 3.0.0-alpha-3" + end + + s.subspec "Services" do |ss| + ss.source_files = "#{dir}/*.pbrpc.{h,m}", "#{dir}/**/*.pbrpc.{h,m}" + ss.header_mappings_dir = dir + ss.requires_arc = true + ss.dependency "gRPC", "~> 0.6" + ss.dependency "#{s.name}/Messages" + end +end diff --git a/examples/objective-c/helloworld/HelloWorld.xcodeproj/project.pbxproj b/examples/objective-c/helloworld/HelloWorld.xcodeproj/project.pbxproj new file mode 100644 index 00000000..702ad3ff --- /dev/null +++ b/examples/objective-c/helloworld/HelloWorld.xcodeproj/project.pbxproj @@ -0,0 +1,349 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 5E3690661B2A23800040F884 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E3690651B2A23800040F884 /* main.m */; }; + 5E3690691B2A23800040F884 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E3690681B2A23800040F884 /* AppDelegate.m */; }; + 5E36906C1B2A23800040F884 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E36906B1B2A23800040F884 /* ViewController.m */; }; + 5E36906F1B2A23800040F884 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5E36906D1B2A23800040F884 /* Main.storyboard */; }; + 5E3690711B2A23800040F884 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5E3690701B2A23800040F884 /* Images.xcassets */; }; + EF61CF6AE2536A31D47F0E63 /* libPods-HelloWorld.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6B4E1F55F8A2EC95A0E7EE88 /* libPods-HelloWorld.a */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 0C432EF610DB15C0F47A66BB /* Pods-HelloWorld.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HelloWorld.release.xcconfig"; path = "Pods/Target Support Files/Pods-HelloWorld/Pods-HelloWorld.release.xcconfig"; sourceTree = ""; }; + 5E3690601B2A23800040F884 /* HelloWorld.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HelloWorld.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 5E3690641B2A23800040F884 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 5E3690651B2A23800040F884 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 5E3690671B2A23800040F884 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 5E3690681B2A23800040F884 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 5E36906B1B2A23800040F884 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; + 5E36906E1B2A23800040F884 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 5E3690701B2A23800040F884 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + 6B4E1F55F8A2EC95A0E7EE88 /* libPods-HelloWorld.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-HelloWorld.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + DBDE3E48389499064CD664B8 /* Pods-HelloWorld.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HelloWorld.debug.xcconfig"; path = "Pods/Target Support Files/Pods-HelloWorld/Pods-HelloWorld.debug.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 5E36905D1B2A23800040F884 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + EF61CF6AE2536A31D47F0E63 /* libPods-HelloWorld.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 5E3690571B2A23800040F884 = { + isa = PBXGroup; + children = ( + 5E3690651B2A23800040F884 /* main.m */, + 5E3690621B2A23800040F884 /* HelloWorld */, + 5E3690611B2A23800040F884 /* Products */, + BD9CE6458E7C4FF49A1DF69F /* Pods */, + 66CEC7120220DDD2221DD075 /* Frameworks */, + ); + sourceTree = ""; + }; + 5E3690611B2A23800040F884 /* Products */ = { + isa = PBXGroup; + children = ( + 5E3690601B2A23800040F884 /* HelloWorld.app */, + ); + name = Products; + sourceTree = ""; + }; + 5E3690621B2A23800040F884 /* HelloWorld */ = { + isa = PBXGroup; + children = ( + 5E3690631B2A23800040F884 /* Supporting Files */, + ); + path = HelloWorld; + sourceTree = ""; + }; + 5E3690631B2A23800040F884 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 5E3690701B2A23800040F884 /* Images.xcassets */, + 5E36906D1B2A23800040F884 /* Main.storyboard */, + 5E36906B1B2A23800040F884 /* ViewController.m */, + 5E3690681B2A23800040F884 /* AppDelegate.m */, + 5E3690671B2A23800040F884 /* AppDelegate.h */, + 5E3690641B2A23800040F884 /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 66CEC7120220DDD2221DD075 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 6B4E1F55F8A2EC95A0E7EE88 /* libPods-HelloWorld.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + BD9CE6458E7C4FF49A1DF69F /* Pods */ = { + isa = PBXGroup; + children = ( + DBDE3E48389499064CD664B8 /* Pods-HelloWorld.debug.xcconfig */, + 0C432EF610DB15C0F47A66BB /* Pods-HelloWorld.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 5E36905F1B2A23800040F884 /* HelloWorld */ = { + isa = PBXNativeTarget; + buildConfigurationList = 5E3690831B2A23810040F884 /* Build configuration list for PBXNativeTarget "HelloWorld" */; + buildPhases = ( + ACF9162361FB8F24C70657DE /* Check Pods Manifest.lock */, + 5E36905C1B2A23800040F884 /* Sources */, + 5E36905D1B2A23800040F884 /* Frameworks */, + 5E36905E1B2A23800040F884 /* Resources */, + 4C7D815378D98AB3BFC1A7D5 /* Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = HelloWorld; + productName = HelloWorld; + productReference = 5E3690601B2A23800040F884 /* HelloWorld.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 5E3690581B2A23800040F884 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0620; + ORGANIZATIONNAME = Google; + TargetAttributes = { + 5E36905F1B2A23800040F884 = { + CreatedOnToolsVersion = 6.2; + }; + }; + }; + buildConfigurationList = 5E36905B1B2A23800040F884 /* Build configuration list for PBXProject "HelloWorld" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 5E3690571B2A23800040F884; + productRefGroup = 5E3690611B2A23800040F884 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 5E36905F1B2A23800040F884 /* HelloWorld */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 5E36905E1B2A23800040F884 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5E36906F1B2A23800040F884 /* Main.storyboard in Resources */, + 5E3690711B2A23800040F884 /* Images.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 4C7D815378D98AB3BFC1A7D5 /* Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-HelloWorld/Pods-HelloWorld-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + ACF9162361FB8F24C70657DE /* Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 5E36905C1B2A23800040F884 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5E36906C1B2A23800040F884 /* ViewController.m in Sources */, + 5E3690691B2A23800040F884 /* AppDelegate.m in Sources */, + 5E3690661B2A23800040F884 /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 5E36906D1B2A23800040F884 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 5E36906E1B2A23800040F884 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 5E3690811B2A23810040F884 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.2; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 5E3690821B2A23810040F884 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.2; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 5E3690841B2A23810040F884 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = DBDE3E48389499064CD664B8 /* Pods-HelloWorld.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = HelloWorld/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 5E3690851B2A23810040F884 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 0C432EF610DB15C0F47A66BB /* Pods-HelloWorld.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = HelloWorld/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 5E36905B1B2A23800040F884 /* Build configuration list for PBXProject "HelloWorld" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5E3690811B2A23810040F884 /* Debug */, + 5E3690821B2A23810040F884 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 5E3690831B2A23810040F884 /* Build configuration list for PBXNativeTarget "HelloWorld" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5E3690841B2A23810040F884 /* Debug */, + 5E3690851B2A23810040F884 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 5E3690581B2A23800040F884 /* Project object */; +} diff --git a/examples/objective-c/helloworld/HelloWorld.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/examples/objective-c/helloworld/HelloWorld.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..174a04ec --- /dev/null +++ b/examples/objective-c/helloworld/HelloWorld.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/examples/objective-c/helloworld/HelloWorld/AppDelegate.h b/examples/objective-c/helloworld/HelloWorld/AppDelegate.h new file mode 100644 index 00000000..102e7f3a --- /dev/null +++ b/examples/objective-c/helloworld/HelloWorld/AppDelegate.h @@ -0,0 +1,38 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import + +@interface AppDelegate : UIResponder +@property (strong, nonatomic) UIWindow *window; +@end diff --git a/examples/objective-c/helloworld/HelloWorld/AppDelegate.m b/examples/objective-c/helloworld/HelloWorld/AppDelegate.m new file mode 100644 index 00000000..a38e3665 --- /dev/null +++ b/examples/objective-c/helloworld/HelloWorld/AppDelegate.m @@ -0,0 +1,37 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "AppDelegate.h" + +@implementation AppDelegate +@end diff --git a/examples/objective-c/helloworld/HelloWorld/Base.lproj/Main.storyboard b/examples/objective-c/helloworld/HelloWorld/Base.lproj/Main.storyboard new file mode 100644 index 00000000..f56d2f3b --- /dev/null +++ b/examples/objective-c/helloworld/HelloWorld/Base.lproj/Main.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/objective-c/helloworld/HelloWorld/Images.xcassets/AppIcon.appiconset/Contents.json b/examples/objective-c/helloworld/HelloWorld/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..36d2c80d --- /dev/null +++ b/examples/objective-c/helloworld/HelloWorld/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/objective-c/helloworld/HelloWorld/Info.plist b/examples/objective-c/helloworld/HelloWorld/Info.plist new file mode 100644 index 00000000..1078fff7 --- /dev/null +++ b/examples/objective-c/helloworld/HelloWorld/Info.plist @@ -0,0 +1,47 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + Google.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + Main + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/examples/objective-c/helloworld/HelloWorld/ViewController.m b/examples/objective-c/helloworld/HelloWorld/ViewController.m new file mode 100644 index 00000000..090fd936 --- /dev/null +++ b/examples/objective-c/helloworld/HelloWorld/ViewController.m @@ -0,0 +1,40 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import + +@interface ViewController : UIViewController +@end + +@implementation ViewController +@end diff --git a/examples/objective-c/helloworld/Podfile b/examples/objective-c/helloworld/Podfile new file mode 100644 index 00000000..2934ebc2 --- /dev/null +++ b/examples/objective-c/helloworld/Podfile @@ -0,0 +1,7 @@ +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '8.0' + +target 'HelloWorld' do + # Depend on the generated HelloWorld library. + pod 'HelloWorld', :path => '.' +end diff --git a/examples/objective-c/helloworld/README.md b/examples/objective-c/helloworld/README.md new file mode 100644 index 00000000..75df1a7a --- /dev/null +++ b/examples/objective-c/helloworld/README.md @@ -0,0 +1,56 @@ +#gRPC in 3 minutes (Objective-C) + +## Installation + +To run this example you should have [Cocoapods](https://cocoapods.org/#install) installed, as well +as the relevant tools to generate the client library code (and a server in another language, for +testing). You can obtain the latter by following [these setup instructions](https://github.com/grpc/homebrew-grpc). + +## Hello Objective-C gRPC! + +Here's how to build and run the Objective-C implementation of the [Hello World](../../protos/helloworld.proto) +example used in [Getting started](https://github.com/grpc/grpc/tree/master/examples). + +The example code for this and our other examples lives in the `examples` directory. Clone +this repository to your local machine by running the following command: + + +```sh +$ git clone https://github.com/grpc/grpc.git +``` + +Change your current directory to `examples/objective-c/helloworld` + +```sh +$ cd examples/objective-c/helloworld +``` + +### Try it! +To try the sample app, we need a gRPC server running locally. Let's compile and run, for example, +the C++ server in this repository: + +```shell +$ pushd ../../cpp/helloworld +$ make +$ ./greeter_server & +$ popd +``` + +Now have Cocoapods generate and install the client library for our .proto files: + +```shell +$ pod install +``` + +(This might have to compile OpenSSL, which takes around 15 minutes if Cocoapods doesn't have it yet +on your computer's cache.) + +Finally, open the XCode workspace created by Cocoapods, and run the app. You can check the calling +code in `main.m` and see the results in XCode's log console. + +The code sends a `HLWHelloRequest` containing the string "Objective-C" to a local server. The server +responds with a `HLWHelloResponse`, which contains a string that is then output to the log. + +## Tutorial + +You can find a more detailed tutorial in [gRPC Basics: Objective-C](../route_guide/README.md). diff --git a/examples/objective-c/helloworld/main.m b/examples/objective-c/helloworld/main.m new file mode 100644 index 00000000..458580be --- /dev/null +++ b/examples/objective-c/helloworld/main.m @@ -0,0 +1,51 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import +#import "AppDelegate.h" + +#import + +static NSString * const kHostAddress = @"http://localhost:50051"; + +int main(int argc, char * argv[]) { + @autoreleasepool { + HLWGreeter *client = [[HLWGreeter alloc] initWithHost:kHostAddress]; + HLWHelloRequest *request = [HLWHelloRequest message]; + request.name = @"Objective-C"; + [client sayHelloWithRequest:request handler:^(HLWHelloReply *response, NSError *error) { + NSLog(@"%@", response.message); + }]; + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/examples/objective-c/route_guide/Misc/AppDelegate.h b/examples/objective-c/route_guide/Misc/AppDelegate.h new file mode 100644 index 00000000..102e7f3a --- /dev/null +++ b/examples/objective-c/route_guide/Misc/AppDelegate.h @@ -0,0 +1,38 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import + +@interface AppDelegate : UIResponder +@property (strong, nonatomic) UIWindow *window; +@end diff --git a/examples/objective-c/route_guide/Misc/AppDelegate.m b/examples/objective-c/route_guide/Misc/AppDelegate.m new file mode 100644 index 00000000..a38e3665 --- /dev/null +++ b/examples/objective-c/route_guide/Misc/AppDelegate.m @@ -0,0 +1,37 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "AppDelegate.h" + +@implementation AppDelegate +@end diff --git a/examples/objective-c/route_guide/Misc/Base.lproj/Main.storyboard b/examples/objective-c/route_guide/Misc/Base.lproj/Main.storyboard new file mode 100644 index 00000000..9bf9498d --- /dev/null +++ b/examples/objective-c/route_guide/Misc/Base.lproj/Main.storyboard @@ -0,0 +1,193 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/objective-c/route_guide/Misc/Images.xcassets/AppIcon.appiconset/Contents.json b/examples/objective-c/route_guide/Misc/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..36d2c80d --- /dev/null +++ b/examples/objective-c/route_guide/Misc/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/objective-c/route_guide/Misc/Images.xcassets/first.imageset/Contents.json b/examples/objective-c/route_guide/Misc/Images.xcassets/first.imageset/Contents.json new file mode 100644 index 00000000..33a74510 --- /dev/null +++ b/examples/objective-c/route_guide/Misc/Images.xcassets/first.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "first.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/objective-c/route_guide/Misc/Images.xcassets/first.imageset/first.pdf b/examples/objective-c/route_guide/Misc/Images.xcassets/first.imageset/first.pdf new file mode 100644 index 0000000000000000000000000000000000000000..47d911dea647d55983671ead4d08b6f6b3600715 GIT binary patch literal 2465 zcmai03se(l7FLvisY+L#f-EBsK`11Xkc1=%MIujmB|xQ!>5vQ%APFP`Mlf5nSPOKE z2-^C>7DZkM!J;Da5{kfrC?Kvp3J41-3shFXvhuJqKo*oer*r0H{{P-P_x0cTzLzjD!bE z0qT;#Q7l4Gy%fMoXJaKT`@{5#R(MOqJPwQifv8iK6A%Ot9L14h2`38T!2s4PM=1!< zmL06}VYAA|ay#jZRs>HpA%X+eQW4rufWU%d1w5GTy!X#LeZsF_+~ccZmn3Fi)v^Z; zIG;?uU*yLLEYs61tjD>gXOFvSWsh{48xJvPNqKrIJtMdCz2cA2aC7TF?b@K`V!Lw- zE;zpH&ApqhoRjAHt}gK}>(qAc8dvrkD31*`<5<%C(&4_LqhQ3-GOf8~n^leG?+-@@^pjPa$J2gW@O)!b9hdTJ zTauyIJ&~rqeEZC1p9dWgx7{_WRc2=drMO=wcT7B{Zd58z`d)r_36r4V<6PGkCjpGfbnVMtm@;b}F!T?)Qav$_y7H5T;Mf!T}M zWxP9TNqrV?e5;b|pWd3^u2KAatk&3aDB@0 zZBaQNdCZ2#fblzYZnRCCjQ-GQWb-s8bX&<)?SxnUGdDYVFVk`xIf7@g3~$DX`H71_;r5OSZz1p`$_8y_9 z{;MJJ+n=>7Ewg;GnGHoz)&ID0z@F2!e$F7cWQ?d6s(!VY)_Gw})xCyMvsD={5i&H* zAIr_ACo8;Se6<*!-mm9Am79Iz^RVlc?%S5sg|E*SyIV{dd9{Mpf#d3cih5WKt=%ps zBEo)bt8EjmeCFYJRYU|b7d`p+-V|X2wOCYtyLP6t=!WH-kgdf0A};ytPfZiCwVPx{ z`g;zpe{8a4RQxQUwVU02<4X3w|9h;}XjhGWquqn{vVqq8g{*}BQF9=ybe_TM*!M=rFs1pN)yzctIXAoicsCe6>fit>wgZ#vp^hZOY0`J`rx zwdSK?GwR_xm9;5XjH|vf{O+Yg-)z;s*xt>;-vU`D-_SK<=~4eEVQkV~57{vyH6ebu>DqwE~Mx_A31Lg~$ zy`i52ew?GEluE%AN>ju*5z~dx2QQLP-G+E>o|En2+s&<^4*d zWD+<8PXs_Jp7_5Dpi(Gg3J?M;%Rn0{dcxQRh!ip+iHJ_e`!bM1MGxwI8Iedr+x>wI zq@&04fsADHfBccq^gpm8Q_u-~Uj|Z$YwSR(^_mzUb!}Xb2Cj`uqphg}v_Y%?Fs=;= zWq3s{hy)Tu!ji@RJsd$C+G{F6V2j1*Xk+7xo|LO7M2yb)(wc=%x-AclP9pLsbTF7m tf?*P!2EjJu5MD57P32Q4pdJ3tO_shCDT2=K;$ebx8ymcd$v!V9{2$sQiO>K5 literal 0 HcmV?d00001 diff --git a/examples/objective-c/route_guide/Misc/Images.xcassets/second.imageset/Contents.json b/examples/objective-c/route_guide/Misc/Images.xcassets/second.imageset/Contents.json new file mode 100644 index 00000000..03bd9c92 --- /dev/null +++ b/examples/objective-c/route_guide/Misc/Images.xcassets/second.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "second.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/objective-c/route_guide/Misc/Images.xcassets/second.imageset/second.pdf b/examples/objective-c/route_guide/Misc/Images.xcassets/second.imageset/second.pdf new file mode 100644 index 0000000000000000000000000000000000000000..401614e288b4b160471c2776bed6f09762af3e1c GIT binary patch literal 2423 zcmai02~-nz8dqq7rWU#)x2z)&iMWtV?gK$2a)?|BP!TbkV}J-Gl1wmy+3G9S0=uOY zwCjai6uBOPMMdNiN`VDYKwLQr2#W^`R93*Ua=0^)EGYY4=grIf|KIohziYnv{elnR zdN_hk6db<(+3RPcxs!iut8c(j0TLh=9mY931H^rhEE0|aXo!dh5Iv+ZFr+}9F+v#P zLSnfD0$f~hN>~92V{z)WJA&2)5rW!|I?;o?oI*%zejJiuY>hFLQwKlAa9O+PnPh#% zJp~JAfs-De6u@;{cREMs}D-sGib_4>50m8_|Uj-zC0m@Qx zJV+^5Da4Qxpf3sBtKZs<_aI$Cb1T+h<->yL^rfiGNelYkqgC3#SqVP_iy*_RPcbjH)xYf zvYdC-)D_t8c{=5%;$VJQxs_#61J*kuXMC?|&PbPo-7uSxoNr~&J94|gKk+03JGXXM zx4w~=^VUzWqQveu_ilRgPdgNNdnLZCH`=;w91}cP5ihdm*B_jI+rk=Pz2Lk!)XUu| zkls3P5&YHJ!t0kF+MUVBHOAx)a zy|JxHNd37z*~b5#%*0w_F|#4?erNP;pG`Fevoq2%?9Y>%Cmzv1(ks;aPIGaiy1mhU zr{7lB^Mem&`QK{b{uiDC zLD8muY(FNk{YdV|zO#o_VB%);8|~Bh(Z4p?GM}g-HN2;q7~YNl-lD~HZ$taYw$Fzbn6c?c5eo=@`i>()el#qwB)hI-gp0 ztsw@dh-I5NJkk)RCoBC`L8ffKfY)=oG=#4LA9TO6eA}9~@a6dupB+=S{%s&NY}^0GY`hAqod)uxXHitrN%bWOJohV>t?!&Z#d4y^7J@=Oh`HV zee**4j>xRUd)|D0^I-B#<`m1-5PO{)!v(q?ecFYqr_8Q{+z7#n0uFbq%_?hZ-Ck(6NRn=vDS7Nb1Xxx_)9`!i- zGR>av*PJ+P_AGO*a_$FbtCCISg5PK44p&W#t$)+HVcxpR9LAL%_Pv$p`0tO*sYZD0 zCG3-ipr4WoVbjpw%Nf^r^rnWcDcjumgBPEjO;NM6QtZ8wU#0rjHx5zs%CahF4$W*U zxu!q8kYP+>v!BNPVIRJUGIWU@7qVvx2VZDw4gV8%z%bjpF#aF(W|PmliwlakuRq>= zhaCRc^R!7T)#Twb0{!Tns=CxGcGX|leErg`f3|v0eBXxHU&6Sj-_>zOL!!d+j5Xt* zPS;rWd^zEF{5d5%)nNK=)zFJ8Uc=aQKD%UK)_TNes1Gh{SD*Mb_@O` z7*zs9Uq~Vq?v|^8FcM-yLGMKzR3YFB0z@CFM2X-dio%Gu6&MJj=I?uS7sA3AdE^p8 zE49$PjJzPIJwfn;jso7X2#jynr3dO@p5QAK1O9wqKLC=PkQjj~p#n|-5O`ihbTzzu zL{}2QEe28pJYfVZRW56FL`LvO&|C^1Th{0}hQMW$Ng$QPpnzlwnMnf=kVy8(y==L% zLMa>r0gELdT + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + gRPC.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + Main + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UIStatusBarTintParameters + + UINavigationBar + + Style + UIBarStyleDefault + Translucent + + + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/examples/objective-c/route_guide/Misc/main.m b/examples/objective-c/route_guide/Misc/main.m new file mode 100644 index 00000000..fb701005 --- /dev/null +++ b/examples/objective-c/route_guide/Misc/main.m @@ -0,0 +1,41 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import +#import "AppDelegate.h" + +int main(int argc, char * argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/examples/objective-c/route_guide/Podfile b/examples/objective-c/route_guide/Podfile new file mode 100644 index 00000000..a97f4146 --- /dev/null +++ b/examples/objective-c/route_guide/Podfile @@ -0,0 +1,7 @@ +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '8.0' + +target 'RouteGuideClient' do + # Depend on the generated RouteGuide library. + pod 'RouteGuide', :path => '.' +end diff --git a/examples/objective-c/route_guide/README.md b/examples/objective-c/route_guide/README.md new file mode 100644 index 00000000..15864c01 --- /dev/null +++ b/examples/objective-c/route_guide/README.md @@ -0,0 +1,360 @@ +#gRPC Basics: Objective-C + +This tutorial provides a basic Objective-C programmer's introduction to working with gRPC. By +walking through this example you'll learn how to: + +- Define a service in a .proto file. +- Generate client code using the protocol buffer compiler. +- Use the Objective-C gRPC API to write a simple client for your service. + +It assumes a passing familiarity with [protocol buffers](https://developers.google.com/protocol-buffers/docs/overview). +Note that the example in this tutorial uses the proto3 version of the protocol buffers language, +which is currently in alpha release: you can find out more in the [proto3 language guide](https://developers.google.com/protocol-buffers/docs/proto3) +and see the [release notes](https://github.com/google/protobuf/releases) for the new version in the +protocol buffers Github repository. + +This isn't a comprehensive guide to using gRPC in Objective-C: more reference documentation is +coming soon. + +- [Why use gRPC?](#why-grpc) +- [Example code and setup](#setup) +- [Try it out!](#try) +- [Defining the service](#proto) +- [Generating client code](#protoc) +- [Creating the client](#client) + + +## Why use gRPC? + +With gRPC you can define your service once in a .proto file and implement clients and servers in any +of gRPC's supported languages, which in turn can be run in environments ranging from servers inside +Google to your own tablet - all the complexity of communication between different languages and +environments is handled for you by gRPC. You also get all the advantages of working with protocol +buffers, including efficient serialization, a simple IDL, and easy interface updating. + +gRPC and proto3 are specially suited for mobile clients: gRPC is implemented on top of HTTP/2, which +results in network bandwidth savings over using HTTP/1.1. Serialization and parsing of the proto +binary format is more efficient than the equivalent JSON, resulting in CPU and battery savings. And +proto3 uses a runtime that has been optimized over the years at Google to keep code size to a +minimum. The latter is important in Objective-C, because the ability of the compiler to strip unused +code is limited by the dynamic nature of the language. + + + +## Example code and setup + +The example code for our tutorial is in [examples/objective-c/route_guide](.). +To download the example, clone this repository by running the following command: +```shell +$ git clone https://github.com/grpc/grpc.git +``` + +Then change your current directory to `examples/objective-c/route_guide`: +```shell +$ cd examples/objective-c/route_guide +``` + +Our example is a simple route mapping application that lets clients get information about features +on their route, create a summary of their route, and exchange route information such as traffic +updates with the server and other clients. + +You also should have [Cocoapods](https://cocoapods.org/#install) installed, as well as the relevant +tools to generate the client library code (and a server in another language, for testing). You can +obtain the latter by following [these setup instructions](https://github.com/grpc/homebrew-grpc). + + + +## Try it out! + +To try the sample app, we need a gRPC server running locally. Let's compile and run, for example, +the C++ server in this repository: + +```shell +$ pushd ../../cpp/route_guide +$ make +$ ./route_guide_server & +$ popd +``` + +Now have Cocoapods generate and install the client library for our .proto files: + +```shell +$ pod install +``` + +(This might have to compile OpenSSL, which takes around 15 minutes if Cocoapods doesn't have it yet +on your computer's cache). + +Finally, open the XCode workspace created by Cocoapods, and run the app. You can check the calling +code in `ViewControllers.m` and see the results in XCode's log console. + +The next sections guide you step-by-step through how this proto service is defined, how to generate +a client library from it, and how to create an app that uses that library. + + + +## Defining the service + +First let's look at how the service we're using is defined. A gRPC *service* and its method +*request* and *response* types using [protocol buffers](https://developers.google.com/protocol-buffers/docs/overview). +You can see the complete .proto file for our example in [`examples/protos/route_guide.proto`](../../protos/route_guide.proto). + +To define a service, you specify a named `service` in your .proto file: + +```protobuf +service RouteGuide { + ... +} +``` + +Then you define `rpc` methods inside your service definition, specifying their request and response +types. Protocol buffers let you define four kinds of service method, all of which are used in the +`RouteGuide` service: + +- A *simple RPC* where the client sends a request to the server and receives a response later, just +like a normal remote procedure call. +```protobuf + // Obtains the feature at a given position. + rpc GetFeature(Point) returns (Feature) {} +``` + +- A *response-streaming RPC* where the client sends a request to the server and gets back a stream +of response messages. You specify a response-streaming method by placing the `stream` keyword before +the *response* type. +```protobuf + // Obtains the Features available within the given Rectangle. Results are + // streamed rather than returned at once (e.g. in a response message with a + // repeated field), as the rectangle may cover a large area and contain a + // huge number of features. + rpc ListFeatures(Rectangle) returns (stream Feature) {} +``` + +- A *request-streaming RPC* where the client sends a sequence of messages to the server. Once the +client has finished writing the messages, it waits for the server to read them all and return its +response. You specify a request-streaming method by placing the `stream` keyword before the +*request* type. +```protobuf + // Accepts a stream of Points on a route being traversed, returning a + // RouteSummary when traversal is completed. + rpc RecordRoute(stream Point) returns (RouteSummary) {} +``` + +- A *bidirectional streaming RPC* where both sides send a sequence of messages to the other. The two +streams operate independently, so clients and servers can read and write in whatever order they +like: for example, the server could wait to receive all the client messages before writing its +responses, or it could alternately read a message then write a message, or some other combination of +reads and writes. The order of messages in each stream is preserved. You specify this type of method +by placing the `stream` keyword before both the request and the response. +```protobuf + // Accepts a stream of RouteNotes sent while a route is being traversed, + // while receiving other RouteNotes (e.g. from other users). + rpc RouteChat(stream RouteNote) returns (stream RouteNote) {} +``` + +Our .proto file also contains protocol buffer message type definitions for all the request and +response types used in our service methods - for example, here's the `Point` message type: +```protobuf +// Points are represented as latitude-longitude pairs in the E7 representation +// (degrees multiplied by 10**7 and rounded to the nearest integer). +// Latitudes should be in the range +/- 90 degrees and longitude should be in +// the range +/- 180 degrees (inclusive). +message Point { + int32 latitude = 1; + int32 longitude = 2; +} +``` + +You can specify a prefix to be used for your generated classes by adding the `objc_class_prefix` +option at the top of the file. For example: +```protobuf +option objc_class_prefix = "RTG"; +``` + + + +## Generating client code + +Next we need to generate the gRPC client interfaces from our .proto service definition. We do this +using the protocol buffer compiler (`protoc`) with a special gRPC Objective-C plugin. + +For simplicity, we've provided a [Podspec file](RouteGuide.podspec) +that runs `protoc` for you with the appropriate plugin, input, and output, and describes how to +compile the generated files. You just need to run in this directory (`examples/objective-c/route_guide`): + +```shell +$ pod install +``` + +which, before installing the generated library in the XCode project of this sample, runs: + +```shell +$ protoc -I ../../protos --objc_out=Pods/RouteGuide --objcgrpc_out=Pods/RouteGuide ../../protos/route_guide.proto +``` + +Running this command generates the following files under `Pods/RouteGuide/`: +- `RouteGuide.pbobjc.h`, the header which declares your generated message classes. +- `RouteGuide.pbobjc.m`, which contains the implementation of your message classes. +- `RouteGuide.pbrpc.h`, the header which declares your generated service classes. +- `RouteGuide.pbrpc.m`, which contains the implementation of your service classes. + +These contain: +- All the protocol buffer code to populate, serialize, and retrieve our request and response message +types. +- A class called `RTGRouteGuide` that lets clients call the methods defined in the `RouteGuide` +service. + +You can also use the provided Podspec file to generate client code from any other proto service +definition; just replace the name (matching the file name), version, and other metadata. + + + +## Creating the client + +In this section, we'll look at creating an Objective-C client for our `RouteGuide` service. You can +see our complete example client code in [ViewControllers.m](ViewControllers.m). +(Note: In your apps, for maintainability and readability reasons, you shouldn't put all of your view +controllers in a single file; it's done here only to simplify the learning process). + +### Constructing a client object + +To call service methods, we first need to create a client object, an instance of the generated +`RTGRouteGuide` class. The designated initializer of the class expects a `NSString *` with the +server address and port we want to connect to: + +```objective-c +#import + +static NSString * const kHostAddress = @"http://localhost:50051"; + +... + +RTGRouteGuide *client = [[RTGRouteGuide alloc] initWithHost:kHostAddress]; +``` + +Notice that we've specified the HTTP scheme in the host address. This is because the server we will +be using to test our client doesn't use [TLS](http://en.wikipedia.org/wiki/Transport_Layer_Security). +This is fine because it will be running locally on our development machine. The most common case, +though, is connecting with a gRPC server on the internet, running gRPC over TLS. For that case, the +HTTPS scheme can be specified (or no scheme at all, as HTTPS is the default value). The default +value of the port is that of the scheme selected: 443 for HTTPS and 80 for HTTP. + + +### Calling service methods + +Now let's look at how we call our service methods. As you will see, all these methods are +asynchronous, so you can call them from the main thread of your app without worrying about freezing +your UI or the OS killing your app. + +#### Simple RPC + +Calling the simple RPC `GetFeature` is nearly as straightforward as calling any other asynchronous +method on Cocoa. + +```objective-c +RTGPoint *point = [RTGPoint message]; +point.latitude = 40E7; +point.longitude = -74E7; + +[client getFeatureWithRequest:point handler:^(RTGFeature *response, NSError *error) { + if (response) { + // Successful response received + } else { + // RPC error + } +}]; +``` + +As you can see, we create and populate a request protocol buffer object (in our case `RTGPoint`). +Then, we call the method on the client object, passing it the request, and a block to handle the +response (or any RPC error). If the RPC finishes successfully, the handler block is called with a +`nil` error argument, and we can read the response information from the server from the response +argument. If, instead, some RPC error happens, the handler block is called with a `nil` response +argument, and we can read the details of the problem from the error argument. + +```objective-c +NSLog(@"Found feature called %@ at %@.", response.name, response.location); +``` + +#### Streaming RPCs + +Now let's look at our streaming methods. Here's where we call the response-streaming method +`ListFeatures`, which results in our client receiving a stream of geographical `RTGFeature`s: + +```objective-c +[client listFeaturesWithRequest:rectangle + eventHandler:^(BOOL done, RTGFeature *response, NSError *error) { + if (response) { + // Element of the stream of responses received + } else if (error) { + // RPC error; the stream is over. + } + if (done) { + // The stream is over (all the responses were received, or an error occured). Do any cleanup. + } +}]; +``` + +Notice how the signature of the `eventHandler` block now includes a `BOOL done` parameter. The +`eventHandler` block can be called any number of times; only on the last call is the `done` argument +value set to `YES`. If an error occurs, the RPC finishes and the block is called with the arguments +`(YES, nil, error)`. + +The request-streaming method `RecordRoute` expects a stream of `RTGPoint`s from the cient. This +stream is passed to the method as an object of class `GRXWriter`. The simplest way to create one is +to initialize one from a `NSArray` object: + + +```objective-c +#import + +... + +RTGPoint *point1 = [RTGPoint message]; +point.latitude = 40E7; +point.longitude = -74E7; + +RTGPoint *point2 = [RTGPoint message]; +point.latitude = 40E7; +point.longitude = -74E7; + +GRXWriter *locationsWriter = [GRXWriter writerWithContainer:@[point1, point2]]; + +[client recordRouteWithRequestsWriter:locationsWriter + handler:^(RTGRouteSummary *response, NSError *error) { + if (response) { + NSLog(@"Finished trip with %i points", response.pointCount); + NSLog(@"Passed %i features", response.featureCount); + NSLog(@"Travelled %i meters", response.distance); + NSLog(@"It took %i seconds", response.elapsedTime); + } else { + NSLog(@"RPC error: %@", error); + } +}]; + +``` + +The `GRXWriter` class is generic enough to allow for asynchronous streams, streams of future values, +or even infinite streams. + +Finally, let's look at our bidirectional streaming RPC `RouteChat()`. The way to call a +bidirectional streaming RPC is just a combination of how to call request-streaming RPCs and +response-streaming RPCs. + +```objective-c +[client routeChatWithRequestsWriter:notesWriter + eventHandler:^(BOOL done, RTGRouteNote *note, NSError *error) { + if (note) { + NSLog(@"Got message %@ at %@", note.message, note.location); + } else if (error) { + NSLog(@"RPC error: %@", error); + } + if (done) { + NSLog(@"Chat ended."); + } +}]; +``` + +The semantics for the handler block and the `GRXWriter` argument here are exactly the same as for +our request-streaming and response-streaming methods. Although both client and server will always +get the other's messages in the order they were written, the two streams operate completely +independently. diff --git a/examples/objective-c/route_guide/RouteGuide.podspec b/examples/objective-c/route_guide/RouteGuide.podspec new file mode 100644 index 00000000..7b99a6c6 --- /dev/null +++ b/examples/objective-c/route_guide/RouteGuide.podspec @@ -0,0 +1,35 @@ +Pod::Spec.new do |s| + s.name = "RouteGuide" + s.version = "0.0.1" + s.license = "New BSD" + + s.ios.deployment_target = "6.0" + s.osx.deployment_target = "10.8" + + # Base directory where the .proto files are. + src = "../../protos" + + # Directory where the generated files will be place. + dir = "Pods/" + s.name + + # Run protoc with the Objective-C and gRPC plugins to generate protocol messages and gRPC clients. + s.prepare_command = <<-CMD + mkdir -p #{dir} + protoc -I #{src} --objc_out=#{dir} --objcgrpc_out=#{dir} #{src}/route_guide.proto + CMD + + s.subspec "Messages" do |ms| + ms.source_files = "#{dir}/*.pbobjc.{h,m}", "#{dir}/**/*.pbobjc.{h,m}" + ms.header_mappings_dir = dir + ms.requires_arc = false + ms.dependency "Protobuf", "~> 3.0.0-alpha-3" + end + + s.subspec "Services" do |ss| + ss.source_files = "#{dir}/*.pbrpc.{h,m}", "#{dir}/**/*.pbrpc.{h,m}" + ss.header_mappings_dir = dir + ss.requires_arc = true + ss.dependency "gRPC", "~> 0.6" + ss.dependency "#{s.name}/Messages" + end +end diff --git a/examples/objective-c/route_guide/RouteGuideClient.xcodeproj/project.pbxproj b/examples/objective-c/route_guide/RouteGuideClient.xcodeproj/project.pbxproj new file mode 100644 index 00000000..6ab6b27a --- /dev/null +++ b/examples/objective-c/route_guide/RouteGuideClient.xcodeproj/project.pbxproj @@ -0,0 +1,366 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 124E20A2FC8EAE54460D4ED2 /* libPods-RouteGuideClient.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 71CEE03D66D40FC37264D6E4 /* libPods-RouteGuideClient.a */; }; + 632527831B1D0396003073D9 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 632527821B1D0396003073D9 /* main.m */; }; + 632527861B1D0396003073D9 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 632527851B1D0396003073D9 /* AppDelegate.m */; }; + 6325278F1B1D0396003073D9 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6325278D1B1D0396003073D9 /* Main.storyboard */; }; + 632527911B1D0396003073D9 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 632527901B1D0396003073D9 /* Images.xcassets */; }; + 6367B55B1B223AFA008861F5 /* route_guide_db.json in Resources */ = {isa = PBXBuildFile; fileRef = 6367B55A1B223AFA008861F5 /* route_guide_db.json */; }; + 63A6015C1B1DAB5000FA5B86 /* ViewControllers.m in Sources */ = {isa = PBXBuildFile; fileRef = 63A6015B1B1DAB5000FA5B86 /* ViewControllers.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 6325277D1B1D0396003073D9 /* RouteGuideClient.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RouteGuideClient.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 632527811B1D0396003073D9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 632527821B1D0396003073D9 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 632527841B1D0396003073D9 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 632527851B1D0396003073D9 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 6325278E1B1D0396003073D9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 632527901B1D0396003073D9 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + 6367B55A1B223AFA008861F5 /* route_guide_db.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = route_guide_db.json; sourceTree = ""; }; + 63A6015B1B1DAB5000FA5B86 /* ViewControllers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ViewControllers.m; sourceTree = ""; }; + 71CEE03D66D40FC37264D6E4 /* libPods-RouteGuideClient.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RouteGuideClient.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + ADA4C647BAE906F79AD9A45E /* Pods-RouteGuideClient.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RouteGuideClient.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RouteGuideClient/Pods-RouteGuideClient.debug.xcconfig"; sourceTree = ""; }; + C83C5A54D1A4EA07569F1AED /* Pods-RouteGuideClient.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RouteGuideClient.release.xcconfig"; path = "Pods/Target Support Files/Pods-RouteGuideClient/Pods-RouteGuideClient.release.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 6325277A1B1D0395003073D9 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 124E20A2FC8EAE54460D4ED2 /* libPods-RouteGuideClient.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 56849C29DC376BF4B902CD77 /* Pods */ = { + isa = PBXGroup; + children = ( + ADA4C647BAE906F79AD9A45E /* Pods-RouteGuideClient.debug.xcconfig */, + C83C5A54D1A4EA07569F1AED /* Pods-RouteGuideClient.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; + 631C63891B1DBC41001295D5 /* Misc */ = { + isa = PBXGroup; + children = ( + 632527841B1D0396003073D9 /* AppDelegate.h */, + 632527851B1D0396003073D9 /* AppDelegate.m */, + 632527901B1D0396003073D9 /* Images.xcassets */, + 632527801B1D0396003073D9 /* Supporting Files */, + ); + path = Misc; + sourceTree = ""; + }; + 632527741B1D0395003073D9 = { + isa = PBXGroup; + children = ( + 6325277F1B1D0396003073D9 /* RouteGuideClient */, + 6325277E1B1D0396003073D9 /* Products */, + 56849C29DC376BF4B902CD77 /* Pods */, + 7482B8A18481F7B13ADE4530 /* Frameworks */, + ); + sourceTree = ""; + }; + 6325277E1B1D0396003073D9 /* Products */ = { + isa = PBXGroup; + children = ( + 6325277D1B1D0396003073D9 /* RouteGuideClient.app */, + ); + name = Products; + sourceTree = ""; + }; + 6325277F1B1D0396003073D9 /* RouteGuideClient */ = { + isa = PBXGroup; + children = ( + 63A6015B1B1DAB5000FA5B86 /* ViewControllers.m */, + 6367B55A1B223AFA008861F5 /* route_guide_db.json */, + 6325278D1B1D0396003073D9 /* Main.storyboard */, + 631C63891B1DBC41001295D5 /* Misc */, + ); + name = RouteGuideClient; + sourceTree = SOURCE_ROOT; + }; + 632527801B1D0396003073D9 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 632527811B1D0396003073D9 /* Info.plist */, + 632527821B1D0396003073D9 /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 7482B8A18481F7B13ADE4530 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 71CEE03D66D40FC37264D6E4 /* libPods-RouteGuideClient.a */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 6325277C1B1D0395003073D9 /* RouteGuideClient */ = { + isa = PBXNativeTarget; + buildConfigurationList = 632527A31B1D0396003073D9 /* Build configuration list for PBXNativeTarget "RouteGuideClient" */; + buildPhases = ( + C6FC30AD2376EC04317237C5 /* Check Pods Manifest.lock */, + 632527791B1D0395003073D9 /* Sources */, + 6325277A1B1D0395003073D9 /* Frameworks */, + 6325277B1B1D0395003073D9 /* Resources */, + FFE0BCF30339E7A50A989EAB /* Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = RouteGuideClient; + productName = RouteGuideClient; + productReference = 6325277D1B1D0396003073D9 /* RouteGuideClient.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 632527751B1D0395003073D9 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0630; + ORGANIZATIONNAME = gRPC; + TargetAttributes = { + 6325277C1B1D0395003073D9 = { + CreatedOnToolsVersion = 6.3.1; + }; + }; + }; + buildConfigurationList = 632527781B1D0395003073D9 /* Build configuration list for PBXProject "RouteGuideClient" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 632527741B1D0395003073D9; + productRefGroup = 6325277E1B1D0396003073D9 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 6325277C1B1D0395003073D9 /* RouteGuideClient */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 6325277B1B1D0395003073D9 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6325278F1B1D0396003073D9 /* Main.storyboard in Resources */, + 632527911B1D0396003073D9 /* Images.xcassets in Resources */, + 6367B55B1B223AFA008861F5 /* route_guide_db.json in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + C6FC30AD2376EC04317237C5 /* Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; + FFE0BCF30339E7A50A989EAB /* Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-RouteGuideClient/Pods-RouteGuideClient-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 632527791B1D0395003073D9 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 632527861B1D0396003073D9 /* AppDelegate.m in Sources */, + 632527831B1D0396003073D9 /* main.m in Sources */, + 63A6015C1B1DAB5000FA5B86 /* ViewControllers.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 6325278D1B1D0396003073D9 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 6325278E1B1D0396003073D9 /* Base */, + ); + name = Main.storyboard; + path = Misc; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 632527A11B1D0396003073D9 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.3; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 632527A21B1D0396003073D9 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.3; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 632527A41B1D0396003073D9 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = ADA4C647BAE906F79AD9A45E /* Pods-RouteGuideClient.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = Misc/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 632527A51B1D0396003073D9 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = C83C5A54D1A4EA07569F1AED /* Pods-RouteGuideClient.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = Misc/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 632527781B1D0395003073D9 /* Build configuration list for PBXProject "RouteGuideClient" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 632527A11B1D0396003073D9 /* Debug */, + 632527A21B1D0396003073D9 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 632527A31B1D0396003073D9 /* Build configuration list for PBXNativeTarget "RouteGuideClient" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 632527A41B1D0396003073D9 /* Debug */, + 632527A51B1D0396003073D9 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 632527751B1D0395003073D9 /* Project object */; +} diff --git a/examples/objective-c/route_guide/RouteGuideClient.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/examples/objective-c/route_guide/RouteGuideClient.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..f208589e --- /dev/null +++ b/examples/objective-c/route_guide/RouteGuideClient.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/examples/objective-c/route_guide/ViewControllers.m b/examples/objective-c/route_guide/ViewControllers.m new file mode 100644 index 00000000..cfc3338b --- /dev/null +++ b/examples/objective-c/route_guide/ViewControllers.m @@ -0,0 +1,228 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import +#import +#import +#import + +static NSString * const kHostAddress = @"http://localhost:50051"; + +// Category to override RTGPoint's description. +@interface RTGPoint (Description) +- (NSString *)description; +@end + +@implementation RTGPoint (Description) +- (NSString *)description { + NSString *verticalDirection = self.latitude >= 0 ? @"N" : @"S"; + NSString *horizontalDirection = self.longitude >= 0 ? @"E" : @"W"; + return [NSString stringWithFormat:@"%.02f%@ %.02f%@", + abs(self.latitude) / 1E7f, verticalDirection, + abs(self.longitude) / 1E7f, horizontalDirection]; +} +@end + +// Category to give RTGRouteNote a convenience constructor. +@interface RTGRouteNote (Constructors) ++ (instancetype)noteWithMessage:(NSString *)message + latitude:(float)latitude + longitude:(float)longitude; +@end + +@implementation RTGRouteNote (Constructors) ++ (instancetype)noteWithMessage:(NSString *)message + latitude:(float)latitude + longitude:(float)longitude { + RTGRouteNote *note = [self message]; + note.message = message; + note.location.latitude = (int32_t) latitude * 1E7; + note.location.longitude = (int32_t) longitude * 1E7; + return note; +} +@end + + +#pragma mark Demo: Get Feature + +// Run the getFeature demo. Calls getFeature with a point known to have a feature and a point known +// not to have a feature. + +@interface GetFeatureViewController : UIViewController +@end + +@implementation GetFeatureViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + RTGRouteGuide *client = [[RTGRouteGuide alloc] initWithHost:kHostAddress]; + + void (^handler)(RTGFeature *response, NSError *error) = ^(RTGFeature *response, NSError *error) { + if (response.name.length) { + NSLog(@"Found feature called %@ at %@.", response.name, response.location); + } else if (response) { + NSLog(@"Found no features at %@", response.location); + } else { + NSLog(@"RPC error: %@", error); + } + }; + + RTGPoint *point = [RTGPoint message]; + point.latitude = 409146138; + point.longitude = -746188906; + + [client getFeatureWithRequest:point handler:handler]; + [client getFeatureWithRequest:[RTGPoint message] handler:handler]; +} + +@end + + +#pragma mark Demo: List Features + +// Run the listFeatures demo. Calls listFeatures with a rectangle containing all of the features in +// the pre-generated database. Prints each response as it comes in. + +@interface ListFeaturesViewController : UIViewController +@end + +@implementation ListFeaturesViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + RTGRouteGuide *client = [[RTGRouteGuide alloc] initWithHost:kHostAddress]; + + RTGRectangle *rectangle = [RTGRectangle message]; + rectangle.lo.latitude = 405E6; + rectangle.lo.longitude = -750E6; + rectangle.hi.latitude = 410E6; + rectangle.hi.longitude = -745E6; + + NSLog(@"Looking for features between %@ and %@", rectangle.lo, rectangle.hi); + [client listFeaturesWithRequest:rectangle + eventHandler:^(BOOL done, RTGFeature *response, NSError *error) { + if (response) { + NSLog(@"Found feature at %@ called %@.", response.location, response.name); + } else if (error) { + NSLog(@"RPC error: %@", error); + } + }]; +} + +@end + + +#pragma mark Demo: Record Route + +// Run the recordRoute demo. Sends several randomly chosen points from the pre-generated feature +// database with a variable delay in between. Prints the statistics when they are sent from the +// server. + +@interface RecordRouteViewController : UIViewController +@end + +@implementation RecordRouteViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + NSString *dataBasePath = [NSBundle.mainBundle pathForResource:@"route_guide_db" + ofType:@"json"]; + NSData *dataBaseContent = [NSData dataWithContentsOfFile:dataBasePath]; + NSArray *features = [NSJSONSerialization JSONObjectWithData:dataBaseContent options:0 error:NULL]; + + GRXWriter *locations = [[GRXWriter writerWithContainer:features] map:^id(id feature) { + RTGPoint *location = [RTGPoint message]; + location.longitude = [((NSNumber *) feature[@"location"][@"longitude"]) intValue]; + location.latitude = [((NSNumber *) feature[@"location"][@"latitude"]) intValue]; + NSLog(@"Visiting point %@", location); + return location; + }]; + + RTGRouteGuide *client = [[RTGRouteGuide alloc] initWithHost:kHostAddress]; + + [client recordRouteWithRequestsWriter:locations handler:^(RTGRouteSummary *response, NSError *error) { + if (response) { + NSLog(@"Finished trip with %i points", response.pointCount); + NSLog(@"Passed %i features", response.featureCount); + NSLog(@"Travelled %i meters", response.distance); + NSLog(@"It took %i seconds", response.elapsedTime); + } else { + NSLog(@"RPC error: %@", error); + } + }]; +} + +@end + + +#pragma mark Demo: Route Chat + +// Run the routeChat demo. Send some chat messages, and print any chat messages that are sent from +// the server. + +@interface RouteChatViewController : UIViewController +@end + +@implementation RouteChatViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + NSArray *notes = @[[RTGRouteNote noteWithMessage:@"First message" latitude:0 longitude:0], + [RTGRouteNote noteWithMessage:@"Second message" latitude:0 longitude:1], + [RTGRouteNote noteWithMessage:@"Third message" latitude:1 longitude:0], + [RTGRouteNote noteWithMessage:@"Fourth message" latitude:0 longitude:0]]; + GRXWriter *notesWriter = [[GRXWriter writerWithContainer:notes] map:^id(RTGRouteNote *note) { + NSLog(@"Sending message %@ at %@", note.message, note.location); + return note; + }]; + + RTGRouteGuide *client = [[RTGRouteGuide alloc] initWithHost:kHostAddress]; + + [client routeChatWithRequestsWriter:notesWriter + eventHandler:^(BOOL done, RTGRouteNote *note, NSError *error) { + if (note) { + NSLog(@"Got message %@ at %@", note.message, note.location); + } else if (error) { + NSLog(@"RPC error: %@", error); + } + if (done) { + NSLog(@"Chat ended."); + } + }]; +} + +@end diff --git a/examples/objective-c/route_guide/route_guide_db.json b/examples/objective-c/route_guide/route_guide_db.json new file mode 100644 index 00000000..9caebaa4 --- /dev/null +++ b/examples/objective-c/route_guide/route_guide_db.json @@ -0,0 +1,121 @@ +[{ + "location": { + "latitude": 407838351, + "longitude": -746143763 + }, + "name": "Patriots Path, Mendham, NJ 07945, USA" +}, { + "location": { + "latitude": 408122808, + "longitude": -743999179 + }, + "name": "101 New Jersey 10, Whippany, NJ 07981, USA" +}, { + "location": { + "latitude": 413628156, + "longitude": -749015468 + }, + "name": "U.S. 6, Shohola, PA 18458, USA" +}, { + "location": { + "latitude": 419999544, + "longitude": -740371136 + }, + "name": "5 Conners Road, Kingston, NY 12401, USA" +}, { + "location": { + "latitude": 414008389, + "longitude": -743951297 + }, + "name": "Mid Hudson Psychiatric Center, New Hampton, NY 10958, USA" +}, { + "location": { + "latitude": 419611318, + "longitude": -746524769 + }, + "name": "287 Flugertown Road, Livingston Manor, NY 12758, USA" +}, { + "location": { + "latitude": 406109563, + "longitude": -742186778 + }, + "name": "4001 Tremley Point Road, Linden, NJ 07036, USA" +}, { + "location": { + "latitude": 416802456, + "longitude": -742370183 + }, + "name": "352 South Mountain Road, Wallkill, NY 12589, USA" +}, { + "location": { + "latitude": 412950425, + "longitude": -741077389 + }, + "name": "Bailey Turn Road, Harriman, NY 10926, USA" +}, { + "location": { + "latitude": 412144655, + "longitude": -743949739 + }, + "name": "193-199 Wawayanda Road, Hewitt, NJ 07421, USA" +}, { + "location": { + "latitude": 404306372, + "longitude": -741079661 + }, + "name": "" +}, { + "location": { + "latitude": 403966326, + "longitude": -748519297 + }, + "name": "" +}, { + "location": { + "latitude": 405002031, + "longitude": -748407866 + }, + "name": "" +}, { + "location": { + "latitude": 409532885, + "longitude": -742200683 + }, + "name": "" +}, { + "location": { + "latitude": 416851321, + "longitude": -742674555 + }, + "name": "" +}, { + "location": { + "latitude": 406411633, + "longitude": -741722051 + }, + "name": "3387 Richmond Terrace, Staten Island, NY 10303, USA" +}, { + "location": { + "latitude": 413069058, + "longitude": -744597778 + }, + "name": "261 Van Sickle Road, Goshen, NY 10924, USA" +}, { + "location": { + "latitude": 418465462, + "longitude": -746859398 + }, + "name": "" +}, { + "location": { + "latitude": 411733222, + "longitude": -744228360 + }, + "name": "" +}, { + "location": { + "latitude": 410248224, + "longitude": -747127767 + }, + "name": "3 Hasta Way, Newton, NJ 07860, USA" +}] diff --git a/examples/php/.gitignore b/examples/php/.gitignore new file mode 100644 index 00000000..d8a7996a --- /dev/null +++ b/examples/php/.gitignore @@ -0,0 +1,2 @@ +composer.lock +vendor/ diff --git a/examples/php/README.md b/examples/php/README.md new file mode 100644 index 00000000..5c327f10 --- /dev/null +++ b/examples/php/README.md @@ -0,0 +1,64 @@ +gRPC in 3 minutes (PHP) +=========================== + +PREREQUISITES +------------- + +This requires PHP 5.5 or greater. + +INSTALL +------- + - On Mac OS X, install [homebrew][]. On Linux, install [linuxbrew][]. Run the following command to install gRPC. + + ```sh + $ curl -fsSL https://goo.gl/getgrpc | bash -s php + ``` + This will download and run the [gRPC install script][] and compile the gRPC PHP extension. + + - Clone this repository + + ```sh + $ git clone https://github.com/grpc/grpc.git + ``` + + - Install composer + + ``` + $ cd examples/php + $ curl -sS https://getcomposer.org/installer | php + $ php composer.phar install + ``` + +TRY IT! +------- + + - Run the server + + Please follow the instruction in [Node][] to run the server + ``` + $ cd examples/node + $ nodejs greeter_server.js + ``` + + - Run the client + + ``` + $ cd examples/php + $ ./run_greeter_client.sh + ``` + +NOTE +---- + +This directory has a copy of `helloworld.proto` because it currently depends on +some Protocol Buffer 2.0 syntax. There is no proto3 support for PHP yet. + +TUTORIAL +-------- + +Coming soon + +[homebrew]:http://brew.sh +[linuxbrew]:https://github.com/Homebrew/linuxbrew#installation +[gRPC install script]:https://raw.githubusercontent.com/grpc/homebrew-grpc/master/scripts/install +[Node]:https://github.com/grpc/grpc/tree/master/examples/node diff --git a/examples/php/composer.json b/examples/php/composer.json new file mode 100644 index 00000000..f0ce3a2a --- /dev/null +++ b/examples/php/composer.json @@ -0,0 +1,17 @@ +{ + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/stanley-cheung/Protobuf-PHP" + } + ], + "name": "grpc/grpc-demo", + "description": "gRPC example for PHP", + "minimum-stability": "dev", + "require": { + "php": ">=5.5.0", + "datto/protobuf-php": "dev-master", + "google/auth": "dev-master", + "grpc/grpc": "dev-master" + } +} diff --git a/examples/php/greeter_client.php b/examples/php/greeter_client.php new file mode 100644 index 00000000..8ae19ae4 --- /dev/null +++ b/examples/php/greeter_client.php @@ -0,0 +1,49 @@ +setName($name); + list($reply, $status) = $client->SayHello($request)->wait(); + $message = $reply->getMessage(); + return $message; +} + +$name = !empty($argv[1]) ? $argv[1] : 'world'; +print(greet($name)."\n"); diff --git a/examples/php/helloworld.php b/examples/php/helloworld.php new file mode 100644 index 00000000..22da3d39 --- /dev/null +++ b/examples/php/helloworld.php @@ -0,0 +1,160 @@ +number = 1; + $f->name = "name"; + $f->type = \DrSlump\Protobuf::TYPE_STRING; + $f->rule = \DrSlump\Protobuf::RULE_OPTIONAL; + $descriptor->addField($f); + + foreach (self::$__extensions as $cb) { + $descriptor->addField($cb(), true); + } + + return $descriptor; + } + + /** + * Check if has a value + * + * @return boolean + */ + public function hasName(){ + return $this->_has(1); + } + + /** + * Clear value + * + * @return \helloworld\HelloRequest + */ + public function clearName(){ + return $this->_clear(1); + } + + /** + * Get value + * + * @return string + */ + public function getName(){ + return $this->_get(1); + } + + /** + * Set value + * + * @param string $value + * @return \helloworld\HelloRequest + */ + public function setName( $value){ + return $this->_set(1, $value); + } + } +} + +namespace helloworld { + + class HelloReply extends \DrSlump\Protobuf\Message { + + /** @var string */ + public $message = null; + + + /** @var \Closure[] */ + protected static $__extensions = array(); + + public static function descriptor() + { + $descriptor = new \DrSlump\Protobuf\Descriptor(__CLASS__, 'helloworld.HelloReply'); + + // OPTIONAL STRING message = 1 + $f = new \DrSlump\Protobuf\Field(); + $f->number = 1; + $f->name = "message"; + $f->type = \DrSlump\Protobuf::TYPE_STRING; + $f->rule = \DrSlump\Protobuf::RULE_OPTIONAL; + $descriptor->addField($f); + + foreach (self::$__extensions as $cb) { + $descriptor->addField($cb(), true); + } + + return $descriptor; + } + + /** + * Check if has a value + * + * @return boolean + */ + public function hasMessage(){ + return $this->_has(1); + } + + /** + * Clear value + * + * @return \helloworld\HelloReply + */ + public function clearMessage(){ + return $this->_clear(1); + } + + /** + * Get value + * + * @return string + */ + public function getMessage(){ + return $this->_get(1); + } + + /** + * Set value + * + * @param string $value + * @return \helloworld\HelloReply + */ + public function setMessage( $value){ + return $this->_set(1, $value); + } + } +} + +namespace helloworld { + + class GreeterClient{ + + private $rpc_impl; + + public function __construct($rpc_impl) { + $this->rpc_impl = $rpc_impl; + } + /** + * @param helloworld\HelloRequest $input + */ + public function SayHello(\helloworld\HelloRequest $argument, $metadata = array()) { + return $this->rpc_impl->_simpleRequest('/helloworld.Greeter/SayHello', $argument, '\helloworld\HelloReply::deserialize', $metadata); + } + } +} diff --git a/examples/php/helloworld.proto b/examples/php/helloworld.proto new file mode 100644 index 00000000..ad8f7a15 --- /dev/null +++ b/examples/php/helloworld.proto @@ -0,0 +1,50 @@ +// Copyright 2015, 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. + +syntax = "proto2"; + +option java_package = "ex.grpc"; + +package helloworld; + +// The greeting service definition. +service Greeter { + // Sends a greeting + rpc SayHello (HelloRequest) returns (HelloReply) {} +} + +// The request message containing the user's name. +message HelloRequest { + optional string name = 1; +} + +// The response message containing the greetings +message HelloReply { + optional string message = 1; +} diff --git a/examples/php/route_guide/README.md b/examples/php/route_guide/README.md new file mode 100644 index 00000000..00610ffb --- /dev/null +++ b/examples/php/route_guide/README.md @@ -0,0 +1,262 @@ +#gRPC Basics: PHP + +This tutorial provides a basic PHP programmer's introduction to working with gRPC. By walking through this example you'll learn how to: + +- Define a service in a .proto file. +- Generate client code using the protocol buffer compiler. +- Use the PHP gRPC API to write a simple client for your service. + +It assumes a passing familiarity with [protocol buffers](https://developers.google.com/protocol-buffers/docs/overview). Note that the example in this tutorial uses the proto2 version of the protocol buffers language. + +Also note that currently you can only create clients in PHP for gRPC services - you can find out how to create gRPC servers in our other tutorials, e.g. [Node.js](../node/route_guide). + +This isn't a comprehensive guide to using gRPC in PHP: more reference documentation is coming soon. + +- [Why use gRPC?](#why-grpc) +- [Example code and setup](#setup) +- [Try it out!](#try) +- [Defining the service](#proto) +- [Generating client code](#protoc) +- [Creating the client](#client) + + + +## Why use gRPC? + +With gRPC you can define your service once in a .proto file and implement clients and servers in any of gRPC's supported languages, which in turn can be run in environments ranging from servers inside Google to your own tablet - all the complexity of communication between different languages and environments is handled for you by gRPC. You also get all the advantages of working with protocol buffers, including efficient serialization, a simple IDL, and easy interface updating. + + + +## Example code and setup + +The example code for our tutorial is in [examples/php/route_guide](.). To download the example, clone this repository by running the following command: +```shell +$ git clone https://github.com/grpc/grpc.git +``` + +Then change your current directory to `examples/php/route_guide`: +```shell +$ cd examples/php/route_guide +``` + +Our example is a simple route mapping application that lets clients get information about features on their route, create a summary of their route, and exchange route information such as traffic updates with the server and other clients. + +You also should have the relevant tools installed to generate the client interface code (and a server in another language, for testing). You can obtain the latter by following [these setup instructions](https://github.com/grpc/homebrew-grpc). + + + +## Try it out! + +To try the sample app, we need a gRPC server running locally. Let's compile and run, for example, the Node.js server in this repository: + +```shell +$ cd ../../node +$ npm install +$ cd route_guide +$ nodejs ./route_guide_server.js --db_path=route_guide_db.json +``` + +Run the PHP client (in a different terminal): + +```shell +$ ./run_route_guide_client.sh +``` + +The next sections guide you step-by-step through how this proto service is defined, how to generate a client library from it, and how to create a client stub that uses that library. + + + +## Defining the service + +First let's look at how the service we're using is defined. A gRPC *service* and its method *request* and *response* types using [protocol buffers](https://developers.google.com/protocol-buffers/docs/overview). You can see the complete .proto file for our example in [`route_guide.proto`](route_guide.proto). + +To define a service, you specify a named `service` in your .proto file: + +```protobuf +service RouteGuide { + ... +} +``` + +Then you define `rpc` methods inside your service definition, specifying their request and response types. Protocol buffers let you define four kinds of service method, all of which are used in the `RouteGuide` service: + +- A *simple RPC* where the client sends a request to the server and receives a response later, just like a normal remote procedure call. +```protobuf + // Obtains the feature at a given position. + rpc GetFeature(Point) returns (Feature) {} +``` + +- A *response-streaming RPC* where the client sends a request to the server and gets back a stream of response messages. You specify a response-streaming method by placing the `stream` keyword before the *response* type. +```protobuf + // Obtains the Features available within the given Rectangle. Results are + // streamed rather than returned at once (e.g. in a response message with a + // repeated field), as the rectangle may cover a large area and contain a + // huge number of features. + rpc ListFeatures(Rectangle) returns (stream Feature) {} +``` + +- A *request-streaming RPC* where the client sends a sequence of messages to the server. Once the client has finished writing the messages, it waits for the server to read them all and return its response. You specify a request-streaming method by placing the `stream` keyword before the *request* type. +```protobuf + // Accepts a stream of Points on a route being traversed, returning a + // RouteSummary when traversal is completed. + rpc RecordRoute(stream Point) returns (RouteSummary) {} +``` + +- A *bidirectional streaming RPC* where both sides send a sequence of messages to the other. The two streams operate independently, so clients and servers can read and write in whatever order they like: for example, the server could wait to receive all the client messages before writing its responses, or it could alternately read a message then write a message, or some other combination of reads and writes. The order of messages in each stream is preserved. You specify this type of method by placing the `stream` keyword before both the request and the response. +```protobuf + // Accepts a stream of RouteNotes sent while a route is being traversed, + // while receiving other RouteNotes (e.g. from other users). + rpc RouteChat(stream RouteNote) returns (stream RouteNote) {} +``` + +Our .proto file also contains protocol buffer message type definitions for all the request and response types used in our service methods - for example, here's the `Point` message type: +```protobuf +// Points are represented as latitude-longitude pairs in the E7 representation +// (degrees multiplied by 10**7 and rounded to the nearest integer). +// Latitudes should be in the range +/- 90 degrees and longitude should be in +// the range +/- 180 degrees (inclusive). +message Point { + int32 latitude = 1; + int32 longitude = 2; +} +``` + + + +## Generating client code + +The PHP client stub implementation of the proto files can be generated by the [`protoc-gen-php`](https://github.com/datto/protobuf-php) tool. To install the tool: + +```sh +$ cd examples/php +$ php composer.phar install +$ cd vendor/datto/protobuf-php +$ gem install rake ronn +$ rake pear:package version=1.0 +$ sudo pear install Protobuf-1.0.tgz +``` + +To generate the client stub implementation .php file: + +```sh +$ cd php/route_guide +$ protoc-gen-php -i . -o . ./route_guide.proto +``` + +A `route_guide.php` file will be generated in the `php/route_guide` directory. You do not need to modify the file. + +To load the generated client stub file, simply `require` it in your PHP application: + +```php +require dirname(__FILE__) . '/route_guide.php'; +``` + +The file contains: +- All the protocol buffer code to populate, serialize, and retrieve our request and response message types. +- A class called `routeguide\RouteGuideClient` that lets clients call the methods defined in the `RouteGuide` service. + + + +## Creating the client + +In this section, we'll look at creating a PHP client for our `RouteGuide` service. You can see our complete example client code in [route_guide_client.php](route_guide_client.php). + +### Constructing a client object + +To call service methods, we first need to create a client object, an instance of the generated `RouteGuideClient` class. The constructor of the class expects the server address and port we want to connect to: + +```php +$client = new routeguide\RouteGuideClient(new Grpc\BaseStub('localhost:50051', [])); +``` + +### Calling service methods + +Now let's look at how we call our service methods. + +#### Simple RPC + +Calling the simple RPC `GetFeature` is nearly as straightforward as calling a local asynchronous method. + +```php + $point = new routeguide\Point(); + $point->setLatitude(409146138); + $point->setLongitude(-746188906); + list($feature, $status) = $client->GetFeature($point)->wait(); +``` + +As you can see, we create and populate a request object, i.e. an `routeguide\Point` object. Then, we call the method on the stub, passing it the request object. If there is no error, then we can read the response information from the server from our response object, i.e. an `routeguide\Feature` object. + +```php + print sprintf("Found %s \n at %f, %f\n", $feature->getName(), + $feature->getLocation()->getLatitude() / COORD_FACTOR, + $feature->getLocation()->getLongitude() / COORD_FACTOR); +``` + +#### Streaming RPCs + +Now let's look at our streaming methods. Here's where we call the server-side streaming method `ListFeatures`, which returns a stream of geographical `Feature`s: + +```php + $lo_point = new routeguide\Point(); + $hi_point = new routeguide\Point(); + + $lo_point->setLatitude(400000000); + $lo_point->setLongitude(-750000000); + $hi_point->setLatitude(420000000); + $hi_point->setLongitude(-730000000); + + $rectangle = new routeguide\Rectangle(); + $rectangle->setLo($lo_point); + $rectangle->setHi($hi_point); + + $call = $client->ListFeatures($rectangle); + // an iterator over the server streaming responses + $features = $call->responses(); + foreach ($features as $feature) { + // process each feature + } // the loop will end when the server indicates there is no more responses to be sent. +``` + +The `$call->responses()` method call returns an iterator. When the server sends a response, a `$feature` object will be returned in the `foreach` loop, until the server indiciates that there will be no more responses to be sent. + +The client-side streaming method `RecordRoute` is similar, except there we pass the method an iterator and get back a `routeguide\RouteSummary`. + +```php + $points_iter = function($db) { + for ($i = 0; $i < $num_points; $i++) { + $point = new routeguide\Point(); + $point->setLatitude($lat); + $point->setLongitude($long); + yield $point; + } + }; + // $points_iter is an iterator simulating client streaming + list($route_summary, $status) = + $client->RecordRoute($points_iter($db))->wait(); +``` + +Finally, let's look at our bidirectional streaming RPC `routeChat()`. In this case, we just pass a context to the method and get back a `BidiStreamingCall` stream object, which we can use to both write and read messages. + +```php +$call = $client->RouteChat(); +``` + +To write messages from the client: + +```php + foreach ($notes as $n) { + $route_note = new routerguide\RouteNote(); + $call->write($route_note); + } + $call->writesDone(); +``` + +To read messages from the server: + +```php + while ($route_note_reply = $call->read()) { + // process $route_note_reply + } +``` + +Each side will always get the other's messages in the order they were written, both the client and server can read and write in any order — the streams operate completely independently. diff --git a/examples/php/route_guide/route_guide.php b/examples/php/route_guide/route_guide.php new file mode 100644 index 00000000..76086f97 --- /dev/null +++ b/examples/php/route_guide/route_guide.php @@ -0,0 +1,729 @@ +number = 1; + $f->name = "latitude"; + $f->type = \DrSlump\Protobuf::TYPE_INT32; + $f->rule = \DrSlump\Protobuf::RULE_OPTIONAL; + $f->default = 0; + $descriptor->addField($f); + + // OPTIONAL INT32 longitude = 2 + $f = new \DrSlump\Protobuf\Field(); + $f->number = 2; + $f->name = "longitude"; + $f->type = \DrSlump\Protobuf::TYPE_INT32; + $f->rule = \DrSlump\Protobuf::RULE_OPTIONAL; + $f->default = 0; + $descriptor->addField($f); + + foreach (self::$__extensions as $cb) { + $descriptor->addField($cb(), true); + } + + return $descriptor; + } + + /** + * Check if has a value + * + * @return boolean + */ + public function hasLatitude(){ + return $this->_has(1); + } + + /** + * Clear value + * + * @return \routeguide\Point + */ + public function clearLatitude(){ + return $this->_clear(1); + } + + /** + * Get value + * + * @return int + */ + public function getLatitude(){ + return $this->_get(1); + } + + /** + * Set value + * + * @param int $value + * @return \routeguide\Point + */ + public function setLatitude( $value){ + return $this->_set(1, $value); + } + + /** + * Check if has a value + * + * @return boolean + */ + public function hasLongitude(){ + return $this->_has(2); + } + + /** + * Clear value + * + * @return \routeguide\Point + */ + public function clearLongitude(){ + return $this->_clear(2); + } + + /** + * Get value + * + * @return int + */ + public function getLongitude(){ + return $this->_get(2); + } + + /** + * Set value + * + * @param int $value + * @return \routeguide\Point + */ + public function setLongitude( $value){ + return $this->_set(2, $value); + } + } +} + +namespace routeguide { + + class Rectangle extends \DrSlump\Protobuf\Message { + + /** @var \routeguide\Point */ + public $lo = null; + + /** @var \routeguide\Point */ + public $hi = null; + + + /** @var \Closure[] */ + protected static $__extensions = array(); + + public static function descriptor() + { + $descriptor = new \DrSlump\Protobuf\Descriptor(__CLASS__, 'routeguide.Rectangle'); + + // OPTIONAL MESSAGE lo = 1 + $f = new \DrSlump\Protobuf\Field(); + $f->number = 1; + $f->name = "lo"; + $f->type = \DrSlump\Protobuf::TYPE_MESSAGE; + $f->rule = \DrSlump\Protobuf::RULE_OPTIONAL; + $f->reference = '\routeguide\Point'; + $descriptor->addField($f); + + // OPTIONAL MESSAGE hi = 2 + $f = new \DrSlump\Protobuf\Field(); + $f->number = 2; + $f->name = "hi"; + $f->type = \DrSlump\Protobuf::TYPE_MESSAGE; + $f->rule = \DrSlump\Protobuf::RULE_OPTIONAL; + $f->reference = '\routeguide\Point'; + $descriptor->addField($f); + + foreach (self::$__extensions as $cb) { + $descriptor->addField($cb(), true); + } + + return $descriptor; + } + + /** + * Check if has a value + * + * @return boolean + */ + public function hasLo(){ + return $this->_has(1); + } + + /** + * Clear value + * + * @return \routeguide\Rectangle + */ + public function clearLo(){ + return $this->_clear(1); + } + + /** + * Get value + * + * @return \routeguide\Point + */ + public function getLo(){ + return $this->_get(1); + } + + /** + * Set value + * + * @param \routeguide\Point $value + * @return \routeguide\Rectangle + */ + public function setLo(\routeguide\Point $value){ + return $this->_set(1, $value); + } + + /** + * Check if has a value + * + * @return boolean + */ + public function hasHi(){ + return $this->_has(2); + } + + /** + * Clear value + * + * @return \routeguide\Rectangle + */ + public function clearHi(){ + return $this->_clear(2); + } + + /** + * Get value + * + * @return \routeguide\Point + */ + public function getHi(){ + return $this->_get(2); + } + + /** + * Set value + * + * @param \routeguide\Point $value + * @return \routeguide\Rectangle + */ + public function setHi(\routeguide\Point $value){ + return $this->_set(2, $value); + } + } +} + +namespace routeguide { + + class Feature extends \DrSlump\Protobuf\Message { + + /** @var string */ + public $name = null; + + /** @var \routeguide\Point */ + public $location = null; + + + /** @var \Closure[] */ + protected static $__extensions = array(); + + public static function descriptor() + { + $descriptor = new \DrSlump\Protobuf\Descriptor(__CLASS__, 'routeguide.Feature'); + + // OPTIONAL STRING name = 1 + $f = new \DrSlump\Protobuf\Field(); + $f->number = 1; + $f->name = "name"; + $f->type = \DrSlump\Protobuf::TYPE_STRING; + $f->rule = \DrSlump\Protobuf::RULE_OPTIONAL; + $descriptor->addField($f); + + // OPTIONAL MESSAGE location = 2 + $f = new \DrSlump\Protobuf\Field(); + $f->number = 2; + $f->name = "location"; + $f->type = \DrSlump\Protobuf::TYPE_MESSAGE; + $f->rule = \DrSlump\Protobuf::RULE_OPTIONAL; + $f->reference = '\routeguide\Point'; + $descriptor->addField($f); + + foreach (self::$__extensions as $cb) { + $descriptor->addField($cb(), true); + } + + return $descriptor; + } + + /** + * Check if has a value + * + * @return boolean + */ + public function hasName(){ + return $this->_has(1); + } + + /** + * Clear value + * + * @return \routeguide\Feature + */ + public function clearName(){ + return $this->_clear(1); + } + + /** + * Get value + * + * @return string + */ + public function getName(){ + return $this->_get(1); + } + + /** + * Set value + * + * @param string $value + * @return \routeguide\Feature + */ + public function setName( $value){ + return $this->_set(1, $value); + } + + /** + * Check if has a value + * + * @return boolean + */ + public function hasLocation(){ + return $this->_has(2); + } + + /** + * Clear value + * + * @return \routeguide\Feature + */ + public function clearLocation(){ + return $this->_clear(2); + } + + /** + * Get value + * + * @return \routeguide\Point + */ + public function getLocation(){ + return $this->_get(2); + } + + /** + * Set value + * + * @param \routeguide\Point $value + * @return \routeguide\Feature + */ + public function setLocation(\routeguide\Point $value){ + return $this->_set(2, $value); + } + } +} + +namespace routeguide { + + class RouteNote extends \DrSlump\Protobuf\Message { + + /** @var \routeguide\Point */ + public $location = null; + + /** @var string */ + public $message = null; + + + /** @var \Closure[] */ + protected static $__extensions = array(); + + public static function descriptor() + { + $descriptor = new \DrSlump\Protobuf\Descriptor(__CLASS__, 'routeguide.RouteNote'); + + // OPTIONAL MESSAGE location = 1 + $f = new \DrSlump\Protobuf\Field(); + $f->number = 1; + $f->name = "location"; + $f->type = \DrSlump\Protobuf::TYPE_MESSAGE; + $f->rule = \DrSlump\Protobuf::RULE_OPTIONAL; + $f->reference = '\routeguide\Point'; + $descriptor->addField($f); + + // OPTIONAL STRING message = 2 + $f = new \DrSlump\Protobuf\Field(); + $f->number = 2; + $f->name = "message"; + $f->type = \DrSlump\Protobuf::TYPE_STRING; + $f->rule = \DrSlump\Protobuf::RULE_OPTIONAL; + $descriptor->addField($f); + + foreach (self::$__extensions as $cb) { + $descriptor->addField($cb(), true); + } + + return $descriptor; + } + + /** + * Check if has a value + * + * @return boolean + */ + public function hasLocation(){ + return $this->_has(1); + } + + /** + * Clear value + * + * @return \routeguide\RouteNote + */ + public function clearLocation(){ + return $this->_clear(1); + } + + /** + * Get value + * + * @return \routeguide\Point + */ + public function getLocation(){ + return $this->_get(1); + } + + /** + * Set value + * + * @param \routeguide\Point $value + * @return \routeguide\RouteNote + */ + public function setLocation(\routeguide\Point $value){ + return $this->_set(1, $value); + } + + /** + * Check if has a value + * + * @return boolean + */ + public function hasMessage(){ + return $this->_has(2); + } + + /** + * Clear value + * + * @return \routeguide\RouteNote + */ + public function clearMessage(){ + return $this->_clear(2); + } + + /** + * Get value + * + * @return string + */ + public function getMessage(){ + return $this->_get(2); + } + + /** + * Set value + * + * @param string $value + * @return \routeguide\RouteNote + */ + public function setMessage( $value){ + return $this->_set(2, $value); + } + } +} + +namespace routeguide { + + class RouteSummary extends \DrSlump\Protobuf\Message { + + /** @var int */ + public $point_count = 0; + + /** @var int */ + public $feature_count = 0; + + /** @var int */ + public $distance = 0; + + /** @var int */ + public $elapsed_time = 0; + + + /** @var \Closure[] */ + protected static $__extensions = array(); + + public static function descriptor() + { + $descriptor = new \DrSlump\Protobuf\Descriptor(__CLASS__, 'routeguide.RouteSummary'); + + // OPTIONAL INT32 point_count = 1 + $f = new \DrSlump\Protobuf\Field(); + $f->number = 1; + $f->name = "point_count"; + $f->type = \DrSlump\Protobuf::TYPE_INT32; + $f->rule = \DrSlump\Protobuf::RULE_OPTIONAL; + $f->default = 0; + $descriptor->addField($f); + + // OPTIONAL INT32 feature_count = 2 + $f = new \DrSlump\Protobuf\Field(); + $f->number = 2; + $f->name = "feature_count"; + $f->type = \DrSlump\Protobuf::TYPE_INT32; + $f->rule = \DrSlump\Protobuf::RULE_OPTIONAL; + $f->default = 0; + $descriptor->addField($f); + + // OPTIONAL INT32 distance = 3 + $f = new \DrSlump\Protobuf\Field(); + $f->number = 3; + $f->name = "distance"; + $f->type = \DrSlump\Protobuf::TYPE_INT32; + $f->rule = \DrSlump\Protobuf::RULE_OPTIONAL; + $f->default = 0; + $descriptor->addField($f); + + // OPTIONAL INT32 elapsed_time = 4 + $f = new \DrSlump\Protobuf\Field(); + $f->number = 4; + $f->name = "elapsed_time"; + $f->type = \DrSlump\Protobuf::TYPE_INT32; + $f->rule = \DrSlump\Protobuf::RULE_OPTIONAL; + $f->default = 0; + $descriptor->addField($f); + + foreach (self::$__extensions as $cb) { + $descriptor->addField($cb(), true); + } + + return $descriptor; + } + + /** + * Check if has a value + * + * @return boolean + */ + public function hasPointCount(){ + return $this->_has(1); + } + + /** + * Clear value + * + * @return \routeguide\RouteSummary + */ + public function clearPointCount(){ + return $this->_clear(1); + } + + /** + * Get value + * + * @return int + */ + public function getPointCount(){ + return $this->_get(1); + } + + /** + * Set value + * + * @param int $value + * @return \routeguide\RouteSummary + */ + public function setPointCount( $value){ + return $this->_set(1, $value); + } + + /** + * Check if has a value + * + * @return boolean + */ + public function hasFeatureCount(){ + return $this->_has(2); + } + + /** + * Clear value + * + * @return \routeguide\RouteSummary + */ + public function clearFeatureCount(){ + return $this->_clear(2); + } + + /** + * Get value + * + * @return int + */ + public function getFeatureCount(){ + return $this->_get(2); + } + + /** + * Set value + * + * @param int $value + * @return \routeguide\RouteSummary + */ + public function setFeatureCount( $value){ + return $this->_set(2, $value); + } + + /** + * Check if has a value + * + * @return boolean + */ + public function hasDistance(){ + return $this->_has(3); + } + + /** + * Clear value + * + * @return \routeguide\RouteSummary + */ + public function clearDistance(){ + return $this->_clear(3); + } + + /** + * Get value + * + * @return int + */ + public function getDistance(){ + return $this->_get(3); + } + + /** + * Set value + * + * @param int $value + * @return \routeguide\RouteSummary + */ + public function setDistance( $value){ + return $this->_set(3, $value); + } + + /** + * Check if has a value + * + * @return boolean + */ + public function hasElapsedTime(){ + return $this->_has(4); + } + + /** + * Clear value + * + * @return \routeguide\RouteSummary + */ + public function clearElapsedTime(){ + return $this->_clear(4); + } + + /** + * Get value + * + * @return int + */ + public function getElapsedTime(){ + return $this->_get(4); + } + + /** + * Set value + * + * @param int $value + * @return \routeguide\RouteSummary + */ + public function setElapsedTime( $value){ + return $this->_set(4, $value); + } + } +} + +namespace routeguide { + + class RouteGuideClient extends \Grpc\BaseStub { + + public function __construct($hostname, $opts) { + parent::__construct($hostname, $opts); + } + /** + * @param routeguide\Point $input + */ + public function GetFeature(\routeguide\Point $argument, $metadata = array(), $options = array()) { + return $this->_simpleRequest('/routeguide.RouteGuide/GetFeature', $argument, '\routeguide\Feature::deserialize', $metadata, $options); + } + /** + * @param routeguide\Rectangle $input + */ + public function ListFeatures($argument, $metadata = array(), $options = array()) { + return $this->_serverStreamRequest('/routeguide.RouteGuide/ListFeatures', $argument, '\routeguide\Feature::deserialize', $metadata, $options); + } + /** + * @param routeguide\Point $input + */ + public function RecordRoute($metadata = array()) { + return $this->_clientStreamRequest('/routeguide.RouteGuide/RecordRoute', '\routeguide\RouteSummary::deserialize', $metadata); + } + /** + * @param routeguide\RouteNote $input + */ + public function RouteChat($metadata = array()) { + return $this->_bidiRequest('/routeguide.RouteGuide/RouteChat', '\routeguide\RouteNote::deserialize', $metadata); + } + } +} diff --git a/examples/php/route_guide/route_guide.proto b/examples/php/route_guide/route_guide.proto new file mode 100644 index 00000000..d50f8a51 --- /dev/null +++ b/examples/php/route_guide/route_guide.proto @@ -0,0 +1,120 @@ +// Copyright 2015, 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. + +syntax = "proto2"; + +option java_package = "io.grpc.examples"; + +package routeguide; + +// Interface exported by the server. +service RouteGuide { + // A simple RPC. + // + // Obtains the feature at a given position. + rpc GetFeature(Point) returns (Feature) {} + + // A server-to-client streaming RPC. + // + // Obtains the Features available within the given Rectangle. Results are + // streamed rather than returned at once (e.g. in a response message with a + // repeated field), as the rectangle may cover a large area and contain a + // huge number of features. + rpc ListFeatures(Rectangle) returns (stream Feature) {} + + // A client-to-server streaming RPC. + // + // Accepts a stream of Points on a route being traversed, returning a + // RouteSummary when traversal is completed. + rpc RecordRoute(stream Point) returns (RouteSummary) {} + + // A Bidirectional streaming RPC. + // + // Accepts a stream of RouteNotes sent while a route is being traversed, + // while receiving other RouteNotes (e.g. from other users). + rpc RouteChat(stream RouteNote) returns (stream RouteNote) {} +} + +// Points are represented as latitude-longitude pairs in the E7 representation +// (degrees multiplied by 10**7 and rounded to the nearest integer). +// Latitudes should be in the range +/- 90 degrees and longitude should be in +// the range +/- 180 degrees (inclusive). +message Point { + optional int32 latitude = 1 [default = 0]; + optional int32 longitude = 2 [default = 0]; +} + +// A latitude-longitude rectangle, represented as two diagonally opposite +// points "lo" and "hi". +message Rectangle { + // One corner of the rectangle. + optional Point lo = 1; + + // The other corner of the rectangle. + optional Point hi = 2; +} + +// A feature names something at a given point. +// +// If a feature could not be named, the name is empty. +message Feature { + // The name of the feature. + optional string name = 1; + + // The point where the feature is detected. + optional Point location = 2; +} + +// A RouteNote is a message sent while at a given point. +message RouteNote { + // The location from which the message is sent. + optional Point location = 1; + + // The message to be sent. + optional string message = 2; +} + +// A RouteSummary is received in response to a RecordRoute rpc. +// +// It contains the number of individual points received, the number of +// detected features, and the total distance covered as the cumulative sum of +// the distance between each point. +message RouteSummary { + // The number of points received. + optional int32 point_count = 1 [default = 0]; + + // The number of known features passed while traversing the route. + optional int32 feature_count = 2 [default = 0]; + + // The distance covered in metres. + optional int32 distance = 3 [default = 0]; + + // The duration of the traversal in seconds. + optional int32 elapsed_time = 4 [default = 0]; +} diff --git a/examples/php/route_guide/route_guide_client.php b/examples/php/route_guide/route_guide_client.php new file mode 100644 index 00000000..d21a0809 --- /dev/null +++ b/examples/php/route_guide/route_guide_client.php @@ -0,0 +1,205 @@ +getName(); + if (!$name) { + $name_str = "no feature"; + } else { + $name_str = "feature called $name"; + } + print sprintf("Found %s \n at %f, %f\n", $name_str, + $feature->getLocation()->getLatitude() / COORD_FACTOR, + $feature->getLocation()->getLongitude() / COORD_FACTOR); +} + +/** + * Run the getFeature demo. Calls getFeature with a point known to have a + * feature and a point known not to have a feature. + */ +function runGetFeature() { + print "Running GetFeature...\n"; + global $client; + + $point = new routeguide\Point(); + $points = array( + array(409146138, -746188906), + array(0, 0), + ); + + foreach ($points as $p) { + $point->setLatitude($p[0]); + $point->setLongitude($p[1]); + // make a unary grpc call + list($feature, $status) = $client->GetFeature($point)->wait(); + printFeature($feature); + } +} + +/** + * Run the listFeatures demo. Calls listFeatures with a rectangle + * containing all of the features in the pre-generated + * database. Prints each response as it comes in. + */ +function runListFeatures() { + print "Running ListFeatures...\n"; + global $client; + + $lo_point = new routeguide\Point(); + $hi_point = new routeguide\Point(); + + $lo_point->setLatitude(400000000); + $lo_point->setLongitude(-750000000); + $hi_point->setLatitude(420000000); + $hi_point->setLongitude(-730000000); + + $rectangle = new routeguide\Rectangle(); + $rectangle->setLo($lo_point); + $rectangle->setHi($hi_point); + + $call = $client->ListFeatures($rectangle); + // an iterator over the server streaming responses + $features = $call->responses(); + foreach ($features as $feature) { + printFeature($feature); + } +} + +/** + * Run the recordRoute demo. Sends several randomly chosen points from the + * pre-generated feature database with a variable delay in between. Prints + * the statistics when they are sent from the server. + */ +function runRecordRoute() { + print "Running RecordRoute...\n"; + global $client, $argv; + + $db = json_decode(file_get_contents($argv[1]), true); + $points_iter = function($db) { + $num_points_in_db = count($db); + $num_points = 10; + for ($i = 0; $i < $num_points; $i++) { + $point = new routeguide\Point(); + $index = rand(0, $num_points_in_db - 1); + $lat = $db[$index]['location']['latitude']; + $long = $db[$index]['location']['longitude']; + $feature_name = $db[$index]['name']; + $point->setLatitude($lat); + $point->setLongitude($long); + print sprintf("Visiting point %f, %f,\n with feature name: %s\n", + $lat / COORD_FACTOR, $long / COORD_FACTOR, + $feature_name ? $feature_name : ''); + usleep(rand(300000, 800000)); + yield $point; + } + }; + // $points_iter is an iterator simulating client streaming + list($route_summary, $status) = + $client->RecordRoute($points_iter($db))->wait(); + print sprintf("Finished trip with %d points\nPassed %d features\n". + "Travelled %d meters\nIt took %d seconds\n", + $route_summary->getPointCount(), + $route_summary->getFeatureCount(), + $route_summary->getDistance(), + $route_summary->getElapsedTime()); +} + +/** + * Run the routeChat demo. Send some chat messages, and print any chat + * messages that are sent from the server. + */ +function runRouteChat() { + print "Running RouteChat...\n"; + global $client; + + // start the bidirectional streaming call + $call = $client->RouteChat(); + + $notes = array( + array(1, 1, 'first message'), + array(1, 2, 'second message'), + array(2, 1, 'third message'), + array(1, 1, 'fourth message'), + array(1, 1, 'fifth message'), + ); + + foreach ($notes as $n) { + $point = new routeguide\Point(); + $point->setLatitude($lat = $n[0]); + $point->setLongitude($long = $n[1]); + + $route_note = new routeguide\RouteNote(); + $route_note->setLocation($point); + $route_note->setMessage($message = $n[2]); + + print sprintf("Sending message: '%s' at (%d, %d)\n", + $message, $lat, $long); + // send a bunch of messages to the server + $call->write($route_note); + } + $call->writesDone(); + + // read from the server until there's no more + while ($route_note_reply = $call->read()) { + print sprintf("Previous left message at (%d, %d): '%s'\n", + $route_note_reply->getLocation()->getLatitude(), + $route_note_reply->getLocation()->getLongitude(), + $route_note_reply->getMessage()); + } +} + +/** + * Run all of the demos in order + */ +function main() { + runGetFeature(); + runListFeatures(); + runRecordRoute(); + runRouteChat(); +} + +if (empty($argv[1])) { + print "Usage: php -d extension=grpc.so route_guide_client.php " . + "\n"; + exit(1); +} +main(); diff --git a/examples/php/route_guide/run_route_guide_client.sh b/examples/php/route_guide/run_route_guide_client.sh new file mode 100755 index 00000000..e5ca0779 --- /dev/null +++ b/examples/php/route_guide/run_route_guide_client.sh @@ -0,0 +1,36 @@ +#!/bin/bash +# Copyright 2015, 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. + +set -e +cd $(dirname $0) +command -v brew >/dev/null 2>&1 && \ + extension_dir="-d extension_dir="`brew --prefix`/opt/grpc-php +php $extension_dir -d extension=grpc.so \ + route_guide_client.php ../../node/route_guide/route_guide_db.json diff --git a/examples/php/run_greeter_client.sh b/examples/php/run_greeter_client.sh new file mode 100755 index 00000000..2906de9a --- /dev/null +++ b/examples/php/run_greeter_client.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# Copyright 2015, 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. + +set -e +cd $(dirname $0) +command -v brew >/dev/null 2>&1 && \ + extension_dir="-d extension_dir="`brew --prefix`/opt/grpc-php +php $extension_dir -d extension=grpc.so greeter_client.php $1 diff --git a/examples/protos/README.md b/examples/protos/README.md new file mode 100644 index 00000000..48df7c89 --- /dev/null +++ b/examples/protos/README.md @@ -0,0 +1,8 @@ +# Example protos + +## Contents + +- [helloworld.proto] + - The simple example used in the overview. +- [route_guide.proto] + - An example service described in detail in the tutorial. diff --git a/examples/protos/auth_sample.proto b/examples/protos/auth_sample.proto new file mode 100644 index 00000000..a49caca6 --- /dev/null +++ b/examples/protos/auth_sample.proto @@ -0,0 +1,57 @@ +// Copyright 2015, 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. + +syntax = "proto3"; + +package grpc.testing; + +option objc_class_prefix = "AUTH"; + +// Unary request. +message Request { + // Whether Response should include username. + bool fill_username = 4; + + // Whether Response should include OAuth scope. + bool fill_oauth_scope = 5; +} + +// Unary response, as configured by the request. +message Response { + // The user the request came from, for verifying authentication was + // successful. + string username = 2; + // OAuth scope. + string oauth_scope = 3; +} + +service TestService { + // One request followed by one response. + rpc UnaryCall(Request) returns (Response); +} diff --git a/examples/protos/hellostreamingworld.proto b/examples/protos/hellostreamingworld.proto new file mode 100644 index 00000000..bd5af3b2 --- /dev/null +++ b/examples/protos/hellostreamingworld.proto @@ -0,0 +1,54 @@ +// Copyright 2015, 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. + +syntax = "proto3"; + +option java_package = "ex.grpc"; +option objc_class_prefix = "HSW"; + +package hellostreamingworld; + +// The greeting service definition. +service MultiGreeter { + // Sends multiple greetings + rpc sayHello (HelloRequest) returns (stream HelloReply) {} +} + +// The request message containing the user's name and how many greetings +// they want. +message HelloRequest { + string name = 1; + string num_greetings = 2; +} + +// A response message containing a greeting +message HelloReply { + string message = 1; +} + diff --git a/examples/protos/helloworld.proto b/examples/protos/helloworld.proto new file mode 100644 index 00000000..7d58870a --- /dev/null +++ b/examples/protos/helloworld.proto @@ -0,0 +1,51 @@ +// Copyright 2015, 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. + +syntax = "proto3"; + +option java_package = "io.grpc.examples"; +option objc_class_prefix = "HLW"; + +package helloworld; + +// The greeting service definition. +service Greeter { + // Sends a greeting + rpc SayHello (HelloRequest) returns (HelloReply) {} +} + +// The request message containing the user's name. +message HelloRequest { + string name = 1; +} + +// The response message containing the greetings +message HelloReply { + string message = 1; +} diff --git a/examples/protos/route_guide.proto b/examples/protos/route_guide.proto new file mode 100644 index 00000000..7a70040e --- /dev/null +++ b/examples/protos/route_guide.proto @@ -0,0 +1,124 @@ +// Copyright 2015, 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. + +syntax = "proto3"; + +option java_package = "ex.grpc"; +option objc_class_prefix = "RTG"; + +package routeguide; + +// Interface exported by the server. +service RouteGuide { + // A simple RPC. + // + // Obtains the feature at a given position. + // + // A feature with an empty name is returned if there's no feature at the given + // position. + rpc GetFeature(Point) returns (Feature) {} + + // A server-to-client streaming RPC. + // + // Obtains the Features available within the given Rectangle. Results are + // streamed rather than returned at once (e.g. in a response message with a + // repeated field), as the rectangle may cover a large area and contain a + // huge number of features. + rpc ListFeatures(Rectangle) returns (stream Feature) {} + + // A client-to-server streaming RPC. + // + // Accepts a stream of Points on a route being traversed, returning a + // RouteSummary when traversal is completed. + rpc RecordRoute(stream Point) returns (RouteSummary) {} + + // A Bidirectional streaming RPC. + // + // Accepts a stream of RouteNotes sent while a route is being traversed, + // while receiving other RouteNotes (e.g. from other users). + rpc RouteChat(stream RouteNote) returns (stream RouteNote) {} +} + +// Points are represented as latitude-longitude pairs in the E7 representation +// (degrees multiplied by 10**7 and rounded to the nearest integer). +// Latitudes should be in the range +/- 90 degrees and longitude should be in +// the range +/- 180 degrees (inclusive). +message Point { + int32 latitude = 1; + int32 longitude = 2; +} + +// A latitude-longitude rectangle, represented as two diagonally opposite +// points "lo" and "hi". +message Rectangle { + // One corner of the rectangle. + Point lo = 1; + + // The other corner of the rectangle. + Point hi = 2; +} + +// A feature names something at a given point. +// +// If a feature could not be named, the name is empty. +message Feature { + // The name of the feature. + string name = 1; + + // The point where the feature is detected. + Point location = 2; +} + +// A RouteNote is a message sent while at a given point. +message RouteNote { + // The location from which the message is sent. + Point location = 1; + + // The message to be sent. + string message = 2; +} + +// A RouteSummary is received in response to a RecordRoute rpc. +// +// It contains the number of individual points received, the number of +// detected features, and the total distance covered as the cumulative sum of +// the distance between each point. +message RouteSummary { + // The number of points received. + int32 point_count = 1; + + // The number of known features passed while traversing the route. + int32 feature_count = 2; + + // The distance covered in metres. + int32 distance = 3; + + // The duration of the traversal in seconds. + int32 elapsed_time = 4; +} diff --git a/examples/python/helloworld/.gitignore b/examples/python/helloworld/.gitignore new file mode 100644 index 00000000..0d20b648 --- /dev/null +++ b/examples/python/helloworld/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/examples/python/helloworld/README.md b/examples/python/helloworld/README.md new file mode 100644 index 00000000..070b9e88 --- /dev/null +++ b/examples/python/helloworld/README.md @@ -0,0 +1,113 @@ +# gRPC Python Hello World + +This is a quick introduction with a simple example and installation instructions: for a more complete tutorial see [gRPC Basics: Python](../route_guide). + +### Install gRPC +Make sure you have built gRPC Python from source on your system. Follow the instructions here: +[https://github.com/grpc/grpc/blob/master/src/python/README.md](https://github.com/grpc/grpc/blob/master/src/python/README.md). + +This gives you a python virtual environment with installed gRPC Python +in GRPC_ROOT/python2.7_virtual_environment. GRPC_ROOT is the path to which you +have cloned the [gRPC git repo](https://github.com/grpc/grpc). + +### Get the source code + +The example code for our Hello World and our other examples live in the `examples` +directory. Clone this repository to your local machine by running the +following command: + + +```sh +$ git clone https://github.com/grpc/grpc.git +``` + +Change your current directory to examples/python/helloworld + +```sh +$ cd examples/python/helloworld/ +``` + +### Defining a service + +The first step in creating our example is to define a *service*: an RPC +service specifies the methods that can be called remotely with their parameters +and return types. As you saw in the +[overview](#protocolbuffers) above, gRPC does this using [protocol +buffers](https://developers.google.com/protocol-buffers/docs/overview). We +use the protocol buffers interface definition language (IDL) to define our +service methods, and define the parameters and return +types as protocol buffer message types. Both the client and the +server use interface code generated from the service definition. + +Here's our example service definition. The `Greeting` +service has one method, `hello`, that lets the server receive a single +`HelloRequest` +message from the remote client containing the user's name, then send back +a greeting in a single `HelloReply`. This is the simplest type of RPC you +can specify in gRPC. + +``` +syntax = "proto3"; + +option java_package = "io.grpc.examples"; + +package helloworld; + +// The greeting service definition. +service Greeter { + // Sends a greeting + rpc SayHello (HelloRequest) returns (HelloReply) {} +} + +// The request message containing the user's name. +message HelloRequest { + string name = 1; +} + +// The response message containing the greetings +message HelloReply { + string message = 1; +} + +``` + + +### Generating gRPC code + +Once we've defined our service, we use the protocol buffer compiler +`protoc` to generate the special client and server code we need to create +our application. The generated code contains both stub code for clients to +use and an abstract interface for servers to implement, both with the method +defined in our `Greeting` service. + +To generate the client and server side interfaces: + +```sh +$ ./run_codegen.sh +``` +Which internally invokes the proto-compiler as: + +```sh +$ protoc -I ../../protos --python_out=. --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_python_plugin` ../../protos/helloworld.proto +``` + +### The client + +Client-side code can be found in [greeter_client.py](greeter_client.py). + +You can run the client using: + +```sh +$ ./run_client.sh +``` + + +### The server + +Server side code can be found in [greeter_server.py](greeter_server.py). + +You can run the server using: + +```sh +$ ./run_server.sh +``` diff --git a/examples/python/helloworld/greeter_client.py b/examples/python/helloworld/greeter_client.py new file mode 100644 index 00000000..561b25bc --- /dev/null +++ b/examples/python/helloworld/greeter_client.py @@ -0,0 +1,47 @@ +# Copyright 2015, 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. + +"""The Python implementation of the GRPC helloworld.Greeter client.""" + +from grpc.beta import implementations + +import helloworld_pb2 + +_TIMEOUT_SECONDS = 10 + + +def run(): + channel = implementations.insecure_channel('localhost', 50051) + stub = helloworld_pb2.beta_create_Greeter_stub(channel) + response = stub.SayHello(helloworld_pb2.HelloRequest(name='you'), _TIMEOUT_SECONDS) + print "Greeter client received: " + response.message + + +if __name__ == '__main__': + run() diff --git a/examples/python/helloworld/greeter_server.py b/examples/python/helloworld/greeter_server.py new file mode 100644 index 00000000..1514d8f2 --- /dev/null +++ b/examples/python/helloworld/greeter_server.py @@ -0,0 +1,56 @@ +# Copyright 2015, 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. + +"""The Python implementation of the GRPC helloworld.Greeter server.""" + +import time + +import helloworld_pb2 + +_ONE_DAY_IN_SECONDS = 60 * 60 * 24 + + +class Greeter(helloworld_pb2.BetaGreeterServicer): + + def SayHello(self, request, context): + return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name) + + +def serve(): + server = helloworld_pb2.beta_create_Greeter_server(Greeter()) + server.add_insecure_port('[::]:50051') + server.start() + try: + while True: + time.sleep(_ONE_DAY_IN_SECONDS) + except KeyboardInterrupt: + server.stop() + +if __name__ == '__main__': + serve() diff --git a/examples/python/helloworld/run_client.sh b/examples/python/helloworld/run_client.sh new file mode 100755 index 00000000..095e6bc2 --- /dev/null +++ b/examples/python/helloworld/run_client.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# This is where you have cloned out the https://github.com/grpc/grpc repository +# And built gRPC Python. +# ADJUST THIS PATH TO WHERE YOUR ACTUAL LOCATION IS +GRPC_ROOT=~/github/grpc + +$GRPC_ROOT/python2.7_virtual_environment/bin/python greeter_client.py diff --git a/examples/python/helloworld/run_codegen.sh b/examples/python/helloworld/run_codegen.sh new file mode 100755 index 00000000..4d826c79 --- /dev/null +++ b/examples/python/helloworld/run_codegen.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +# Runs the protoc with gRPC plugin to generate protocol messages and gRPC stubs. +protoc -I ../../protos --python_out=. --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_python_plugin` ../../protos/helloworld.proto diff --git a/examples/python/helloworld/run_server.sh b/examples/python/helloworld/run_server.sh new file mode 100755 index 00000000..13b009e6 --- /dev/null +++ b/examples/python/helloworld/run_server.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +# This is where you have cloned out the https://github.com/grpc/grpc repository +# And built gRPC Python. +# ADJUST THIS PATH TO WHERE YOUR ACTUAL LOCATION IS +GRPC_ROOT=~/github/grpc + +$GRPC_ROOT/python2.7_virtual_environment/bin/python greeter_server.py + diff --git a/examples/python/route_guide/.gitignore b/examples/python/route_guide/.gitignore new file mode 100644 index 00000000..0d20b648 --- /dev/null +++ b/examples/python/route_guide/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/examples/python/route_guide/README.md b/examples/python/route_guide/README.md new file mode 100644 index 00000000..cb1aa7d7 --- /dev/null +++ b/examples/python/route_guide/README.md @@ -0,0 +1,299 @@ +#gRPC Basics: Python + +This tutorial provides a basic Python programmer's introduction to working with gRPC. By walking through this example you'll learn how to: + +- Define a service in a .proto file. +- Generate server and client code using the protocol buffer compiler. +- Use the Python gRPC API to write a simple client and server for your service. + +It assumes that you have read the [Getting started](https://github.com/grpc/grpc/tree/master/examples) guide and are familiar with [protocol buffers] (https://developers.google.com/protocol-buffers/docs/overview). Note that the example in this tutorial uses the proto3 version of the protocol buffers language, which is currently in alpha release:you can find out more in the [proto3 language guide](https://developers.google.com/protocol-buffers/docs/proto3) and see the [release notes](https://github.com/google/protobuf/releases) for the new version in the protocol buffers Github repository. + +This isn't a comprehensive guide to using gRPC in Python: more reference documentation is coming soon. + + +## Why use gRPC? + +This example is a simple route mapping application that lets clients get information about features on their route, create a summary of their route, and exchange route information such as traffic updates with the server and other clients. + +With gRPC you can define your service once in a .proto file and implement clients and servers in any of gRPC's supported languages, which in turn can be run in environments ranging from servers inside Google to your own tablet, with all the complexity of communication between different languages and environments is handled for you by gRPC. You also get all the advantages of working with protocol buffers, including efficient serialization, a simple IDL, and easy interface updating. + +## Example code and setup + +The example code for this tutorial is in [examples/python/route_guide](.). To download the example, clone this repository by running the following command: +```shell +$ git clone https://github.com/grpc/grpc.git +``` + +Then change your current directory to `examples/python/route_guide`: +```shell +$ cd examples/python/route_guide +``` + +You also should have the relevant tools installed to generate the server and client interface code - if you don't already, follow the setup instructions in [the Python quick start guide](../helloworld). + +## Defining the service + +Your first step (as you'll know from [Getting started](https://github.com/grpc/grpc/tree/master/examples)) is to define the gRPC *service* and the method *request* and *response* types using [protocol buffers](https://developers.google.com/protocol-buffers/docs/overview). You can see the complete .proto file in [`examples/protos/route_guide.proto`](../../protos/route_guide.proto). + +To define a service, you specify a named `service` in your .proto file: + +```protobuf +service RouteGuide { + // (Method definitions not shown) +} +``` + +Then you define `rpc` methods inside your service definition, specifying their request and response types. gRPC lets you define four kinds of service method, all of which are used in the `RouteGuide` service: + +- A *simple RPC* where the client sends a request to the server using the stub and waits for a response to come back, just like a normal function call. +```protobuf + // Obtains the feature at a given position. + rpc GetFeature(Point) returns (Feature) {} +``` + +- A *response-streaming RPC* where the client sends a request to the server and gets a stream to read a sequence of messages back. The client reads from the returned stream until there are no more messages. As you can see in the example, you specify a response-streaming method by placing the `stream` keyword before the *response* type. +```protobuf + // Obtains the Features available within the given Rectangle. Results are + // streamed rather than returned at once (e.g. in a response message with a + // repeated field), as the rectangle may cover a large area and contain a + // huge number of features. + rpc ListFeatures(Rectangle) returns (stream Feature) {} +``` + +- A *request-streaming RPC* where the client writes a sequence of messages and sends them to the server, again using a provided stream. Once the client has finished writing the messages, it waits for the server to read them all and return its response. You specify a request-streaming method by placing the `stream` keyword before the *request* type. +```protobuf + // Accepts a stream of Points on a route being traversed, returning a + // RouteSummary when traversal is completed. + rpc RecordRoute(stream Point) returns (RouteSummary) {} +``` + +- A *bidirectionally-streaming RPC* where both sides send a sequence of messages using a read-write stream. The two streams operate independently, so clients and servers can read and write in whatever order they like: for example, the server could wait to receive all the client messages before writing its responses, or it could alternately read a message then write a message, or some other combination of reads and writes. The order of messages in each stream is preserved. You specify this type of method by placing the `stream` keyword before both the request and the response. +```protobuf + // Accepts a stream of RouteNotes sent while a route is being traversed, + // while receiving other RouteNotes (e.g. from other users). + rpc RouteChat(stream RouteNote) returns (stream RouteNote) {} +``` + +Your .proto file also contains protocol buffer message type definitions for all the request and response types used in our service methods - for example, here's the `Point` message type: +```protobuf +// Points are represented as latitude-longitude pairs in the E7 representation +// (degrees multiplied by 10**7 and rounded to the nearest integer). +// Latitudes should be in the range +/- 90 degrees and longitude should be in +// the range +/- 180 degrees (inclusive). +message Point { + int32 latitude = 1; + int32 longitude = 2; +} +``` + +## Generating client and server code + +Next you need to generate the gRPC client and server interfaces from your .proto service definition. You do this using the protocol buffer compiler `protoc` with a special gRPC Python plugin. Make sure you've installed protoc and followed the gRPC Python plugin [installation instructions](https://github.com/grpc/grpc/blob/master/INSTALL) first): + +With `protoc` and the gRPC Python plugin installed, use the following command to generate the Python code: + +```shell +$ protoc -I ../../protos --python_out=. --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_python_plugin` ../../protos/route_guide.proto +``` + +Note that as we've already provided a version of the generated code in the example repository, running this command regenerates the appropriate file rather than creates a new one. The generated code file is called `route_guide_pb2.py` and contains: +- classes for the messages defined in route_guide.proto +- abstract classes for the service defined in route_guide.proto + - `BetaRouteGuideServicer`, which defines the interface for implementations of the RouteGuide service + - `BetaRouteGuideStub`, which can be used by clients to invoke RouteGuide RPCs +- functions for application use + - `beta_create_RouteGuide_server`, which creates a gRPC server given a `BetaRouteGuideServicer` object + - `beta_create_RouteGuide_stub`, which can be used by clients to create a stub object + + +## Creating the server + +First let's look at how you create a `RouteGuide` server. If you're only interested in creating gRPC clients, you can skip this section and go straight to [Creating the client](#client) (though you might find it interesting anyway!). + +Creating and running a `RouteGuide` server breaks down into two work items: +- Implementing the servicer interface generated from our service definition with functions that perform the actual "work" of the service. +- Running a gRPC server to listen for requests from clients and transmit responses. + +You can find the example `RouteGuide` server in [route_guide_server.py](route_guide_server.py). + +### Implementing RouteGuide + +`route_guide_server.py` has a `RouteGuideServicer` class that implements the generated interface `route_guide_pb2.BetaRouteGuideServicer`: + +```python +# RouteGuideServicer provides an implementation of the methods of the RouteGuide service. +class RouteGuideServicer(route_guide_pb2.BetaRouteGuideServicer): +``` + +`RouteGuideServicer` implements all the `RouteGuide` service methods. + +#### Simple RPC + +Let's look at the simplest type first, `GetFeature`, which just gets a `Point` from the client and returns the corresponding feature information from its database in a `Feature`. + +```python + def GetFeature(self, request, context): + feature = get_feature(self.db, request) + if feature is None: + return route_guide_pb2.Feature(name="", location=request) + else: + return feature +``` + +The method is passed a `route_guide_pb2.Point` request for the RPC, and a `ServicerContext` object that provides RPC-specific information such as timeout limits. It returns a `route_guide_pb2.Feature` response. + +#### Response-streaming RPC + +Now let's look at the next method. `ListFeatures` is a response-streaming RPC that sends multiple `Feature`s to the client. + +```python + def ListFeatures(self, request, context): + left = min(request.lo.longitude, request.hi.longitude) + right = max(request.lo.longitude, request.hi.longitude) + top = max(request.lo.latitude, request.hi.latitude) + bottom = min(request.lo.latitude, request.hi.latitude) + for feature in self.db: + if (feature.location.longitude >= left and + feature.location.longitude <= right and + feature.location.latitude >= bottom and + feature.location.latitude <= top): + yield feature +``` + +Here the request message is a `route_guide_pb2.Rectangle` within which the client wants to find `Feature`s. Instead of returning a single response the method yields zero or more responses. + +#### Request-streaming RPC + +The request-streaming method `RecordRoute` uses an [iterator](https://docs.python.org/2/library/stdtypes.html#iterator-types) of request values and returns a single response value. + +```python + def RecordRoute(self, request_iterator, context): + point_count = 0 + feature_count = 0 + distance = 0.0 + prev_point = None + + start_time = time.time() + for point in request_iterator: + point_count += 1 + if get_feature(self.db, point): + feature_count += 1 + if prev_point: + distance += get_distance(prev_point, point) + prev_point = point + + elapsed_time = time.time() - start_time + return route_guide_pb2.RouteSummary(point_count=point_count, + feature_count=feature_count, + distance=int(distance), + elapsed_time=int(elapsed_time)) +``` + +#### Bidirectional streaming RPC + +Lastly let's look at the bidirectionally-streaming method `RouteChat`. + +```python + def RouteChat(self, request_iterator, context): + prev_notes = [] + for new_note in request_iterator: + for prev_note in prev_notes: + if prev_note.location == new_note.location: + yield prev_note + prev_notes.append(new_note) +``` + +This method's semantics are a combination of those of the request-streaming method and the response-streaming method. It is passed an iterator of request values and is itself an iterator of response values. + +### Starting the server + +Once you have implemented all the `RouteGuide` methods, the next step is to start up a gRPC server so that clients can actually use your service: + +```python +def serve(): + server = route_guide_pb2.beta_create_RouteGuide_server(RouteGuideServicer()) + server.add_insecure_port('[::]:50051') + server.start() +``` + +Because `start()` does not block you may need to sleep-loop if there is nothing else for your code to do while serving. + + +## Creating the client + +You can see the complete example client code in [route_guide_client.py](route_guide_client.py). + +### Creating a stub + +To call service methods, we first need to create a *stub*. + +We use the `beta_create_RouteGuide_stub` function of the `route_guide_pb2` module, generated from our .proto. + +```python +channel = implementations.insecure_channel('localhost', 50051) +stub = beta_create_RouteGuide_stub(channel) +``` + +The returned object implements all the methods defined by the `BetaRouteGuideStub` interface. + +### Calling service methods + +For RPC methods that return a single response ("response-unary" methods), gRPC Python supports both synchronous (blocking) and asynchronous (non-blocking) control flow semantics. For response-streaming RPC methods, calls immediately return an iterator of response values. Calls to that iterator's `next()` method block until the response to be yielded from the iterator becomes available. + +#### Simple RPC + +A synchronous call to the simple RPC `GetFeature` is nearly as straightforward as calling a local method. The RPC call waits for the server to respond, and will either return a response or raise an exception: + +```python +feature = stub.GetFeature(point, timeout_in_seconds) +``` + +An asynchronous call to `GetFeature` is similar, but like calling a local method asynchronously in a thread pool: + +```python +feature_future = stub.GetFeature.future(point, timeout_in_seconds) +feature = feature_future.result() +``` + +#### Response-streaming RPC + +Calling the response-streaming `ListFeatures` is similar to working with sequence types: + +```python +for feature in stub.ListFeatures(rectangle, timeout_in_seconds): +``` + +#### Request-streaming RPC + +Calling the request-streaming `RecordRoute` is similar to passing a sequence to a local method. Like the simple RPC above that also returns a single response, it can be called synchronously or asynchronously: + +```python +route_summary = stub.RecordRoute(point_sequence, timeout_in_seconds) +``` + +```python +route_summary_future = stub.RecordRoute.future(point_sequence, timeout_in_seconds) +route_summary = route_summary_future.result() +``` + +#### Bidirectional streaming RPC + +Calling the bidirectionally-streaming `RouteChat` has (as is the case on the service-side) a combination of the request-streaming and response-streaming semantics: + +```python +for received_route_note in stub.RouteChat(sent_routes, timeout_in_seconds): +``` + +## Try it out! + +Run the server, which will listen on port 50051: + +```shell +$ python route_guide_server.py +``` + +Run the client (in a different terminal): + +```shell +$ python route_guide_client.py +``` diff --git a/examples/python/route_guide/route_guide_client.py b/examples/python/route_guide/route_guide_client.py new file mode 100644 index 00000000..b1dfad55 --- /dev/null +++ b/examples/python/route_guide/route_guide_client.py @@ -0,0 +1,133 @@ +# Copyright 2015, 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. + +"""The Python implementation of the gRPC route guide client.""" + +import random +import time + +from grpc.beta import implementations + +import route_guide_pb2 +import route_guide_resources + +_TIMEOUT_SECONDS = 30 + + +def make_route_note(message, latitude, longitude): + return route_guide_pb2.RouteNote( + message=message, + location=route_guide_pb2.Point(latitude=latitude, longitude=longitude)) + + +def guide_get_one_feature(stub, point): + feature = stub.GetFeature(point, _TIMEOUT_SECONDS) + if not feature.location: + print "Server returned incomplete feature" + return + + if feature.name: + print "Feature called %s at %s" % (feature.name, feature.location) + else: + print "Found no feature at %s" % feature.location + + +def guide_get_feature(stub): + guide_get_one_feature(stub, route_guide_pb2.Point(latitude=409146138, longitude=-746188906)) + guide_get_one_feature(stub, route_guide_pb2.Point(latitude=0, longitude=0)) + + +def guide_list_features(stub): + rect = route_guide_pb2.Rectangle( + lo=route_guide_pb2.Point( + latitude=400000000, longitude = -750000000), + hi=route_guide_pb2.Point( + latitude = 420000000, longitude = -730000000)) + print "Looking for features between 40, -75 and 42, -73" + + features = stub.ListFeatures(rect, _TIMEOUT_SECONDS) + + for feature in features: + print "Feature called %s at %s" % (feature.name, feature.location) + + +def generate_route(feature_list): + for _ in range(0, 10): + random_feature = feature_list[random.randint(0, len(feature_list) - 1)] + print "Visiting point %s" % random_feature.location + yield random_feature.location + time.sleep(random.uniform(0.5, 1.5)) + + +def guide_record_route(stub): + feature_list = route_guide_resources.read_route_guide_database() + + route_iter = generate_route(feature_list) + route_summary = stub.RecordRoute(route_iter, _TIMEOUT_SECONDS) + print "Finished trip with %s points " % route_summary.point_count + print "Passed %s features " % route_summary.feature_count + print "Travelled %s meters " % route_summary.distance + print "It took %s seconds " % route_summary.elapsed_time + + +def generate_messages(): + messages = [ + make_route_note("First message", 0, 0), + make_route_note("Second message", 0, 1), + make_route_note("Third message", 1, 0), + make_route_note("Fourth message", 0, 0), + make_route_note("Fifth message", 1, 0), + ] + for msg in messages: + print "Sending %s at %s" % (msg.message, msg.location) + yield msg + time.sleep(random.uniform(0.5, 1.0)) + + +def guide_route_chat(stub): + responses = stub.RouteChat(generate_messages(), _TIMEOUT_SECONDS) + for response in responses: + print "Received message %s at %s" % (response.message, response.location) + + +def run(): + channel = implementations.insecure_channel('localhost', 50051) + stub = route_guide_pb2.beta_create_RouteGuide_stub(channel) + print "-------------- GetFeature --------------" + guide_get_feature(stub) + print "-------------- ListFeatures --------------" + guide_list_features(stub) + print "-------------- RecordRoute --------------" + guide_record_route(stub) + print "-------------- RouteChat --------------" + guide_route_chat(stub) + + +if __name__ == '__main__': + run() diff --git a/examples/python/route_guide/route_guide_db.json b/examples/python/route_guide/route_guide_db.json new file mode 100644 index 00000000..9d6a980a --- /dev/null +++ b/examples/python/route_guide/route_guide_db.json @@ -0,0 +1,601 @@ +[{ + "location": { + "latitude": 407838351, + "longitude": -746143763 + }, + "name": "Patriots Path, Mendham, NJ 07945, USA" +}, { + "location": { + "latitude": 408122808, + "longitude": -743999179 + }, + "name": "101 New Jersey 10, Whippany, NJ 07981, USA" +}, { + "location": { + "latitude": 413628156, + "longitude": -749015468 + }, + "name": "U.S. 6, Shohola, PA 18458, USA" +}, { + "location": { + "latitude": 419999544, + "longitude": -740371136 + }, + "name": "5 Conners Road, Kingston, NY 12401, USA" +}, { + "location": { + "latitude": 414008389, + "longitude": -743951297 + }, + "name": "Mid Hudson Psychiatric Center, New Hampton, NY 10958, USA" +}, { + "location": { + "latitude": 419611318, + "longitude": -746524769 + }, + "name": "287 Flugertown Road, Livingston Manor, NY 12758, USA" +}, { + "location": { + "latitude": 406109563, + "longitude": -742186778 + }, + "name": "4001 Tremley Point Road, Linden, NJ 07036, USA" +}, { + "location": { + "latitude": 416802456, + "longitude": -742370183 + }, + "name": "352 South Mountain Road, Wallkill, NY 12589, USA" +}, { + "location": { + "latitude": 412950425, + "longitude": -741077389 + }, + "name": "Bailey Turn Road, Harriman, NY 10926, USA" +}, { + "location": { + "latitude": 412144655, + "longitude": -743949739 + }, + "name": "193-199 Wawayanda Road, Hewitt, NJ 07421, USA" +}, { + "location": { + "latitude": 415736605, + "longitude": -742847522 + }, + "name": "406-496 Ward Avenue, Pine Bush, NY 12566, USA" +}, { + "location": { + "latitude": 413843930, + "longitude": -740501726 + }, + "name": "162 Merrill Road, Highland Mills, NY 10930, USA" +}, { + "location": { + "latitude": 410873075, + "longitude": -744459023 + }, + "name": "Clinton Road, West Milford, NJ 07480, USA" +}, { + "location": { + "latitude": 412346009, + "longitude": -744026814 + }, + "name": "16 Old Brook Lane, Warwick, NY 10990, USA" +}, { + "location": { + "latitude": 402948455, + "longitude": -747903913 + }, + "name": "3 Drake Lane, Pennington, NJ 08534, USA" +}, { + "location": { + "latitude": 406337092, + "longitude": -740122226 + }, + "name": "6324 8th Avenue, Brooklyn, NY 11220, USA" +}, { + "location": { + "latitude": 406421967, + "longitude": -747727624 + }, + "name": "1 Merck Access Road, Whitehouse Station, NJ 08889, USA" +}, { + "location": { + "latitude": 416318082, + "longitude": -749677716 + }, + "name": "78-98 Schalck Road, Narrowsburg, NY 12764, USA" +}, { + "location": { + "latitude": 415301720, + "longitude": -748416257 + }, + "name": "282 Lakeview Drive Road, Highland Lake, NY 12743, USA" +}, { + "location": { + "latitude": 402647019, + "longitude": -747071791 + }, + "name": "330 Evelyn Avenue, Hamilton Township, NJ 08619, USA" +}, { + "location": { + "latitude": 412567807, + "longitude": -741058078 + }, + "name": "New York State Reference Route 987E, Southfields, NY 10975, USA" +}, { + "location": { + "latitude": 416855156, + "longitude": -744420597 + }, + "name": "103-271 Tempaloni Road, Ellenville, NY 12428, USA" +}, { + "location": { + "latitude": 404663628, + "longitude": -744820157 + }, + "name": "1300 Airport Road, North Brunswick Township, NJ 08902, USA" +}, { + "location": { + "latitude": 407113723, + "longitude": -749746483 + }, + "name": "" +}, { + "location": { + "latitude": 402133926, + "longitude": -743613249 + }, + "name": "" +}, { + "location": { + "latitude": 400273442, + "longitude": -741220915 + }, + "name": "" +}, { + "location": { + "latitude": 411236786, + "longitude": -744070769 + }, + "name": "" +}, { + "location": { + "latitude": 411633782, + "longitude": -746784970 + }, + "name": "211-225 Plains Road, Augusta, NJ 07822, USA" +}, { + "location": { + "latitude": 415830701, + "longitude": -742952812 + }, + "name": "" +}, { + "location": { + "latitude": 413447164, + "longitude": -748712898 + }, + "name": "165 Pedersen Ridge Road, Milford, PA 18337, USA" +}, { + "location": { + "latitude": 405047245, + "longitude": -749800722 + }, + "name": "100-122 Locktown Road, Frenchtown, NJ 08825, USA" +}, { + "location": { + "latitude": 418858923, + "longitude": -746156790 + }, + "name": "" +}, { + "location": { + "latitude": 417951888, + "longitude": -748484944 + }, + "name": "650-652 Willi Hill Road, Swan Lake, NY 12783, USA" +}, { + "location": { + "latitude": 407033786, + "longitude": -743977337 + }, + "name": "26 East 3rd Street, New Providence, NJ 07974, USA" +}, { + "location": { + "latitude": 417548014, + "longitude": -740075041 + }, + "name": "" +}, { + "location": { + "latitude": 410395868, + "longitude": -744972325 + }, + "name": "" +}, { + "location": { + "latitude": 404615353, + "longitude": -745129803 + }, + "name": "" +}, { + "location": { + "latitude": 406589790, + "longitude": -743560121 + }, + "name": "611 Lawrence Avenue, Westfield, NJ 07090, USA" +}, { + "location": { + "latitude": 414653148, + "longitude": -740477477 + }, + "name": "18 Lannis Avenue, New Windsor, NY 12553, USA" +}, { + "location": { + "latitude": 405957808, + "longitude": -743255336 + }, + "name": "82-104 Amherst Avenue, Colonia, NJ 07067, USA" +}, { + "location": { + "latitude": 411733589, + "longitude": -741648093 + }, + "name": "170 Seven Lakes Drive, Sloatsburg, NY 10974, USA" +}, { + "location": { + "latitude": 412676291, + "longitude": -742606606 + }, + "name": "1270 Lakes Road, Monroe, NY 10950, USA" +}, { + "location": { + "latitude": 409224445, + "longitude": -748286738 + }, + "name": "509-535 Alphano Road, Great Meadows, NJ 07838, USA" +}, { + "location": { + "latitude": 406523420, + "longitude": -742135517 + }, + "name": "652 Garden Street, Elizabeth, NJ 07202, USA" +}, { + "location": { + "latitude": 401827388, + "longitude": -740294537 + }, + "name": "349 Sea Spray Court, Neptune City, NJ 07753, USA" +}, { + "location": { + "latitude": 410564152, + "longitude": -743685054 + }, + "name": "13-17 Stanley Street, West Milford, NJ 07480, USA" +}, { + "location": { + "latitude": 408472324, + "longitude": -740726046 + }, + "name": "47 Industrial Avenue, Teterboro, NJ 07608, USA" +}, { + "location": { + "latitude": 412452168, + "longitude": -740214052 + }, + "name": "5 White Oak Lane, Stony Point, NY 10980, USA" +}, { + "location": { + "latitude": 409146138, + "longitude": -746188906 + }, + "name": "Berkshire Valley Management Area Trail, Jefferson, NJ, USA" +}, { + "location": { + "latitude": 404701380, + "longitude": -744781745 + }, + "name": "1007 Jersey Avenue, New Brunswick, NJ 08901, USA" +}, { + "location": { + "latitude": 409642566, + "longitude": -746017679 + }, + "name": "6 East Emerald Isle Drive, Lake Hopatcong, NJ 07849, USA" +}, { + "location": { + "latitude": 408031728, + "longitude": -748645385 + }, + "name": "1358-1474 New Jersey 57, Port Murray, NJ 07865, USA" +}, { + "location": { + "latitude": 413700272, + "longitude": -742135189 + }, + "name": "367 Prospect Road, Chester, NY 10918, USA" +}, { + "location": { + "latitude": 404310607, + "longitude": -740282632 + }, + "name": "10 Simon Lake Drive, Atlantic Highlands, NJ 07716, USA" +}, { + "location": { + "latitude": 409319800, + "longitude": -746201391 + }, + "name": "11 Ward Street, Mount Arlington, NJ 07856, USA" +}, { + "location": { + "latitude": 406685311, + "longitude": -742108603 + }, + "name": "300-398 Jefferson Avenue, Elizabeth, NJ 07201, USA" +}, { + "location": { + "latitude": 419018117, + "longitude": -749142781 + }, + "name": "43 Dreher Road, Roscoe, NY 12776, USA" +}, { + "location": { + "latitude": 412856162, + "longitude": -745148837 + }, + "name": "Swan Street, Pine Island, NY 10969, USA" +}, { + "location": { + "latitude": 416560744, + "longitude": -746721964 + }, + "name": "66 Pleasantview Avenue, Monticello, NY 12701, USA" +}, { + "location": { + "latitude": 405314270, + "longitude": -749836354 + }, + "name": "" +}, { + "location": { + "latitude": 414219548, + "longitude": -743327440 + }, + "name": "" +}, { + "location": { + "latitude": 415534177, + "longitude": -742900616 + }, + "name": "565 Winding Hills Road, Montgomery, NY 12549, USA" +}, { + "location": { + "latitude": 406898530, + "longitude": -749127080 + }, + "name": "231 Rocky Run Road, Glen Gardner, NJ 08826, USA" +}, { + "location": { + "latitude": 407586880, + "longitude": -741670168 + }, + "name": "100 Mount Pleasant Avenue, Newark, NJ 07104, USA" +}, { + "location": { + "latitude": 400106455, + "longitude": -742870190 + }, + "name": "517-521 Huntington Drive, Manchester Township, NJ 08759, USA" +}, { + "location": { + "latitude": 400066188, + "longitude": -746793294 + }, + "name": "" +}, { + "location": { + "latitude": 418803880, + "longitude": -744102673 + }, + "name": "40 Mountain Road, Napanoch, NY 12458, USA" +}, { + "location": { + "latitude": 414204288, + "longitude": -747895140 + }, + "name": "" +}, { + "location": { + "latitude": 414777405, + "longitude": -740615601 + }, + "name": "" +}, { + "location": { + "latitude": 415464475, + "longitude": -747175374 + }, + "name": "48 North Road, Forestburgh, NY 12777, USA" +}, { + "location": { + "latitude": 404062378, + "longitude": -746376177 + }, + "name": "" +}, { + "location": { + "latitude": 405688272, + "longitude": -749285130 + }, + "name": "" +}, { + "location": { + "latitude": 400342070, + "longitude": -748788996 + }, + "name": "" +}, { + "location": { + "latitude": 401809022, + "longitude": -744157964 + }, + "name": "" +}, { + "location": { + "latitude": 404226644, + "longitude": -740517141 + }, + "name": "9 Thompson Avenue, Leonardo, NJ 07737, USA" +}, { + "location": { + "latitude": 410322033, + "longitude": -747871659 + }, + "name": "" +}, { + "location": { + "latitude": 407100674, + "longitude": -747742727 + }, + "name": "" +}, { + "location": { + "latitude": 418811433, + "longitude": -741718005 + }, + "name": "213 Bush Road, Stone Ridge, NY 12484, USA" +}, { + "location": { + "latitude": 415034302, + "longitude": -743850945 + }, + "name": "" +}, { + "location": { + "latitude": 411349992, + "longitude": -743694161 + }, + "name": "" +}, { + "location": { + "latitude": 404839914, + "longitude": -744759616 + }, + "name": "1-17 Bergen Court, New Brunswick, NJ 08901, USA" +}, { + "location": { + "latitude": 414638017, + "longitude": -745957854 + }, + "name": "35 Oakland Valley Road, Cuddebackville, NY 12729, USA" +}, { + "location": { + "latitude": 412127800, + "longitude": -740173578 + }, + "name": "" +}, { + "location": { + "latitude": 401263460, + "longitude": -747964303 + }, + "name": "" +}, { + "location": { + "latitude": 412843391, + "longitude": -749086026 + }, + "name": "" +}, { + "location": { + "latitude": 418512773, + "longitude": -743067823 + }, + "name": "" +}, { + "location": { + "latitude": 404318328, + "longitude": -740835638 + }, + "name": "42-102 Main Street, Belford, NJ 07718, USA" +}, { + "location": { + "latitude": 419020746, + "longitude": -741172328 + }, + "name": "" +}, { + "location": { + "latitude": 404080723, + "longitude": -746119569 + }, + "name": "" +}, { + "location": { + "latitude": 401012643, + "longitude": -744035134 + }, + "name": "" +}, { + "location": { + "latitude": 404306372, + "longitude": -741079661 + }, + "name": "" +}, { + "location": { + "latitude": 403966326, + "longitude": -748519297 + }, + "name": "" +}, { + "location": { + "latitude": 405002031, + "longitude": -748407866 + }, + "name": "" +}, { + "location": { + "latitude": 409532885, + "longitude": -742200683 + }, + "name": "" +}, { + "location": { + "latitude": 416851321, + "longitude": -742674555 + }, + "name": "" +}, { + "location": { + "latitude": 406411633, + "longitude": -741722051 + }, + "name": "3387 Richmond Terrace, Staten Island, NY 10303, USA" +}, { + "location": { + "latitude": 413069058, + "longitude": -744597778 + }, + "name": "261 Van Sickle Road, Goshen, NY 10924, USA" +}, { + "location": { + "latitude": 418465462, + "longitude": -746859398 + }, + "name": "" +}, { + "location": { + "latitude": 411733222, + "longitude": -744228360 + }, + "name": "" +}, { + "location": { + "latitude": 410248224, + "longitude": -747127767 + }, + "name": "3 Hasta Way, Newton, NJ 07860, USA" +}] diff --git a/examples/python/route_guide/route_guide_pb2.py b/examples/python/route_guide/route_guide_pb2.py new file mode 100644 index 00000000..d4d9f8dc --- /dev/null +++ b/examples/python/route_guide/route_guide_pb2.py @@ -0,0 +1,481 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: route_guide.proto + +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='route_guide.proto', + package='routeguide', + syntax='proto3', + serialized_pb=b'\n\x11route_guide.proto\x12\nrouteguide\",\n\x05Point\x12\x10\n\x08latitude\x18\x01 \x01(\x05\x12\x11\n\tlongitude\x18\x02 \x01(\x05\"I\n\tRectangle\x12\x1d\n\x02lo\x18\x01 \x01(\x0b\x32\x11.routeguide.Point\x12\x1d\n\x02hi\x18\x02 \x01(\x0b\x32\x11.routeguide.Point\"<\n\x07\x46\x65\x61ture\x12\x0c\n\x04name\x18\x01 \x01(\t\x12#\n\x08location\x18\x02 \x01(\x0b\x32\x11.routeguide.Point\"A\n\tRouteNote\x12#\n\x08location\x18\x01 \x01(\x0b\x32\x11.routeguide.Point\x12\x0f\n\x07message\x18\x02 \x01(\t\"b\n\x0cRouteSummary\x12\x13\n\x0bpoint_count\x18\x01 \x01(\x05\x12\x15\n\rfeature_count\x18\x02 \x01(\x05\x12\x10\n\x08\x64istance\x18\x03 \x01(\x05\x12\x14\n\x0c\x65lapsed_time\x18\x04 \x01(\x05\x32\x85\x02\n\nRouteGuide\x12\x36\n\nGetFeature\x12\x11.routeguide.Point\x1a\x13.routeguide.Feature\"\x00\x12>\n\x0cListFeatures\x12\x15.routeguide.Rectangle\x1a\x13.routeguide.Feature\"\x00\x30\x01\x12>\n\x0bRecordRoute\x12\x11.routeguide.Point\x1a\x18.routeguide.RouteSummary\"\x00(\x01\x12?\n\tRouteChat\x12\x15.routeguide.RouteNote\x1a\x15.routeguide.RouteNote\"\x00(\x01\x30\x01\x42\x0f\n\x07\x65x.grpc\xa2\x02\x03RTGb\x06proto3' +) +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + + + + +_POINT = _descriptor.Descriptor( + name='Point', + full_name='routeguide.Point', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='latitude', full_name='routeguide.Point.latitude', index=0, + number=1, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='longitude', full_name='routeguide.Point.longitude', index=1, + number=2, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=33, + serialized_end=77, +) + + +_RECTANGLE = _descriptor.Descriptor( + name='Rectangle', + full_name='routeguide.Rectangle', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='lo', full_name='routeguide.Rectangle.lo', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='hi', full_name='routeguide.Rectangle.hi', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=79, + serialized_end=152, +) + + +_FEATURE = _descriptor.Descriptor( + name='Feature', + full_name='routeguide.Feature', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='routeguide.Feature.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='location', full_name='routeguide.Feature.location', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=154, + serialized_end=214, +) + + +_ROUTENOTE = _descriptor.Descriptor( + name='RouteNote', + full_name='routeguide.RouteNote', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='location', full_name='routeguide.RouteNote.location', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='message', full_name='routeguide.RouteNote.message', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=216, + serialized_end=281, +) + + +_ROUTESUMMARY = _descriptor.Descriptor( + name='RouteSummary', + full_name='routeguide.RouteSummary', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='point_count', full_name='routeguide.RouteSummary.point_count', index=0, + number=1, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='feature_count', full_name='routeguide.RouteSummary.feature_count', index=1, + number=2, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='distance', full_name='routeguide.RouteSummary.distance', index=2, + number=3, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='elapsed_time', full_name='routeguide.RouteSummary.elapsed_time', index=3, + number=4, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=283, + serialized_end=381, +) + +_RECTANGLE.fields_by_name['lo'].message_type = _POINT +_RECTANGLE.fields_by_name['hi'].message_type = _POINT +_FEATURE.fields_by_name['location'].message_type = _POINT +_ROUTENOTE.fields_by_name['location'].message_type = _POINT +DESCRIPTOR.message_types_by_name['Point'] = _POINT +DESCRIPTOR.message_types_by_name['Rectangle'] = _RECTANGLE +DESCRIPTOR.message_types_by_name['Feature'] = _FEATURE +DESCRIPTOR.message_types_by_name['RouteNote'] = _ROUTENOTE +DESCRIPTOR.message_types_by_name['RouteSummary'] = _ROUTESUMMARY + +Point = _reflection.GeneratedProtocolMessageType('Point', (_message.Message,), dict( + DESCRIPTOR = _POINT, + __module__ = 'route_guide_pb2' + # @@protoc_insertion_point(class_scope:routeguide.Point) + )) +_sym_db.RegisterMessage(Point) + +Rectangle = _reflection.GeneratedProtocolMessageType('Rectangle', (_message.Message,), dict( + DESCRIPTOR = _RECTANGLE, + __module__ = 'route_guide_pb2' + # @@protoc_insertion_point(class_scope:routeguide.Rectangle) + )) +_sym_db.RegisterMessage(Rectangle) + +Feature = _reflection.GeneratedProtocolMessageType('Feature', (_message.Message,), dict( + DESCRIPTOR = _FEATURE, + __module__ = 'route_guide_pb2' + # @@protoc_insertion_point(class_scope:routeguide.Feature) + )) +_sym_db.RegisterMessage(Feature) + +RouteNote = _reflection.GeneratedProtocolMessageType('RouteNote', (_message.Message,), dict( + DESCRIPTOR = _ROUTENOTE, + __module__ = 'route_guide_pb2' + # @@protoc_insertion_point(class_scope:routeguide.RouteNote) + )) +_sym_db.RegisterMessage(RouteNote) + +RouteSummary = _reflection.GeneratedProtocolMessageType('RouteSummary', (_message.Message,), dict( + DESCRIPTOR = _ROUTESUMMARY, + __module__ = 'route_guide_pb2' + # @@protoc_insertion_point(class_scope:routeguide.RouteSummary) + )) +_sym_db.RegisterMessage(RouteSummary) + + +DESCRIPTOR.has_options = True +DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), b'\n\007ex.grpc\242\002\003RTG') +import abc +from grpc.beta import implementations as beta_implementations +from grpc.early_adopter import implementations as early_adopter_implementations +from grpc.framework.alpha import utilities as alpha_utilities +from grpc.framework.common import cardinality +from grpc.framework.interfaces.face import utilities as face_utilities +class EarlyAdopterRouteGuideServicer(object): + """""" + __metaclass__ = abc.ABCMeta + @abc.abstractmethod + def GetFeature(self, request, context): + raise NotImplementedError() + @abc.abstractmethod + def ListFeatures(self, request, context): + raise NotImplementedError() + @abc.abstractmethod + def RecordRoute(self, request_iterator, context): + raise NotImplementedError() + @abc.abstractmethod + def RouteChat(self, request_iterator, context): + raise NotImplementedError() +class EarlyAdopterRouteGuideServer(object): + """""" + __metaclass__ = abc.ABCMeta + @abc.abstractmethod + def start(self): + raise NotImplementedError() + @abc.abstractmethod + def stop(self): + raise NotImplementedError() +class EarlyAdopterRouteGuideStub(object): + """""" + __metaclass__ = abc.ABCMeta + @abc.abstractmethod + def GetFeature(self, request): + raise NotImplementedError() + GetFeature.async = None + @abc.abstractmethod + def ListFeatures(self, request): + raise NotImplementedError() + ListFeatures.async = None + @abc.abstractmethod + def RecordRoute(self, request_iterator): + raise NotImplementedError() + RecordRoute.async = None + @abc.abstractmethod + def RouteChat(self, request_iterator): + raise NotImplementedError() + RouteChat.async = None +def early_adopter_create_RouteGuide_server(servicer, port, private_key=None, certificate_chain=None): + import route_guide_pb2 + import route_guide_pb2 + import route_guide_pb2 + import route_guide_pb2 + import route_guide_pb2 + import route_guide_pb2 + import route_guide_pb2 + import route_guide_pb2 + method_service_descriptions = { + "GetFeature": alpha_utilities.unary_unary_service_description( + servicer.GetFeature, + route_guide_pb2.Point.FromString, + route_guide_pb2.Feature.SerializeToString, + ), + "ListFeatures": alpha_utilities.unary_stream_service_description( + servicer.ListFeatures, + route_guide_pb2.Rectangle.FromString, + route_guide_pb2.Feature.SerializeToString, + ), + "RecordRoute": alpha_utilities.stream_unary_service_description( + servicer.RecordRoute, + route_guide_pb2.Point.FromString, + route_guide_pb2.RouteSummary.SerializeToString, + ), + "RouteChat": alpha_utilities.stream_stream_service_description( + servicer.RouteChat, + route_guide_pb2.RouteNote.FromString, + route_guide_pb2.RouteNote.SerializeToString, + ), + } + return early_adopter_implementations.server("routeguide.RouteGuide", method_service_descriptions, port, private_key=private_key, certificate_chain=certificate_chain) +def early_adopter_create_RouteGuide_stub(host, port, metadata_transformer=None, secure=False, root_certificates=None, private_key=None, certificate_chain=None, server_host_override=None): + import route_guide_pb2 + import route_guide_pb2 + import route_guide_pb2 + import route_guide_pb2 + import route_guide_pb2 + import route_guide_pb2 + import route_guide_pb2 + import route_guide_pb2 + method_invocation_descriptions = { + "GetFeature": alpha_utilities.unary_unary_invocation_description( + route_guide_pb2.Point.SerializeToString, + route_guide_pb2.Feature.FromString, + ), + "ListFeatures": alpha_utilities.unary_stream_invocation_description( + route_guide_pb2.Rectangle.SerializeToString, + route_guide_pb2.Feature.FromString, + ), + "RecordRoute": alpha_utilities.stream_unary_invocation_description( + route_guide_pb2.Point.SerializeToString, + route_guide_pb2.RouteSummary.FromString, + ), + "RouteChat": alpha_utilities.stream_stream_invocation_description( + route_guide_pb2.RouteNote.SerializeToString, + route_guide_pb2.RouteNote.FromString, + ), + } + return early_adopter_implementations.stub("routeguide.RouteGuide", method_invocation_descriptions, host, port, metadata_transformer=metadata_transformer, secure=secure, root_certificates=root_certificates, private_key=private_key, certificate_chain=certificate_chain, server_host_override=server_host_override) + +class BetaRouteGuideServicer(object): + """""" + __metaclass__ = abc.ABCMeta + @abc.abstractmethod + def GetFeature(self, request, context): + raise NotImplementedError() + @abc.abstractmethod + def ListFeatures(self, request, context): + raise NotImplementedError() + @abc.abstractmethod + def RecordRoute(self, request_iterator, context): + raise NotImplementedError() + @abc.abstractmethod + def RouteChat(self, request_iterator, context): + raise NotImplementedError() + +class BetaRouteGuideStub(object): + """The interface to which stubs will conform.""" + __metaclass__ = abc.ABCMeta + @abc.abstractmethod + def GetFeature(self, request, timeout): + raise NotImplementedError() + GetFeature.future = None + @abc.abstractmethod + def ListFeatures(self, request, timeout): + raise NotImplementedError() + @abc.abstractmethod + def RecordRoute(self, request_iterator, timeout): + raise NotImplementedError() + RecordRoute.future = None + @abc.abstractmethod + def RouteChat(self, request_iterator, timeout): + raise NotImplementedError() + +def beta_create_RouteGuide_server(servicer, pool=None, pool_size=None, default_timeout=None, maximum_timeout=None): + import route_guide_pb2 + import route_guide_pb2 + import route_guide_pb2 + import route_guide_pb2 + import route_guide_pb2 + import route_guide_pb2 + import route_guide_pb2 + import route_guide_pb2 + request_deserializers = { + ('routeguide.RouteGuide', 'GetFeature'): route_guide_pb2.Point.FromString, + ('routeguide.RouteGuide', 'ListFeatures'): route_guide_pb2.Rectangle.FromString, + ('routeguide.RouteGuide', 'RecordRoute'): route_guide_pb2.Point.FromString, + ('routeguide.RouteGuide', 'RouteChat'): route_guide_pb2.RouteNote.FromString, + } + response_serializers = { + ('routeguide.RouteGuide', 'GetFeature'): route_guide_pb2.Feature.SerializeToString, + ('routeguide.RouteGuide', 'ListFeatures'): route_guide_pb2.Feature.SerializeToString, + ('routeguide.RouteGuide', 'RecordRoute'): route_guide_pb2.RouteSummary.SerializeToString, + ('routeguide.RouteGuide', 'RouteChat'): route_guide_pb2.RouteNote.SerializeToString, + } + method_implementations = { + ('routeguide.RouteGuide', 'GetFeature'): face_utilities.unary_unary_inline(servicer.GetFeature), + ('routeguide.RouteGuide', 'ListFeatures'): face_utilities.unary_stream_inline(servicer.ListFeatures), + ('routeguide.RouteGuide', 'RecordRoute'): face_utilities.stream_unary_inline(servicer.RecordRoute), + ('routeguide.RouteGuide', 'RouteChat'): face_utilities.stream_stream_inline(servicer.RouteChat), + } + server_options = beta_implementations.server_options(request_deserializers=request_deserializers, response_serializers=response_serializers, thread_pool=pool, thread_pool_size=pool_size, default_timeout=default_timeout, maximum_timeout=maximum_timeout) + return beta_implementations.server(method_implementations, options=server_options) + +def beta_create_RouteGuide_stub(channel, host=None, metadata_transformer=None, pool=None, pool_size=None): + import route_guide_pb2 + import route_guide_pb2 + import route_guide_pb2 + import route_guide_pb2 + import route_guide_pb2 + import route_guide_pb2 + import route_guide_pb2 + import route_guide_pb2 + request_serializers = { + ('routeguide.RouteGuide', 'GetFeature'): route_guide_pb2.Point.SerializeToString, + ('routeguide.RouteGuide', 'ListFeatures'): route_guide_pb2.Rectangle.SerializeToString, + ('routeguide.RouteGuide', 'RecordRoute'): route_guide_pb2.Point.SerializeToString, + ('routeguide.RouteGuide', 'RouteChat'): route_guide_pb2.RouteNote.SerializeToString, + } + response_deserializers = { + ('routeguide.RouteGuide', 'GetFeature'): route_guide_pb2.Feature.FromString, + ('routeguide.RouteGuide', 'ListFeatures'): route_guide_pb2.Feature.FromString, + ('routeguide.RouteGuide', 'RecordRoute'): route_guide_pb2.RouteSummary.FromString, + ('routeguide.RouteGuide', 'RouteChat'): route_guide_pb2.RouteNote.FromString, + } + cardinalities = { + 'GetFeature': cardinality.Cardinality.UNARY_UNARY, + 'ListFeatures': cardinality.Cardinality.UNARY_STREAM, + 'RecordRoute': cardinality.Cardinality.STREAM_UNARY, + 'RouteChat': cardinality.Cardinality.STREAM_STREAM, + } + stub_options = beta_implementations.stub_options(host=host, metadata_transformer=metadata_transformer, request_serializers=request_serializers, response_deserializers=response_deserializers, thread_pool=pool, thread_pool_size=pool_size) + return beta_implementations.dynamic_stub(channel, 'routeguide.RouteGuide', cardinalities, options=stub_options) +# @@protoc_insertion_point(module_scope) diff --git a/examples/python/route_guide/route_guide_resources.py b/examples/python/route_guide/route_guide_resources.py new file mode 100755 index 00000000..30c77110 --- /dev/null +++ b/examples/python/route_guide/route_guide_resources.py @@ -0,0 +1,53 @@ +# Copyright 2015, 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. + +"""Common resources used in the gRPC route guide example.""" + +import json + +import route_guide_pb2 + + +def read_route_guide_database(): + """Reads the route guide database. + + Returns: + The full contents of the route guide database as a sequence of + route_guide_pb2.Features. + """ + feature_list = [] + with open("route_guide_db.json") as route_guide_db_file: + for item in json.load(route_guide_db_file): + feature = route_guide_pb2.Feature( + name=item["name"], + location=route_guide_pb2.Point( + latitude=item["location"]["latitude"], + longitude=item["location"]["longitude"])) + feature_list.append(feature) + return feature_list diff --git a/examples/python/route_guide/route_guide_server.py b/examples/python/route_guide/route_guide_server.py new file mode 100644 index 00000000..f23b98bf --- /dev/null +++ b/examples/python/route_guide/route_guide_server.py @@ -0,0 +1,134 @@ +# Copyright 2015, 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. + +"""The Python implementation of the gRPC route guide server.""" + +import time +import math + +import route_guide_pb2 +import route_guide_resources + +_ONE_DAY_IN_SECONDS = 60 * 60 * 24 + + +def get_feature(feature_db, point): + """Returns Feature at given location or None.""" + for feature in feature_db: + if feature.location == point: + return feature + return None + + +def get_distance(start, end): + """Distance between two points.""" + coord_factor = 10000000.0 + lat_1 = start.latitude / coord_factor + lat_2 = end.latitude / coord_factor + lon_1 = start.latitude / coord_factor + lon_2 = end.longitude / coord_factor + lat_rad_1 = math.radians(lat_1) + lat_rad_2 = math.radians(lat_2) + delta_lat_rad = math.radians(lat_2 - lat_1) + delta_lon_rad = math.radians(lon_2 - lon_1) + + a = (pow(math.sin(delta_lat_rad / 2), 2) + + (math.cos(lat_rad_1) * math.cos(lat_rad_2) * + pow(math.sin(delta_lon_rad / 2), 2))) + c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a)) + R = 6371000; # metres + return R * c; + +class RouteGuideServicer(route_guide_pb2.BetaRouteGuideServicer): + """Provides methods that implement functionality of route guide server.""" + + def __init__(self): + self.db = route_guide_resources.read_route_guide_database() + + def GetFeature(self, request, context): + feature = get_feature(self.db, request) + if feature is None: + return route_guide_pb2.Feature(name="", location=request) + else: + return feature + + def ListFeatures(self, request, context): + left = min(request.lo.longitude, request.hi.longitude) + right = max(request.lo.longitude, request.hi.longitude) + top = max(request.lo.latitude, request.hi.latitude) + bottom = min(request.lo.latitude, request.hi.latitude) + for feature in self.db: + if (feature.location.longitude >= left and + feature.location.longitude <= right and + feature.location.latitude >= bottom and + feature.location.latitude <= top): + yield feature + + def RecordRoute(self, request_iterator, context): + point_count = 0 + feature_count = 0 + distance = 0.0 + prev_point = None + + start_time = time.time() + for point in request_iterator: + point_count += 1 + if get_feature(self.db, point): + feature_count += 1 + if prev_point: + distance += get_distance(prev_point, point) + prev_point = point + + elapsed_time = time.time() - start_time + return route_guide_pb2.RouteSummary(point_count=point_count, + feature_count=feature_count, + distance=int(distance), + elapsed_time=int(elapsed_time)) + + def RouteChat(self, request_iterator, context): + prev_notes = [] + for new_note in request_iterator: + for prev_note in prev_notes: + if prev_note.location == new_note.location: + yield prev_note + prev_notes.append(new_note) + + +def serve(): + server = route_guide_pb2.beta_create_RouteGuide_server(RouteGuideServicer()) + server.add_insecure_port('[::]:50051') + server.start() + try: + while True: + time.sleep(_ONE_DAY_IN_SECONDS) + except KeyboardInterrupt: + server.stop() + +if __name__ == '__main__': + serve() diff --git a/examples/python/route_guide/run_client.sh b/examples/python/route_guide/run_client.sh new file mode 100755 index 00000000..d2552c28 --- /dev/null +++ b/examples/python/route_guide/run_client.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# This is where you have cloned out the https://github.com/grpc/grpc repository +# And built gRPC Python. +# ADJUST THIS PATH TO WHERE YOUR ACTUAL LOCATION IS +GRPC_ROOT=~/github/grpc + +$GRPC_ROOT/python2.7_virtual_environment/bin/python -B route_guide_client.py diff --git a/examples/python/route_guide/run_codegen.sh b/examples/python/route_guide/run_codegen.sh new file mode 100755 index 00000000..a5759025 --- /dev/null +++ b/examples/python/route_guide/run_codegen.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +# Runs the protoc with gRPC plugin to generate protocol messages and gRPC stubs. +protoc -I ../../protos --python_out=. --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_python_plugin` ../../protos/route_guide.proto diff --git a/examples/python/route_guide/run_server.sh b/examples/python/route_guide/run_server.sh new file mode 100755 index 00000000..8f759250 --- /dev/null +++ b/examples/python/route_guide/run_server.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# This is where you have cloned out the https://github.com/grpc/grpc repository +# And built gRPC Python. +# ADJUST THIS PATH TO WHERE YOUR ACTUAL LOCATION IS +GRPC_ROOT=~/github/grpc + +$GRPC_ROOT/python2.7_virtual_environment/bin/python -B route_guide_server.py diff --git a/examples/ruby/.gitignore b/examples/ruby/.gitignore new file mode 100644 index 00000000..62fcb4fa --- /dev/null +++ b/examples/ruby/.gitignore @@ -0,0 +1,15 @@ +/.bundle/ +/.yardoc +/Gemfile.lock +/_yardoc/ +/coverage/ +/doc/ +/pkg/ +/spec/reports/ +/tmp/ +*.bundle +*.so +*.o +*.a +mkmf.log +vendor diff --git a/examples/ruby/Gemfile b/examples/ruby/Gemfile new file mode 100644 index 00000000..65d3f0ad --- /dev/null +++ b/examples/ruby/Gemfile @@ -0,0 +1,6 @@ +# -*- ruby -*- +# encoding: utf-8 + +source 'https://rubygems.org/' + +gem 'grpc', :git => 'https://github.com/grpc/grpc.git', :submodules => true, glob: 'src/ruby/*.gemspec' diff --git a/examples/ruby/README.md b/examples/ruby/README.md new file mode 100644 index 00000000..36f35270 --- /dev/null +++ b/examples/ruby/README.md @@ -0,0 +1,61 @@ +gRPC in 3 minutes (Ruby) +======================== + +BACKGROUND +------------- +For this sample, we've already generated the server and client stubs from [helloworld.proto][] + +PREREQUISITES +------------- + +- Ruby 2.x +This requires Ruby 2.x, as the gRPC API surface uses keyword args. +If you don't have that installed locally, you can use [RVM][] to use Ruby 2.x for testing without upgrading the version of Ruby on your whole system. +RVM is also useful if you don't have the necessary privileges to update your system's Ruby. + + ```sh + $ # RVM installation as specified at https://rvm.io/rvm/install + $ gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 + $ \curl -sSL https://get.rvm.io | bash -s stable --ruby=ruby-2 + $ + $ # follow the instructions to ensure that your're using the latest stable version of Ruby + $ # and that the rvm command is installed + ``` +- *N.B* Make sure your run `source $HOME/.rvm/scripts/rvm` as instructed to complete the set-up of RVM. + +INSTALL +------- + +- Clone this repository +- Use bundler to install the example package's dependencies + + ```sh + $ # from this directory + $ gem install bundler # if you don't already have bundler available + $ bundle install + ``` + +Try it! +------- + +- Run the server + + ```sh + $ # from this directory + $ bundle exec ./greeter_server.rb & + ``` + +- Run the client + + ```sh + $ # from this directory + $ bundle exec ./greeter_client.rb + ``` + +Tutorial +-------- + +You can find a more detailed tutorial in [gRPC Basics: Ruby](route_guide/README.md) + +[helloworld.proto]:../protos/helloworld.proto +[RVM]:https://www.rvm.io/ diff --git a/examples/ruby/greeter_client.rb b/examples/ruby/greeter_client.rb new file mode 100755 index 00000000..e6cb4bad --- /dev/null +++ b/examples/ruby/greeter_client.rb @@ -0,0 +1,50 @@ +#!/usr/bin/env ruby + +# Copyright 2015, 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. + +# Sample app that connects to a Greeter service. +# +# Usage: $ path/to/greeter_client.rb + +this_dir = File.expand_path(File.dirname(__FILE__)) +lib_dir = File.join(this_dir, 'lib') +$LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir) + +require 'grpc' +require 'helloworld_services' + +def main + stub = Helloworld::Greeter::Stub.new('localhost:50051') + user = ARGV.size > 0 ? ARGV[0] : 'world' + message = stub.say_hello(Helloworld::HelloRequest.new(name: user)).message + p "Greeting: #{message}" +end + +main diff --git a/examples/ruby/greeter_server.rb b/examples/ruby/greeter_server.rb new file mode 100755 index 00000000..d4f9cf7d --- /dev/null +++ b/examples/ruby/greeter_server.rb @@ -0,0 +1,60 @@ +#!/usr/bin/env ruby + +# Copyright 2015, 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. + +# Sample gRPC server that implements the Greeter::Helloworld service. +# +# Usage: $ path/to/greeter_server.rb + +this_dir = File.expand_path(File.dirname(__FILE__)) +lib_dir = File.join(this_dir, 'lib') +$LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir) + +require 'grpc' +require 'helloworld_services' + +# GreeterServer is simple server that implements the Helloworld Greeter server. +class GreeterServer < Helloworld::Greeter::Service + # say_hello implements the SayHello rpc method. + def say_hello(hello_req, _unused_call) + Helloworld::HelloReply.new(message: "Hello #{hello_req.name}") + end +end + +# main starts an RpcServer that receives requests to GreeterServer at the sample +# server port. +def main + s = GRPC::RpcServer.new + s.add_http2_port('0.0.0.0:50051') + s.handle(GreeterServer) + s.run +end + +main diff --git a/examples/ruby/grpc-demo.gemspec b/examples/ruby/grpc-demo.gemspec new file mode 100644 index 00000000..2cc1eb8f --- /dev/null +++ b/examples/ruby/grpc-demo.gemspec @@ -0,0 +1,23 @@ +# -*- ruby -*- +# encoding: utf-8 + +Gem::Specification.new do |s| + s.name = 'grpc-demo' + s.version = '0.5.0' + s.authors = ['gRPC Authors'] + s.email = 'temiola@google.com' + s.homepage = 'https://github.com/grpc/grpc' + s.summary = 'gRPC Ruby overview sample' + s.description = 'Simple demo of using gRPC from Ruby' + + s.files = `git ls-files -- ruby/*`.split("\n") + s.executables = `git ls-files -- ruby/greeter*.rb ruby/route_guide/*.rb`.split("\n").map do |f| + File.basename(f) + end + s.require_paths = ['lib'] + s.platform = Gem::Platform::RUBY + + s.add_dependency 'grpc', '~> 0.6' + + s.add_development_dependency 'bundler', '~> 1.7' +end diff --git a/examples/ruby/lib/helloworld.rb b/examples/ruby/lib/helloworld.rb new file mode 100644 index 00000000..82bdd78e --- /dev/null +++ b/examples/ruby/lib/helloworld.rb @@ -0,0 +1,18 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: helloworld.proto + +require 'google/protobuf' + +Google::Protobuf::DescriptorPool.generated_pool.build do + add_message "helloworld.HelloRequest" do + optional :name, :string, 1 + end + add_message "helloworld.HelloReply" do + optional :message, :string, 1 + end +end + +module Helloworld + HelloRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("helloworld.HelloRequest").msgclass + HelloReply = Google::Protobuf::DescriptorPool.generated_pool.lookup("helloworld.HelloReply").msgclass +end diff --git a/examples/ruby/lib/helloworld_services.rb b/examples/ruby/lib/helloworld_services.rb new file mode 100644 index 00000000..7da45ebc --- /dev/null +++ b/examples/ruby/lib/helloworld_services.rb @@ -0,0 +1,24 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# Source: helloworld.proto for package 'helloworld' + +require 'grpc' +require 'helloworld' + +module Helloworld + module Greeter + + # TODO: add proto service documentation here + class Service + + include GRPC::GenericService + + self.marshal_class_method = :encode + self.unmarshal_class_method = :decode + self.service_name = 'helloworld.Greeter' + + rpc :SayHello, HelloRequest, HelloReply + end + + Stub = Service.rpc_stub_class + end +end diff --git a/examples/ruby/lib/route_guide.rb b/examples/ruby/lib/route_guide.rb new file mode 100644 index 00000000..98bac839 --- /dev/null +++ b/examples/ruby/lib/route_guide.rb @@ -0,0 +1,37 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: route_guide.proto + +require 'google/protobuf' + +Google::Protobuf::DescriptorPool.generated_pool.build do + add_message "examples.Point" do + optional :latitude, :int32, 1 + optional :longitude, :int32, 2 + end + add_message "examples.Rectangle" do + optional :lo, :message, 1, "examples.Point" + optional :hi, :message, 2, "examples.Point" + end + add_message "examples.Feature" do + optional :name, :string, 1 + optional :location, :message, 2, "examples.Point" + end + add_message "examples.RouteNote" do + optional :location, :message, 1, "examples.Point" + optional :message, :string, 2 + end + add_message "examples.RouteSummary" do + optional :point_count, :int32, 1 + optional :feature_count, :int32, 2 + optional :distance, :int32, 3 + optional :elapsed_time, :int32, 4 + end +end + +module Examples + Point = Google::Protobuf::DescriptorPool.generated_pool.lookup("examples.Point").msgclass + Rectangle = Google::Protobuf::DescriptorPool.generated_pool.lookup("examples.Rectangle").msgclass + Feature = Google::Protobuf::DescriptorPool.generated_pool.lookup("examples.Feature").msgclass + RouteNote = Google::Protobuf::DescriptorPool.generated_pool.lookup("examples.RouteNote").msgclass + RouteSummary = Google::Protobuf::DescriptorPool.generated_pool.lookup("examples.RouteSummary").msgclass +end diff --git a/examples/ruby/lib/route_guide_services.rb b/examples/ruby/lib/route_guide_services.rb new file mode 100644 index 00000000..6e07653c --- /dev/null +++ b/examples/ruby/lib/route_guide_services.rb @@ -0,0 +1,27 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# Source: route_guide.proto for package 'examples' + +require 'grpc' +require 'route_guide' + +module Examples + module RouteGuide + + # TODO: add proto service documentation here + class Service + + include GRPC::GenericService + + self.marshal_class_method = :encode + self.unmarshal_class_method = :decode + self.service_name = 'examples.RouteGuide' + + rpc :GetFeature, Point, Feature + rpc :ListFeatures, Rectangle, stream(Feature) + rpc :RecordRoute, stream(Point), RouteSummary + rpc :RouteChat, stream(RouteNote), stream(RouteNote) + end + + Stub = Service.rpc_stub_class + end +end diff --git a/examples/ruby/route_guide/README.md b/examples/ruby/route_guide/README.md new file mode 100644 index 00000000..77e93d8b --- /dev/null +++ b/examples/ruby/route_guide/README.md @@ -0,0 +1,285 @@ +#gRPC Basics: Ruby + +This tutorial provides a basic Ruby programmer's introduction to working with gRPC. By walking through this example you'll learn how to: + +- Define a service in a .proto file. +- Generate server and client code using the protocol buffer compiler. +- Use the Ruby gRPC API to write a simple client and server for your service. + +It assumes that you have read the [Getting started](https://github.com/grpc/grpc/tree/master/examples) guide and are familiar with [protocol buffers] (https://developers.google.com/protocol-buffers/docs/overview). Note that the example in this tutorial uses the proto3 version of the protocol buffers language, which is currently in alpha release:you can find out more in the [proto3 language guide](https://developers.google.com/protocol-buffers/docs/proto3) and see the [release notes](https://github.com/google/protobuf/releases) for the new version in the protocol buffers Github repository. + +This isn't a comprehensive guide to using gRPC in Ruby: more reference documentation is coming soon. + +## Why use gRPC? + +Our example is a simple route mapping application that lets clients get information about features on their route, create a summary of their route, and exchange route information such as traffic updates with the server and other clients. + +With gRPC we can define our service once in a .proto file and implement clients and servers in any of gRPC's supported languages, which in turn can be run in environments ranging from servers inside Google to your own tablet - all the complexity of communication between different languages and environments is handled for you by gRPC. We also get all the advantages of working with protocol buffers, including efficient serialization, a simple IDL, and easy interface updating. + +## Example code and setup + +The example code for our tutorial is in [examples/ruby/route_guide](.). To download the example, clone this repository by running the following command: +```shell +$ git clone https://github.com/grpc/grpc.git +``` + +Then change your current directory to `examples/ruby/route_guide`: +```shell +$ cd examples/ruby/route_guide +``` + +You also should have the relevant tools installed to generate the server and client interface code - if you don't already, follow the setup instructions in [the Ruby quick start guide](..). + + +## Defining the service + +Our first step (as you'll know from [Getting started](https://github.com/grpc/grpc/tree/master/examples)) is to define the gRPC *service* and the method *request* and *response* types using [protocol buffers] (https://developers.google.com/protocol-buffers/docs/overview). You can see the complete .proto file in [`examples/protos/route_guide.proto`](../../route_guide.proto). + +To define a service, you specify a named `service` in your .proto file: + +```protobuf +service RouteGuide { + ... +} +``` + +Then you define `rpc` methods inside your service definition, specifying their request and response types. gRPC lets you define four kinds of service method, all of which are used in the `RouteGuide` service: + +- A *simple RPC* where the client sends a request to the server using the stub and waits for a response to come back, just like a normal function call. +```protobuf + // Obtains the feature at a given position. + rpc GetFeature(Point) returns (Feature) {} +``` + +- A *server-side streaming RPC* where the client sends a request to the server and gets a stream to read a sequence of messages back. The client reads from the returned stream until there are no more messages. As you can see in our example, you specify a server-side streaming method by placing the `stream` keyword before the *response* type. +```protobuf + // Obtains the Features available within the given Rectangle. Results are + // streamed rather than returned at once (e.g. in a response message with a + // repeated field), as the rectangle may cover a large area and contain a + // huge number of features. + rpc ListFeatures(Rectangle) returns (stream Feature) {} +``` + +- A *client-side streaming RPC* where the client writes a sequence of messages and sends them to the server, again using a provided stream. Once the client has finished writing the messages, it waits for the server to read them all and return its response. You specify a server-side streaming method by placing the `stream` keyword before the *request* type. +```protobuf + // Accepts a stream of Points on a route being traversed, returning a + // RouteSummary when traversal is completed. + rpc RecordRoute(stream Point) returns (RouteSummary) {} +``` + +- A *bidirectional streaming RPC* where both sides send a sequence of messages using a read-write stream. The two streams operate independently, so clients and servers can read and write in whatever order they like: for example, the server could wait to receive all the client messages before writing its responses, or it could alternately read a message then write a message, or some other combination of reads and writes. The order of messages in each stream is preserved. You specify this type of method by placing the `stream` keyword before both the request and the response. +```protobuf + // Accepts a stream of RouteNotes sent while a route is being traversed, + // while receiving other RouteNotes (e.g. from other users). + rpc RouteChat(stream RouteNote) returns (stream RouteNote) {} +``` + +Our .proto file also contains protocol buffer message type definitions for all the request and response types used in our service methods - for example, here's the `Point` message type: +```protobuf +// Points are represented as latitude-longitude pairs in the E7 representation +// (degrees multiplied by 10**7 and rounded to the nearest integer). +// Latitudes should be in the range +/- 90 degrees and longitude should be in +// the range +/- 180 degrees (inclusive). +message Point { + int32 latitude = 1; + int32 longitude = 2; +} +``` + + +## Generating client and server code + +Next we need to generate the gRPC client and server interfaces from our .proto service definition. We do this using the protocol buffer compiler `protoc` with a special gRPC Ruby plugin. + +If you want to run this yourself, make sure you've installed protoc and followed the gRPC Ruby plugin [installation instructions](https://github.com/grpc/grpc/blob/master/INSTALL) first): + +Once that's done, the following command can be used to generate the ruby code. + +```shell +$ protoc -I ../../protos --ruby_out=lib --grpc_out=lib --plugin=protoc-gen-grpc=`which grpc_ruby_plugin` ../../protos/route_guide.proto +``` + +Running this command regenerates the following files in the lib directory: +- `lib/route_guide.pb` defines a module `Examples::RouteGuide` + - This contain all the protocol buffer code to populate, serialize, and retrieve our request and response message types +- `lib/route_guide_services.pb`, extends `Examples::RouteGuide` with stub and service classes + - a class `Service` for use as a base class when defining RouteGuide service implementations + - a class `Stub` that can be used to access remote RouteGuide instances + + + +## Creating the server + +First let's look at how we create a `RouteGuide` server. If you're only interested in creating gRPC clients, you can skip this section and go straight to [Creating the client](#client) (though you might find it interesting anyway!). + +There are two parts to making our `RouteGuide` service do its job: +- Implementing the service interface generated from our service definition: doing the actual "work" of our service. +- Running a gRPC server to listen for requests from clients and return the service responses. + +You can find our example `RouteGuide` server in [route_guide_server.rb](route_guide_server.rb). Let's take a closer look at how it works. + +### Implementing RouteGuide + +As you can see, our server has a `ServerImpl` class that extends the generated `RouteGuide::Service`: + +```ruby +# ServerImpl provides an implementation of the RouteGuide service. +class ServerImpl < RouteGuide::Service +``` + +`ServerImpl` implements all our service methods. Let's look at the simplest type first, `GetFeature`, which just gets a `Point` from the client and returns the corresponding feature information from its database in a `Feature`. + +```ruby + def get_feature(point, _call) + name = @feature_db[{ + 'longitude' => point.longitude, + 'latitude' => point.latitude }] || '' + Feature.new(location: point, name: name) + end +``` + +The method is passed a _call for the RPC, the client's `Point` protocol buffer request, and returns a `Feature` protocol buffer. In the method we create the `Feature` with the appropriate information, and then `return` it. + +Now let's look at something a bit more complicated - a streaming RPC. `ListFeatures` is a server-side streaming RPC, so we need to send back multiple `Feature`s to our client. + +```ruby +# in ServerImpl + + def list_features(rectangle, _call) + RectangleEnum.new(@feature_db, rectangle).each + end +``` + +As you can see, here the request object is a `Rectangle` in which our client wants to find `Feature`s, but instead of returning a simple response we need to return an [Enumerator](http://ruby-doc.org//core-2.2.0/Enumerator.html) that yields the responses. In the method, we use a helper class `RectangleEnum`, to act as an Enumerator implementation. + +Similarly, the client-side streaming method `record_route` uses an [Enumerable](http://ruby-doc.org//core-2.2.0/Enumerable.html), but here it's obtained from the call object, which we've ignored in the earlier examples. `call.each_remote_read` yields each message sent by the client in turn. + +```ruby + call.each_remote_read do |point| + ... + end +``` +Finally, let's look at our bidirectional streaming RPC `route_chat`. + +```ruby + def route_chat(notes) + q = EnumeratorQueue.new(self) + t = Thread.new do + begin + notes.each do |n| + ... + end + end + q = EnumeratorQueue.new(self) + ... + return q.each_item + end +``` + +Here the method receives an [Enumerable](http://ruby-doc.org//core-2.2.0/Enumerable.html), but also returns an [Enumerator](http://ruby-doc.org//core-2.2.0/Enumerator.html) that yields the responses. The implementation demonstrates how to set these up so that the requests and responses can be handled concurrently. Although each side will always get the other's messages in the order they were written, both the client and server can read and write in any order — the streams operate completely independently. + +### Starting the server + +Once we've implemented all our methods, we also need to start up a gRPC server so that clients can actually use our service. The following snippet shows how we do this for our `RouteGuide` service: + +```ruby + s = GRPC::RpcServer.new + s.add_http2_port(port) + logger.info("... running insecurely on #{port}") + s.handle(ServerImpl.new(feature_db)) + s.run +``` +As you can see, we build and start our server using a `GRPC::RpcServer`. To do this, we: + +1. Create an instance of our service implementation class `ServerImpl`. +2. Specify the address and port we want to use to listen for client requests using the builder's `add_http2_port` method. +3. Register our service implementation with the `GRPC::RpcServer`. +4. Call `run` on the`GRPC::RpcServer` to create and start an RPC server for our service. + + +## Creating the client + +In this section, we'll look at creating a Ruby client for our `RouteGuide` service. You can see our complete example client code in [route_guide_client.rb](route_guide_client.rb). + +### Creating a stub + +To call service methods, we first need to create a *stub*. + +We use the `Stub` class of the `RouteGuide` module generated from our .proto. + +```ruby + stub = RouteGuide::Stub.new('localhost:50051') +``` + +### Calling service methods + +Now let's look at how we call our service methods. Note that the gRPC Ruby only provides *blocking/synchronous* versions of each method: this means that the RPC call waits for the server to respond, and will either return a response or raise an exception. + +#### Simple RPC + +Calling the simple RPC `GetFeature` is nearly as straightforward as calling a local method. + +```ruby +GET_FEATURE_POINTS = [ + Point.new(latitude: 409_146_138, longitude: -746_188_906), + Point.new(latitude: 0, longitude: 0) +] +.. + GET_FEATURE_POINTS.each do |pt| + resp = stub.get_feature(pt) + ... + p "- found '#{resp.name}' at #{pt.inspect}" + end +``` + +As you can see, we create and populate a request protocol buffer object (in our case `Point`), and create a response protocol buffer object for the server to fill in. Finally, we call the method on the stub, passing it the context, request, and response. If the method returns `OK`, then we can read the response information from the server from our response object. + + +#### Streaming RPCs + +Now let's look at our streaming methods. If you've already read [Creating the server](#server) some of this may look very familiar - streaming RPCs are implemented in a similar way on both sides. Here's where we call the server-side streaming method `list_features`, which returns an `Enumerable` of `Features` + +```ruby + resps = stub.list_features(LIST_FEATURES_RECT) + resps.each do |r| + p "- found '#{r.name}' at #{r.location.inspect}" + end +``` + +The client-side streaming method `record_route` is similar, except there we pass the server an `Enumerable`. + +```ruby + ... + reqs = RandomRoute.new(features, points_on_route) + resp = stub.record_route(reqs.each, deadline) + ... +``` + +Finally, let's look at our bidirectional streaming RPC `route_chat`. In this case, we pass `Enumerable` to the method and get back an `Enumerable`. + +```ruby + resps = stub.route_chat(ROUTE_CHAT_NOTES) + resps.each { |r| p "received #{r.inspect}" } +``` + +Although it's not shown well by this example, each enumerable is independent of the other - both the client and server can read and write in any order — the streams operate completely independently. + +## Try it out! + +Build client and server: + +```shell +$ # from examples/ruby +$ gem install bundler && bundle install +``` +Run the server, which will listen on port 50051: +```shell +$ # from examples/ruby +$ bundle exec route_guide/route_guide_server.rb ../node/route_guide/route_guide_db.json & +``` +Run the client (in a different terminal): +```shell +$ # from examples/ruby +$ bundle exec route_guide/route_guide_client.rb ../node/route_guide/route_guide_db.json & +``` + diff --git a/examples/ruby/route_guide/route_guide_client.rb b/examples/ruby/route_guide/route_guide_client.rb new file mode 100755 index 00000000..181623a6 --- /dev/null +++ b/examples/ruby/route_guide/route_guide_client.rb @@ -0,0 +1,165 @@ +#!/usr/bin/env ruby + +# Copyright 2015, 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. + +# Sample app that connects to a Route Guide service. +# +# Usage: $ path/to/route_guide_client.rb path/to/route_guide_db.json & + +this_dir = File.expand_path(File.dirname(__FILE__)) +lib_dir = File.join(File.dirname(this_dir), 'lib') +$LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir) + +require 'grpc' +require 'route_guide_services' + +include Examples + +GET_FEATURE_POINTS = [ + Point.new(latitude: 409_146_138, longitude: -746_188_906), + Point.new(latitude: 0, longitude: 0) +] + +# runs a GetFeature rpc. +# +# - once with a point known to be present in the sample route database +# - once with a point that is not in the sample database +def run_get_feature(stub) + p 'GetFeature' + p '----------' + GET_FEATURE_POINTS.each do |pt| + resp = stub.get_feature(pt) + if resp.name != '' + p "- found '#{resp.name}' at #{pt.inspect}" + else + p "- found nothing at #{pt.inspect}" + end + end +end + +LIST_FEATURES_RECT = Rectangle.new( + lo: Point.new(latitude: 400_000_000, longitude: -750_000_000), + hi: Point.new(latitude: 420_000_000, longitude: -730_000_000)) + +# runs a ListFeatures rpc. +# +# - the rectangle to chosen to include most of the known features +# in the sample db. +def run_list_features(stub) + p 'ListFeatures' + p '------------' + resps = stub.list_features(LIST_FEATURES_RECT) + resps.each do |r| + p "- found '#{r.name}' at #{r.location.inspect}" + end +end + +# RandomRoute provides an Enumerable that yields a random 'route' of points +# from a list of Features. +class RandomRoute + def initialize(features, size) + @features = features + @size = size + end + + # yields a point, waiting between 0 and 1 seconds between each yield + # + # @return an Enumerable that yields a random point + def each + return enum_for(:each) unless block_given? + @size.times do + json_feature = @features[rand(0..@features.length)] + next if json_feature.nil? + location = json_feature['location'] + pt = Point.new( + Hash[location.each_pair.map { |k, v| [k.to_sym, v] }]) + p "- next point is #{pt.inspect}" + yield pt + sleep(rand(0..1)) + end + end +end + +# runs a RecordRoute rpc. +# +# - the rectangle to chosen to include most of the known features +# in the sample db. +def run_record_route(stub, features) + p 'RecordRoute' + p '-----------' + points_on_route = 10 # arbitrary + deadline = points_on_route # as delay b/w each is max 1 second + reqs = RandomRoute.new(features, points_on_route) + resp = stub.record_route(reqs.each, deadline) + p "summary: #{resp.inspect}" +end + +ROUTE_CHAT_NOTES = [ + RouteNote.new(message: 'doh - a deer', + location: Point.new(latitude: 0, longitude: 0)), + RouteNote.new(message: 'ray - a drop of golden sun', + location: Point.new(latitude: 0, longitude: 1)), + RouteNote.new(message: 'me - the name I call myself', + location: Point.new(latitude: 1, longitude: 0)), + RouteNote.new(message: 'fa - a longer way to run', + location: Point.new(latitude: 1, longitude: 1)), + RouteNote.new(message: 'soh - with needle and a thread', + location: Point.new(latitude: 0, longitude: 1)) +] + +# runs a RouteChat rpc. +# +# sends a canned set of route notes and prints out the responses. +def run_route_chat(stub) + p 'Route Chat' + p '----------' + # TODO: decouple sending and receiving, i.e have the response enumerator run + # on its own thread. + resps = stub.route_chat(ROUTE_CHAT_NOTES) + resps.each { |r| p "received #{r.inspect}" } +end + +def main + stub = RouteGuide::Stub.new('localhost:50051') + run_get_feature(stub) + run_list_features(stub) + run_route_chat(stub) + if ARGV.length == 0 + p 'no feature database; skipping record_route' + exit + end + raw_data = [] + File.open(ARGV[0]) do |f| + raw_data = MultiJson.load(f.read) + end + run_record_route(stub, raw_data) +end + +main diff --git a/examples/ruby/route_guide/route_guide_server.rb b/examples/ruby/route_guide/route_guide_server.rb new file mode 100755 index 00000000..2b2b8084 --- /dev/null +++ b/examples/ruby/route_guide/route_guide_server.rb @@ -0,0 +1,211 @@ +#!/usr/bin/env ruby +# -*- coding: utf-8 -*- + +# Copyright 2015, 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. + +# Sample app that connects to a Route Guide service. +# +# Usage: $ path/to/route_guide_server.rb path/to/route_guide_db.json & + +this_dir = File.expand_path(File.dirname(__FILE__)) +lib_dir = File.join(File.dirname(this_dir), 'lib') +$LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir) + +require 'grpc' +require 'multi_json' +require 'route_guide_services' + +include Examples +COORD_FACTOR = 1e7 +RADIUS = 637_100 + +# Determines the distance between two points. +def calculate_distance(point_a, point_b) + to_radians = proc { |x| x * Math::PI / 180 } + lat_a = point_a.latitude / COORD_FACTOR + lat_b = point_b.latitude / COORD_FACTOR + long_a = point_a.longitude / COORD_FACTOR + long_b = point_b.longitude / COORD_FACTOR + φ1 = to_radians.call(lat_a) + φ2 = to_radians.call(lat_b) + Δφ = to_radians.call(lat_a - lat_b) + Δλ = to_radians.call(long_a - long_b) + a = Math.sin(Δφ / 2)**2 + + Math.cos(φ1) * Math.cos(φ2) + + Math.sin(Δλ / 2)**2 + (2 * RADIUS * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))).to_i +end + +# RectangleEnum provides an Enumerator of the points in a feature_db within a +# given Rectangle. +class RectangleEnum + # @param [Hash] feature_db + # @param [Rectangle] bounds + def initialize(feature_db, bounds) + @feature_db = feature_db + @bounds = bounds + lats = [@bounds.lo.latitude, @bounds.hi.latitude] + longs = [@bounds.lo.longitude, @bounds.hi.longitude] + @lo_lat, @hi_lat = lats.min, lats.max + @lo_long, @hi_long = longs.min, longs.max + end + + # in? determines if location lies within the bounds of this instances + # Rectangle. + def in?(location) + location['longitude'] >= @lo_long && + location['longitude'] <= @hi_long && + location['latitude'] >= @lo_lat && + location['latitude'] <= @hi_lat + end + + # each yields the features in the instances feature_db that lie within the + # instance rectangle. + def each + return enum_for(:each) unless block_given? + @feature_db.each_pair do |location, name| + next unless in?(location) + next if name.nil? || name == '' + pt = Point.new( + Hash[location.each_pair.map { |k, v| [k.to_sym, v] }]) + yield Feature.new(location: pt, name: name) + end + end +end + +# A EnumeratorQueue wraps a Queue to yield the items added to it. +class EnumeratorQueue + extend Forwardable + def_delegators :@q, :push + + def initialize(sentinel) + @q = Queue.new + @sentinel = sentinel + @received_notes = {} + end + + def each_item + return enum_for(:each_item) unless block_given? + loop do + r = @q.pop + break if r.equal?(@sentinel) + fail r if r.is_a? Exception + yield r + end + end +end + +# ServerImpl provides an implementation of the RouteGuide service. +class ServerImpl < RouteGuide::Service + # @param [Hash] feature_db {location => name} + def initialize(feature_db) + @feature_db = feature_db + @received_notes = Hash.new { |h, k| h[k] = [] } + end + + def get_feature(point, _call) + name = @feature_db[{ + 'longitude' => point.longitude, + 'latitude' => point.latitude }] || '' + Feature.new(location: point, name: name) + end + + def list_features(rectangle, _call) + RectangleEnum.new(@feature_db, rectangle).each + end + + def record_route(call) + started, elapsed_time = 0, 0 + distance, count, features, last = 0, 0, 0, nil + call.each_remote_read do |point| + count += 1 + name = @feature_db[{ + 'longitude' => point.longitude, + 'latitude' => point.latitude }] || '' + features += 1 unless name == '' + if last.nil? + last = point + started = Time.now.to_i + next + end + elapsed_time = Time.now.to_i - started + distance += calculate_distance(point, last) + last = point + end + RouteSummary.new(point_count: count, + feature_count: features, + distance: distance, + elapsed_time: elapsed_time) + end + + def route_chat(notes) + q = EnumeratorQueue.new(self) + # run a separate thread that processes the incoming requests + t = Thread.new do + begin + notes.each do |n| + key = { + 'latitude' => n.location.latitude, + 'longitude' => n.location.longitude + } + earlier_msgs = @received_notes[key] + @received_notes[key] << n.message + # send back the earlier messages at this point + earlier_msgs.each do |r| + q.push(RouteNote.new(location: n.location, message: r)) + end + end + q.push(self) # signal completion + rescue StandardError => e + q.push(e) # signal completion via an error + end + end + q.each_item + end +end + +def main + if ARGV.length == 0 + fail 'Please specify the path to the route_guide json database' + end + raw_data = [] + File.open(ARGV[0]) do |f| + raw_data = MultiJson.load(f.read) + end + feature_db = Hash[raw_data.map { |x| [x['location'], x['name']] }] + port = '0.0.0.0:50051' + s = GRPC::RpcServer.new + s.add_http2_port(port) + GRPC.logger.info("... running insecurely on #{port}") + s.handle(ServerImpl.new(feature_db)) + s.run +end + +main diff --git a/gRPC.podspec b/gRPC.podspec new file mode 100644 index 00000000..4edc3e5d --- /dev/null +++ b/gRPC.podspec @@ -0,0 +1,590 @@ +# GRPC CocoaPods podspec +# This file has been automatically generated from a template file. +# Please look at the templates directory instead. +# This file can be regenerated from the template by running +# tools/buildgen/generate_projects.sh + +# Copyright 2015, 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. + + +Pod::Spec.new do |s| + s.name = 'gRPC' + s.version = '0.11.0' + s.summary = 'gRPC client library for iOS/OSX' + s.homepage = 'http://www.grpc.io' + s.license = 'New BSD' + s.authors = { 'The gRPC contributors' => 'grpc-packages@google.com' } + + # s.source = { :git => 'https://github.com/grpc/grpc.git', + # :tag => 'release-0_11_0-objectivec-0.11.0' } + + s.ios.deployment_target = '7.1' + s.osx.deployment_target = '10.9' + s.requires_arc = true + + objc_dir = 'src/objective-c' + + # Reactive Extensions library for iOS. + s.subspec 'RxLibrary' do |ss| + src_dir = "#{objc_dir}/RxLibrary" + ss.source_files = "#{src_dir}/*.{h,m}", "#{src_dir}/**/*.{h,m}" + ss.private_header_files = "#{src_dir}/private/*.h" + ss.header_mappings_dir = "#{objc_dir}" + end + + # Core cross-platform gRPC library, written in C. + s.subspec 'C-Core' do |ss| + ss.source_files = 'src/core/support/env.h', + 'src/core/support/file.h', + 'src/core/support/murmur_hash.h', + 'src/core/support/stack_lockfree.h', + 'src/core/support/grpc_string.h', + 'src/core/support/string_win32.h', + 'src/core/support/thd_internal.h', + 'src/core/support/time_precise.h', + 'grpc/support/alloc.h', + 'grpc/support/atm.h', + 'grpc/support/atm_gcc_atomic.h', + 'grpc/support/atm_gcc_sync.h', + 'grpc/support/atm_win32.h', + 'grpc/support/cmdline.h', + 'grpc/support/cpu.h', + 'grpc/support/histogram.h', + 'grpc/support/host_port.h', + 'grpc/support/log.h', + 'grpc/support/log_win32.h', + 'grpc/support/port_platform.h', + 'grpc/support/slice.h', + 'grpc/support/slice_buffer.h', + 'grpc/support/string_util.h', + 'grpc/support/subprocess.h', + 'grpc/support/sync.h', + 'grpc/support/sync_generic.h', + 'grpc/support/sync_posix.h', + 'grpc/support/sync_win32.h', + 'grpc/support/thd.h', + 'grpc/support/grpc_time.h', + 'grpc/support/tls.h', + 'grpc/support/tls_gcc.h', + 'grpc/support/tls_msvc.h', + 'grpc/support/tls_pthread.h', + 'grpc/support/useful.h', + 'src/core/support/alloc.c', + 'src/core/support/cmdline.c', + 'src/core/support/cpu_iphone.c', + 'src/core/support/cpu_linux.c', + 'src/core/support/cpu_posix.c', + 'src/core/support/cpu_windows.c', + 'src/core/support/env_linux.c', + 'src/core/support/env_posix.c', + 'src/core/support/env_win32.c', + 'src/core/support/file.c', + 'src/core/support/file_posix.c', + 'src/core/support/file_win32.c', + 'src/core/support/histogram.c', + 'src/core/support/host_port.c', + 'src/core/support/log.c', + 'src/core/support/log_android.c', + 'src/core/support/log_linux.c', + 'src/core/support/log_posix.c', + 'src/core/support/log_win32.c', + 'src/core/support/murmur_hash.c', + 'src/core/support/slice.c', + 'src/core/support/slice_buffer.c', + 'src/core/support/stack_lockfree.c', + 'src/core/support/string.c', + 'src/core/support/string_posix.c', + 'src/core/support/string_win32.c', + 'src/core/support/subprocess_posix.c', + 'src/core/support/sync.c', + 'src/core/support/sync_posix.c', + 'src/core/support/sync_win32.c', + 'src/core/support/thd.c', + 'src/core/support/thd_posix.c', + 'src/core/support/thd_win32.c', + 'src/core/support/time.c', + 'src/core/support/time_posix.c', + 'src/core/support/time_win32.c', + 'src/core/support/tls_pthread.c', + 'src/core/security/auth_filters.h', + 'src/core/security/base64.h', + 'src/core/security/credentials.h', + 'src/core/security/json_token.h', + 'src/core/security/jwt_verifier.h', + 'src/core/security/secure_endpoint.h', + 'src/core/security/secure_transport_setup.h', + 'src/core/security/security_connector.h', + 'src/core/security/security_context.h', + 'src/core/tsi/fake_transport_security.h', + 'src/core/tsi/ssl_transport_security.h', + 'src/core/tsi/transport_security.h', + 'src/core/tsi/transport_security_interface.h', + 'src/core/census/grpc_filter.h', + 'src/core/channel/channel_args.h', + 'src/core/channel/channel_stack.h', + 'src/core/channel/client_channel.h', + 'src/core/channel/compress_filter.h', + 'src/core/channel/connected_channel.h', + 'src/core/channel/context.h', + 'src/core/channel/http_client_filter.h', + 'src/core/channel/http_server_filter.h', + 'src/core/channel/noop_filter.h', + 'src/core/client_config/client_config.h', + 'src/core/client_config/connector.h', + 'src/core/client_config/lb_policies/pick_first.h', + 'src/core/client_config/lb_policy.h', + 'src/core/client_config/resolver.h', + 'src/core/client_config/resolver_factory.h', + 'src/core/client_config/resolver_registry.h', + 'src/core/client_config/resolvers/dns_resolver.h', + 'src/core/client_config/resolvers/sockaddr_resolver.h', + 'src/core/client_config/subchannel.h', + 'src/core/client_config/subchannel_factory.h', + 'src/core/client_config/subchannel_factory_decorators/add_channel_arg.h', + 'src/core/client_config/subchannel_factory_decorators/merge_channel_args.h', + 'src/core/client_config/uri_parser.h', + 'src/core/compression/message_compress.h', + 'src/core/debug/trace.h', + 'src/core/httpcli/format_request.h', + 'src/core/httpcli/httpcli.h', + 'src/core/httpcli/parser.h', + 'src/core/iomgr/alarm.h', + 'src/core/iomgr/alarm_heap.h', + 'src/core/iomgr/alarm_internal.h', + 'src/core/iomgr/endpoint.h', + 'src/core/iomgr/endpoint_pair.h', + 'src/core/iomgr/fd_posix.h', + 'src/core/iomgr/iocp_windows.h', + 'src/core/iomgr/iomgr.h', + 'src/core/iomgr/iomgr_internal.h', + 'src/core/iomgr/iomgr_posix.h', + 'src/core/iomgr/pollset.h', + 'src/core/iomgr/pollset_posix.h', + 'src/core/iomgr/pollset_set.h', + 'src/core/iomgr/pollset_set_posix.h', + 'src/core/iomgr/pollset_set_windows.h', + 'src/core/iomgr/pollset_windows.h', + 'src/core/iomgr/resolve_address.h', + 'src/core/iomgr/sockaddr.h', + 'src/core/iomgr/sockaddr_posix.h', + 'src/core/iomgr/sockaddr_utils.h', + 'src/core/iomgr/sockaddr_win32.h', + 'src/core/iomgr/socket_utils_posix.h', + 'src/core/iomgr/socket_windows.h', + 'src/core/iomgr/tcp_client.h', + 'src/core/iomgr/tcp_posix.h', + 'src/core/iomgr/tcp_server.h', + 'src/core/iomgr/tcp_windows.h', + 'src/core/iomgr/time_averaged_stats.h', + 'src/core/iomgr/udp_server.h', + 'src/core/iomgr/wakeup_fd_pipe.h', + 'src/core/iomgr/wakeup_fd_posix.h', + 'src/core/json/json.h', + 'src/core/json/json_common.h', + 'src/core/json/json_reader.h', + 'src/core/json/json_writer.h', + 'src/core/profiling/timers.h', + 'src/core/statistics/census_interface.h', + 'src/core/statistics/census_rpc_stats.h', + 'src/core/surface/byte_buffer_queue.h', + 'src/core/surface/call.h', + 'src/core/surface/channel.h', + 'src/core/surface/completion_queue.h', + 'src/core/surface/event_string.h', + 'src/core/surface/init.h', + 'src/core/surface/server.h', + 'src/core/surface/surface_trace.h', + 'src/core/transport/chttp2/alpn.h', + 'src/core/transport/chttp2/bin_encoder.h', + 'src/core/transport/chttp2/frame.h', + 'src/core/transport/chttp2/frame_data.h', + 'src/core/transport/chttp2/frame_goaway.h', + 'src/core/transport/chttp2/frame_ping.h', + 'src/core/transport/chttp2/frame_rst_stream.h', + 'src/core/transport/chttp2/frame_settings.h', + 'src/core/transport/chttp2/frame_window_update.h', + 'src/core/transport/chttp2/hpack_parser.h', + 'src/core/transport/chttp2/hpack_table.h', + 'src/core/transport/chttp2/http2_errors.h', + 'src/core/transport/chttp2/huffsyms.h', + 'src/core/transport/chttp2/incoming_metadata.h', + 'src/core/transport/chttp2/internal.h', + 'src/core/transport/chttp2/status_conversion.h', + 'src/core/transport/chttp2/stream_encoder.h', + 'src/core/transport/chttp2/stream_map.h', + 'src/core/transport/chttp2/timeout_encoding.h', + 'src/core/transport/chttp2/varint.h', + 'src/core/transport/chttp2_transport.h', + 'src/core/transport/connectivity_state.h', + 'src/core/transport/metadata.h', + 'src/core/transport/stream_op.h', + 'src/core/transport/transport.h', + 'src/core/transport/transport_impl.h', + 'src/core/census/aggregation.h', + 'src/core/census/context.h', + 'src/core/census/rpc_metric_id.h', + 'grpc/grpc_security.h', + 'grpc/byte_buffer.h', + 'grpc/byte_buffer_reader.h', + 'grpc/compression.h', + 'grpc/grpc.h', + 'grpc/status.h', + 'grpc/census.h', + 'src/core/httpcli/httpcli_security_connector.c', + 'src/core/security/base64.c', + 'src/core/security/client_auth_filter.c', + 'src/core/security/credentials.c', + 'src/core/security/credentials_metadata.c', + 'src/core/security/credentials_posix.c', + 'src/core/security/credentials_win32.c', + 'src/core/security/google_default_credentials.c', + 'src/core/security/json_token.c', + 'src/core/security/jwt_verifier.c', + 'src/core/security/secure_endpoint.c', + 'src/core/security/secure_transport_setup.c', + 'src/core/security/security_connector.c', + 'src/core/security/security_context.c', + 'src/core/security/server_auth_filter.c', + 'src/core/security/server_secure_chttp2.c', + 'src/core/surface/init_secure.c', + 'src/core/surface/secure_channel_create.c', + 'src/core/tsi/fake_transport_security.c', + 'src/core/tsi/ssl_transport_security.c', + 'src/core/tsi/transport_security.c', + 'src/core/census/grpc_context.c', + 'src/core/census/grpc_filter.c', + 'src/core/channel/channel_args.c', + 'src/core/channel/channel_stack.c', + 'src/core/channel/client_channel.c', + 'src/core/channel/compress_filter.c', + 'src/core/channel/connected_channel.c', + 'src/core/channel/http_client_filter.c', + 'src/core/channel/http_server_filter.c', + 'src/core/channel/noop_filter.c', + 'src/core/client_config/client_config.c', + 'src/core/client_config/connector.c', + 'src/core/client_config/lb_policies/pick_first.c', + 'src/core/client_config/lb_policy.c', + 'src/core/client_config/resolver.c', + 'src/core/client_config/resolver_factory.c', + 'src/core/client_config/resolver_registry.c', + 'src/core/client_config/resolvers/dns_resolver.c', + 'src/core/client_config/resolvers/sockaddr_resolver.c', + 'src/core/client_config/subchannel.c', + 'src/core/client_config/subchannel_factory.c', + 'src/core/client_config/subchannel_factory_decorators/add_channel_arg.c', + 'src/core/client_config/subchannel_factory_decorators/merge_channel_args.c', + 'src/core/client_config/uri_parser.c', + 'src/core/compression/algorithm.c', + 'src/core/compression/message_compress.c', + 'src/core/debug/trace.c', + 'src/core/httpcli/format_request.c', + 'src/core/httpcli/httpcli.c', + 'src/core/httpcli/parser.c', + 'src/core/iomgr/alarm.c', + 'src/core/iomgr/alarm_heap.c', + 'src/core/iomgr/endpoint.c', + 'src/core/iomgr/endpoint_pair_posix.c', + 'src/core/iomgr/endpoint_pair_windows.c', + 'src/core/iomgr/fd_posix.c', + 'src/core/iomgr/iocp_windows.c', + 'src/core/iomgr/iomgr.c', + 'src/core/iomgr/iomgr_posix.c', + 'src/core/iomgr/iomgr_windows.c', + 'src/core/iomgr/pollset_multipoller_with_epoll.c', + 'src/core/iomgr/pollset_multipoller_with_poll_posix.c', + 'src/core/iomgr/pollset_posix.c', + 'src/core/iomgr/pollset_set_posix.c', + 'src/core/iomgr/pollset_set_windows.c', + 'src/core/iomgr/pollset_windows.c', + 'src/core/iomgr/resolve_address_posix.c', + 'src/core/iomgr/resolve_address_windows.c', + 'src/core/iomgr/sockaddr_utils.c', + 'src/core/iomgr/socket_utils_common_posix.c', + 'src/core/iomgr/socket_utils_linux.c', + 'src/core/iomgr/socket_utils_posix.c', + 'src/core/iomgr/socket_windows.c', + 'src/core/iomgr/tcp_client_posix.c', + 'src/core/iomgr/tcp_client_windows.c', + 'src/core/iomgr/tcp_posix.c', + 'src/core/iomgr/tcp_server_posix.c', + 'src/core/iomgr/tcp_server_windows.c', + 'src/core/iomgr/tcp_windows.c', + 'src/core/iomgr/time_averaged_stats.c', + 'src/core/iomgr/udp_server.c', + 'src/core/iomgr/wakeup_fd_eventfd.c', + 'src/core/iomgr/wakeup_fd_nospecial.c', + 'src/core/iomgr/wakeup_fd_pipe.c', + 'src/core/iomgr/wakeup_fd_posix.c', + 'src/core/json/json.c', + 'src/core/json/json_reader.c', + 'src/core/json/json_string.c', + 'src/core/json/json_writer.c', + 'src/core/profiling/basic_timers.c', + 'src/core/profiling/stap_timers.c', + 'src/core/surface/byte_buffer.c', + 'src/core/surface/byte_buffer_queue.c', + 'src/core/surface/byte_buffer_reader.c', + 'src/core/surface/call.c', + 'src/core/surface/call_details.c', + 'src/core/surface/call_log_batch.c', + 'src/core/surface/channel.c', + 'src/core/surface/channel_connectivity.c', + 'src/core/surface/channel_create.c', + 'src/core/surface/completion_queue.c', + 'src/core/surface/event_string.c', + 'src/core/surface/init.c', + 'src/core/surface/lame_client.c', + 'src/core/surface/metadata_array.c', + 'src/core/surface/server.c', + 'src/core/surface/server_chttp2.c', + 'src/core/surface/server_create.c', + 'src/core/surface/surface_trace.c', + 'src/core/surface/version.c', + 'src/core/transport/chttp2/alpn.c', + 'src/core/transport/chttp2/bin_encoder.c', + 'src/core/transport/chttp2/frame_data.c', + 'src/core/transport/chttp2/frame_goaway.c', + 'src/core/transport/chttp2/frame_ping.c', + 'src/core/transport/chttp2/frame_rst_stream.c', + 'src/core/transport/chttp2/frame_settings.c', + 'src/core/transport/chttp2/frame_window_update.c', + 'src/core/transport/chttp2/hpack_parser.c', + 'src/core/transport/chttp2/hpack_table.c', + 'src/core/transport/chttp2/huffsyms.c', + 'src/core/transport/chttp2/incoming_metadata.c', + 'src/core/transport/chttp2/parsing.c', + 'src/core/transport/chttp2/status_conversion.c', + 'src/core/transport/chttp2/stream_encoder.c', + 'src/core/transport/chttp2/stream_lists.c', + 'src/core/transport/chttp2/stream_map.c', + 'src/core/transport/chttp2/timeout_encoding.c', + 'src/core/transport/chttp2/varint.c', + 'src/core/transport/chttp2/writing.c', + 'src/core/transport/chttp2_transport.c', + 'src/core/transport/connectivity_state.c', + 'src/core/transport/metadata.c', + 'src/core/transport/stream_op.c', + 'src/core/transport/transport.c', + 'src/core/transport/transport_op_string.c', + 'src/core/census/context.c', + 'src/core/census/initialize.c', + 'src/core/census/operation.c', + 'src/core/census/tracing.c' + + ss.private_header_files = 'src/core/support/env.h', + 'src/core/support/file.h', + 'src/core/support/murmur_hash.h', + 'src/core/support/stack_lockfree.h', + 'src/core/support/string.h', + 'src/core/support/string_win32.h', + 'src/core/support/thd_internal.h', + 'src/core/support/time_precise.h', + 'src/core/security/auth_filters.h', + 'src/core/security/base64.h', + 'src/core/security/credentials.h', + 'src/core/security/json_token.h', + 'src/core/security/jwt_verifier.h', + 'src/core/security/secure_endpoint.h', + 'src/core/security/secure_transport_setup.h', + 'src/core/security/security_connector.h', + 'src/core/security/security_context.h', + 'src/core/tsi/fake_transport_security.h', + 'src/core/tsi/ssl_transport_security.h', + 'src/core/tsi/transport_security.h', + 'src/core/tsi/transport_security_interface.h', + 'src/core/census/grpc_filter.h', + 'src/core/channel/channel_args.h', + 'src/core/channel/channel_stack.h', + 'src/core/channel/client_channel.h', + 'src/core/channel/compress_filter.h', + 'src/core/channel/connected_channel.h', + 'src/core/channel/context.h', + 'src/core/channel/http_client_filter.h', + 'src/core/channel/http_server_filter.h', + 'src/core/channel/noop_filter.h', + 'src/core/client_config/client_config.h', + 'src/core/client_config/connector.h', + 'src/core/client_config/lb_policies/pick_first.h', + 'src/core/client_config/lb_policy.h', + 'src/core/client_config/resolver.h', + 'src/core/client_config/resolver_factory.h', + 'src/core/client_config/resolver_registry.h', + 'src/core/client_config/resolvers/dns_resolver.h', + 'src/core/client_config/resolvers/sockaddr_resolver.h', + 'src/core/client_config/subchannel.h', + 'src/core/client_config/subchannel_factory.h', + 'src/core/client_config/subchannel_factory_decorators/add_channel_arg.h', + 'src/core/client_config/subchannel_factory_decorators/merge_channel_args.h', + 'src/core/client_config/uri_parser.h', + 'src/core/compression/message_compress.h', + 'src/core/debug/trace.h', + 'src/core/httpcli/format_request.h', + 'src/core/httpcli/httpcli.h', + 'src/core/httpcli/parser.h', + 'src/core/iomgr/alarm.h', + 'src/core/iomgr/alarm_heap.h', + 'src/core/iomgr/alarm_internal.h', + 'src/core/iomgr/endpoint.h', + 'src/core/iomgr/endpoint_pair.h', + 'src/core/iomgr/fd_posix.h', + 'src/core/iomgr/iocp_windows.h', + 'src/core/iomgr/iomgr.h', + 'src/core/iomgr/iomgr_internal.h', + 'src/core/iomgr/iomgr_posix.h', + 'src/core/iomgr/pollset.h', + 'src/core/iomgr/pollset_posix.h', + 'src/core/iomgr/pollset_set.h', + 'src/core/iomgr/pollset_set_posix.h', + 'src/core/iomgr/pollset_set_windows.h', + 'src/core/iomgr/pollset_windows.h', + 'src/core/iomgr/resolve_address.h', + 'src/core/iomgr/sockaddr.h', + 'src/core/iomgr/sockaddr_posix.h', + 'src/core/iomgr/sockaddr_utils.h', + 'src/core/iomgr/sockaddr_win32.h', + 'src/core/iomgr/socket_utils_posix.h', + 'src/core/iomgr/socket_windows.h', + 'src/core/iomgr/tcp_client.h', + 'src/core/iomgr/tcp_posix.h', + 'src/core/iomgr/tcp_server.h', + 'src/core/iomgr/tcp_windows.h', + 'src/core/iomgr/time_averaged_stats.h', + 'src/core/iomgr/udp_server.h', + 'src/core/iomgr/wakeup_fd_pipe.h', + 'src/core/iomgr/wakeup_fd_posix.h', + 'src/core/json/json.h', + 'src/core/json/json_common.h', + 'src/core/json/json_reader.h', + 'src/core/json/json_writer.h', + 'src/core/profiling/timers.h', + 'src/core/statistics/census_interface.h', + 'src/core/statistics/census_rpc_stats.h', + 'src/core/surface/byte_buffer_queue.h', + 'src/core/surface/call.h', + 'src/core/surface/channel.h', + 'src/core/surface/completion_queue.h', + 'src/core/surface/event_string.h', + 'src/core/surface/init.h', + 'src/core/surface/server.h', + 'src/core/surface/surface_trace.h', + 'src/core/transport/chttp2/alpn.h', + 'src/core/transport/chttp2/bin_encoder.h', + 'src/core/transport/chttp2/frame.h', + 'src/core/transport/chttp2/frame_data.h', + 'src/core/transport/chttp2/frame_goaway.h', + 'src/core/transport/chttp2/frame_ping.h', + 'src/core/transport/chttp2/frame_rst_stream.h', + 'src/core/transport/chttp2/frame_settings.h', + 'src/core/transport/chttp2/frame_window_update.h', + 'src/core/transport/chttp2/hpack_parser.h', + 'src/core/transport/chttp2/hpack_table.h', + 'src/core/transport/chttp2/http2_errors.h', + 'src/core/transport/chttp2/huffsyms.h', + 'src/core/transport/chttp2/incoming_metadata.h', + 'src/core/transport/chttp2/internal.h', + 'src/core/transport/chttp2/status_conversion.h', + 'src/core/transport/chttp2/stream_encoder.h', + 'src/core/transport/chttp2/stream_map.h', + 'src/core/transport/chttp2/timeout_encoding.h', + 'src/core/transport/chttp2/varint.h', + 'src/core/transport/chttp2_transport.h', + 'src/core/transport/connectivity_state.h', + 'src/core/transport/metadata.h', + 'src/core/transport/stream_op.h', + 'src/core/transport/transport.h', + 'src/core/transport/transport_impl.h', + 'src/core/census/aggregation.h', + 'src/core/census/context.h', + 'src/core/census/rpc_metric_id.h' + + ss.header_mappings_dir = '.' + + ss.requires_arc = false + ss.libraries = 'z' + ss.dependency 'OpenSSL', '~> 1.0.200' + + # ss.compiler_flags = '-GCC_WARN_INHIBIT_ALL_WARNINGS', '-w' + end + + # This is a workaround for Cocoapods Issue #1437. + # It renames time.h and string.h to grpc_time.h and grpc_string.h. + # It needs to be here (top-level) instead of in the C-Core subspec because Cocoapods doesn't run + # prepare_command's of subspecs. + # + # TODO(jcanizales): Try out others' solutions at Issue #1437. + s.prepare_command = <<-CMD + # Move contents of include up a level to avoid manually specifying include paths + cp -r "include/grpc" "." + + DIR_TIME="grpc/support" + BAD_TIME="$DIR_TIME/time.h" + GOOD_TIME="$DIR_TIME/grpc_time.h" + grep -rl "$BAD_TIME" grpc src/core src/objective-c/GRPCClient | xargs sed -i '' -e s@$BAD_TIME@$GOOD_TIME@g + if [ -f "$BAD_TIME" ]; + then + mv -f "$BAD_TIME" "$GOOD_TIME" + fi + + DIR_STRING="src/core/support" + BAD_STRING="$DIR_STRING/string.h" + GOOD_STRING="$DIR_STRING/grpc_string.h" + grep -rl "$BAD_STRING" grpc src/core src/objective-c/GRPCClient | xargs sed -i '' -e s@$BAD_STRING@$GOOD_STRING@g + if [ -f "$BAD_STRING" ]; + then + mv -f "$BAD_STRING" "$GOOD_STRING" + fi + CMD + + # Objective-C wrapper around the core gRPC library. + s.subspec 'GRPCClient' do |ss| + src_dir = "#{objc_dir}/GRPCClient" + ss.source_files = "#{src_dir}/*.{h,m}", "#{src_dir}/**/*.{h,m}" + ss.private_header_files = "#{src_dir}/private/*.h" + ss.header_mappings_dir = "#{objc_dir}" + + ss.dependency 'gRPC/C-Core' + ss.dependency 'gRPC/RxLibrary' + + # Certificates, to be able to establish TLS connections: + ss.resource_bundles = { 'gRPCCertificates' => ['etc/roots.pem'] } + end + + # RPC library for ProtocolBuffers, based on gRPC + s.subspec 'ProtoRPC' do |ss| + src_dir = "#{objc_dir}/ProtoRPC" + ss.source_files = "#{src_dir}/*.{h,m}" + ss.header_mappings_dir = "#{objc_dir}" + + ss.dependency 'gRPC/GRPCClient' + ss.dependency 'gRPC/RxLibrary' + ss.dependency 'Protobuf', '~> 3.0.0-alpha-4' + end +end diff --git a/grpc.bzl b/grpc.bzl new file mode 100644 index 00000000..9f269312 --- /dev/null +++ b/grpc.bzl @@ -0,0 +1,128 @@ +# Copyright 2015, 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. + +""" +Bazel macros to declare gRPC libraries automatically generated from proto files. + +This file declares two macros: +- objc_proto_library +- objc_grpc_library +""" + +def _lower_underscore_to_upper_camel(str): + humps = [] + for hump in str.split('_'): + humps += [hump[0].upper() + hump[1:]] + return "".join(humps) + +def _file_to_upper_camel(src): + elements = src.rpartition('/') + upper_camel = _lower_underscore_to_upper_camel(elements[-1]) + return "".join(elements[:-1] + [upper_camel]) + +def _file_with_extension(src, ext): + elements = src.rpartition('/') + basename = elements[-1].partition('.')[0] + return "".join(elements[:-1] + [basename, ext]) + +def _protoc_invocation(srcs, flags): + """Returns a command line to invoke protoc from a genrule, on the given + sources, using the given flags. + """ + protoc_command = "$(location //external:protoc) -I . " + srcs_params = "" + for src in srcs: + srcs_params += " $(location %s)" % (src) + return protoc_command + flags + srcs_params + +def objc_proto_library(name, srcs, visibility=None): + """Declares an objc_library for the code generated by protoc from the given + proto sources. This generated code doesn't include proto services. + """ + h_files = [] + m_files = [] + for src in srcs: + src = _file_to_upper_camel(src) + h_files += [_file_with_extension(src, ".pbobjc.h")] + m_files += [_file_with_extension(src, ".pbobjc.m")] + + protoc_flags = "--objc_out=$(GENDIR)" + + native.genrule( + name = name + "_codegen", + srcs = srcs + ["//external:protoc"], + outs = h_files + m_files, + cmd = _protoc_invocation(srcs, protoc_flags), + ) + native.objc_library( + name = name, + hdrs = h_files, + includes = ["."], + non_arc_srcs = m_files, + deps = ["//external:protobuf_objc"], + visibility = visibility, + ) + +def objc_grpc_library(name, services, other_messages, visibility=None): + """Declares an objc_library for the code generated by gRPC and protoc from the + given proto sources (services and other_messages). The generated code doesn't + include proto services of the files passed as other_messages. + """ + objc_proto_library(name + "_messages", services + other_messages) + + h_files = [] + m_files = [] + for src in services: + src = _file_to_upper_camel(src) + h_files += [_file_with_extension(src, ".pbrpc.h")] + m_files += [_file_with_extension(src, ".pbrpc.m")] + + protoc_flags = ("--grpc_out=$(GENDIR) --plugin=" + + "protoc-gen-grpc=$(location //external:grpc_protoc_plugin_objc)") + + native.genrule( + name = name + "_codegen", + srcs = services + [ + "//external:grpc_protoc_plugin_objc", + "//external:protoc", + ], + outs = h_files + m_files, + cmd = _protoc_invocation(services, protoc_flags), + ) + native.objc_library( + name = name, + hdrs = h_files, + includes = ["."], + srcs = m_files, + deps = [ + ":" + name + "_messages", + "//external:proto_objc_rpc", + ], + visibility = visibility, + ) diff --git a/include/grpc++/channel.h b/include/grpc++/channel.h new file mode 100644 index 00000000..60c816d5 --- /dev/null +++ b/include/grpc++/channel.h @@ -0,0 +1,139 @@ +/* + * + * Copyright 2015, 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_CHANNEL_H +#define GRPCXX_CHANNEL_H + +#include + +#include +#include +#include +#include + +struct grpc_channel; + +namespace grpc { +class CallOpSetInterface; +class ChannelArguments; +class CompletionQueue; +class Credentials; +class SecureCredentials; + +template +class ClientReader; +template +class ClientWriter; +template +class ClientReaderWriter; +template +class ClientAsyncReader; +template +class ClientAsyncWriter; +template +class ClientAsyncReaderWriter; +template +class ClientAsyncResponseReader; + +/// Channels represent a connection to an endpoint. Created by \a CreateChannel. +class Channel GRPC_FINAL : public GrpcLibrary, + public CallHook, + public std::enable_shared_from_this { + public: + ~Channel(); + + /// Get the current channel state. If the channel is in IDLE and + /// \a try_to_connect is set to true, try to connect. + grpc_connectivity_state GetState(bool try_to_connect); + + /// Return the \a tag on \a cq when the channel state is changed or \a + /// deadline expires. \a GetState needs to called to get the current state. + template + void NotifyOnStateChange(grpc_connectivity_state last_observed, T deadline, + CompletionQueue* cq, void* tag) { + TimePoint deadline_tp(deadline); + NotifyOnStateChangeImpl(last_observed, deadline_tp.raw_time(), cq, tag); + } + + /// Blocking wait for channel state change or \a deadline expiration. + /// \a GetState needs to called to get the current state. + template + bool WaitForStateChange(grpc_connectivity_state last_observed, T deadline) { + TimePoint deadline_tp(deadline); + return WaitForStateChangeImpl(last_observed, deadline_tp.raw_time()); + } + + private: + template + friend class ::grpc::ClientReader; + template + friend class ::grpc::ClientWriter; + template + friend class ::grpc::ClientReaderWriter; + template + friend class ::grpc::ClientAsyncReader; + template + friend class ::grpc::ClientAsyncWriter; + template + friend class ::grpc::ClientAsyncReaderWriter; + template + friend class ::grpc::ClientAsyncResponseReader; + template + friend Status BlockingUnaryCall(Channel* channel, const RpcMethod& method, + ClientContext* context, + const InputMessage& request, + OutputMessage* result); + friend class ::grpc::RpcMethod; + friend std::shared_ptr CreateChannelInternal( + const grpc::string& host, grpc_channel* c_channel); + + Channel(const grpc::string& host, grpc_channel* c_channel); + + Call CreateCall(const RpcMethod& method, ClientContext* context, + CompletionQueue* cq); + void PerformOpsOnCall(CallOpSetInterface* ops, Call* call); + void* RegisterMethod(const char* method); + + void NotifyOnStateChangeImpl(grpc_connectivity_state last_observed, + gpr_timespec deadline, CompletionQueue* cq, + void* tag); + bool WaitForStateChangeImpl(grpc_connectivity_state last_observed, + gpr_timespec deadline); + + const grpc::string host_; + grpc_channel* const c_channel_; // owned +}; + +} // namespace grpc + +#endif // GRPCXX_CHANNEL_H diff --git a/include/grpc++/client_context.h b/include/grpc++/client_context.h new file mode 100644 index 00000000..7046f939 --- /dev/null +++ b/include/grpc++/client_context.h @@ -0,0 +1,336 @@ +/* + * + * Copyright 2015, 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. + * + */ + +/// A ClientContext allows the person implementing a service client to: +/// +/// - Add custom metadata key-value pairs that will propagated to the server +/// side. +/// - Control call settings such as compression and authentication. +/// - Initial and trailing metadata coming from the server. +/// - Get performance metrics (ie, census). +/// +/// Context settings are only relevant to the call they are invoked with, that +/// is to say, they aren't sticky. Some of these settings, such as the +/// compression options, can be made persistant at channel construction time +/// (see \a grpc::CreateCustomChannel). +/// +/// \warning ClientContext instances should \em not be reused across rpcs. + +#ifndef GRPCXX_CLIENT_CONTEXT_H +#define GRPCXX_CLIENT_CONTEXT_H + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct census_context; + +namespace grpc { + +class Channel; +class CompletionQueue; +class Credentials; +class RpcMethod; +template +class ClientReader; +template +class ClientWriter; +template +class ClientReaderWriter; +template +class ClientAsyncReader; +template +class ClientAsyncWriter; +template +class ClientAsyncReaderWriter; +template +class ClientAsyncResponseReader; +class ServerContext; + +/// Options for \a ClientContext::FromServerContext specifying which traits from +/// the \a ServerContext to propagate (copy) from it into a new \a +/// ClientContext. +/// +/// \see ClientContext::FromServerContext +class PropagationOptions { + public: + PropagationOptions() : propagate_(GRPC_PROPAGATE_DEFAULTS) {} + + PropagationOptions& enable_deadline_propagation() { + propagate_ |= GRPC_PROPAGATE_DEADLINE; + return *this; + } + + PropagationOptions& disable_deadline_propagation() { + propagate_ &= ~GRPC_PROPAGATE_DEADLINE; + return *this; + } + + PropagationOptions& enable_census_stats_propagation() { + propagate_ |= GRPC_PROPAGATE_CENSUS_STATS_CONTEXT; + return *this; + } + + PropagationOptions& disable_census_stats_propagation() { + propagate_ &= ~GRPC_PROPAGATE_CENSUS_STATS_CONTEXT; + return *this; + } + + PropagationOptions& enable_census_tracing_propagation() { + propagate_ |= GRPC_PROPAGATE_CENSUS_TRACING_CONTEXT; + return *this; + } + + PropagationOptions& disable_census_tracing_propagation() { + propagate_ &= ~GRPC_PROPAGATE_CENSUS_TRACING_CONTEXT; + return *this; + } + + PropagationOptions& enable_cancellation_propagation() { + propagate_ |= GRPC_PROPAGATE_CANCELLATION; + return *this; + } + + PropagationOptions& disable_cancellation_propagation() { + propagate_ &= ~GRPC_PROPAGATE_CANCELLATION; + return *this; + } + + gpr_uint32 c_bitmask() const { return propagate_; } + + private: + gpr_uint32 propagate_; +}; + +namespace testing { +class InteropClientContextInspector; +} // namespace testing + +class ClientContext { + public: + ClientContext(); + ~ClientContext(); + + /// Create a new \a ClientContext as a child of an incoming server call, + /// according to \a options (\see PropagationOptions). + /// + /// \param server_context The source server context to use as the basis for + /// constructing the client context. + /// \param options The options controlling what to copy from the \a + /// server_context. + /// + /// \return A newly constructed \a ClientContext instance based on \a + /// server_context, with traits propagated (copied) according to \a options. + static std::unique_ptr FromServerContext( + const ServerContext& server_context, + PropagationOptions options = PropagationOptions()); + + /// Add the (\a meta_key, \a meta_value) pair to the metadata associated with + /// a client call. These are made available at the server side by the \a + /// grpc::ServerContext::client_metadata() method. + /// + /// \warning This method should only be called before invoking the rpc. + /// + /// \param meta_key The metadata key. If \a meta_value is binary data, it must + /// end in "-bin". + /// \param meta_value The metadata value. If its value is binary, it must be + /// base64-encoding (see https://tools.ietf.org/html/rfc4648#section-4) and \a + /// meta_key must end in "-bin". + void AddMetadata(const grpc::string& meta_key, + const grpc::string& meta_value); + + /// Return a collection of initial metadata key-value pairs. Note that keys + /// may happen more than once (ie, a \a std::multimap is returned). + /// + /// \warning This method should only be called after initial metadata has been + /// received. For streaming calls, see \a + /// ClientReaderInterface::WaitForInitialMetadata(). + /// + /// \return A multimap of initial metadata key-value pairs from the server. + const std::multimap& + GetServerInitialMetadata() { + GPR_ASSERT(initial_metadata_received_); + return recv_initial_metadata_; + } + + /// Return a collection of trailing metadata key-value pairs. Note that keys + /// may happen more than once (ie, a \a std::multimap is returned). + /// + /// \warning This method is only callable once the stream has finished. + /// + /// \return A multimap of metadata trailing key-value pairs from the server. + const std::multimap& + GetServerTrailingMetadata() { + // TODO(yangg) check finished + return trailing_metadata_; + } + + /// Set the deadline for the client call. + /// + /// \warning This method should only be called before invoking the rpc. + /// + /// \param deadline the deadline for the client call. Units are determined by + /// the type used. + template + void set_deadline(const T& deadline) { + TimePoint deadline_tp(deadline); + deadline_ = deadline_tp.raw_time(); + } + +#ifndef GRPC_CXX0X_NO_CHRONO + /// Return the deadline for the client call. + std::chrono::system_clock::time_point deadline() { + return Timespec2Timepoint(deadline_); + } +#endif // !GRPC_CXX0X_NO_CHRONO + + /// Return a \a gpr_timespec representation of the client call's deadline. + gpr_timespec raw_deadline() { return deadline_; } + + /// Set the per call authority header (see + /// https://tools.ietf.org/html/rfc7540#section-8.1.2.3). + void set_authority(const grpc::string& authority) { authority_ = authority; } + + /// Return the authentication context for this client call. + /// + /// \see grpc::AuthContext. + std::shared_ptr auth_context() const; + + /// Set credentials for the client call. + /// + /// A credentials object encapsulates all the state needed by a client to + /// authenticate with a server and make various assertions, e.g., about the + /// client’s identity, role, or whether it is authorized to make a particular + /// call. + /// + /// \see https://github.com/grpc/grpc/blob/master/doc/grpc-auth-support.md + void set_credentials(const std::shared_ptr& creds) { + creds_ = creds; + } + + /// Return the compression algorithm to be used by the client call. + grpc_compression_algorithm compression_algorithm() const { + return compression_algorithm_; + } + + /// Set \a algorithm to be the compression algorithm used for the client call. + /// + /// \param algorith The compression algorithm used for the client call. + void set_compression_algorithm(grpc_compression_algorithm algorithm); + + /// Return the peer uri in a string. + /// + /// \warning This value is never authenticated or subject to any security + /// related code. It must not be used for any authentication related + /// functionality. Instead, use auth_context. + /// + /// \return The call's peer URI. + grpc::string peer() const; + + /// Get and set census context + void set_census_context(struct census_context* ccp) { census_context_ = ccp; } + struct census_context* census_context() const { + return census_context_; + } + + /// Send a best-effort out-of-band cancel. The call could be in any stage. + /// e.g. if it is already finished, it may still return success. + /// + /// There is no guarantee the call will be cancelled. + void TryCancel(); + + private: + // Disallow copy and assign. + ClientContext(const ClientContext&); + ClientContext& operator=(const ClientContext&); + + friend class ::grpc::testing::InteropClientContextInspector; + friend class CallOpClientRecvStatus; + friend class CallOpRecvInitialMetadata; + friend class Channel; + template + friend class ::grpc::ClientReader; + template + friend class ::grpc::ClientWriter; + template + friend class ::grpc::ClientReaderWriter; + template + friend class ::grpc::ClientAsyncReader; + template + friend class ::grpc::ClientAsyncWriter; + template + friend class ::grpc::ClientAsyncReaderWriter; + template + friend class ::grpc::ClientAsyncResponseReader; + template + friend Status BlockingUnaryCall(Channel* channel, const RpcMethod& method, + ClientContext* context, + const InputMessage& request, + OutputMessage* result); + + grpc_call* call() { return call_; } + void set_call(grpc_call* call, const std::shared_ptr& channel); + + grpc::string authority() { return authority_; } + + bool initial_metadata_received_; + std::shared_ptr channel_; + grpc_call* call_; + gpr_timespec deadline_; + grpc::string authority_; + std::shared_ptr creds_; + mutable std::shared_ptr auth_context_; + struct census_context* census_context_; + std::multimap send_initial_metadata_; + std::multimap recv_initial_metadata_; + std::multimap trailing_metadata_; + + grpc_call* propagate_from_call_; + PropagationOptions propagation_options_; + + grpc_compression_algorithm compression_algorithm_; +}; + +} // namespace grpc + +#endif // GRPCXX_CLIENT_CONTEXT_H diff --git a/include/grpc++/completion_queue.h b/include/grpc++/completion_queue.h new file mode 100644 index 00000000..0ea97041 --- /dev/null +++ b/include/grpc++/completion_queue.h @@ -0,0 +1,212 @@ +/* + * + * Copyright 2015, 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. + * + */ + +/// A completion queue implements a concurrent producer-consumer queue, with two +/// main methods, \a Next and \a AsyncNext. +#ifndef GRPCXX_COMPLETION_QUEUE_H +#define GRPCXX_COMPLETION_QUEUE_H + +#include +#include +#include +#include + +struct grpc_completion_queue; + +namespace grpc { + +template +class ClientReader; +template +class ClientWriter; +template +class ClientReaderWriter; +template +class ServerReader; +template +class ServerWriter; +template +class ServerReaderWriter; +template +class RpcMethodHandler; +template +class ClientStreamingHandler; +template +class ServerStreamingHandler; +template +class BidiStreamingHandler; +class UnknownMethodHandler; + +class Channel; +class ClientContext; +class CompletionQueueTag; +class CompletionQueue; +class RpcMethod; +class Server; +class ServerBuilder; +class ServerContext; + +/// A thin wrapper around \a grpc_completion_queue (see / \a +/// src/core/surface/completion_queue.h). +class CompletionQueue : public GrpcLibrary { + public: + /// Default constructor. Implicitly creates a \a grpc_completion_queue + /// instance. + CompletionQueue(); + + /// Wrap \a take, taking ownership of the instance. + /// + /// \param take The completion queue instance to wrap. Ownership is taken. + explicit CompletionQueue(grpc_completion_queue* take); + + /// Destructor. Destroys the owned wrapped completion queue / instance. + ~CompletionQueue() GRPC_OVERRIDE; + + /// Tri-state return for AsyncNext: SHUTDOWN, GOT_EVENT, TIMEOUT. + enum NextStatus { + SHUTDOWN, ///< The completion queue has been shutdown. + GOT_EVENT, ///< Got a new event; \a tag will be filled in with its + ///< associated value; \a ok indicating its success. + TIMEOUT ///< deadline was reached. + }; + + /// Read from the queue, blocking up to \a deadline (or the queue's shutdown). + /// Both \a tag and \a ok are updated upon success (if an event is available + /// within the \a deadline). A \a tag points to an arbitrary location usually + /// employed to uniquely identify an event. + /// + /// \param tag[out] Upon sucess, updated to point to the event's tag. + /// \param ok[out] Upon sucess, true if read a regular event, false otherwise. + /// \param deadline[in] How long to block in wait for an event. + /// + /// \return The type of event read. + template + NextStatus AsyncNext(void** tag, bool* ok, const T& deadline) { + TimePoint deadline_tp(deadline); + return AsyncNextInternal(tag, ok, deadline_tp.raw_time()); + } + + /// Read from the queue, blocking until an event is available or the queue is + /// shutting down. + /// + /// \param tag[out] Updated to point to the read event's tag. + /// \param ok[out] true if read a regular event, false otherwise. + /// + /// \return true if read a regular event, false if the queue is shutting down. + bool Next(void** tag, bool* ok) { + return (AsyncNextInternal(tag, ok, gpr_inf_future(GPR_CLOCK_REALTIME)) != + SHUTDOWN); + } + + /// Request the shutdown of the queue. + /// + /// \warning This method must be called at some point. Once invoked, \a Next + /// will start to return false and \a AsyncNext will return \a + /// NextStatus::SHUTDOWN. Only once either one of these methods does that + /// (that is, once the queue has been \em drained) can an instance of this + /// class be destroyed. + void Shutdown(); + + /// Returns a \em raw pointer to the underlying \a grpc_completion_queue + /// instance. + /// + /// \warning Remember that the returned instance is owned. No transfer of + /// owership is performed. + grpc_completion_queue* cq() { return cq_; } + + private: + // Friend synchronous wrappers so that they can access Pluck(), which is + // a semi-private API geared towards the synchronous implementation. + template + friend class ::grpc::ClientReader; + template + friend class ::grpc::ClientWriter; + template + friend class ::grpc::ClientReaderWriter; + template + friend class ::grpc::ServerReader; + template + friend class ::grpc::ServerWriter; + template + friend class ::grpc::ServerReaderWriter; + template + friend class RpcMethodHandler; + template + friend class ClientStreamingHandler; + template + friend class ServerStreamingHandler; + template + friend class BidiStreamingHandler; + friend class UnknownMethodHandler; + friend class ::grpc::Server; + friend class ::grpc::ServerContext; + template + friend Status BlockingUnaryCall(Channel* channel, const RpcMethod& method, + ClientContext* context, + const InputMessage& request, + OutputMessage* result); + + NextStatus AsyncNextInternal(void** tag, bool* ok, gpr_timespec deadline); + + /// Wraps \a grpc_completion_queue_pluck. + /// \warning Must not be mixed with calls to \a Next. + bool Pluck(CompletionQueueTag* tag); + + /// Performs a single polling pluck on \a tag. + void TryPluck(CompletionQueueTag* tag); + + grpc_completion_queue* cq_; // owned +}; + +/// An interface allowing implementors to process and filter event tags. +class CompletionQueueTag { + public: + virtual ~CompletionQueueTag() {} + // Called prior to returning from Next(), return value is the status of the + // operation (return status is the default thing to do). If this function + // returns false, the tag is dropped and not returned from the completion + // queue + virtual bool FinalizeResult(void** tag, bool* status) = 0; +}; + +/// A specific type of completion queue used by the processing of notifications +/// by servers. Instantiated by \a ServerBuilder. +class ServerCompletionQueue : public CompletionQueue { + private: + friend class ServerBuilder; + ServerCompletionQueue() {} +}; + +} // namespace grpc + +#endif // GRPCXX_COMPLETION_QUEUE_H diff --git a/include/grpc++/create_channel.h b/include/grpc++/create_channel.h new file mode 100644 index 00000000..196d2927 --- /dev/null +++ b/include/grpc++/create_channel.h @@ -0,0 +1,69 @@ +/* + * + * Copyright 2015, 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_CREATE_CHANNEL_H +#define GRPCXX_CREATE_CHANNEL_H + +#include + +#include +#include +#include + +namespace grpc { + +/// Create a new \a Channel pointing to \a target +/// +/// \param target The URI of the endpoint to connect to. +/// \param creds Credentials to use for the created channel. If it does not hold +/// an object or is invalid, a lame channel is returned. +/// \param args Options for channel creation. +std::shared_ptr CreateChannel( + const grpc::string& target, const std::shared_ptr& creds); + +/// Create a new \em custom \a Channel pointing to \a target +/// +/// \warning For advanced use and testing ONLY. Override default channel +/// arguments only if necessary. +/// +/// \param target The URI of the endpoint to connect to. +/// \param creds Credentials to use for the created channel. If it does not hold +/// an object or is invalid, a lame channel is returned. +/// \param args Options for channel creation. +std::shared_ptr CreateCustomChannel( + const grpc::string& target, const std::shared_ptr& creds, + const ChannelArguments& args); + +} // namespace grpc + +#endif // GRPCXX_CREATE_CHANNEL_H diff --git a/include/grpc++/generic/async_generic_service.h b/include/grpc++/generic/async_generic_service.h new file mode 100644 index 00000000..8578d850 --- /dev/null +++ b/include/grpc++/generic/async_generic_service.h @@ -0,0 +1,78 @@ +/* + * + * Copyright 2015, 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_GENERIC_ASYNC_GENERIC_SERVICE_H +#define GRPCXX_GENERIC_ASYNC_GENERIC_SERVICE_H + +#include +#include + +struct grpc_server; + +namespace grpc { + +typedef ServerAsyncReaderWriter + GenericServerAsyncReaderWriter; + +class GenericServerContext GRPC_FINAL : public ServerContext { + public: + const grpc::string& method() const { return method_; } + const grpc::string& host() const { return host_; } + + private: + friend class Server; + + grpc::string method_; + grpc::string host_; +}; + +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(const grpc::string& methods) : server_(nullptr) {} + + void RequestCall(GenericServerContext* ctx, + GenericServerAsyncReaderWriter* reader_writer, + CompletionQueue* call_cq, + ServerCompletionQueue* notification_cq, void* tag); + + private: + friend class Server; + Server* server_; +}; + +} // namespace grpc + +#endif // GRPCXX_GENERIC_ASYNC_GENERIC_SERVICE_H diff --git a/include/grpc++/generic/generic_stub.h b/include/grpc++/generic/generic_stub.h new file mode 100644 index 00000000..1bb7900b --- /dev/null +++ b/include/grpc++/generic/generic_stub.h @@ -0,0 +1,63 @@ +/* + * + * Copyright 2015, 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_GENERIC_GENERIC_STUB_H +#define GRPCXX_GENERIC_GENERIC_STUB_H + +#include +#include + +namespace grpc { + +class CompletionQueue; +typedef ClientAsyncReaderWriter + GenericClientAsyncReaderWriter; + +// Generic stubs provide a type-unsafe interface to call gRPC methods +// by name. +class GenericStub GRPC_FINAL { + public: + explicit GenericStub(std::shared_ptr channel) : channel_(channel) {} + + // begin a call to a named method + std::unique_ptr Call( + ClientContext* context, const grpc::string& method, CompletionQueue* cq, + void* tag); + + private: + std::shared_ptr channel_; +}; + +} // namespace grpc + +#endif // GRPCXX_GENERIC_GENERIC_STUB_H diff --git a/include/grpc++/grpc++.h b/include/grpc++/grpc++.h new file mode 100644 index 00000000..b7d5fb0b --- /dev/null +++ b/include/grpc++/grpc++.h @@ -0,0 +1,64 @@ +/* + * + * Copyright 2015, 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. + * + */ + +/// \mainpage gRPC C++ API +/// +/// The gRPC C++ API mainly consists of the following classes: +/// - grpc::Channel, which represents the connection to an endpoint. See [the +/// gRPC Concepts page](http://www.grpc.io/docs/guides/concepts.html) for more +/// details. Channels are created by the factory function grpc::CreateChannel. +/// - grpc::CompletionQueue, the producer-consumer queue used for all +/// asynchronous communication with the gRPC runtime. +/// - grpc::ClientContext and grpc::ServerContext, where optional configuration +/// for an RPC can be set, such as setting custom metadata to be conveyed to the +/// peer, compression settings, authentication, etc. +/// - grpc::Server, representing a gRPC server, created by grpc::ServerBuilder. +/// +/// Refer to the +/// [examples](https://github.com/grpc/grpc/blob/master/examples/cpp) +/// for code putting these pieces into play. + +#ifndef GRPCXX_GRPCXX_H +#define GRPCXX_GRPCXX_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +#endif // GRPCXX_GRPCXX_H diff --git a/include/grpc++/impl/README.md b/include/grpc++/impl/README.md new file mode 100644 index 00000000..612150ca --- /dev/null +++ b/include/grpc++/impl/README.md @@ -0,0 +1,4 @@ +**The APIs in this directory are not stable!** + +This directory contains header files that need to be installed but are not part +of the public API. Users should not use these headers directly. diff --git a/include/grpc++/impl/call.h b/include/grpc++/impl/call.h new file mode 100644 index 00000000..fca56030 --- /dev/null +++ b/include/grpc++/impl/call.h @@ -0,0 +1,583 @@ +/* + * + * Copyright 2015, 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_CALL_H +#define GRPCXX_IMPL_CALL_H + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +struct grpc_call; +struct grpc_op; + +namespace grpc { + +class ByteBuffer; +class Call; + +void FillMetadataMap( + grpc_metadata_array* arr, + std::multimap* metadata); +grpc_metadata* FillMetadataArray( + const std::multimap& metadata); + +/// Per-message write options. +class WriteOptions { + public: + WriteOptions() : flags_(0) {} + WriteOptions(const WriteOptions& other) : flags_(other.flags_) {} + + /// Clear all flags. + inline void Clear() { flags_ = 0; } + + /// Returns raw flags bitset. + inline gpr_uint32 flags() const { return flags_; } + + /// Sets flag for the disabling of compression for the next message write. + /// + /// \sa GRPC_WRITE_NO_COMPRESS + inline WriteOptions& set_no_compression() { + SetBit(GRPC_WRITE_NO_COMPRESS); + return *this; + } + + /// Clears flag for the disabling of compression for the next message write. + /// + /// \sa GRPC_WRITE_NO_COMPRESS + inline WriteOptions& clear_no_compression() { + ClearBit(GRPC_WRITE_NO_COMPRESS); + return *this; + } + + /// Get value for the flag indicating whether compression for the next + /// message write is forcefully disabled. + /// + /// \sa GRPC_WRITE_NO_COMPRESS + inline bool get_no_compression() const { + return GetBit(GRPC_WRITE_NO_COMPRESS); + } + + /// Sets flag indicating that the write may be buffered and need not go out on + /// the wire immediately. + /// + /// \sa GRPC_WRITE_BUFFER_HINT + inline WriteOptions& set_buffer_hint() { + SetBit(GRPC_WRITE_BUFFER_HINT); + return *this; + } + + /// Clears flag indicating that the write may be buffered and need not go out + /// on the wire immediately. + /// + /// \sa GRPC_WRITE_BUFFER_HINT + inline WriteOptions& clear_buffer_hint() { + ClearBit(GRPC_WRITE_BUFFER_HINT); + return *this; + } + + /// Get value for the flag indicating that the write may be buffered and need + /// not go out on the wire immediately. + /// + /// \sa GRPC_WRITE_BUFFER_HINT + inline bool get_buffer_hint() const { return GetBit(GRPC_WRITE_BUFFER_HINT); } + + WriteOptions& operator=(const WriteOptions& rhs) { + flags_ = rhs.flags_; + return *this; + } + + private: + void SetBit(const gpr_int32 mask) { flags_ |= mask; } + + void ClearBit(const gpr_int32 mask) { flags_ &= ~mask; } + + bool GetBit(const gpr_int32 mask) const { return flags_ & mask; } + + gpr_uint32 flags_; +}; + +/// Default argument for CallOpSet. I is unused by the class, but can be +/// used for generating multiple names for the same thing. +template +class CallNoOp { + protected: + void AddOp(grpc_op* ops, size_t* nops) {} + void FinishOp(bool* status, int max_message_size) {} +}; + +class CallOpSendInitialMetadata { + public: + CallOpSendInitialMetadata() : send_(false) {} + + void SendInitialMetadata( + const std::multimap& metadata) { + send_ = true; + initial_metadata_count_ = metadata.size(); + initial_metadata_ = FillMetadataArray(metadata); + } + + protected: + void AddOp(grpc_op* ops, size_t* nops) { + if (!send_) return; + grpc_op* op = &ops[(*nops)++]; + op->op = GRPC_OP_SEND_INITIAL_METADATA; + op->flags = 0; + op->reserved = NULL; + op->data.send_initial_metadata.count = initial_metadata_count_; + op->data.send_initial_metadata.metadata = initial_metadata_; + } + void FinishOp(bool* status, int max_message_size) { + if (!send_) return; + gpr_free(initial_metadata_); + send_ = false; + } + + bool send_; + size_t initial_metadata_count_; + grpc_metadata* initial_metadata_; +}; + +class CallOpSendMessage { + public: + CallOpSendMessage() : send_buf_(nullptr), own_buf_(false) {} + + /// Send \a message using \a options for the write. The \a options are cleared + /// after use. + template + Status SendMessage(const M& message, + const WriteOptions& options) GRPC_MUST_USE_RESULT; + + template + Status SendMessage(const M& message) GRPC_MUST_USE_RESULT; + + protected: + void AddOp(grpc_op* ops, size_t* nops) { + if (send_buf_ == nullptr) return; + grpc_op* op = &ops[(*nops)++]; + op->op = GRPC_OP_SEND_MESSAGE; + op->flags = write_options_.flags(); + op->reserved = NULL; + op->data.send_message = send_buf_; + // Flags are per-message: clear them after use. + write_options_.Clear(); + } + void FinishOp(bool* status, int max_message_size) { + if (own_buf_) grpc_byte_buffer_destroy(send_buf_); + send_buf_ = nullptr; + } + + private: + grpc_byte_buffer* send_buf_; + WriteOptions write_options_; + bool own_buf_; +}; + +template +Status CallOpSendMessage::SendMessage(const M& message, + const WriteOptions& options) { + write_options_ = options; + return SerializationTraits::Serialize(message, &send_buf_, &own_buf_); +} + +template +Status CallOpSendMessage::SendMessage(const M& message) { + return SendMessage(message, WriteOptions()); +} + +template +class CallOpRecvMessage { + public: + CallOpRecvMessage() : got_message(false), message_(nullptr) {} + + void RecvMessage(R* message) { message_ = message; } + + bool got_message; + + protected: + void AddOp(grpc_op* ops, size_t* nops) { + if (message_ == nullptr) return; + grpc_op* op = &ops[(*nops)++]; + op->op = GRPC_OP_RECV_MESSAGE; + op->flags = 0; + op->reserved = NULL; + op->data.recv_message = &recv_buf_; + } + + void FinishOp(bool* status, int max_message_size) { + if (message_ == nullptr) return; + if (recv_buf_) { + if (*status) { + got_message = true; + *status = SerializationTraits::Deserialize(recv_buf_, message_, + max_message_size) + .ok(); + } else { + got_message = false; + grpc_byte_buffer_destroy(recv_buf_); + } + } else { + got_message = false; + *status = false; + } + message_ = nullptr; + } + + private: + R* message_; + grpc_byte_buffer* recv_buf_; +}; + +namespace CallOpGenericRecvMessageHelper { +class DeserializeFunc { + public: + virtual Status Deserialize(grpc_byte_buffer* buf, int max_message_size) = 0; +}; + +template +class DeserializeFuncType GRPC_FINAL : public DeserializeFunc { + public: + DeserializeFuncType(R* message) : message_(message) {} + Status Deserialize(grpc_byte_buffer* buf, + int max_message_size) GRPC_OVERRIDE { + return SerializationTraits::Deserialize(buf, message_, max_message_size); + } + + private: + R* message_; // Not a managed pointer because management is external to this +}; +} // namespace CallOpGenericRecvMessageHelper + +class CallOpGenericRecvMessage { + public: + CallOpGenericRecvMessage() : got_message(false) {} + + template + void RecvMessage(R* message) { + deserialize_.reset( + new CallOpGenericRecvMessageHelper::DeserializeFuncType(message)); + } + + bool got_message; + + protected: + void AddOp(grpc_op* ops, size_t* nops) { + if (!deserialize_) return; + grpc_op* op = &ops[(*nops)++]; + op->op = GRPC_OP_RECV_MESSAGE; + op->flags = 0; + op->reserved = NULL; + op->data.recv_message = &recv_buf_; + } + + void FinishOp(bool* status, int max_message_size) { + if (!deserialize_) return; + if (recv_buf_) { + if (*status) { + got_message = true; + *status = deserialize_->Deserialize(recv_buf_, max_message_size).ok(); + } else { + got_message = false; + grpc_byte_buffer_destroy(recv_buf_); + } + } else { + got_message = false; + *status = false; + } + deserialize_.reset(); + } + + private: + std::unique_ptr deserialize_; + grpc_byte_buffer* recv_buf_; +}; + +class CallOpClientSendClose { + public: + CallOpClientSendClose() : send_(false) {} + + void ClientSendClose() { send_ = true; } + + protected: + void AddOp(grpc_op* ops, size_t* nops) { + if (!send_) return; + grpc_op* op = &ops[(*nops)++]; + op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; + op->flags = 0; + op->reserved = NULL; + } + void FinishOp(bool* status, int max_message_size) { send_ = false; } + + private: + bool send_; +}; + +class CallOpServerSendStatus { + public: + CallOpServerSendStatus() : send_status_available_(false) {} + + void ServerSendStatus( + const std::multimap& trailing_metadata, + const Status& status) { + trailing_metadata_count_ = trailing_metadata.size(); + trailing_metadata_ = FillMetadataArray(trailing_metadata); + send_status_available_ = true; + send_status_code_ = static_cast(status.error_code()); + send_status_details_ = status.error_message(); + } + + protected: + void AddOp(grpc_op* ops, size_t* nops) { + if (!send_status_available_) return; + grpc_op* op = &ops[(*nops)++]; + op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; + op->data.send_status_from_server.trailing_metadata_count = + trailing_metadata_count_; + op->data.send_status_from_server.trailing_metadata = trailing_metadata_; + op->data.send_status_from_server.status = send_status_code_; + op->data.send_status_from_server.status_details = + send_status_details_.empty() ? nullptr : send_status_details_.c_str(); + op->flags = 0; + op->reserved = NULL; + } + + void FinishOp(bool* status, int max_message_size) { + if (!send_status_available_) return; + gpr_free(trailing_metadata_); + send_status_available_ = false; + } + + private: + bool send_status_available_; + grpc_status_code send_status_code_; + grpc::string send_status_details_; + size_t trailing_metadata_count_; + grpc_metadata* trailing_metadata_; +}; + +class CallOpRecvInitialMetadata { + public: + CallOpRecvInitialMetadata() : recv_initial_metadata_(nullptr) {} + + void RecvInitialMetadata(ClientContext* context) { + context->initial_metadata_received_ = true; + recv_initial_metadata_ = &context->recv_initial_metadata_; + } + + protected: + void AddOp(grpc_op* ops, size_t* nops) { + if (!recv_initial_metadata_) return; + memset(&recv_initial_metadata_arr_, 0, sizeof(recv_initial_metadata_arr_)); + grpc_op* op = &ops[(*nops)++]; + op->op = GRPC_OP_RECV_INITIAL_METADATA; + op->data.recv_initial_metadata = &recv_initial_metadata_arr_; + op->flags = 0; + op->reserved = NULL; + } + void FinishOp(bool* status, int max_message_size) { + if (recv_initial_metadata_ == nullptr) return; + FillMetadataMap(&recv_initial_metadata_arr_, recv_initial_metadata_); + recv_initial_metadata_ = nullptr; + } + + private: + std::multimap* recv_initial_metadata_; + grpc_metadata_array recv_initial_metadata_arr_; +}; + +class CallOpClientRecvStatus { + public: + CallOpClientRecvStatus() : recv_status_(nullptr) {} + + void ClientRecvStatus(ClientContext* context, Status* status) { + recv_trailing_metadata_ = &context->trailing_metadata_; + recv_status_ = status; + } + + protected: + void AddOp(grpc_op* ops, size_t* nops) { + if (recv_status_ == nullptr) return; + memset(&recv_trailing_metadata_arr_, 0, + sizeof(recv_trailing_metadata_arr_)); + status_details_ = nullptr; + status_details_capacity_ = 0; + grpc_op* op = &ops[(*nops)++]; + op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; + op->data.recv_status_on_client.trailing_metadata = + &recv_trailing_metadata_arr_; + op->data.recv_status_on_client.status = &status_code_; + op->data.recv_status_on_client.status_details = &status_details_; + op->data.recv_status_on_client.status_details_capacity = + &status_details_capacity_; + op->flags = 0; + op->reserved = NULL; + } + + void FinishOp(bool* status, int max_message_size) { + if (recv_status_ == nullptr) return; + FillMetadataMap(&recv_trailing_metadata_arr_, recv_trailing_metadata_); + *recv_status_ = Status( + static_cast(status_code_), + status_details_ ? grpc::string(status_details_) : grpc::string()); + gpr_free(status_details_); + recv_status_ = nullptr; + } + + private: + std::multimap* recv_trailing_metadata_; + Status* recv_status_; + grpc_metadata_array recv_trailing_metadata_arr_; + grpc_status_code status_code_; + char* status_details_; + size_t status_details_capacity_; +}; + +/// An abstract collection of call ops, used to generate the +/// grpc_call_op structure to pass down to the lower layers, +/// and as it is-a CompletionQueueTag, also massages the final +/// completion into the correct form for consumption in the C++ +/// API. +class CallOpSetInterface : public CompletionQueueTag { + public: + CallOpSetInterface() : max_message_size_(0) {} + /// Fills in grpc_op, starting from ops[*nops] and moving + /// upwards. + virtual void FillOps(grpc_op* ops, size_t* nops) = 0; + + void set_max_message_size(int max_message_size) { + max_message_size_ = max_message_size; + } + + protected: + int max_message_size_; +}; + +/// Primary implementaiton of CallOpSetInterface. +/// Since we cannot use variadic templates, we declare slots up to +/// the maximum count of ops we'll need in a set. We leverage the +/// empty base class optimization to slim this class (especially +/// when there are many unused slots used). To avoid duplicate base classes, +/// the template parmeter for CallNoOp is varied by argument position. +template , class Op2 = CallNoOp<2>, + class Op3 = CallNoOp<3>, class Op4 = CallNoOp<4>, + class Op5 = CallNoOp<5>, class Op6 = CallNoOp<6>> +class CallOpSet : public CallOpSetInterface, + public Op1, + public Op2, + public Op3, + public Op4, + public Op5, + public Op6 { + public: + CallOpSet() : return_tag_(this) {} + void FillOps(grpc_op* ops, size_t* nops) GRPC_OVERRIDE { + this->Op1::AddOp(ops, nops); + this->Op2::AddOp(ops, nops); + this->Op3::AddOp(ops, nops); + this->Op4::AddOp(ops, nops); + this->Op5::AddOp(ops, nops); + this->Op6::AddOp(ops, nops); + } + + bool FinalizeResult(void** tag, bool* status) GRPC_OVERRIDE { + this->Op1::FinishOp(status, max_message_size_); + this->Op2::FinishOp(status, max_message_size_); + this->Op3::FinishOp(status, max_message_size_); + this->Op4::FinishOp(status, max_message_size_); + this->Op5::FinishOp(status, max_message_size_); + this->Op6::FinishOp(status, max_message_size_); + *tag = return_tag_; + return true; + } + + void set_output_tag(void* return_tag) { return_tag_ = return_tag; } + + private: + void* return_tag_; +}; + +/// A CallOpSet that does not post completions to the completion queue. +/// +/// Allows hiding some completions that the C core must generate from +/// C++ users. +template , class Op2 = CallNoOp<2>, + class Op3 = CallNoOp<3>, class Op4 = CallNoOp<4>, + class Op5 = CallNoOp<5>, class Op6 = CallNoOp<6>> +class SneakyCallOpSet : public CallOpSet { + public: + bool FinalizeResult(void** tag, bool* status) GRPC_OVERRIDE { + typedef CallOpSet Base; + return Base::FinalizeResult(tag, status) && false; + } +}; + +// Channel and Server implement this to allow them to hook performing ops +class CallHook { + public: + virtual ~CallHook() {} + virtual void PerformOpsOnCall(CallOpSetInterface* ops, Call* call) = 0; +}; + +// Straightforward wrapping of the C call object +class Call GRPC_FINAL { + public: + /* call is owned by the caller */ + Call(grpc_call* call, CallHook* call_hook_, CompletionQueue* cq); + Call(grpc_call* call, CallHook* call_hook_, CompletionQueue* cq, + int max_message_size); + + void PerformOps(CallOpSetInterface* ops); + + grpc_call* call() { return call_; } + CompletionQueue* cq() { return cq_; } + + int max_message_size() { return max_message_size_; } + + private: + CallHook* call_hook_; + CompletionQueue* cq_; + grpc_call* call_; + int max_message_size_; +}; + +} // namespace grpc + +#endif // GRPCXX_IMPL_CALL_H diff --git a/include/grpc++/impl/client_unary_call.h b/include/grpc++/impl/client_unary_call.h new file mode 100644 index 00000000..4cdc8002 --- /dev/null +++ b/include/grpc++/impl/client_unary_call.h @@ -0,0 +1,74 @@ +/* + * + * Copyright 2015, 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_CLIENT_UNARY_CALL_H +#define GRPCXX_IMPL_CLIENT_UNARY_CALL_H + +#include +#include +#include + +namespace grpc { + +class Channel; +class ClientContext; +class CompletionQueue; +class RpcMethod; + +// Wrapper that performs a blocking unary call +template +Status BlockingUnaryCall(Channel* channel, const RpcMethod& method, + ClientContext* context, const InputMessage& request, + OutputMessage* result) { + CompletionQueue cq; + Call call(channel->CreateCall(method, context, &cq)); + CallOpSet, + CallOpClientSendClose, CallOpClientRecvStatus> ops; + Status status = ops.SendMessage(request); + if (!status.ok()) { + return status; + } + ops.SendInitialMetadata(context->send_initial_metadata_); + ops.RecvInitialMetadata(context); + ops.RecvMessage(result); + ops.ClientSendClose(); + ops.ClientRecvStatus(context, &status); + call.PerformOps(&ops); + GPR_ASSERT((cq.Pluck(&ops) && ops.got_message) || !status.ok()); + return status; +} + +} // namespace grpc + +#endif // GRPCXX_IMPL_CLIENT_UNARY_CALL_H diff --git a/include/grpc++/impl/grpc_library.h b/include/grpc++/impl/grpc_library.h new file mode 100644 index 00000000..ce421141 --- /dev/null +++ b/include/grpc++/impl/grpc_library.h @@ -0,0 +1,49 @@ +/* + * + * Copyright 2015, 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_GRPC_LIBRARY_H +#define GRPCXX_IMPL_GRPC_LIBRARY_H + +#include + +namespace grpc { + +class GrpcLibrary { + public: + GrpcLibrary() { grpc_init(); } + virtual ~GrpcLibrary() { grpc_shutdown(); } +}; + +} // namespace grpc + +#endif // GRPCXX_IMPL_GRPC_LIBRARY_H diff --git a/include/grpc++/impl/proto_utils.h b/include/grpc++/impl/proto_utils.h new file mode 100644 index 00000000..283e3348 --- /dev/null +++ b/include/grpc++/impl/proto_utils.h @@ -0,0 +1,76 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CPP_PROTO_PROTO_UTILS_H +#define GRPC_INTERNAL_CPP_PROTO_PROTO_UTILS_H + +#include + +#include +#include +#include +#include + +namespace grpc { + +// Serialize the msg into a buffer created inside the function. The caller +// should destroy the returned buffer when done with it. If serialization fails, +// false is returned and buffer is left unchanged. +Status SerializeProto(const grpc::protobuf::Message& msg, + grpc_byte_buffer** buffer); + +// The caller keeps ownership of buffer and msg. +Status DeserializeProto(grpc_byte_buffer* buffer, grpc::protobuf::Message* msg, + int max_message_size); + +template +class SerializationTraits::value>::type> { + public: + static Status Serialize(const grpc::protobuf::Message& msg, + grpc_byte_buffer** buffer, bool* own_buffer) { + *own_buffer = true; + return SerializeProto(msg, buffer); + } + static Status Deserialize(grpc_byte_buffer* buffer, + grpc::protobuf::Message* msg, + int max_message_size) { + auto status = DeserializeProto(buffer, msg, max_message_size); + grpc_byte_buffer_destroy(buffer); + return status; + } +}; + +} // namespace grpc + +#endif // GRPC_INTERNAL_CPP_PROTO_PROTO_UTILS_H diff --git a/include/grpc++/impl/rpc_method.h b/include/grpc++/impl/rpc_method.h new file mode 100644 index 00000000..98002680 --- /dev/null +++ b/include/grpc++/impl/rpc_method.h @@ -0,0 +1,73 @@ +/* + * + * Copyright 2015, 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_RPC_METHOD_H +#define GRPCXX_IMPL_RPC_METHOD_H + +#include + +#include + +namespace grpc { + +class RpcMethod { + public: + enum RpcType { + NORMAL_RPC = 0, + CLIENT_STREAMING, // request streaming + SERVER_STREAMING, // response streaming + BIDI_STREAMING + }; + + RpcMethod(const char* name, RpcType type) + : name_(name), method_type_(type), channel_tag_(NULL) {} + + RpcMethod(const char* name, RpcType type, + const std::shared_ptr& channel) + : name_(name), + method_type_(type), + channel_tag_(channel->RegisterMethod(name)) {} + + const char* name() const { return name_; } + RpcType method_type() const { return method_type_; } + void* channel_tag() const { return channel_tag_; } + + private: + const char* const name_; + const RpcType method_type_; + void* const channel_tag_; +}; + +} // namespace grpc + +#endif // GRPCXX_IMPL_RPC_METHOD_H diff --git a/include/grpc++/impl/rpc_service_method.h b/include/grpc++/impl/rpc_service_method.h new file mode 100644 index 00000000..fcb0b7cc --- /dev/null +++ b/include/grpc++/impl/rpc_service_method.h @@ -0,0 +1,262 @@ +/* + * + * Copyright 2015, 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_RPC_SERVICE_METHOD_H +#define GRPCXX_IMPL_RPC_SERVICE_METHOD_H + +#include +#include +#include +#include + +#include +#include +#include +#include + +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: + virtual ~MethodHandler() {} + struct HandlerParameter { + HandlerParameter(Call* c, ServerContext* context, grpc_byte_buffer* req, + int max_size) + : call(c), + server_context(context), + request(req), + max_message_size(max_size) {} + Call* call; + ServerContext* server_context; + // Handler required to grpc_byte_buffer_destroy this + grpc_byte_buffer* request; + int max_message_size; + }; + virtual void RunHandler(const HandlerParameter& param) = 0; +}; + +// A wrapper class of an application provided rpc method handler. +template +class RpcMethodHandler : public MethodHandler { + public: + RpcMethodHandler( + std::function func, + ServiceType* service) + : func_(func), service_(service) {} + + void RunHandler(const HandlerParameter& param) GRPC_FINAL { + RequestType req; + Status status = SerializationTraits::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 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 func_; + // The class the above handler function lives in. + ServiceType* service_; +}; + +// A wrapper class of an application provided client streaming handler. +template +class ClientStreamingHandler : public MethodHandler { + public: + ClientStreamingHandler( + std::function*, ResponseType*)> func, + ServiceType* service) + : func_(func), service_(service) {} + + void RunHandler(const HandlerParameter& param) GRPC_FINAL { + ServerReader 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 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*, + ResponseType*)> func_; + ServiceType* service_; +}; + +// A wrapper class of an application provided server streaming handler. +template +class ServerStreamingHandler : public MethodHandler { + public: + ServerStreamingHandler( + std::function*)> func, + ServiceType* service) + : func_(func), service_(service) {} + + void RunHandler(const HandlerParameter& param) GRPC_FINAL { + RequestType req; + Status status = SerializationTraits::Deserialize( + param.request, &req, param.max_message_size); + + if (status.ok()) { + ServerWriter writer(param.call, param.server_context); + status = func_(service_, param.server_context, &req, &writer); + } + + CallOpSet 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*)> func_; + ServiceType* service_; +}; + +// A wrapper class of an application provided bidi-streaming handler. +template +class BidiStreamingHandler : public MethodHandler { + public: + BidiStreamingHandler( + std::function*)> + func, + ServiceType* service) + : func_(func), service_(service) {} + + void RunHandler(const HandlerParameter& param) GRPC_FINAL { + ServerReaderWriter stream(param.call, + param.server_context); + Status status = func_(service_, param.server_context, &stream); + + CallOpSet 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*)> func_; + ServiceType* service_; +}; + +// Handle unknown method by returning UNIMPLEMENTED error. +class UnknownMethodHandler : public MethodHandler { + public: + template + 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 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) {} + + MethodHandler* handler() { return handler_.get(); } + + private: + std::unique_ptr 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 { return methods_.size(); } + + private: + std::vector> methods_; +}; + +} // namespace grpc + +#endif // GRPCXX_IMPL_RPC_SERVICE_METHOD_H diff --git a/include/grpc++/impl/serialization_traits.h b/include/grpc++/impl/serialization_traits.h new file mode 100644 index 00000000..3ea66a34 --- /dev/null +++ b/include/grpc++/impl/serialization_traits.h @@ -0,0 +1,68 @@ +/* + * + * Copyright 2015, 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_SERIALIZATION_TRAITS_H +#define GRPCXX_IMPL_SERIALIZATION_TRAITS_H + +namespace grpc { + +/// Defines how to serialize and deserialize some type. +/// +/// Used for hooking different message serialization API's into GRPC. +/// Each SerializationTraits implementation must provide the following +/// functions: +/// static Status Serialize(const Message& msg, +/// grpc_byte_buffer** buffer, +// bool* own_buffer); +/// static Status Deserialize(grpc_byte_buffer* buffer, +/// Message* msg, +/// int max_message_size); +/// +/// Serialize is required to convert message to a grpc_byte_buffer, and +/// to store a pointer to that byte buffer at *buffer. *own_buffer should +/// be set to true if the caller owns said byte buffer, or false if +/// ownership is retained elsewhere. +/// +/// Deserialize is required to convert buffer into the message stored at +/// msg. max_message_size is passed in as a bound on the maximum number of +/// message bytes Deserialize should accept. +/// +/// Both functions return a Status, allowing them to explain what went +/// wrong if required. +template +class SerializationTraits; + +} // namespace grpc + +#endif // GRPCXX_IMPL_SERIALIZATION_TRAITS_H diff --git a/include/grpc++/impl/service_type.h b/include/grpc++/impl/service_type.h new file mode 100644 index 00000000..3b6ac1de --- /dev/null +++ b/include/grpc++/impl/service_type.h @@ -0,0 +1,123 @@ +/* + * + * Copyright 2015, 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_SERVICE_TYPE_H +#define GRPCXX_IMPL_SERVICE_TYPE_H + +#include +#include +#include +#include + +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() {} + + virtual void SendInitialMetadata(void* tag) = 0; + + private: + friend class Server; + virtual void BindCall(Call* call) = 0; +}; + +class AsynchronousService { + public: + AsynchronousService(const char** method_names, size_t method_count) + : server_(nullptr), + method_names_(method_names), + method_count_(method_count), + request_args_(nullptr) {} + + ~AsynchronousService() { delete[] request_args_; } + + protected: + template + void RequestAsyncUnary(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, + 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, + notification_cq, tag); + } + template + 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, + 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, + notification_cq, tag); + } + + private: + friend class Server; + Server* server_; + const char** const method_names_; + size_t method_count_; + void** request_args_; +}; + +} // namespace grpc + +#endif // GRPCXX_IMPL_SERVICE_TYPE_H diff --git a/include/grpc++/impl/sync.h b/include/grpc++/impl/sync.h new file mode 100644 index 00000000..999c4303 --- /dev/null +++ b/include/grpc++/impl/sync.h @@ -0,0 +1,45 @@ +/* + * + * Copyright 2015, 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_SYNC_H +#define GRPCXX_IMPL_SYNC_H + +#include + +#ifdef GRPC_CXX0X_NO_THREAD +#include +#else +#include +#endif + +#endif // GRPCXX_IMPL_SYNC_H diff --git a/include/grpc++/impl/sync_cxx11.h b/include/grpc++/impl/sync_cxx11.h new file mode 100644 index 00000000..4e6f1da3 --- /dev/null +++ b/include/grpc++/impl/sync_cxx11.h @@ -0,0 +1,49 @@ +/* + * + * Copyright 2015, 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_SYNC_CXX11_H +#define GRPCXX_IMPL_SYNC_CXX11_H + +#include +#include + +namespace grpc { + +using std::condition_variable; +using std::mutex; +using std::lock_guard; +using std::unique_lock; + +} // namespace grpc + +#endif // GRPCXX_IMPL_SYNC_CXX11_H diff --git a/include/grpc++/impl/sync_no_cxx11.h b/include/grpc++/impl/sync_no_cxx11.h new file mode 100644 index 00000000..120a0310 --- /dev/null +++ b/include/grpc++/impl/sync_no_cxx11.h @@ -0,0 +1,105 @@ +/* + * + * Copyright 2015, 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_SYNC_NO_CXX11_H +#define GRPCXX_IMPL_SYNC_NO_CXX11_H + +#include + +namespace grpc { + +template +class lock_guard; +class condition_variable; + +class mutex { + public: + mutex() { gpr_mu_init(&mu_); } + ~mutex() { gpr_mu_destroy(&mu_); } + + private: + ::gpr_mu mu_; + template + friend class lock_guard; + friend class condition_variable; +}; + +template +class lock_guard { + public: + lock_guard(mutex &mu) : mu_(mu), locked(true) { gpr_mu_lock(&mu.mu_); } + ~lock_guard() { unlock_internal(); } + + protected: + void lock_internal() { + if (!locked) gpr_mu_lock(&mu_.mu_); + locked = true; + } + void unlock_internal() { + if (locked) gpr_mu_unlock(&mu_.mu_); + locked = false; + } + + private: + mutex &mu_; + bool locked; + friend class condition_variable; +}; + +template +class unique_lock : public lock_guard { + public: + unique_lock(mutex &mu) : lock_guard(mu) {} + void lock() { this->lock_internal(); } + void unlock() { this->unlock_internal(); } +}; + +class condition_variable { + public: + condition_variable() { gpr_cv_init(&cv_); } + ~condition_variable() { gpr_cv_destroy(&cv_); } + void wait(lock_guard &mu) { + mu.locked = false; + gpr_cv_wait(&cv_, &mu.mu_.mu_, gpr_inf_future(GPR_CLOCK_REALTIME)); + mu.locked = true; + } + void notify_one() { gpr_cv_signal(&cv_); } + void notify_all() { gpr_cv_broadcast(&cv_); } + + private: + gpr_cv cv_; +}; + +} // namespace grpc + +#endif // GRPCXX_IMPL_SYNC_NO_CXX11_H diff --git a/include/grpc++/impl/thd.h b/include/grpc++/impl/thd.h new file mode 100644 index 00000000..f8d4258a --- /dev/null +++ b/include/grpc++/impl/thd.h @@ -0,0 +1,45 @@ +/* + * + * Copyright 2015, 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_THD_H +#define GRPCXX_IMPL_THD_H + +#include + +#ifdef GRPC_CXX0X_NO_THREAD +#include +#else +#include +#endif + +#endif // GRPCXX_IMPL_THD_H diff --git a/include/grpc++/impl/thd_cxx11.h b/include/grpc++/impl/thd_cxx11.h new file mode 100644 index 00000000..2055b1d5 --- /dev/null +++ b/include/grpc++/impl/thd_cxx11.h @@ -0,0 +1,45 @@ +/* + * + * Copyright 2015, 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_THD_CXX11_H +#define GRPCXX_IMPL_THD_CXX11_H + +#include + +namespace grpc { + +using std::thread; + +} // namespace grpc + +#endif // GRPCXX_IMPL_THD_CXX11_H diff --git a/include/grpc++/impl/thd_no_cxx11.h b/include/grpc++/impl/thd_no_cxx11.h new file mode 100644 index 00000000..84d03ce1 --- /dev/null +++ b/include/grpc++/impl/thd_no_cxx11.h @@ -0,0 +1,94 @@ +/* + * + * Copyright 2015, 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_THD_NO_CXX11_H +#define GRPCXX_IMPL_THD_NO_CXX11_H + +#include + +namespace grpc { + +class thread { + public: + template + thread(void (T::*fptr)(), T *obj) { + func_ = new thread_function(fptr, obj); + joined_ = false; + start(); + } + ~thread() { + if (!joined_) std::terminate(); + delete func_; + } + void join() { + gpr_thd_join(thd_); + joined_ = true; + } + + private: + void start() { + gpr_thd_options options = gpr_thd_options_default(); + gpr_thd_options_set_joinable(&options); + gpr_thd_new(&thd_, thread_func, (void *)func_, &options); + } + static void thread_func(void *arg) { + thread_function_base *func = (thread_function_base *)arg; + func->call(); + } + class thread_function_base { + public: + virtual ~thread_function_base() {} + virtual void call() = 0; + }; + template + class thread_function : public thread_function_base { + public: + thread_function(void (T::*fptr)(), T *obj) : fptr_(fptr), obj_(obj) {} + virtual void call() { (obj_->*fptr_)(); } + + private: + void (T::*fptr_)(); + T *obj_; + }; + thread_function_base *func_; + gpr_thd_id thd_; + bool joined_; + + // Disallow copy and assign. + thread(const thread &); + void operator=(const thread &); +}; + +} // namespace grpc + +#endif // GRPCXX_IMPL_THD_NO_CXX11_H diff --git a/include/grpc++/security/auth_context.h b/include/grpc++/security/auth_context.h new file mode 100644 index 00000000..b924ec90 --- /dev/null +++ b/include/grpc++/security/auth_context.h @@ -0,0 +1,110 @@ +/* + * + * Copyright 2015, 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_SUPPORT_AUTH_CONTEXT_H +#define GRPCXX_SUPPORT_AUTH_CONTEXT_H + +#include +#include + +#include +#include + +struct grpc_auth_context; +struct grpc_auth_property; +struct grpc_auth_property_iterator; + +namespace grpc { +class SecureAuthContext; + +typedef std::pair AuthProperty; + +class AuthPropertyIterator + : public std::iterator { + public: + ~AuthPropertyIterator(); + AuthPropertyIterator& operator++(); + AuthPropertyIterator operator++(int); + bool operator==(const AuthPropertyIterator& rhs) const; + bool operator!=(const AuthPropertyIterator& rhs) const; + const AuthProperty operator*(); + + protected: + AuthPropertyIterator(); + AuthPropertyIterator(const grpc_auth_property* property, + const grpc_auth_property_iterator* iter); + + private: + friend class SecureAuthContext; + const grpc_auth_property* property_; + // The following items form a grpc_auth_property_iterator. + const grpc_auth_context* ctx_; + size_t index_; + const char* name_; +}; + +/// Class encapsulating the Authentication Information. +/// +/// It includes the secure identity of the peer, the type of secure transport +/// used as well as any other properties required by the authorization layer. +class AuthContext { + public: + virtual ~AuthContext() {} + + /// Returns true if the peer is authenticated. + virtual bool IsPeerAuthenticated() const = 0; + + /// A peer identity. + /// + /// It is, in general, comprised of one or more properties (in which case they + /// have the same name). + virtual std::vector GetPeerIdentity() const = 0; + virtual grpc::string GetPeerIdentityPropertyName() const = 0; + + /// Returns all the property values with the given name. + virtual std::vector FindPropertyValues( + const grpc::string& name) const = 0; + + /// Iteration over all the properties. + virtual AuthPropertyIterator begin() const = 0; + virtual AuthPropertyIterator end() const = 0; + + // Mutation functions: should only be used by an AuthMetadataProcessor. + virtual void AddProperty(const grpc::string& key, + const grpc::string_ref& value) = 0; + virtual bool SetPeerIdentityPropertyName(const grpc::string& name) = 0; +}; + +} // namespace grpc + +#endif // GRPCXX_SUPPORT_AUTH_CONTEXT_H diff --git a/include/grpc++/security/auth_metadata_processor.h b/include/grpc++/security/auth_metadata_processor.h new file mode 100644 index 00000000..9b9c06e3 --- /dev/null +++ b/include/grpc++/security/auth_metadata_processor.h @@ -0,0 +1,74 @@ +/* + * + * Copyright 2015, 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_AUTH_METADATA_PROCESSOR_H_ +#define GRPCXX_AUTH_METADATA_PROCESSOR_H_ + +#include + +#include +#include +#include + +namespace grpc { + +class AuthMetadataProcessor { + public: + typedef std::multimap InputMetadata; + typedef std::multimap OutputMetadata; + + virtual ~AuthMetadataProcessor() {} + + // If this method returns true, the Process function will be scheduled in + // a different thread from the one processing the call. + virtual bool IsBlocking() const { return true; } + + // context is read/write: it contains the properties of the channel peer and + // it is the job of the Process method to augment it with properties derived + // from the passed-in auth_metadata. + // consumed_auth_metadata needs to be filled with metadata that has been + // consumed by the processor and will be removed from the call. + // response_metadata is the metadata that will be sent as part of the + // response. + // If the return value is not Status::OK, the rpc call will be aborted with + // the error code and error message sent back to the client. + virtual Status Process(const InputMetadata& auth_metadata, + AuthContext* context, + OutputMetadata* consumed_auth_metadata, + OutputMetadata* response_metadata) = 0; +}; + +} // namespace grpc + +#endif // GRPCXX_AUTH_METADATA_PROCESSOR_H_ + diff --git a/include/grpc++/security/credentials.h b/include/grpc++/security/credentials.h new file mode 100644 index 00000000..e4238497 --- /dev/null +++ b/include/grpc++/security/credentials.h @@ -0,0 +1,170 @@ +/* + * + * Copyright 2015, 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_CREDENTIALS_H +#define GRPCXX_CREDENTIALS_H + +#include + +#include +#include + +namespace grpc { +class ChannelArguments; +class Channel; +class SecureCredentials; + +/// A credentials object encapsulates all the state needed by a client to +/// authenticate with a server and make various assertions, e.g., about the +/// client’s identity, role, or whether it is authorized to make a particular +/// call. +/// +/// \see https://github.com/grpc/grpc/blob/master/doc/grpc-auth-support.md +class Credentials : public GrpcLibrary { + public: + ~Credentials() GRPC_OVERRIDE; + + /// Apply this instance's credentials to \a call. + virtual bool ApplyToCall(grpc_call* call) = 0; + + protected: + friend std::shared_ptr CompositeCredentials( + const std::shared_ptr& creds1, + const std::shared_ptr& creds2); + + virtual SecureCredentials* AsSecureCredentials() = 0; + + private: + friend std::shared_ptr CreateCustomChannel( + const grpc::string& target, const std::shared_ptr& creds, + const ChannelArguments& args); + + virtual std::shared_ptr CreateChannel( + const grpc::string& target, const ChannelArguments& args) = 0; +}; + +/// Options used to build SslCredentials. +struct SslCredentialsOptions { + /// The buffer containing the PEM encoding of the server root certificates. If + /// this parameter is empty, the default roots will be used. The default + /// roots can be overridden using the \a GRPC_DEFAULT_SSL_ROOTS_FILE_PATH + /// environment variable pointing to a file on the file system containing the + /// roots. + grpc::string pem_root_certs; + + /// The buffer containing the PEM encoding of the client's private key. This + /// parameter can be empty if the client does not have a private key. + grpc::string pem_private_key; + + /// The buffer containing the PEM encoding of the client's certificate chain. + /// This parameter can be empty if the client does not have a certificate + /// chain. + grpc::string pem_cert_chain; +}; + +// Factories for building different types of Credentials The functions may +// return empty shared_ptr when credentials cannot be created. If a +// Credentials pointer is returned, it can still be invalid when used to create +// a channel. A lame channel will be created then and all rpcs will fail on it. + +/// Builds credentials with reasonable defaults. +/// +/// \warning Only use these credentials when connecting to a Google endpoint. +/// Using these credentials to connect to any other service may result in this +/// service being able to impersonate your client for requests to Google +/// services. +std::shared_ptr GoogleDefaultCredentials(); + +/// Builds SSL Credentials given SSL specific options +std::shared_ptr SslCredentials( + const SslCredentialsOptions& options); + +/// Builds credentials for use when running in GCE +/// +/// \warning Only use these credentials when connecting to a Google endpoint. +/// Using these credentials to connect to any other service may result in this +/// service being able to impersonate your client for requests to Google +/// services. +std::shared_ptr GoogleComputeEngineCredentials(); + +/// Builds Service Account JWT Access credentials. +/// json_key is the JSON key string containing the client's private key. +/// token_lifetime_seconds is the lifetime in seconds of each Json Web Token +/// (JWT) created with this credentials. It should not exceed +/// grpc_max_auth_token_lifetime or will be cropped to this value. +std::shared_ptr ServiceAccountJWTAccessCredentials( + const grpc::string& json_key, long token_lifetime_seconds); + +/// Builds refresh token credentials. +/// json_refresh_token is the JSON string containing the refresh token along +/// with a client_id and client_secret. +/// +/// \warning Only use these credentials when connecting to a Google endpoint. +/// Using these credentials to connect to any other service may result in this +/// service being able to impersonate your client for requests to Google +/// services. +std::shared_ptr GoogleRefreshTokenCredentials( + const grpc::string& json_refresh_token); + +/// Builds access token credentials. +/// access_token is an oauth2 access token that was fetched using an out of band +/// mechanism. +/// +/// \warning Only use these credentials when connecting to a Google endpoint. +/// Using these credentials to connect to any other service may result in this +/// service being able to impersonate your client for requests to Google +/// services. +std::shared_ptr AccessTokenCredentials( + const grpc::string& access_token); + +/// Builds IAM credentials. +/// +/// \warning Only use these credentials when connecting to a Google endpoint. +/// Using these credentials to connect to any other service may result in this +/// service being able to impersonate your client for requests to Google +/// services. +std::shared_ptr GoogleIAMCredentials( + const grpc::string& authorization_token, + const grpc::string& authority_selector); + +/// Combines two credentials objects into a composite credentials +std::shared_ptr CompositeCredentials( + const std::shared_ptr& creds1, + const std::shared_ptr& creds2); + +/// Credentials for an unencrypted, unauthenticated channel +std::shared_ptr InsecureCredentials(); + +} // namespace grpc + +#endif // GRPCXX_CREDENTIALS_H diff --git a/include/grpc++/security/server_credentials.h b/include/grpc++/security/server_credentials.h new file mode 100644 index 00000000..e933825e --- /dev/null +++ b/include/grpc++/security/server_credentials.h @@ -0,0 +1,92 @@ +/* + * + * Copyright 2015, 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_SERVER_CREDENTIALS_H +#define GRPCXX_SERVER_CREDENTIALS_H + +#include +#include + +#include +#include + +struct grpc_server; + +namespace grpc { +class Server; + +// Wrapper around \a grpc_server_credentials, a way to authenticate a server. +class ServerCredentials { + public: + virtual ~ServerCredentials(); + + // This method is not thread-safe and has to be called before the server is + // started. The last call to this function wins. + virtual void SetAuthMetadataProcessor( + const std::shared_ptr& processor) = 0; + + private: + friend class ::grpc::Server; + + /// Tries to bind \a server to the given \a addr (eg, localhost:1234, + /// 192.168.1.1:31416, [::1]:27182, etc.) + /// + /// \return bound port number on sucess, 0 on failure. + // TODO(dgq): the "port" part seems to be a misnomer. + virtual int AddPortToServer(const grpc::string& addr, + grpc_server* server) = 0; +}; + +/// Options to create ServerCredentials with SSL +struct SslServerCredentialsOptions { + SslServerCredentialsOptions() : force_client_auth(false) {} + + struct PemKeyCertPair { + grpc::string private_key; + grpc::string cert_chain; + }; + grpc::string pem_root_certs; + std::vector pem_key_cert_pairs; + bool force_client_auth; +}; + +/// Builds SSL ServerCredentials given SSL specific options +std::shared_ptr SslServerCredentials( + const SslServerCredentialsOptions& options); + +/// Builds insecure server credentials. +std::shared_ptr InsecureServerCredentials(); + +} // namespace grpc + +#endif // GRPCXX_SERVER_CREDENTIALS_H diff --git a/include/grpc++/server.h b/include/grpc++/server.h new file mode 100644 index 00000000..18a80178 --- /dev/null +++ b/include/grpc++/server.h @@ -0,0 +1,302 @@ +/* + * + * Copyright 2015, 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_SERVER_H +#define GRPCXX_SERVER_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +struct grpc_server; + +namespace grpc { + +class AsynchronousService; +class GenericServerContext; +class AsyncGenericService; +class RpcService; +class RpcServiceMethod; +class ServerAsyncStreamingInterface; +class ThreadPoolInterface; + +/// Models a gRPC server. +/// +/// Servers are configured and started via \a grpc::ServerBuilder. +class Server GRPC_FINAL : public GrpcLibrary, private CallHook { + public: + ~Server(); + + /// Shutdown the server, blocking until all rpc processing finishes. + /// Forcefully terminate pending calls after \a deadline expires. + /// + /// \param deadline How long to wait until pending rpcs are forcefully + /// terminated. + template + void Shutdown(const T& deadline) { + ShutdownInternal(TimePoint(deadline).raw_time()); + } + + /// Shutdown the server, waiting for all rpc processing to finish. + void Shutdown() { ShutdownInternal(gpr_inf_future(GPR_CLOCK_MONOTONIC)); } + + /// Block waiting for all work to complete. + /// + /// \warning The server must be either shutting down or some other thread must + /// call \a Shutdown for this function to ever return. + void Wait(); + + private: + friend class AsyncGenericService; + friend class AsynchronousService; + friend class ServerBuilder; + + class SyncRequest; + class AsyncRequest; + class ShutdownRequest; + + /// Server constructors. To be used by \a ServerBuilder only. + /// + /// \param thread_pool The threadpool instance to use for call processing. + /// \param thread_pool_owned Does the server own the \a thread_pool instance? + /// \param max_message_size Maximum message length that the channel can + /// receive. + Server(ThreadPoolInterface* thread_pool, bool thread_pool_owned, + int max_message_size); + + /// 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); + + /// Register a generic service. This call does not take ownership of the + /// service. The service must exist for the lifetime of the Server instance. + void RegisterAsyncGenericService(AsyncGenericService* service); + + /// Tries to bind \a server to the given \a addr. + /// + /// It can be invoked multiple times. + /// + /// \param addr The address to try to bind to the server (eg, localhost:1234, + /// 192.168.1.1:31416, [::1]:27182, etc.). + /// \params creds The credentials associated with the server. + /// + /// \return bound port number on sucess, 0 on failure. + /// + /// \warning It's an error to call this method on an already started server. + int AddListeningPort(const grpc::string& addr, ServerCredentials* creds); + + /// Start the server. + /// + /// \param cqs Completion queues for handling asynchronous services. The + /// caller is required to keep all completion queues live until the server is + /// destroyed. + /// \param num_cqs How many completion queues does \a cqs hold. + /// + /// \return true on a successful shutdown. + bool Start(ServerCompletionQueue** cqs, size_t num_cqs); + + void HandleQueueClosed(); + + /// Process one or more incoming calls. + void RunRpc(); + + /// Schedule \a RunRpc to run in the threadpool. + void ScheduleCallback(); + + void PerformOpsOnCall(CallOpSetInterface* ops, Call* call) GRPC_OVERRIDE; + + void ShutdownInternal(gpr_timespec deadline); + + class BaseAsyncRequest : public CompletionQueueTag { + public: + BaseAsyncRequest(Server* server, ServerContext* context, + ServerAsyncStreamingInterface* stream, + CompletionQueue* call_cq, void* tag, + bool delete_on_finalize); + virtual ~BaseAsyncRequest(); + + bool FinalizeResult(void** tag, bool* status) GRPC_OVERRIDE; + + protected: + Server* const server_; + ServerContext* const context_; + ServerAsyncStreamingInterface* const stream_; + CompletionQueue* const call_cq_; + void* const tag_; + const bool delete_on_finalize_; + grpc_call* call_; + grpc_metadata_array initial_metadata_array_; + }; + + class RegisteredAsyncRequest : public BaseAsyncRequest { + public: + RegisteredAsyncRequest(Server* server, ServerContext* context, + ServerAsyncStreamingInterface* stream, + CompletionQueue* call_cq, void* tag); + + // uses BaseAsyncRequest::FinalizeResult + + protected: + void IssueRequest(void* registered_method, grpc_byte_buffer** payload, + ServerCompletionQueue* notification_cq); + }; + + class NoPayloadAsyncRequest GRPC_FINAL : public RegisteredAsyncRequest { + public: + NoPayloadAsyncRequest(void* registered_method, Server* server, + ServerContext* context, + ServerAsyncStreamingInterface* stream, + CompletionQueue* call_cq, + ServerCompletionQueue* notification_cq, void* tag) + : RegisteredAsyncRequest(server, context, stream, call_cq, tag) { + IssueRequest(registered_method, nullptr, notification_cq); + } + + // uses RegisteredAsyncRequest::FinalizeResult + }; + + template + class PayloadAsyncRequest GRPC_FINAL : public RegisteredAsyncRequest { + public: + PayloadAsyncRequest(void* registered_method, Server* server, + ServerContext* context, + ServerAsyncStreamingInterface* stream, + CompletionQueue* call_cq, + ServerCompletionQueue* notification_cq, void* tag, + Message* request) + : RegisteredAsyncRequest(server, context, stream, call_cq, tag), + request_(request) { + IssueRequest(registered_method, &payload_, notification_cq); + } + + bool FinalizeResult(void** tag, bool* status) GRPC_OVERRIDE { + bool serialization_status = + *status && payload_ && + SerializationTraits::Deserialize(payload_, request_, + server_->max_message_size_) + .ok(); + bool ret = RegisteredAsyncRequest::FinalizeResult(tag, status); + *status = serialization_status && *status; + return ret; + } + + private: + grpc_byte_buffer* payload_; + Message* const request_; + }; + + class GenericAsyncRequest : public BaseAsyncRequest { + public: + GenericAsyncRequest(Server* server, GenericServerContext* context, + ServerAsyncStreamingInterface* stream, + CompletionQueue* call_cq, + ServerCompletionQueue* notification_cq, void* tag, + bool delete_on_finalize); + + bool FinalizeResult(void** tag, bool* status) GRPC_OVERRIDE; + + private: + grpc_call_details call_details_; + }; + + class UnimplementedAsyncRequestContext; + class UnimplementedAsyncRequest; + class UnimplementedAsyncResponse; + + template + void RequestAsyncCall(void* registered_method, ServerContext* context, + ServerAsyncStreamingInterface* stream, + CompletionQueue* call_cq, + ServerCompletionQueue* notification_cq, void* tag, + Message* message) { + new PayloadAsyncRequest(registered_method, this, context, stream, + call_cq, notification_cq, tag, message); + } + + void RequestAsyncCall(void* registered_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); + } + + void RequestAsyncGenericCall(GenericServerContext* context, + ServerAsyncStreamingInterface* stream, + CompletionQueue* call_cq, + ServerCompletionQueue* notification_cq, + void* tag) { + new GenericAsyncRequest(this, context, stream, call_cq, notification_cq, + tag, true); + } + + const int max_message_size_; + + // Completion queue. + CompletionQueue cq_; + + // Sever status + grpc::mutex mu_; + bool started_; + bool shutdown_; + // The number of threads which are running callbacks. + int num_running_cb_; + grpc::condition_variable callback_cv_; + + std::list* sync_methods_; + std::unique_ptr unknown_method_; + bool has_generic_service_; + + // Pointer to the c grpc server. + grpc_server* const server_; + + ThreadPoolInterface* thread_pool_; + // Whether the thread pool is created and owned by the server. + bool thread_pool_owned_; +}; + +} // namespace grpc + +#endif // GRPCXX_SERVER_H diff --git a/include/grpc++/server_builder.h b/include/grpc++/server_builder.h new file mode 100644 index 00000000..496e7862 --- /dev/null +++ b/include/grpc++/server_builder.h @@ -0,0 +1,148 @@ +/* + * + * Copyright 2015, 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_SERVER_BUILDER_H +#define GRPCXX_SERVER_BUILDER_H + +#include +#include + +#include + +namespace grpc { + +class AsyncGenericService; +class AsynchronousService; +class CompletionQueue; +class RpcService; +class Server; +class ServerCompletionQueue; +class ServerCredentials; +class SynchronousService; +class ThreadPoolInterface; + +/// A builder class for the creation and startup of \a grpc::Server instances. +class ServerBuilder { + public: + ServerBuilder(); + + /// Register a service. This call does not take ownership of the service. + /// 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); + + /// Register a generic service. + /// Matches requests with any :authority + void RegisterAsyncGenericService(AsyncGenericService* service); + + /// Register a service. This call does not take ownership of the service. + /// 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); + + /// Set max message size in bytes. + void SetMaxMessageSize(int max_message_size) { + max_message_size_ = max_message_size; + } + + /// Tries to bind \a server to the given \a addr. + /// + /// It can be invoked multiple times. + /// + /// \param addr The address to try to bind to the server (eg, localhost:1234, + /// 192.168.1.1:31416, [::1]:27182, etc.). + /// \params creds The credentials associated with the server. + /// \param selected_port[out] Upon success, updated to contain the port + /// number. \a nullptr otherwise. + /// + // TODO(dgq): the "port" part seems to be a misnomer. + void AddListeningPort(const grpc::string& addr, + std::shared_ptr creds, + int* selected_port = nullptr); + + /// Add a completion queue for handling asynchronous services + /// Caller is required to keep this completion queue live until + /// the server is destroyed. + std::unique_ptr AddCompletionQueue(); + + /// Return a running server which is ready for processing calls. + std::unique_ptr BuildAndStart(); + + private: + struct Port { + grpc::string addr; + std::shared_ptr creds; + int* selected_port; + }; + + typedef std::unique_ptr HostString; + template + struct NamedService { + explicit NamedService(T* s) : service(s) {} + NamedService(const grpc::string& h, T* s) + : host(new grpc::string(h)), service(s) {} + HostString host; + T* service; + }; + + int max_message_size_; + std::vector>> services_; + std::vector>> + async_services_; + std::vector ports_; + std::vector cqs_; + std::shared_ptr creds_; + AsyncGenericService* generic_service_; + ThreadPoolInterface* thread_pool_; +}; + +} // namespace grpc + +#endif // GRPCXX_SERVER_BUILDER_H diff --git a/include/grpc++/server_context.h b/include/grpc++/server_context.h new file mode 100644 index 00000000..85f384d4 --- /dev/null +++ b/include/grpc++/server_context.h @@ -0,0 +1,199 @@ +/* + * + * Copyright 2015, 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_SERVER_CONTEXT_H +#define GRPCXX_SERVER_CONTEXT_H + +#include +#include + +#include +#include +#include +#include +#include +#include + +struct gpr_timespec; +struct grpc_metadata; +struct grpc_call; +struct census_context; + +namespace grpc { + +class ClientContext; +template +class ServerAsyncReader; +template +class ServerAsyncWriter; +template +class ServerAsyncResponseWriter; +template +class ServerAsyncReaderWriter; +template +class ServerReader; +template +class ServerWriter; +template +class ServerReaderWriter; +template +class RpcMethodHandler; +template +class ClientStreamingHandler; +template +class ServerStreamingHandler; +template +class BidiStreamingHandler; +class UnknownMethodHandler; + +class Call; +class CallOpBuffer; +class CompletionQueue; +class Server; + +namespace testing { +class InteropServerContextInspector; +} // namespace testing + +// Interface of server side rpc context. +class ServerContext { + public: + ServerContext(); // for async calls + ~ServerContext(); + +#ifndef GRPC_CXX0X_NO_CHRONO + std::chrono::system_clock::time_point deadline() { + return Timespec2Timepoint(deadline_); + } +#endif // !GRPC_CXX0X_NO_CHRONO + + gpr_timespec raw_deadline() { return deadline_; } + + void AddInitialMetadata(const grpc::string& key, const grpc::string& value); + void AddTrailingMetadata(const grpc::string& key, const grpc::string& value); + + bool IsCancelled() const; + + const std::multimap& client_metadata() { + return client_metadata_; + } + + grpc_compression_level compression_level() const { + return compression_level_; + } + void set_compression_level(grpc_compression_level level); + + grpc_compression_algorithm compression_algorithm() const { + return compression_algorithm_; + } + void set_compression_algorithm(grpc_compression_algorithm algorithm); + + std::shared_ptr auth_context() const; + + // Return the peer uri in a string. + // WARNING: this value is never authenticated or subject to any security + // related code. It must not be used for any authentication related + // functionality. Instead, use auth_context. + grpc::string peer() const; + + const struct census_context* census_context() const; + + // Async only. Has to be called before the rpc starts. + // Returns the tag in completion queue when the rpc finishes. + // IsCancelled() can then be called to check whether the rpc was cancelled. + void AsyncNotifyWhenDone(void* tag) { + has_notify_when_done_tag_ = true; + async_notify_when_done_tag_ = tag; + } + + private: + friend class ::grpc::testing::InteropServerContextInspector; + friend class ::grpc::Server; + template + friend class ::grpc::ServerAsyncReader; + template + friend class ::grpc::ServerAsyncWriter; + template + friend class ::grpc::ServerAsyncResponseWriter; + template + friend class ::grpc::ServerAsyncReaderWriter; + template + friend class ::grpc::ServerReader; + template + friend class ::grpc::ServerWriter; + template + friend class ::grpc::ServerReaderWriter; + template + friend class RpcMethodHandler; + template + friend class ClientStreamingHandler; + template + friend class ServerStreamingHandler; + template + friend class BidiStreamingHandler; + friend class UnknownMethodHandler; + friend class ::grpc::ClientContext; + + // Prevent copying. + ServerContext(const ServerContext&); + ServerContext& operator=(const ServerContext&); + + class CompletionOp; + + void BeginCompletionOp(Call* call); + + ServerContext(gpr_timespec deadline, grpc_metadata* metadata, + size_t metadata_count); + + void set_call(grpc_call* call); + + CompletionOp* completion_op_; + bool has_notify_when_done_tag_; + void* async_notify_when_done_tag_; + + gpr_timespec deadline_; + grpc_call* call_; + CompletionQueue* cq_; + bool sent_initial_metadata_; + mutable std::shared_ptr auth_context_; + std::multimap client_metadata_; + std::multimap initial_metadata_; + std::multimap trailing_metadata_; + + grpc_compression_level compression_level_; + grpc_compression_algorithm compression_algorithm_; +}; + +} // namespace grpc + +#endif // GRPCXX_SERVER_CONTEXT_H diff --git a/include/grpc++/support/async_stream.h b/include/grpc++/support/async_stream.h new file mode 100644 index 00000000..b4dae30c --- /dev/null +++ b/include/grpc++/support/async_stream.h @@ -0,0 +1,459 @@ +/* + * + * Copyright 2015, 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_SUPPORT_ASYNC_STREAM_H +#define GRPCXX_SUPPORT_ASYNC_STREAM_H + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace grpc { + +/// Common interface for all client side asynchronous streaming. +class ClientAsyncStreamingInterface { + public: + virtual ~ClientAsyncStreamingInterface() {} + + /// Request notification of the reading of the initial metadata. Completion + /// will be notified by \a tag on the associated completion queue. + /// + /// \param[in] tag Tag identifying this request. + virtual void ReadInitialMetadata(void* tag) = 0; + + /// Request notification completion. + /// + /// \param[out] status To be updated with the operation status. + /// \param[in] tag Tag identifying this request. + virtual void Finish(Status* status, void* tag) = 0; +}; + +/// An interface that yields a sequence of messages of type \a R. +template +class AsyncReaderInterface { + public: + virtual ~AsyncReaderInterface() {} + + /// Read a message of type \a R into \a msg. Completion will be notified by \a + /// tag on the associated completion queue. + /// + /// \param[out] msg Where to eventually store the read message. + /// \param[in] tag The tag identifying the operation. + virtual void Read(R* msg, void* tag) = 0; +}; + +/// An interface that can be fed a sequence of messages of type \a W. +template +class AsyncWriterInterface { + public: + virtual ~AsyncWriterInterface() {} + + /// Request the writing of \a msg with identifying tag \a tag. + /// + /// \param[in] msg The message to be written. + /// \param[in] tag The tag identifying the operation. + virtual void Write(const W& msg, void* tag) = 0; +}; + +template +class ClientAsyncReaderInterface : public ClientAsyncStreamingInterface, + public AsyncReaderInterface {}; + +template +class ClientAsyncReader GRPC_FINAL : public ClientAsyncReaderInterface { + public: + /// Create a stream and write the first request out. + template + ClientAsyncReader(Channel* channel, CompletionQueue* cq, + const RpcMethod& method, ClientContext* context, + const W& request, void* tag) + : context_(context), call_(channel->CreateCall(method, context, cq)) { + init_ops_.set_output_tag(tag); + init_ops_.SendInitialMetadata(context->send_initial_metadata_); + // TODO(ctiller): don't assert + GPR_ASSERT(init_ops_.SendMessage(request).ok()); + init_ops_.ClientSendClose(); + call_.PerformOps(&init_ops_); + } + + void ReadInitialMetadata(void* tag) GRPC_OVERRIDE { + GPR_ASSERT(!context_->initial_metadata_received_); + + meta_ops_.set_output_tag(tag); + meta_ops_.RecvInitialMetadata(context_); + call_.PerformOps(&meta_ops_); + } + + void Read(R* msg, void* tag) GRPC_OVERRIDE { + read_ops_.set_output_tag(tag); + if (!context_->initial_metadata_received_) { + read_ops_.RecvInitialMetadata(context_); + } + read_ops_.RecvMessage(msg); + call_.PerformOps(&read_ops_); + } + + void Finish(Status* status, void* tag) GRPC_OVERRIDE { + finish_ops_.set_output_tag(tag); + if (!context_->initial_metadata_received_) { + finish_ops_.RecvInitialMetadata(context_); + } + finish_ops_.ClientRecvStatus(context_, status); + call_.PerformOps(&finish_ops_); + } + + private: + ClientContext* context_; + Call call_; + CallOpSet + init_ops_; + CallOpSet meta_ops_; + CallOpSet> read_ops_; + CallOpSet finish_ops_; +}; + +/// Common interface for client side asynchronous writing. +template +class ClientAsyncWriterInterface : public ClientAsyncStreamingInterface, + public AsyncWriterInterface { + public: + /// Signal the client is done with the writes. + /// + /// \param[in] tag The tag identifying the operation. + virtual void WritesDone(void* tag) = 0; +}; + +template +class ClientAsyncWriter GRPC_FINAL : public ClientAsyncWriterInterface { + public: + template + ClientAsyncWriter(Channel* channel, CompletionQueue* cq, + const RpcMethod& method, ClientContext* context, + R* response, void* tag) + : context_(context), call_(channel->CreateCall(method, context, cq)) { + finish_ops_.RecvMessage(response); + + init_ops_.set_output_tag(tag); + init_ops_.SendInitialMetadata(context->send_initial_metadata_); + call_.PerformOps(&init_ops_); + } + + void ReadInitialMetadata(void* tag) GRPC_OVERRIDE { + GPR_ASSERT(!context_->initial_metadata_received_); + + meta_ops_.set_output_tag(tag); + meta_ops_.RecvInitialMetadata(context_); + call_.PerformOps(&meta_ops_); + } + + void Write(const W& msg, void* tag) GRPC_OVERRIDE { + write_ops_.set_output_tag(tag); + // TODO(ctiller): don't assert + GPR_ASSERT(write_ops_.SendMessage(msg).ok()); + call_.PerformOps(&write_ops_); + } + + void WritesDone(void* tag) GRPC_OVERRIDE { + writes_done_ops_.set_output_tag(tag); + writes_done_ops_.ClientSendClose(); + call_.PerformOps(&writes_done_ops_); + } + + void Finish(Status* status, void* tag) GRPC_OVERRIDE { + finish_ops_.set_output_tag(tag); + if (!context_->initial_metadata_received_) { + finish_ops_.RecvInitialMetadata(context_); + } + finish_ops_.ClientRecvStatus(context_, status); + call_.PerformOps(&finish_ops_); + } + + private: + ClientContext* context_; + Call call_; + CallOpSet init_ops_; + CallOpSet meta_ops_; + CallOpSet write_ops_; + CallOpSet writes_done_ops_; + CallOpSet finish_ops_; +}; + +/// Client-side interface for asynchronous bi-directional streaming. +template +class ClientAsyncReaderWriterInterface : public ClientAsyncStreamingInterface, + public AsyncWriterInterface, + public AsyncReaderInterface { + public: + /// Signal the client is done with the writes. + /// + /// \param[in] tag The tag identifying the operation. + virtual void WritesDone(void* tag) = 0; +}; + +template +class ClientAsyncReaderWriter GRPC_FINAL + : public ClientAsyncReaderWriterInterface { + public: + ClientAsyncReaderWriter(Channel* channel, CompletionQueue* cq, + const RpcMethod& method, ClientContext* context, + void* tag) + : context_(context), call_(channel->CreateCall(method, context, cq)) { + init_ops_.set_output_tag(tag); + init_ops_.SendInitialMetadata(context->send_initial_metadata_); + call_.PerformOps(&init_ops_); + } + + void ReadInitialMetadata(void* tag) GRPC_OVERRIDE { + GPR_ASSERT(!context_->initial_metadata_received_); + + meta_ops_.set_output_tag(tag); + meta_ops_.RecvInitialMetadata(context_); + call_.PerformOps(&meta_ops_); + } + + void Read(R* msg, void* tag) GRPC_OVERRIDE { + read_ops_.set_output_tag(tag); + if (!context_->initial_metadata_received_) { + read_ops_.RecvInitialMetadata(context_); + } + read_ops_.RecvMessage(msg); + call_.PerformOps(&read_ops_); + } + + void Write(const W& msg, void* tag) GRPC_OVERRIDE { + write_ops_.set_output_tag(tag); + // TODO(ctiller): don't assert + GPR_ASSERT(write_ops_.SendMessage(msg).ok()); + call_.PerformOps(&write_ops_); + } + + void WritesDone(void* tag) GRPC_OVERRIDE { + writes_done_ops_.set_output_tag(tag); + writes_done_ops_.ClientSendClose(); + call_.PerformOps(&writes_done_ops_); + } + + void Finish(Status* status, void* tag) GRPC_OVERRIDE { + finish_ops_.set_output_tag(tag); + if (!context_->initial_metadata_received_) { + finish_ops_.RecvInitialMetadata(context_); + } + finish_ops_.ClientRecvStatus(context_, status); + call_.PerformOps(&finish_ops_); + } + + private: + ClientContext* context_; + Call call_; + CallOpSet init_ops_; + CallOpSet meta_ops_; + CallOpSet> read_ops_; + CallOpSet write_ops_; + CallOpSet writes_done_ops_; + CallOpSet finish_ops_; +}; + +template +class ServerAsyncReader GRPC_FINAL : public ServerAsyncStreamingInterface, + public AsyncReaderInterface { + public: + explicit ServerAsyncReader(ServerContext* ctx) + : call_(nullptr, nullptr, nullptr), ctx_(ctx) {} + + void SendInitialMetadata(void* tag) GRPC_OVERRIDE { + GPR_ASSERT(!ctx_->sent_initial_metadata_); + + meta_ops_.set_output_tag(tag); + meta_ops_.SendInitialMetadata(ctx_->initial_metadata_); + ctx_->sent_initial_metadata_ = true; + call_.PerformOps(&meta_ops_); + } + + void Read(R* msg, void* tag) GRPC_OVERRIDE { + read_ops_.set_output_tag(tag); + read_ops_.RecvMessage(msg); + call_.PerformOps(&read_ops_); + } + + void Finish(const W& msg, const Status& status, void* tag) { + finish_ops_.set_output_tag(tag); + if (!ctx_->sent_initial_metadata_) { + finish_ops_.SendInitialMetadata(ctx_->initial_metadata_); + ctx_->sent_initial_metadata_ = true; + } + // The response is dropped if the status is not OK. + if (status.ok()) { + finish_ops_.ServerSendStatus(ctx_->trailing_metadata_, + finish_ops_.SendMessage(msg)); + } else { + finish_ops_.ServerSendStatus(ctx_->trailing_metadata_, status); + } + call_.PerformOps(&finish_ops_); + } + + void FinishWithError(const Status& status, void* tag) { + GPR_ASSERT(!status.ok()); + finish_ops_.set_output_tag(tag); + if (!ctx_->sent_initial_metadata_) { + finish_ops_.SendInitialMetadata(ctx_->initial_metadata_); + ctx_->sent_initial_metadata_ = true; + } + finish_ops_.ServerSendStatus(ctx_->trailing_metadata_, status); + call_.PerformOps(&finish_ops_); + } + + private: + void BindCall(Call* call) GRPC_OVERRIDE { call_ = *call; } + + Call call_; + ServerContext* ctx_; + CallOpSet meta_ops_; + CallOpSet> read_ops_; + CallOpSet finish_ops_; +}; + +template +class ServerAsyncWriter GRPC_FINAL : public ServerAsyncStreamingInterface, + public AsyncWriterInterface { + public: + explicit ServerAsyncWriter(ServerContext* ctx) + : call_(nullptr, nullptr, nullptr), ctx_(ctx) {} + + void SendInitialMetadata(void* tag) GRPC_OVERRIDE { + GPR_ASSERT(!ctx_->sent_initial_metadata_); + + meta_ops_.set_output_tag(tag); + meta_ops_.SendInitialMetadata(ctx_->initial_metadata_); + ctx_->sent_initial_metadata_ = true; + call_.PerformOps(&meta_ops_); + } + + void Write(const W& msg, void* tag) GRPC_OVERRIDE { + write_ops_.set_output_tag(tag); + if (!ctx_->sent_initial_metadata_) { + write_ops_.SendInitialMetadata(ctx_->initial_metadata_); + ctx_->sent_initial_metadata_ = true; + } + // TODO(ctiller): don't assert + GPR_ASSERT(write_ops_.SendMessage(msg).ok()); + call_.PerformOps(&write_ops_); + } + + void Finish(const Status& status, void* tag) { + finish_ops_.set_output_tag(tag); + if (!ctx_->sent_initial_metadata_) { + finish_ops_.SendInitialMetadata(ctx_->initial_metadata_); + ctx_->sent_initial_metadata_ = true; + } + finish_ops_.ServerSendStatus(ctx_->trailing_metadata_, status); + call_.PerformOps(&finish_ops_); + } + + private: + void BindCall(Call* call) GRPC_OVERRIDE { call_ = *call; } + + Call call_; + ServerContext* ctx_; + CallOpSet meta_ops_; + CallOpSet write_ops_; + CallOpSet finish_ops_; +}; + +/// Server-side interface for asynchronous bi-directional streaming. +template +class ServerAsyncReaderWriter GRPC_FINAL : public ServerAsyncStreamingInterface, + public AsyncWriterInterface, + public AsyncReaderInterface { + public: + explicit ServerAsyncReaderWriter(ServerContext* ctx) + : call_(nullptr, nullptr, nullptr), ctx_(ctx) {} + + void SendInitialMetadata(void* tag) GRPC_OVERRIDE { + GPR_ASSERT(!ctx_->sent_initial_metadata_); + + meta_ops_.set_output_tag(tag); + meta_ops_.SendInitialMetadata(ctx_->initial_metadata_); + ctx_->sent_initial_metadata_ = true; + call_.PerformOps(&meta_ops_); + } + + void Read(R* msg, void* tag) GRPC_OVERRIDE { + read_ops_.set_output_tag(tag); + read_ops_.RecvMessage(msg); + call_.PerformOps(&read_ops_); + } + + void Write(const W& msg, void* tag) GRPC_OVERRIDE { + write_ops_.set_output_tag(tag); + if (!ctx_->sent_initial_metadata_) { + write_ops_.SendInitialMetadata(ctx_->initial_metadata_); + ctx_->sent_initial_metadata_ = true; + } + // TODO(ctiller): don't assert + GPR_ASSERT(write_ops_.SendMessage(msg).ok()); + call_.PerformOps(&write_ops_); + } + + void Finish(const Status& status, void* tag) { + finish_ops_.set_output_tag(tag); + if (!ctx_->sent_initial_metadata_) { + finish_ops_.SendInitialMetadata(ctx_->initial_metadata_); + ctx_->sent_initial_metadata_ = true; + } + finish_ops_.ServerSendStatus(ctx_->trailing_metadata_, status); + call_.PerformOps(&finish_ops_); + } + + private: + friend class ::grpc::Server; + + void BindCall(Call* call) GRPC_OVERRIDE { call_ = *call; } + + Call call_; + ServerContext* ctx_; + CallOpSet meta_ops_; + CallOpSet> read_ops_; + CallOpSet write_ops_; + CallOpSet finish_ops_; +}; + +} // namespace grpc + +#endif // GRPCXX_SUPPORT_ASYNC_STREAM_H diff --git a/include/grpc++/support/async_unary_call.h b/include/grpc++/support/async_unary_call.h new file mode 100644 index 00000000..0f4ad265 --- /dev/null +++ b/include/grpc++/support/async_unary_call.h @@ -0,0 +1,155 @@ +/* + * + * Copyright 2015, 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_SUPPORT_ASYNC_UNARY_CALL_H +#define GRPCXX_SUPPORT_ASYNC_UNARY_CALL_H + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace grpc { + +template +class ClientAsyncResponseReaderInterface { + public: + virtual ~ClientAsyncResponseReaderInterface() {} + virtual void ReadInitialMetadata(void* tag) = 0; + virtual void Finish(R* msg, Status* status, void* tag) = 0; +}; + +template +class ClientAsyncResponseReader GRPC_FINAL + : public ClientAsyncResponseReaderInterface { + public: + template + ClientAsyncResponseReader(Channel* channel, CompletionQueue* cq, + const RpcMethod& method, ClientContext* context, + const W& request) + : context_(context), call_(channel->CreateCall(method, context, cq)) { + init_buf_.SendInitialMetadata(context->send_initial_metadata_); + // TODO(ctiller): don't assert + GPR_ASSERT(init_buf_.SendMessage(request).ok()); + init_buf_.ClientSendClose(); + call_.PerformOps(&init_buf_); + } + + void ReadInitialMetadata(void* tag) { + GPR_ASSERT(!context_->initial_metadata_received_); + + meta_buf_.set_output_tag(tag); + meta_buf_.RecvInitialMetadata(context_); + call_.PerformOps(&meta_buf_); + } + + void Finish(R* msg, Status* status, void* tag) { + finish_buf_.set_output_tag(tag); + if (!context_->initial_metadata_received_) { + finish_buf_.RecvInitialMetadata(context_); + } + finish_buf_.RecvMessage(msg); + finish_buf_.ClientRecvStatus(context_, status); + call_.PerformOps(&finish_buf_); + } + + private: + ClientContext* context_; + Call call_; + SneakyCallOpSet init_buf_; + CallOpSet meta_buf_; + CallOpSet, + CallOpClientRecvStatus> finish_buf_; +}; + +template +class ServerAsyncResponseWriter GRPC_FINAL + : public ServerAsyncStreamingInterface { + public: + explicit ServerAsyncResponseWriter(ServerContext* ctx) + : call_(nullptr, nullptr, nullptr), ctx_(ctx) {} + + void SendInitialMetadata(void* tag) GRPC_OVERRIDE { + GPR_ASSERT(!ctx_->sent_initial_metadata_); + + meta_buf_.set_output_tag(tag); + meta_buf_.SendInitialMetadata(ctx_->initial_metadata_); + ctx_->sent_initial_metadata_ = true; + call_.PerformOps(&meta_buf_); + } + + void Finish(const W& msg, const Status& status, void* tag) { + finish_buf_.set_output_tag(tag); + if (!ctx_->sent_initial_metadata_) { + finish_buf_.SendInitialMetadata(ctx_->initial_metadata_); + ctx_->sent_initial_metadata_ = true; + } + // The response is dropped if the status is not OK. + if (status.ok()) { + finish_buf_.ServerSendStatus(ctx_->trailing_metadata_, + finish_buf_.SendMessage(msg)); + } else { + finish_buf_.ServerSendStatus(ctx_->trailing_metadata_, status); + } + call_.PerformOps(&finish_buf_); + } + + void FinishWithError(const Status& status, void* tag) { + GPR_ASSERT(!status.ok()); + finish_buf_.set_output_tag(tag); + if (!ctx_->sent_initial_metadata_) { + finish_buf_.SendInitialMetadata(ctx_->initial_metadata_); + ctx_->sent_initial_metadata_ = true; + } + finish_buf_.ServerSendStatus(ctx_->trailing_metadata_, status); + call_.PerformOps(&finish_buf_); + } + + private: + void BindCall(Call* call) GRPC_OVERRIDE { call_ = *call; } + + Call call_; + ServerContext* ctx_; + CallOpSet meta_buf_; + CallOpSet finish_buf_; +}; + +} // namespace grpc + +#endif // GRPCXX_SUPPORT_ASYNC_UNARY_CALL_H diff --git a/include/grpc++/support/byte_buffer.h b/include/grpc++/support/byte_buffer.h new file mode 100644 index 00000000..c4137039 --- /dev/null +++ b/include/grpc++/support/byte_buffer.h @@ -0,0 +1,108 @@ +/* + * + * Copyright 2015, 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_SUPPORT_BYTE_BUFFER_H +#define GRPCXX_SUPPORT_BYTE_BUFFER_H + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace grpc { + +/// A sequence of bytes. +class ByteBuffer GRPC_FINAL { + public: + /// Constuct an empty buffer. + ByteBuffer() : buffer_(nullptr) {} + + /// Construct buffer from \a slices, of which there are \a nslices. + ByteBuffer(const Slice* slices, size_t nslices); + + ~ByteBuffer(); + + /// Dump (read) the buffer contents into \a slices. + void Dump(std::vector* slices) const; + + /// Remove all data. + void Clear(); + + /// Buffer size in bytes. + size_t Length() const; + + private: + friend class SerializationTraits; + + ByteBuffer(const ByteBuffer&); + ByteBuffer& operator=(const ByteBuffer&); + + // takes ownership + void set_buffer(grpc_byte_buffer* buf) { + if (buffer_) { + gpr_log(GPR_ERROR, "Overriding existing buffer"); + Clear(); + } + buffer_ = buf; + } + + // For \a SerializationTraits's usage. + grpc_byte_buffer* buffer() const { return buffer_; } + + grpc_byte_buffer* buffer_; +}; + +template <> +class SerializationTraits { + public: + static Status Deserialize(grpc_byte_buffer* byte_buffer, ByteBuffer* dest, + int max_message_size) { + dest->set_buffer(byte_buffer); + return Status::OK; + } + static Status Serialize(const ByteBuffer& source, grpc_byte_buffer** buffer, + bool* own_buffer) { + *buffer = source.buffer(); + *own_buffer = false; + return Status::OK; + } +}; + +} // namespace grpc + +#endif // GRPCXX_SUPPORT_BYTE_BUFFER_H diff --git a/include/grpc++/support/channel_arguments.h b/include/grpc++/support/channel_arguments.h new file mode 100644 index 00000000..9957712a --- /dev/null +++ b/include/grpc++/support/channel_arguments.h @@ -0,0 +1,98 @@ +/* + * + * Copyright 2015, 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_SUPPORT_CHANNEL_ARGUMENTS_H +#define GRPCXX_SUPPORT_CHANNEL_ARGUMENTS_H + +#include +#include + +#include +#include +#include + +namespace grpc { +namespace testing { +class ChannelArgumentsTest; +} // namespace testing + +/// Options for channel creation. The user can use generic setters to pass +/// key value pairs down to c channel creation code. For grpc related options, +/// concrete setters are provided. +class ChannelArguments { + public: + ChannelArguments() {} + ~ChannelArguments() {} + + ChannelArguments(const ChannelArguments& other); + ChannelArguments& operator=(ChannelArguments other) { + Swap(other); + return *this; + } + + void Swap(ChannelArguments& other); + + /// Populates this instance with the arguments from \a channel_args. Does not + /// take ownership of \a channel_args. + /// + /// Note that the underlying arguments are shared. Changes made to either \a + /// channel_args or this instance would be reflected on both. + void SetChannelArgs(grpc_channel_args* channel_args) const; + + // gRPC specific channel argument setters + /// Set target name override for SSL host name checking. + void SetSslTargetNameOverride(const grpc::string& name); + // TODO(yangg) add flow control options + /// Set the compression algorithm for the channel. + void SetCompressionAlgorithm(grpc_compression_algorithm algorithm); + + // Generic channel argument setters. Only for advanced use cases. + /// Set an integer argument \a value under \a key. + void SetInt(const grpc::string& key, int value); + /// Set a textual argument \a value under \a key. + void SetString(const grpc::string& key, const grpc::string& value); + + private: + friend class SecureCredentials; + friend class testing::ChannelArgumentsTest; + + // Returns empty string when it is not set. + grpc::string GetSslTargetNameOverride() const; + + std::vector args_; + std::list strings_; +}; + +} // namespace grpc + +#endif // GRPCXX_SUPPORT_CHANNEL_ARGUMENTS_H diff --git a/include/grpc++/support/config.h b/include/grpc++/support/config.h new file mode 100644 index 00000000..836bd472 --- /dev/null +++ b/include/grpc++/support/config.h @@ -0,0 +1,116 @@ +/* + * + * Copyright 2015, 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_SUPPORT_CONFIG_H +#define GRPCXX_SUPPORT_CONFIG_H + +#if !defined(GRPC_NO_AUTODETECT_PLATFORM) + +#ifdef _MSC_VER +// Visual Studio 2010 is 1600. +#if _MSC_VER < 1600 +#error "gRPC is only supported with Visual Studio starting at 2010" +// Visual Studio 2013 is 1800. +#elif _MSC_VER < 1800 +#define GRPC_CXX0X_NO_FINAL 1 +#define GRPC_CXX0X_NO_OVERRIDE 1 +#define GRPC_CXX0X_NO_CHRONO 1 +#define GRPC_CXX0X_NO_THREAD 1 +#endif +#endif // Visual Studio + +#ifndef __clang__ +#ifdef __GNUC__ +// nullptr was added in gcc 4.6 +#if (__GNUC__ * 100 + __GNUC_MINOR__ < 406) +#define GRPC_CXX0X_NO_NULLPTR 1 +#endif +// final and override were added in gcc 4.7 +#if (__GNUC__ * 100 + __GNUC_MINOR__ < 407) +#define GRPC_CXX0X_NO_FINAL 1 +#define GRPC_CXX0X_NO_OVERRIDE 1 +#endif +#endif +#endif + +#endif + +#ifdef GRPC_CXX0X_NO_FINAL +#define GRPC_FINAL +#else +#define GRPC_FINAL final +#endif + +#ifdef GRPC_CXX0X_NO_OVERRIDE +#define GRPC_OVERRIDE +#else +#define GRPC_OVERRIDE override +#endif + +#ifdef GRPC_CXX0X_NO_NULLPTR +#include +namespace grpc { +const class { + public: + template + operator T *() const { + return static_cast(0); + } + template + operator std::unique_ptr() const { + return std::unique_ptr(static_cast(0)); + } + template + operator std::shared_ptr() const { + return std::shared_ptr(static_cast(0)); + } + operator bool() const { return false; } + + private: + void operator&() const = delete; +} nullptr = {}; +} +#endif + +#ifndef GRPC_CUSTOM_STRING +#include +#define GRPC_CUSTOM_STRING std::string +#endif + +namespace grpc { + +typedef GRPC_CUSTOM_STRING string; + +} // namespace grpc + +#endif // GRPCXX_SUPPORT_CONFIG_H diff --git a/include/grpc++/support/config_protobuf.h b/include/grpc++/support/config_protobuf.h new file mode 100644 index 00000000..8235590d --- /dev/null +++ b/include/grpc++/support/config_protobuf.h @@ -0,0 +1,72 @@ +/* + * + * Copyright 2015, 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_SUPPORT_CONFIG_PROTOBUF_H +#define GRPCXX_SUPPORT_CONFIG_PROTOBUF_H + +#ifndef GRPC_CUSTOM_PROTOBUF_INT64 +#include +#define GRPC_CUSTOM_PROTOBUF_INT64 ::google::protobuf::int64 +#endif + +#ifndef GRPC_CUSTOM_MESSAGE +#include +#define GRPC_CUSTOM_MESSAGE ::google::protobuf::Message +#endif + +#ifndef GRPC_CUSTOM_ZEROCOPYOUTPUTSTREAM +#include +#include +#define GRPC_CUSTOM_ZEROCOPYOUTPUTSTREAM \ + ::google::protobuf::io::ZeroCopyOutputStream +#define GRPC_CUSTOM_ZEROCOPYINPUTSTREAM \ + ::google::protobuf::io::ZeroCopyInputStream +#define GRPC_CUSTOM_CODEDINPUTSTREAM ::google::protobuf::io::CodedInputStream +#endif + +namespace grpc { +namespace protobuf { + +typedef GRPC_CUSTOM_MESSAGE Message; +typedef GRPC_CUSTOM_PROTOBUF_INT64 int64; + +namespace io { +typedef GRPC_CUSTOM_ZEROCOPYOUTPUTSTREAM ZeroCopyOutputStream; +typedef GRPC_CUSTOM_ZEROCOPYINPUTSTREAM ZeroCopyInputStream; +typedef GRPC_CUSTOM_CODEDINPUTSTREAM CodedInputStream; +} // namespace io + +} // namespace protobuf +} // namespace grpc + +#endif // GRPCXX_SUPPORT_CONFIG_PROTOBUF_H diff --git a/include/grpc++/support/slice.h b/include/grpc++/support/slice.h new file mode 100644 index 00000000..456379cc --- /dev/null +++ b/include/grpc++/support/slice.h @@ -0,0 +1,88 @@ +/* + * + * Copyright 2015, 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_SUPPORT_SLICE_H +#define GRPCXX_SUPPORT_SLICE_H + +#include +#include + +namespace grpc { + +/// A wrapper around \a grpc_slice. +/// +/// A slice represents a contiguous reference counted array of bytes. +/// It is cheap to take references to a slice, and it is cheap to create a +/// slice pointing to a subset of another slice. +class Slice GRPC_FINAL { + public: + /// Construct an empty slice. + Slice(); + // Destructor - drops one reference. + ~Slice(); + + enum AddRef { ADD_REF }; + /// Construct a slice from \a slice, adding a reference. + Slice(gpr_slice slice, AddRef); + + enum StealRef { STEAL_REF }; + /// Construct a slice from \a slice, stealing a reference. + Slice(gpr_slice slice, StealRef); + + /// Copy constructor, adds a reference. + Slice(const Slice& other); + + /// Assignment, reference count is unchanged. + Slice& operator=(Slice other) { + std::swap(slice_, other.slice_); + return *this; + } + + /// Byte size. + size_t size() const { return GPR_SLICE_LENGTH(slice_); } + + /// Raw pointer to the beginning (first element) of the slice. + const gpr_uint8* begin() const { return GPR_SLICE_START_PTR(slice_); } + + /// Raw pointer to the end (one byte \em past the last element) of the slice. + const gpr_uint8* end() const { return GPR_SLICE_END_PTR(slice_); } + + private: + friend class ByteBuffer; + + gpr_slice slice_; +}; + +} // namespace grpc + +#endif // GRPCXX_SUPPORT_SLICE_H diff --git a/include/grpc++/support/status.h b/include/grpc++/support/status.h new file mode 100644 index 00000000..e59bac92 --- /dev/null +++ b/include/grpc++/support/status.h @@ -0,0 +1,76 @@ +/* + * + * Copyright 2015, 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_SUPPORT_STATUS_H +#define GRPCXX_SUPPORT_STATUS_H + +#include +#include + +namespace grpc { + +/// Did it work? If it didn't, why? +/// +/// See \a grpc::StatusCode for details on the available code and their meaning. +class Status { + public: + /// Construct an OK instance. + Status() : code_(StatusCode::OK) {} + + /// Construct an instance with associated \a code and \a details (also + // referred to as "error_message"). + Status(StatusCode code, const grpc::string& details) + : code_(code), details_(details) {} + + // Pre-defined special status objects. + /// An OK pre-defined instance. + static const Status& OK; + /// A CANCELLED pre-defined instance. + static const Status& CANCELLED; + + /// Return the instance's error code. + StatusCode error_code() const { return code_; } + /// Return the instance's error message. + grpc::string error_message() const { return details_; } + + /// Is the status OK? + bool ok() const { return code_ == StatusCode::OK; } + + private: + StatusCode code_; + grpc::string details_; +}; + +} // namespace grpc + +#endif // GRPCXX_SUPPORT_STATUS_H diff --git a/include/grpc++/support/status_code_enum.h b/include/grpc++/support/status_code_enum.h new file mode 100644 index 00000000..ee05b40b --- /dev/null +++ b/include/grpc++/support/status_code_enum.h @@ -0,0 +1,152 @@ +/* + * + * Copyright 2015, 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_SUPPORT_STATUS_CODE_ENUM_H +#define GRPCXX_SUPPORT_STATUS_CODE_ENUM_H + +namespace grpc { + +enum StatusCode { + /// Not an error; returned on success. + OK = 0, + + /// The operation was cancelled (typically by the caller). + CANCELLED = 1, + + /// Unknown error. An example of where this error may be returned is if a + /// Status value received from another address space belongs to an error-space + /// that is not known in this address space. Also errors raised by APIs that + /// do not return enough error information may be converted to this error. + UNKNOWN = 2, + + /// Client specified an invalid argument. Note that this differs from + /// FAILED_PRECONDITION. INVALID_ARGUMENT indicates arguments that are + /// problematic regardless of the state of the system (e.g., a malformed file + /// name). + INVALID_ARGUMENT = 3, + + /// Deadline expired before operation could complete. For operations that + /// change the state of the system, this error may be returned even if the + /// operation has completed successfully. For example, a successful response + /// from a server could have been delayed long enough for the deadline to + /// expire. + DEADLINE_EXCEEDED = 4, + + /// Some requested entity (e.g., file or directory) was not found. + NOT_FOUND = 5, + + /// Some entity that we attempted to create (e.g., file or directory) already + /// exists. + ALREADY_EXISTS = 6, + + /// The caller does not have permission to execute the specified operation. + /// PERMISSION_DENIED must not be used for rejections caused by exhausting + /// some resource (use RESOURCE_EXHAUSTED instead for those errors). + /// PERMISSION_DENIED must not be used if the caller can not be identified + /// (use UNAUTHENTICATED instead for those errors). + PERMISSION_DENIED = 7, + + /// The request does not have valid authentication credentials for the + /// operation. + UNAUTHENTICATED = 16, + + /// Some resource has been exhausted, perhaps a per-user quota, or perhaps the + /// entire file system is out of space. + RESOURCE_EXHAUSTED = 8, + + /// Operation was rejected because the system is not in a state required for + /// the operation's execution. For example, directory to be deleted may be + /// non-empty, an rmdir operation is applied to a non-directory, etc. + /// + /// A litmus test that may help a service implementor in deciding + /// between FAILED_PRECONDITION, ABORTED, and UNAVAILABLE: + /// (a) Use UNAVAILABLE if the client can retry just the failing call. + /// (b) Use ABORTED if the client should retry at a higher-level + /// (e.g., restarting a read-modify-write sequence). + /// (c) Use FAILED_PRECONDITION if the client should not retry until + /// the system state has been explicitly fixed. E.g., if an "rmdir" + /// fails because the directory is non-empty, FAILED_PRECONDITION + /// should be returned since the client should not retry unless + /// they have first fixed up the directory by deleting files from it. + /// (d) Use FAILED_PRECONDITION if the client performs conditional + /// REST Get/Update/Delete on a resource and the resource on the + /// server does not match the condition. E.g., conflicting + /// read-modify-write on the same resource. + FAILED_PRECONDITION = 9, + + /// The operation was aborted, typically due to a concurrency issue like + /// sequencer check failures, transaction aborts, etc. + /// + /// See litmus test above for deciding between FAILED_PRECONDITION, ABORTED, + /// and UNAVAILABLE. + ABORTED = 10, + + /// Operation was attempted past the valid range. E.g., seeking or reading + /// past end of file. + /// + /// Unlike INVALID_ARGUMENT, this error indicates a problem that may be fixed + /// if the system state changes. For example, a 32-bit file system will + /// generate INVALID_ARGUMENT if asked to read at an offset that is not in the + /// range [0,2^32-1], but it will generate OUT_OF_RANGE if asked to read from + /// an offset past the current file size. + /// + /// There is a fair bit of overlap between FAILED_PRECONDITION and + /// OUT_OF_RANGE. We recommend using OUT_OF_RANGE (the more specific error) + /// when it applies so that callers who are iterating through a space can + /// easily look for an OUT_OF_RANGE error to detect when they are done. + OUT_OF_RANGE = 11, + + /// Operation is not implemented or not supported/enabled in this service. + UNIMPLEMENTED = 12, + + /// Internal errors. Means some invariants expected by underlying System has + /// been broken. If you see one of these errors, Something is very broken. + INTERNAL = 13, + + /// The service is currently unavailable. This is a most likely a transient + /// condition and may be corrected by retrying with a backoff. + /// + /// See litmus test above for deciding between FAILED_PRECONDITION, ABORTED, + /// and UNAVAILABLE. + UNAVAILABLE = 14, + + /// Unrecoverable data loss or corruption. + DATA_LOSS = 15, + + /// Force users to include a default branch: + DO_NOT_USE = -1 +}; + +} // namespace grpc + +#endif // GRPCXX_SUPPORT_STATUS_CODE_ENUM_H diff --git a/include/grpc++/support/string_ref.h b/include/grpc++/support/string_ref.h new file mode 100644 index 00000000..a17e167d --- /dev/null +++ b/include/grpc++/support/string_ref.h @@ -0,0 +1,123 @@ +/* + * + * Copyright 2015, 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_SUPPORT_STRING_REF_H +#define GRPCXX_SUPPORT_STRING_REF_H + +#include +#include + +#include + +namespace grpc { + +/// This class is a non owning reference to a string. +/// +/// It should be a strict subset of the upcoming std::string_ref. +/// +/// \see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3442.html +/// +/// The constexpr is dropped or replaced with const for legacy compiler +/// compatibility. +class string_ref { + public: + // types + typedef const char* const_iterator; + typedef std::reverse_iterator const_reverse_iterator; + + // constants + const static size_t npos = size_t(-1); + + // construct/copy. + string_ref() : data_(nullptr), length_(0) {} + string_ref(const string_ref& other) + : data_(other.data_), length_(other.length_) {} + string_ref& operator=(const string_ref& rhs); + string_ref(const char* s); + string_ref(const char* s, size_t l) : data_(s), length_(l) {} + string_ref(const grpc::string& s) : data_(s.data()), length_(s.length()) {} + + // iterators + const_iterator begin() const { return data_; } + const_iterator end() const { return data_ + length_; } + const_iterator cbegin() const { return data_; } + const_iterator cend() const { return data_ + length_; } + const_reverse_iterator rbegin() const { + return const_reverse_iterator(end()); + } + const_reverse_iterator rend() const { + return const_reverse_iterator(begin()); + } + const_reverse_iterator crbegin() const { + return const_reverse_iterator(end()); + } + const_reverse_iterator crend() const { + return const_reverse_iterator(begin()); + } + + // capacity + size_t size() const { return length_; } + size_t length() const { return length_; } + size_t max_size() const { return length_; } + bool empty() const { return length_ == 0; } + + // element access + const char* data() const { return data_; } + + // string operations + int compare(string_ref x) const; + bool starts_with(string_ref x) const; + bool ends_with(string_ref x) const; + size_t find(string_ref s) const; + size_t find(char c) const; + + string_ref substr(size_t pos, size_t n = npos) const; + + private: + const char* data_; + size_t length_; +}; + +// Comparison operators +bool operator==(string_ref x, string_ref y); +bool operator!=(string_ref x, string_ref y); +bool operator<(string_ref x, string_ref y); +bool operator>(string_ref x, string_ref y); +bool operator<=(string_ref x, string_ref y); +bool operator>=(string_ref x, string_ref y); + +std::ostream& operator<<(std::ostream& stream, const string_ref& string); + +} // namespace grpc + +#endif // GRPCXX_SUPPORT_STRING_REF_H diff --git a/include/grpc++/support/stub_options.h b/include/grpc++/support/stub_options.h new file mode 100644 index 00000000..973aa9bc --- /dev/null +++ b/include/grpc++/support/stub_options.h @@ -0,0 +1,43 @@ +/* + * + * Copyright 2015, 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_SUPPORT_STUB_OPTIONS_H +#define GRPCXX_SUPPORT_STUB_OPTIONS_H + +namespace grpc { + +class StubOptions {}; + +} // namespace grpc + +#endif // GRPCXX_SUPPORT_STUB_OPTIONS_H diff --git a/include/grpc++/support/sync_stream.h b/include/grpc++/support/sync_stream.h new file mode 100644 index 00000000..51436333 --- /dev/null +++ b/include/grpc++/support/sync_stream.h @@ -0,0 +1,415 @@ +/* + * + * Copyright 2015, 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_SUPPORT_SYNC_STREAM_H +#define GRPCXX_SUPPORT_SYNC_STREAM_H + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace grpc { + +/// Common interface for all synchronous client side streaming. +class ClientStreamingInterface { + public: + virtual ~ClientStreamingInterface() {} + + /// Wait until the stream finishes, and return the final status. When the + /// client side declares it has no more message to send, either implicitly or + /// by calling \a WritesDone(), it needs to make sure there is no more message + /// to be received from the server, either implicitly or by getting a false + /// from a \a Read(). + /// + /// This function will return either: + /// - when all incoming messages have been read and the server has returned + /// status. + /// - OR when the server has returned a non-OK status. + virtual Status Finish() = 0; +}; + +/// An interface that yields a sequence of messages of type \a R. +template +class ReaderInterface { + public: + virtual ~ReaderInterface() {} + + /// Blocking read a message and parse to \a msg. Returns \a true on success. + /// + /// \param[out] msg The read message. + /// + /// \return \a false when there will be no more incoming messages, either + /// because the other side has called \a WritesDone() or the stream has failed + /// (or been cancelled). + virtual bool Read(R* msg) = 0; +}; + +/// An interface that can be fed a sequence of messages of type \a W. +template +class WriterInterface { + public: + virtual ~WriterInterface() {} + + /// Blocking write \a msg to the stream with options. + /// + /// \param msg The message to be written to the stream. + /// \param options Options affecting the write operation. + /// + /// \return \a true on success, \a false when the stream has been closed. + virtual bool Write(const W& msg, const WriteOptions& options) = 0; + + /// Blocking write \a msg to the stream with default options. + /// + /// \param msg The message to be written to the stream. + /// + /// \return \a true on success, \a false when the stream has been closed. + inline bool Write(const W& msg) { return Write(msg, WriteOptions()); } +}; + +/// Client-side interface for streaming reads of message of type \a R. +template +class ClientReaderInterface : public ClientStreamingInterface, + public ReaderInterface { + public: + /// Blocking wait for initial metadata from server. The received metadata + /// can only be accessed after this call returns. Should only be called before + /// the first read. Calling this method is optional, and if it is not called + /// the metadata will be available in ClientContext after the first read. + virtual void WaitForInitialMetadata() = 0; +}; + +template +class ClientReader GRPC_FINAL : public ClientReaderInterface { + public: + /// Blocking create a stream and write the first request out. + template + ClientReader(Channel* channel, const RpcMethod& method, + ClientContext* context, const W& request) + : context_(context), call_(channel->CreateCall(method, context, &cq_)) { + CallOpSet ops; + ops.SendInitialMetadata(context->send_initial_metadata_); + // TODO(ctiller): don't assert + GPR_ASSERT(ops.SendMessage(request).ok()); + ops.ClientSendClose(); + call_.PerformOps(&ops); + cq_.Pluck(&ops); + } + + void WaitForInitialMetadata() { + GPR_ASSERT(!context_->initial_metadata_received_); + + CallOpSet ops; + ops.RecvInitialMetadata(context_); + call_.PerformOps(&ops); + cq_.Pluck(&ops); /// status ignored + } + + bool Read(R* msg) GRPC_OVERRIDE { + CallOpSet> ops; + if (!context_->initial_metadata_received_) { + ops.RecvInitialMetadata(context_); + } + ops.RecvMessage(msg); + call_.PerformOps(&ops); + return cq_.Pluck(&ops) && ops.got_message; + } + + Status Finish() GRPC_OVERRIDE { + CallOpSet ops; + Status status; + ops.ClientRecvStatus(context_, &status); + call_.PerformOps(&ops); + GPR_ASSERT(cq_.Pluck(&ops)); + return status; + } + + private: + ClientContext* context_; + CompletionQueue cq_; + Call call_; +}; + +/// Client-side interface for streaming writes of message of type \a W. +template +class ClientWriterInterface : public ClientStreamingInterface, + public WriterInterface { + public: + /// Half close writing from the client. + /// Block until writes are completed. + /// + /// \return Whether the writes were successful. + virtual bool WritesDone() = 0; +}; + +template +class ClientWriter : public ClientWriterInterface { + public: + /// Blocking create a stream. + template + ClientWriter(Channel* channel, const RpcMethod& method, + ClientContext* context, R* response) + : context_(context), call_(channel->CreateCall(method, context, &cq_)) { + finish_ops_.RecvMessage(response); + + CallOpSet ops; + ops.SendInitialMetadata(context->send_initial_metadata_); + call_.PerformOps(&ops); + cq_.Pluck(&ops); + } + + using WriterInterface::Write; + bool Write(const W& msg, const WriteOptions& options) GRPC_OVERRIDE { + CallOpSet ops; + if (!ops.SendMessage(msg, options).ok()) { + return false; + } + call_.PerformOps(&ops); + return cq_.Pluck(&ops); + } + + bool WritesDone() GRPC_OVERRIDE { + CallOpSet ops; + ops.ClientSendClose(); + call_.PerformOps(&ops); + return cq_.Pluck(&ops); + } + + /// Read the final response and wait for the final status. + Status Finish() GRPC_OVERRIDE { + Status status; + finish_ops_.ClientRecvStatus(context_, &status); + call_.PerformOps(&finish_ops_); + GPR_ASSERT(cq_.Pluck(&finish_ops_)); + return status; + } + + private: + ClientContext* context_; + CallOpSet finish_ops_; + CompletionQueue cq_; + Call call_; +}; + +/// Client-side interface for bi-directional streaming. +template +class ClientReaderWriterInterface : public ClientStreamingInterface, + public WriterInterface, + public ReaderInterface { + public: + /// Blocking wait for initial metadata from server. The received metadata + /// can only be accessed after this call returns. Should only be called before + /// the first read. Calling this method is optional, and if it is not called + /// the metadata will be available in ClientContext after the first read. + virtual void WaitForInitialMetadata() = 0; + + /// Block until writes are completed. + /// + /// \return Whether the writes were successful. + virtual bool WritesDone() = 0; +}; + +template +class ClientReaderWriter GRPC_FINAL : public ClientReaderWriterInterface { + public: + /// Blocking create a stream. + ClientReaderWriter(Channel* channel, const RpcMethod& method, + ClientContext* context) + : context_(context), call_(channel->CreateCall(method, context, &cq_)) { + CallOpSet ops; + ops.SendInitialMetadata(context->send_initial_metadata_); + call_.PerformOps(&ops); + cq_.Pluck(&ops); + } + + void WaitForInitialMetadata() { + GPR_ASSERT(!context_->initial_metadata_received_); + + CallOpSet ops; + ops.RecvInitialMetadata(context_); + call_.PerformOps(&ops); + cq_.Pluck(&ops); // status ignored + } + + bool Read(R* msg) GRPC_OVERRIDE { + CallOpSet> ops; + if (!context_->initial_metadata_received_) { + ops.RecvInitialMetadata(context_); + } + ops.RecvMessage(msg); + call_.PerformOps(&ops); + return cq_.Pluck(&ops) && ops.got_message; + } + + using WriterInterface::Write; + bool Write(const W& msg, const WriteOptions& options) GRPC_OVERRIDE { + CallOpSet ops; + if (!ops.SendMessage(msg, options).ok()) return false; + call_.PerformOps(&ops); + return cq_.Pluck(&ops); + } + + bool WritesDone() GRPC_OVERRIDE { + CallOpSet ops; + ops.ClientSendClose(); + call_.PerformOps(&ops); + return cq_.Pluck(&ops); + } + + Status Finish() GRPC_OVERRIDE { + CallOpSet ops; + Status status; + ops.ClientRecvStatus(context_, &status); + call_.PerformOps(&ops); + GPR_ASSERT(cq_.Pluck(&ops)); + return status; + } + + private: + ClientContext* context_; + CompletionQueue cq_; + Call call_; +}; + +template +class ServerReader GRPC_FINAL : public ReaderInterface { + public: + ServerReader(Call* call, ServerContext* ctx) : call_(call), ctx_(ctx) {} + + void SendInitialMetadata() { + GPR_ASSERT(!ctx_->sent_initial_metadata_); + + CallOpSet ops; + ops.SendInitialMetadata(ctx_->initial_metadata_); + ctx_->sent_initial_metadata_ = true; + call_->PerformOps(&ops); + call_->cq()->Pluck(&ops); + } + + bool Read(R* msg) GRPC_OVERRIDE { + CallOpSet> ops; + ops.RecvMessage(msg); + call_->PerformOps(&ops); + return call_->cq()->Pluck(&ops) && ops.got_message; + } + + private: + Call* const call_; + ServerContext* const ctx_; +}; + +template +class ServerWriter GRPC_FINAL : public WriterInterface { + public: + ServerWriter(Call* call, ServerContext* ctx) : call_(call), ctx_(ctx) {} + + void SendInitialMetadata() { + GPR_ASSERT(!ctx_->sent_initial_metadata_); + + CallOpSet ops; + ops.SendInitialMetadata(ctx_->initial_metadata_); + ctx_->sent_initial_metadata_ = true; + call_->PerformOps(&ops); + call_->cq()->Pluck(&ops); + } + + using WriterInterface::Write; + bool Write(const W& msg, const WriteOptions& options) GRPC_OVERRIDE { + CallOpSet ops; + if (!ops.SendMessage(msg, options).ok()) { + return false; + } + if (!ctx_->sent_initial_metadata_) { + ops.SendInitialMetadata(ctx_->initial_metadata_); + ctx_->sent_initial_metadata_ = true; + } + call_->PerformOps(&ops); + return call_->cq()->Pluck(&ops); + } + + private: + Call* const call_; + ServerContext* const ctx_; +}; + +/// Server-side interface for bi-directional streaming. +template +class ServerReaderWriter GRPC_FINAL : public WriterInterface, + public ReaderInterface { + public: + ServerReaderWriter(Call* call, ServerContext* ctx) : call_(call), ctx_(ctx) {} + + void SendInitialMetadata() { + GPR_ASSERT(!ctx_->sent_initial_metadata_); + + CallOpSet ops; + ops.SendInitialMetadata(ctx_->initial_metadata_); + ctx_->sent_initial_metadata_ = true; + call_->PerformOps(&ops); + call_->cq()->Pluck(&ops); + } + + bool Read(R* msg) GRPC_OVERRIDE { + CallOpSet> ops; + ops.RecvMessage(msg); + call_->PerformOps(&ops); + return call_->cq()->Pluck(&ops) && ops.got_message; + } + + using WriterInterface::Write; + bool Write(const W& msg, const WriteOptions& options) GRPC_OVERRIDE { + CallOpSet ops; + if (!ops.SendMessage(msg, options).ok()) { + return false; + } + if (!ctx_->sent_initial_metadata_) { + ops.SendInitialMetadata(ctx_->initial_metadata_); + ctx_->sent_initial_metadata_ = true; + } + call_->PerformOps(&ops); + return call_->cq()->Pluck(&ops); + } + + private: + Call* const call_; + ServerContext* const ctx_; +}; + +} // namespace grpc + +#endif // GRPCXX_SUPPORT_SYNC_STREAM_H diff --git a/include/grpc++/support/time.h b/include/grpc++/support/time.h new file mode 100644 index 00000000..2d4196b9 --- /dev/null +++ b/include/grpc++/support/time.h @@ -0,0 +1,110 @@ +/* + * + * Copyright 2015, 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_SUPPORT_TIME_H +#define GRPCXX_SUPPORT_TIME_H + +#include + +namespace grpc { + +/* If you are trying to use CompletionQueue::AsyncNext with a time class that + isn't either gpr_timespec or std::chrono::system_clock::time_point, you + will most likely be looking at this comment as your compiler will have + fired an error below. In order to fix this issue, you have two potential + solutions: + + 1. Use gpr_timespec or std::chrono::system_clock::time_point instead + 2. Specialize the TimePoint class with whichever time class that you + want to use here. See below for two examples of how to do this. + */ + +template +class TimePoint { + public: + TimePoint(const T& time) { you_need_a_specialization_of_TimePoint(); } + gpr_timespec raw_time() { + gpr_timespec t; + return t; + } + + private: + void you_need_a_specialization_of_TimePoint(); +}; + +template <> +class TimePoint { + public: + TimePoint(const gpr_timespec& time) : time_(time) {} + gpr_timespec raw_time() { return time_; } + + private: + gpr_timespec time_; +}; + +} // namespace grpc + +#ifndef GRPC_CXX0X_NO_CHRONO + +#include + +#include + +namespace grpc { + +// from and to should be absolute time. +void Timepoint2Timespec(const std::chrono::system_clock::time_point& from, + gpr_timespec* to); +void TimepointHR2Timespec( + const std::chrono::high_resolution_clock::time_point& from, + gpr_timespec* to); + +std::chrono::system_clock::time_point Timespec2Timepoint(gpr_timespec t); + +template <> +class TimePoint { + public: + TimePoint(const std::chrono::system_clock::time_point& time) { + Timepoint2Timespec(time, &time_); + } + gpr_timespec raw_time() const { return time_; } + + private: + gpr_timespec time_; +}; + +} // namespace grpc + +#endif // !GRPC_CXX0X_NO_CHRONO + +#endif // GRPCXX_SUPPORT_TIME_H diff --git a/include/grpc/byte_buffer.h b/include/grpc/byte_buffer.h new file mode 100644 index 00000000..1433ffdf --- /dev/null +++ b/include/grpc/byte_buffer.h @@ -0,0 +1,117 @@ +/* + * + * Copyright 2015, 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_BYTE_BUFFER_H +#define GRPC_BYTE_BUFFER_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + GRPC_BB_RAW + /* Future types may include GRPC_BB_PROTOBUF, etc. */ +} grpc_byte_buffer_type; + +struct grpc_byte_buffer { + void *reserved; + grpc_byte_buffer_type type; + union { + struct { + void *reserved[8]; + } reserved; + struct { + grpc_compression_algorithm compression; + gpr_slice_buffer slice_buffer; + } raw; + } data; +}; +typedef struct grpc_byte_buffer grpc_byte_buffer; + +/** Returns a RAW byte buffer instance over the given slices (up to \a nslices). + * + * Increases the reference count for all \a slices processed. The user is + * responsible for invoking grpc_byte_buffer_destroy on the returned instance.*/ +grpc_byte_buffer *grpc_raw_byte_buffer_create(gpr_slice *slices, + size_t nslices); + +/** Returns a *compressed* RAW byte buffer instance over the given slices (up to + * \a nslices). The \a compression argument defines the compression algorithm + * used to generate the data in \a slices. + * + * Increases the reference count for all \a slices processed. The user is + * responsible for invoking grpc_byte_buffer_destroy on the returned instance.*/ +grpc_byte_buffer *grpc_raw_compressed_byte_buffer_create( + gpr_slice *slices, size_t nslices, grpc_compression_algorithm compression); + +/** Copies input byte buffer \a bb. + * + * Increases the reference count of all the source slices. The user is + * responsible for calling grpc_byte_buffer_destroy over the returned copy. */ +grpc_byte_buffer *grpc_byte_buffer_copy(grpc_byte_buffer *bb); + +/** Returns the size of the given byte buffer, in bytes. */ +size_t grpc_byte_buffer_length(grpc_byte_buffer *bb); + +/** Destroys \a byte_buffer deallocating all its memory. */ +void grpc_byte_buffer_destroy(grpc_byte_buffer *byte_buffer); + +/** Reader for byte buffers. Iterates over slices in the byte buffer */ +struct grpc_byte_buffer_reader; +typedef struct grpc_byte_buffer_reader grpc_byte_buffer_reader; + +/** Initialize \a reader to read over \a buffer */ +void grpc_byte_buffer_reader_init(grpc_byte_buffer_reader *reader, + grpc_byte_buffer *buffer); + +/** Cleanup and destroy \a reader */ +void grpc_byte_buffer_reader_destroy(grpc_byte_buffer_reader *reader); + +/** Updates \a slice with the next piece of data from from \a reader and returns + * 1. Returns 0 at the end of the stream. Caller is responsible for calling + * gpr_slice_unref on the result. */ +int grpc_byte_buffer_reader_next(grpc_byte_buffer_reader *reader, + gpr_slice *slice); + +/** Returns a RAW byte buffer instance from the output of \a reader. */ +grpc_byte_buffer *grpc_raw_byte_buffer_from_reader( + grpc_byte_buffer_reader *reader); + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_BYTE_BUFFER_H */ diff --git a/include/grpc/byte_buffer_reader.h b/include/grpc/byte_buffer_reader.h new file mode 100644 index 00000000..b0e63a6d --- /dev/null +++ b/include/grpc/byte_buffer_reader.h @@ -0,0 +1,58 @@ +/* + * + * Copyright 2015, 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_BYTE_BUFFER_READER_H +#define GRPC_BYTE_BUFFER_READER_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct grpc_byte_buffer_reader { + grpc_byte_buffer *buffer_in; + grpc_byte_buffer *buffer_out; + /* Different current objects correspond to different types of byte buffers */ + union { + /* Index into a slice buffer's array of slices */ + unsigned index; + } current; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_BYTE_BUFFER_READER_H */ diff --git a/include/grpc/census.h b/include/grpc/census.h new file mode 100644 index 00000000..2f36665d --- /dev/null +++ b/include/grpc/census.h @@ -0,0 +1,488 @@ +/* + * + * Copyright 2015, 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. + * + */ + +/* RPC-internal Census API's. These are designed to be generic enough that + * they can (ultimately) be used in many different RPC systems (with differing + * implementations). */ + +#ifndef CENSUS_CENSUS_H +#define CENSUS_CENSUS_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Identify census features that can be enabled via census_initialize(). */ +enum census_features { + CENSUS_FEATURE_NONE = 0, /* Do not enable census. */ + CENSUS_FEATURE_TRACING = 1, /* Enable census tracing. */ + CENSUS_FEATURE_STATS = 2, /* Enable Census stats collection. */ + CENSUS_FEATURE_CPU = 4, /* Enable Census CPU usage collection. */ + CENSUS_FEATURE_ALL = + CENSUS_FEATURE_TRACING | CENSUS_FEATURE_STATS | CENSUS_FEATURE_CPU +}; + +/** Shutdown and startup census subsystem. The 'features' argument should be + * the OR (|) of census_features values. If census fails to initialize, then + * census_initialize() will return a non-zero value. It is an error to call + * census_initialize() more than once (without an intervening + * census_shutdown()). */ +int census_initialize(int features); +void census_shutdown(void); + +/** Return the features supported by the current census implementation (not all + * features will be available on all platforms). */ +int census_supported(void); + +/** Return the census features currently enabled. */ +int census_enabled(void); + +/** + Context is a handle used by census to represent the current tracing and + tagging information. Contexts should be propagated across RPC's. Contexts + are created by any of the census_start_*_op() functions. A context is + typically used as argument to most census functions. Conceptually, contexts + should be thought of as specific to single RPC/thread. The context can be + serialized for passing across the wire, via census_context_serialize(). +*/ +typedef struct census_context census_context; + +/* This function is called by the RPC subsystem whenever it needs to get a + * serialized form of the current census context (presumably to pass across + * the wire). Arguments: + * 'buffer': pointer to memory into which serialized context will be placed + * 'buf_size': size of 'buffer' + * + * Returns: the number of bytes used in buffer if successful, or 0 if the + * buffer is of insufficient size. + * + * TODO(aveitch): determine how best to communicate required/max buffer size + * so caller doesn't have to guess. */ +size_t census_context_serialize(const census_context *context, char *buffer, + size_t buf_size); + +/* Distributed traces can have a number of options. */ +enum census_trace_mask_values { + CENSUS_TRACE_MASK_NONE = 0, /* Default, empty flags */ + CENSUS_TRACE_MASK_IS_SAMPLED = 1 /* RPC tracing enabled for this context. */ +}; + +/** Get the current trace mask associated with this context. The value returned + will be the logical or of census_trace_mask_values values. */ +int census_trace_mask(const census_context *context); + +/** Set the trace mask associated with a context. */ +void census_set_trace_mask(int trace_mask); + +/* The concept of "operation" is a fundamental concept for Census. In an RPC + system, and operation typcially represents a single RPC, or a significant + sub-part thereof (e.g. a single logical "read" RPC to a distributed storage + system might do several other actions in parallel, from looking up metadata + indices to making requests of other services - each of these could be a + sub-operation with the larger RPC operation). Census uses operations for the + following: + + CPU accounting: If enabled, census will measure the thread CPU time + consumed between operation start and end times. + + Active operations: Census will maintain information on all currently + active operations. + + Distributed tracing: Each operation serves as a logical trace span. + + Stats collection: Stats are broken down by operation (e.g. latency + breakdown for each unique RPC path). + + The following functions serve to delineate the start and stop points for + each logical operation. */ + +/** + This structure represents a timestamp as used by census to record the time + at which an operation begins. +*/ +typedef struct { + /* Use gpr_timespec for default implementation. High performance + * implementations should use a cycle-counter based timestamp. */ + gpr_timespec ts; +} census_timestamp; + +/** + Mark the beginning of an RPC operation. The information required to call the + functions to record the start of RPC operations (both client and server) may + not be callable at the true start time of the operation, due to information + not being available (e.g. the census context data will not be available in a + server RPC until at least initial metadata has been processed). To ensure + correct CPU accounting and latency recording, RPC systems can call this + function to get the timestamp of operation beginning. This can later be used + as an argument to census_start_{client,server}_rpc_op(). NB: for correct + CPU accounting, the system must guarantee that the same thread is used + for all request processing after this function is called. + + @return A timestamp representing the operation start time. +*/ +census_timestamp census_start_rpc_op_timestamp(void); + +/** + Represent functions to map RPC name ID to service/method names. Census + breaks down all RPC stats by service and method names. We leave the + definition and format of these to the RPC system. For efficiency purposes, + we encode these as a single 64 bit identifier, and allow the RPC system to + provide a structure for functions that can convert these to service and + method strings. + + TODO(aveitch): Instead of providing this as an argument to the rpc_start_op() + functions, maybe it should be set once at census initialization. +*/ +typedef struct { + const char *(*get_rpc_service_name)(gpr_int64 id); + const char *(*get_rpc_method_name)(gpr_int64 id); +} census_rpc_name_info; + +/** + Start a client rpc operation. This function should be called as early in the + client RPC path as possible. This function will create a new context. If + the context argument is non-null, then the new context will inherit all + its properties, with the following changes: + - create a new operation ID for the new context, marking it as a child of + the previous operation. + - use the new RPC path and peer information for tracing and stats + collection purposes, rather than those from the original context + + If the context argument is NULL, then a new root context is created. This + is particularly important for tracing purposes (the trace spans generated + will be unassociated with any other trace spans, except those + downstream). The trace_mask will be used for tracing operations associated + with the new context. + + In some RPC systems (e.g. where load balancing is used), peer information + may not be available at the time the operation starts. In this case, use a + NULL value for peer, and set it later using the + census_set_rpc_client_peer() function. + + @param context The parent context. Can be NULL. + @param rpc_name_id The rpc name identifier to be associated with this RPC. + @param rpc_name_info Used to decode rpc_name_id. + @param peer RPC peer. If not available at the time, NULL can be used, + and a later census_set_rpc_client_peer() call made. + @param trace_mask An OR of census_trace_mask_values values. Only used in + the creation of a new root context (context == NULL). + @param start_time A timestamp returned from census_start_rpc_op_timestamp(). + Can be NULL. Used to set the true time the operation + begins. + + @return A new census context. + */ +census_context *census_start_client_rpc_op( + const census_context *context, gpr_int64 rpc_name_id, + const census_rpc_name_info *rpc_name_info, const char *peer, int trace_mask, + const census_timestamp *start_time); + +/** + Add peer information to a context representing a client RPC operation. +*/ +void census_set_rpc_client_peer(census_context *context, const char *peer); + +/** + Start a server RPC operation. Returns a new context to be used in future + census calls. If buffer is non-NULL, then the buffer contents should + represent the client context, as generated by census_context_serialize(). + If buffer is NULL, a new root context is created. + + @param buffer Buffer containing bytes output from census_context_serialize(). + @param rpc_name_id The rpc name identifier to be associated with this RPC. + @param rpc_name_info Used to decode rpc_name_id. + @param peer RPC peer. + @param trace_mask An OR of census_trace_mask_values values. Only used in + the creation of a new root context (buffer == NULL). + @param start_time A timestamp returned from census_start_rpc_op_timestamp(). + Can be NULL. Used to set the true time the operation + begins. + + @return A new census context. + */ +census_context *census_start_server_rpc_op( + const char *buffer, gpr_int64 rpc_name_id, + const census_rpc_name_info *rpc_name_info, const char *peer, int trace_mask, + census_timestamp *start_time); + +/** + Start a new, non-RPC operation. In general, this function works very + similarly to census_start_client_rpc_op, with the primary difference being + the replacement of host/path information with the more generic family/name + tags. If the context argument is non-null, then the new context will + inherit all its properties, with the following changes: + - create a new operation ID for the new context, marking it as a child of + the previous operation. + - use the family and name information for tracing and stats collection + purposes, rather than those from the original context + + If the context argument is NULL, then a new root context is created. This + is particularly important for tracing purposes (the trace spans generated + will be unassociated with any other trace spans, except those + downstream). The trace_mask will be used for tracing + operations associated with the new context. + + @param context The base context. Can be NULL. + @param family Family name to associate with the trace + @param name Name within family to associated with traces/stats + @param trace_mask An OR of census_trace_mask_values values. Only used if + context is NULL. + + @return A new census context. + */ +census_context *census_start_op(census_context *context, const char *family, + const char *name, int trace_mask); + +/** + End an operation started by any of the census_start_*_op*() calls. The + context used in this call will no longer be valid once this function + completes. + + @param context Context associated with operation which is ending. + @param status status associated with the operation. Not interpreted by + census. +*/ +void census_end_op(census_context *context, int status); + +#define CENSUS_TRACE_RECORD_START_OP ((gpr_uint32)0) +#define CENSUS_TRACE_RECORD_END_OP ((gpr_uint32)1) + +/** Insert a trace record into the trace stream. The record consists of an + arbitrary size buffer, the size of which is provided in 'n'. + @param context Trace context + @param type User-defined type to associate with trace entry. + @param buffer Pointer to buffer to use + @param n Number of bytes in buffer +*/ +void census_trace_print(census_context *context, gpr_uint32 type, + const char *buffer, size_t n); + +/** Trace record. */ +typedef struct { + census_timestamp timestamp; /* Time of record creation */ + gpr_uint64 trace_id; /* Trace ID associated with record */ + gpr_uint64 op_id; /* Operation ID associated with record */ + gpr_uint32 type; /* Type (as used in census_trace_print() */ + const char *buffer; /* Buffer (from census_trace_print() */ + size_t buf_size; /* Number of bytes inside buffer */ +} census_trace_record; + +/** Start a scan of existing trace records. While a scan is ongoing, addition + of new trace records will be blocked if the underlying trace buffers + fill up, so trace processing systems should endeavor to complete + reading as soon as possible. + @param consume if non-zero, indicates that reading records also "consumes" + the previously read record - i.e. releases space in the trace log + while scanning is ongoing. + @returns 0 on success, non-zero on failure (e.g. if a scan is already ongoing) +*/ +int census_trace_scan_start(int consume); + +/** Get a trace record. The data pointed to by the trace buffer is guaranteed + stable until the next census_get_trace_record() call (if the consume + argument to census_trace_scan_start was non-zero) or census_trace_scan_end() + is called (otherwise). + @param trace_record structure that will be filled in with oldest trace record. + @returns -1 if an error occurred (e.g. no previous call to + census_trace_scan_start()), 0 if there is no more trace data (and + trace_record will not be modified) or 1 otherwise. +*/ +int census_get_trace_record(census_trace_record *trace_record); + +/** End a scan previously started by census_trace_scan_start() */ +void census_trace_scan_end(); + +/* Max number of characters in tag key */ +#define CENSUS_MAX_TAG_KEY_LENGTH 20 +/* Max number of tag value characters */ +#define CENSUS_MAX_TAG_VALUE_LENGTH 50 + +/* A Census tag set is a collection of key:value string pairs; these form the + basis against which Census metrics will be recorded. Keys are unique within + a tag set. All contexts have an associated tag set. */ +typedef struct census_tag_set census_tag_set; + +/* Returns a pointer to a newly created, empty tag set. If size_hint > 0, + indicates that the tag set is intended to hold approximately that number + of tags. */ +census_tag_set *census_tag_set_create(size_t size_hint); + +/* Add a new tag key/value to an existing tag set; if the tag key already exists + in the tag set, then its value is overwritten with the new one. Can also be + used to delete a tag, by specifying a NULL value. If key is NULL, returns + the number of tags in the tag set. + Return values: + -1: invalid length key or value + non-negative value: the number of tags in the tag set. */ +int census_tag_set_add(census_tag_set *tags, const char *key, + const char *value); + +/* Destroys a tag set. This function must be called to prevent memory leaks. + Once called, the tag set cannot be used again. */ +void census_tag_set_destroy(census_tag_set *tags); + +/* Get a contexts tag set. */ +census_tag_set *census_context_tag_set(census_context *context); + +/* A read-only representation of a tag for use by census clients. */ +typedef struct { + size_t key_len; /* Number of bytes in tag key. */ + const char *key; /* A pointer to the tag key. May not be null-terminated. */ + size_t value_len; /* Number of bytes in tag value. */ + const char *value; /* Pointer to the tag value. May not be null-terminated. */ +} census_tag_const; + +/* Used to iterate through a tag sets contents. */ +typedef struct census_tag_set_iterator census_tag_set_iterator; + +/* Open a tag set for iteration. The tag set must not be modified while + iteration is ongoing. Returns an iterator for use in following functions. */ +census_tag_set_iterator *census_tag_set_open(census_tag_set *tags); + +/* Get the next tag in the tag set, by writing into the 'tag' argument. Returns + 1 if there is a "next" tag, 0 if there are no more tags. */ +int census_tag_set_next(census_tag_set_iterator *it, census_tag_const *tag); + +/* Close an iterator opened by census_tag_set_open(). The iterator will be + invalidated, and should not be used once close is called. */ +void census_tag_set_close(census_tag_set_iterator *it); + +/* Core stats collection API's. The following concepts are used: + * Aggregation: A collection of values. Census supports the following + aggregation types: + Sum - a single summation type. Typically used for keeping (e.g.) + counts of events. + Distribution - statistical distribution information, used for + recording average, standard deviation etc. + Histogram - a histogram of measurements falling in defined bucket + boundaries. + Window - a count of events that happen in reolling time window. + New aggregation types can be added by the user, if desired (see + census_register_aggregation()). + * Metric: Each measurement is for a single metric. Examples include RPC + latency, CPU seconds consumed, and bytes transmitted. + * View: A view is a combination of a metric, a tag set (in which the tag + values are regular expressions) and a set of aggregations. When a + measurement for a metric matches the view tags, it is recorded (for each + unique set of tags) against each aggregation. Each metric can have an + arbitrary number of views by which it will be broken down. +*/ + +/* A single value to be recorded comprises two parts: an ID for the particular + * metric and the value to be recorded against it. */ +typedef struct { + gpr_uint32 metric_id; + double value; +} census_value; + +/* Record new usage values against the given context. */ +void census_record_values(census_context *context, census_value *values, + size_t nvalues); + +/** Type representing a particular aggregation */ +typedef struct census_aggregation_ops census_aggregation_ops; + +/* Predefined aggregation types, for use with census_view_create(). */ +extern census_aggregation_ops census_agg_sum; +extern census_aggregation_ops census_agg_distribution; +extern census_aggregation_ops census_agg_histogram; +extern census_aggregation_ops census_agg_window; + +/** Information needed to instantiate a new aggregation. Used in view + construction via census_define_view(). */ +typedef struct { + const census_aggregation_ops *ops; + const void + *create_arg; /* Argument to be used for aggregation initialization. */ +} census_aggregation; + +/** A census view type. Opaque. */ +typedef struct census_view census_view; + +/** Create a new view. + @param metric_id Metric with which this view is associated. + @param tags tags that define the view + @param aggregations aggregations to associate with the view + @param naggregations number of aggregations + + @return A new census view +*/ +census_view *census_view_create(gpr_uint32 metric_id, + const census_tag_set *tags, + const census_aggregation *aggregations, + size_t naggregations); + +/** Destroy a previously created view. */ +void census_view_delete(census_view *view); + +/** Metric ID associated with a view */ +size_t census_view_metric(const census_view *view); + +/** Number of aggregations associated with view. */ +size_t census_view_naggregations(const census_view *view); + +/** Get tags associated with view. */ +const census_tag_set *census_view_tags(const census_view *view); + +/** Get aggregation descriptors associated with a view. */ +const census_aggregation *census_view_aggregrations(const census_view *view); + +/** Holds all the aggregation data for a particular view instantiation. Forms + part of the data returned by census_view_data(). */ +typedef struct { + const census_tag_set *tags; /* Tags for this set of aggregations. */ + const void **data; /* One data set for every aggregation in the view. */ +} census_view_aggregation_data; + +/** Census view data as returned by census_view_get_data(). */ +typedef struct { + size_t n_tag_sets; /* Number of unique tag sets that matched view. */ + const census_view_aggregation_data *data; /* n_tag_sets entries */ +} census_view_data; + +/** Get data from aggregations associated with a view. + @param view View from which to get data. + @return Full set of data for all aggregations for the view. +*/ +const census_view_data *census_view_get_data(const census_view *view); + +/** Reset all view data to zero for the specified view */ +void census_view_reset(census_view *view); + +#ifdef __cplusplus +} +#endif + +#endif /* CENSUS_CENSUS_H */ diff --git a/include/grpc/compression.h b/include/grpc/compression.h new file mode 100644 index 00000000..82e326fe --- /dev/null +++ b/include/grpc/compression.h @@ -0,0 +1,112 @@ +/* + * + * Copyright 2015, 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_COMPRESSION_H +#define GRPC_COMPRESSION_H + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** To be used in channel arguments */ +#define GRPC_COMPRESSION_ALGORITHM_ARG "grpc.compression_algorithm" +#define GRPC_COMPRESSION_ALGORITHM_STATE_ARG "grpc.compression_algorithm_state" + +/* The various compression algorithms supported by GRPC */ +typedef enum { + GRPC_COMPRESS_NONE = 0, + GRPC_COMPRESS_DEFLATE, + GRPC_COMPRESS_GZIP, + /* TODO(ctiller): snappy */ + GRPC_COMPRESS_ALGORITHMS_COUNT +} grpc_compression_algorithm; + +typedef enum { + GRPC_COMPRESS_LEVEL_NONE = 0, + GRPC_COMPRESS_LEVEL_LOW, + GRPC_COMPRESS_LEVEL_MED, + GRPC_COMPRESS_LEVEL_HIGH, + GRPC_COMPRESS_LEVEL_COUNT +} grpc_compression_level; + +typedef struct grpc_compression_options { + gpr_uint32 enabled_algorithms_bitset; /**< All algs are enabled by default */ + grpc_compression_algorithm default_compression_algorithm; /**< for channel */ +} grpc_compression_options; + +/** Parses the first \a name_length bytes of \a name as a + * grpc_compression_algorithm instance, updating \a algorithm. Returns 1 upon + * success, 0 otherwise. */ +int grpc_compression_algorithm_parse(const char *name, size_t name_length, + grpc_compression_algorithm *algorithm); + +/** Updates \a name with the encoding name corresponding to a valid \a + * algorithm. Returns 1 upon success, 0 otherwise. */ +int grpc_compression_algorithm_name(grpc_compression_algorithm algorithm, + char **name); + +/** Returns the compression level corresponding to \a algorithm. + * + * It abort()s for unknown algorithms. */ +grpc_compression_level grpc_compression_level_for_algorithm( + grpc_compression_algorithm algorithm); + +/** Returns the compression algorithm corresponding to \a level. + * + * It abort()s for unknown levels . */ +grpc_compression_algorithm grpc_compression_algorithm_for_level( + grpc_compression_level level); + +void grpc_compression_options_init(grpc_compression_options *opts); + +/** Mark \a algorithm as enabled in \a opts. */ +void grpc_compression_options_enable_algorithm( + grpc_compression_options *opts, grpc_compression_algorithm algorithm); + +/** Mark \a algorithm as disabled in \a opts. */ +void grpc_compression_options_disable_algorithm( + grpc_compression_options *opts, grpc_compression_algorithm algorithm); + +/** Returns true if \a algorithm is marked as enabled in \a opts. */ +int grpc_compression_options_is_algorithm_enabled( + const grpc_compression_options *opts, grpc_compression_algorithm algorithm); + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_COMPRESSION_H */ diff --git a/include/grpc/grpc.h b/include/grpc/grpc.h new file mode 100644 index 00000000..47f3df66 --- /dev/null +++ b/include/grpc/grpc.h @@ -0,0 +1,687 @@ +/* + * + * Copyright 2015, 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_GRPC_H +#define GRPC_GRPC_H + +#include + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/*! \mainpage GRPC Core + * + * The GRPC Core library is a low-level library designed to be wrapped by higher + * level libraries. The top-level API is provided in grpc.h. Security related + * functionality lives in grpc_security.h. + */ + +/** Completion Queues enable notification of the completion of asynchronous + actions. */ +typedef struct grpc_completion_queue grpc_completion_queue; + +/** The Channel interface allows creation of Call objects. */ +typedef struct grpc_channel grpc_channel; + +/** A server listens to some port and responds to request calls */ +typedef struct grpc_server grpc_server; + +/** A Call represents an RPC. When created, it is in a configuration state + allowing properties to be set until it is invoked. After invoke, the Call + can have messages written to it and read from it. */ +typedef struct grpc_call grpc_call; + +/** Type specifier for grpc_arg */ +typedef enum { + GRPC_ARG_STRING, + GRPC_ARG_INTEGER, + GRPC_ARG_POINTER +} grpc_arg_type; + +/** A single argument... each argument has a key and a value + + A note on naming keys: + Keys are namespaced into groups, usually grouped by library, and are + keys for module XYZ are named XYZ.key1, XYZ.key2, etc. Module names must + be restricted to the regex [A-Za-z][_A-Za-z0-9]{,15}. + Key names must be restricted to the regex [A-Za-z][_A-Za-z0-9]{,47}. + + GRPC core library keys are prefixed by grpc. + + Library authors are strongly encouraged to \#define symbolic constants for + their keys so that it's possible to change them in the future. */ +typedef struct { + grpc_arg_type type; + char *key; + union { + char *string; + int integer; + struct { + void *p; + void *(*copy)(void *p); + void (*destroy)(void *p); + } pointer; + } value; +} grpc_arg; + +/** An array of arguments that can be passed around. + + Used to set optional channel-level configuration. + These configuration options are modelled as key-value pairs as defined + by grpc_arg; keys are strings to allow easy backwards-compatible extension + by arbitrary parties. + All evaluation is performed at channel creation time (i.e. the values in + this structure need only live through the creation invocation). */ +typedef struct { + size_t num_args; + grpc_arg *args; +} grpc_channel_args; + +/* Channel argument keys: */ +/** Enable census for tracing and stats collection */ +#define GRPC_ARG_ENABLE_CENSUS "grpc.census" +/** Maximum number of concurrent incoming streams to allow on a http2 + connection */ +#define GRPC_ARG_MAX_CONCURRENT_STREAMS "grpc.max_concurrent_streams" +/** Maximum message length that the channel can receive */ +#define GRPC_ARG_MAX_MESSAGE_LENGTH "grpc.max_message_length" +/** Initial sequence number for http2 transports */ +#define GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER \ + "grpc.http2.initial_sequence_number" +/** Default authority to pass if none specified on call construction */ +#define GRPC_ARG_DEFAULT_AUTHORITY "grpc.default_authority" +/** Primary user agent: goes at the start of the user-agent metadata + sent on each request */ +#define GRPC_ARG_PRIMARY_USER_AGENT_STRING "grpc.primary_user_agent" +/** Secondary user agent: goes at the end of the user-agent metadata + sent on each request */ +#define GRPC_ARG_SECONDARY_USER_AGENT_STRING "grpc.secondary_user_agent" +/* The caller of the secure_channel_create functions may override the target + name used for SSL host name checking using this channel argument which is of + type GRPC_ARG_STRING. This *should* be used for testing only. + If this argument is not specified, the name used for SSL host name checking + will be the target parameter (assuming that the secure channel is an SSL + channel). If this parameter is specified and the underlying is not an SSL + channel, it will just be ignored. */ +#define GRPC_SSL_TARGET_NAME_OVERRIDE_ARG "grpc.ssl_target_name_override" + +/** Connectivity state of a channel. */ +typedef enum { + /** channel is idle */ + GRPC_CHANNEL_IDLE, + /** channel is connecting */ + GRPC_CHANNEL_CONNECTING, + /** channel is ready for work */ + GRPC_CHANNEL_READY, + /** channel has seen a failure but expects to recover */ + GRPC_CHANNEL_TRANSIENT_FAILURE, + /** channel has seen a failure that it cannot recover from */ + GRPC_CHANNEL_FATAL_FAILURE +} grpc_connectivity_state; + +/** Result of a grpc call. If the caller satisfies the prerequisites of a + particular operation, the grpc_call_error returned will be GRPC_CALL_OK. + Receiving any other value listed here is an indication of a bug in the + caller. */ +typedef enum grpc_call_error { + /** everything went ok */ + GRPC_CALL_OK = 0, + /** something failed, we don't know what */ + GRPC_CALL_ERROR, + /** this method is not available on the server */ + GRPC_CALL_ERROR_NOT_ON_SERVER, + /** this method is not available on the client */ + GRPC_CALL_ERROR_NOT_ON_CLIENT, + /** this method must be called before server_accept */ + GRPC_CALL_ERROR_ALREADY_ACCEPTED, + /** this method must be called before invoke */ + GRPC_CALL_ERROR_ALREADY_INVOKED, + /** this method must be called after invoke */ + GRPC_CALL_ERROR_NOT_INVOKED, + /** this call is already finished + (writes_done or write_status has already been called) */ + GRPC_CALL_ERROR_ALREADY_FINISHED, + /** there is already an outstanding read/write operation on the call */ + GRPC_CALL_ERROR_TOO_MANY_OPERATIONS, + /** the flags value was illegal for this call */ + GRPC_CALL_ERROR_INVALID_FLAGS, + /** invalid metadata was passed to this call */ + GRPC_CALL_ERROR_INVALID_METADATA, + /** invalid message was passed to this call */ + GRPC_CALL_ERROR_INVALID_MESSAGE, + /** completion queue for notification has not been registered with the + server */ + GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE, + /** this batch of operations leads to more operations than allowed */ + GRPC_CALL_ERROR_BATCH_TOO_BIG +} grpc_call_error; + +/* Write Flags: */ +/** Hint that the write may be buffered and need not go out on the wire + immediately. GRPC is free to buffer the message until the next non-buffered + write, or until writes_done, but it need not buffer completely or at all. */ +#define GRPC_WRITE_BUFFER_HINT (0x00000001u) +/** Force compression to be disabled for a particular write + (start_write/add_metadata). Illegal on invoke/accept. */ +#define GRPC_WRITE_NO_COMPRESS (0x00000002u) +/** Mask of all valid flags. */ +#define GRPC_WRITE_USED_MASK (GRPC_WRITE_BUFFER_HINT | GRPC_WRITE_NO_COMPRESS) + +/** A single metadata element */ +typedef struct grpc_metadata { + const char *key; + const char *value; + size_t value_length; + gpr_uint32 flags; + + /** The following fields are reserved for grpc internal use. + There is no need to initialize them, and they will be set to garbage + during calls to grpc. */ + struct { + void *obfuscated[4]; + } internal_data; +} grpc_metadata; + +/** The type of completion (for grpc_event) */ +typedef enum grpc_completion_type { + /** Shutting down */ + GRPC_QUEUE_SHUTDOWN, + /** No event before timeout */ + GRPC_QUEUE_TIMEOUT, + /** Operation completion */ + GRPC_OP_COMPLETE +} grpc_completion_type; + +/** The result of an operation. + + Returned by a completion queue when the operation started with tag. */ +typedef struct grpc_event { + /** The type of the completion. */ + grpc_completion_type type; + /** non-zero if the operation was successful, 0 upon failure. + Only GRPC_OP_COMPLETE can succeed or fail. */ + int success; + /** The tag passed to grpc_call_start_batch etc to start this operation. + Only GRPC_OP_COMPLETE has a tag. */ + void *tag; +} grpc_event; + +typedef struct { + size_t count; + size_t capacity; + grpc_metadata *metadata; +} grpc_metadata_array; + +void grpc_metadata_array_init(grpc_metadata_array *array); +void grpc_metadata_array_destroy(grpc_metadata_array *array); + +typedef struct { + char *method; + size_t method_capacity; + char *host; + size_t host_capacity; + gpr_timespec deadline; + void *reserved; +} grpc_call_details; + +void grpc_call_details_init(grpc_call_details *details); +void grpc_call_details_destroy(grpc_call_details *details); + +typedef enum { + /** Send initial metadata: one and only one instance MUST be sent for each + call, unless the call was cancelled - in which case this can be skipped. + This op completes after all bytes of metadata have been accepted by + outgoing flow control. */ + GRPC_OP_SEND_INITIAL_METADATA = 0, + /** Send a message: 0 or more of these operations can occur for each call. + This op completes after all bytes for the message have been accepted by + outgoing flow control. */ + GRPC_OP_SEND_MESSAGE, + /** Send a close from the client: one and only one instance MUST be sent from + the client, unless the call was cancelled - in which case this can be + skipped. + This op completes after all bytes for the call (including the close) + have passed outgoing flow control. */ + GRPC_OP_SEND_CLOSE_FROM_CLIENT, + /** Send status from the server: one and only one instance MUST be sent from + the server unless the call was cancelled - in which case this can be + skipped. + This op completes after all bytes for the call (including the status) + have passed outgoing flow control. */ + GRPC_OP_SEND_STATUS_FROM_SERVER, + /** Receive initial metadata: one and only one MUST be made on the client, + must not be made on the server. + This op completes after all initial metadata has been read from the + peer. */ + GRPC_OP_RECV_INITIAL_METADATA, + /** Receive a message: 0 or more of these operations can occur for each call. + This op completes after all bytes of the received message have been + read, or after a half-close has been received on this call. */ + GRPC_OP_RECV_MESSAGE, + /** Receive status on the client: one and only one must be made on the client. + This operation always succeeds, meaning ops paired with this operation + will also appear to succeed, even though they may not have. In that case + the status will indicate some failure. + This op completes after all activity on the call has completed. */ + GRPC_OP_RECV_STATUS_ON_CLIENT, + /** Receive close on the server: one and only one must be made on the + server. + This op completes after the close has been received by the server. */ + GRPC_OP_RECV_CLOSE_ON_SERVER +} grpc_op_type; + +/** Operation data: one field for each op type (except SEND_CLOSE_FROM_CLIENT + which has no arguments) */ +typedef struct grpc_op { + /** Operation type, as defined by grpc_op_type */ + grpc_op_type op; + /** Write flags bitset for grpc_begin_messages */ + gpr_uint32 flags; + /** Reserved for future usage */ + void *reserved; + union { + /** Reserved for future usage */ + struct { + void *reserved[8]; + } reserved; + struct { + size_t count; + grpc_metadata *metadata; + } send_initial_metadata; + grpc_byte_buffer *send_message; + struct { + size_t trailing_metadata_count; + grpc_metadata *trailing_metadata; + grpc_status_code status; + const char *status_details; + } send_status_from_server; + /** ownership of the array is with the caller, but ownership of the elements + stays with the call object (ie key, value members are owned by the call + object, recv_initial_metadata->array is owned by the caller). + After the operation completes, call grpc_metadata_array_destroy on this + value, or reuse it in a future op. */ + grpc_metadata_array *recv_initial_metadata; + /** ownership of the byte buffer is moved to the caller; the caller must + call grpc_byte_buffer_destroy on this value, or reuse it in a future op. + */ + grpc_byte_buffer **recv_message; + struct { + /** ownership of the array is with the caller, but ownership of the + elements stays with the call object (ie key, value members are owned + by the call object, trailing_metadata->array is owned by the caller). + After the operation completes, call grpc_metadata_array_destroy on + this + value, or reuse it in a future op. */ + grpc_metadata_array *trailing_metadata; + grpc_status_code *status; + /** status_details is a buffer owned by the application before the op + completes and after the op has completed. During the operation + status_details may be reallocated to a size larger than + *status_details_capacity, in which case *status_details_capacity will + be updated with the new array capacity. + + Pre-allocating space: + size_t my_capacity = 8; + char *my_details = gpr_malloc(my_capacity); + x.status_details = &my_details; + x.status_details_capacity = &my_capacity; + + Not pre-allocating space: + size_t my_capacity = 0; + char *my_details = NULL; + x.status_details = &my_details; + x.status_details_capacity = &my_capacity; + + After the call: + gpr_free(my_details); */ + char **status_details; + size_t *status_details_capacity; + } recv_status_on_client; + struct { + /** out argument, set to 1 if the call failed in any way (seen as a + cancellation on the server), or 0 if the call succeeded */ + int *cancelled; + } recv_close_on_server; + } data; +} grpc_op; + +/** Registers a plugin to be initialized and destroyed with the library. + + The \a init and \a destroy functions will be invoked as part of + \a grpc_init() and \a grpc_shutdown(), respectively. + Note that these functions can be invoked an arbitrary number of times + (and hence so will \a init and \a destroy). + It is safe to pass NULL to either argument. Plugins are destroyed in + the reverse order they were initialized. */ +void grpc_register_plugin(void (*init)(void), void (*destroy)(void)); + +/* Propagation bits: this can be bitwise or-ed to form propagation_mask for + * grpc_call */ +/** Propagate deadline */ +#define GRPC_PROPAGATE_DEADLINE ((gpr_uint32)1) +/** Propagate census context */ +#define GRPC_PROPAGATE_CENSUS_STATS_CONTEXT ((gpr_uint32)2) +#define GRPC_PROPAGATE_CENSUS_TRACING_CONTEXT ((gpr_uint32)4) +/** Propagate cancellation */ +#define GRPC_PROPAGATE_CANCELLATION ((gpr_uint32)8) + +/* Default propagation mask: clients of the core API are encouraged to encode + deltas from this in their implementations... ie write: + GRPC_PROPAGATE_DEFAULTS & ~GRPC_PROPAGATE_DEADLINE to disable deadline + propagation. Doing so gives flexibility in the future to define new + propagation types that are default inherited or not. */ +#define GRPC_PROPAGATE_DEFAULTS \ + ((gpr_uint32)(( \ + 0xffff | GRPC_PROPAGATE_DEADLINE | GRPC_PROPAGATE_CENSUS_STATS_CONTEXT | \ + GRPC_PROPAGATE_CENSUS_TRACING_CONTEXT | GRPC_PROPAGATE_CANCELLATION))) + +/** Initialize the grpc library. + + It is not safe to call any other grpc functions before calling this. + (To avoid overhead, little checking is done, and some things may work. We + do not warrant that they will continue to do so in future revisions of this + library). */ +void grpc_init(void); + +/** Shut down the grpc library. + + No memory is used by grpc after this call returns, nor are any instructions + executing within the grpc library. + Prior to calling, all application owned grpc objects must have been + destroyed. */ +void grpc_shutdown(void); + +/** Return a string representing the current version of grpc */ +const char *grpc_version_string(void); + +/** Create a completion queue */ +grpc_completion_queue *grpc_completion_queue_create(void *reserved); + +/** Blocks until an event is available, the completion queue is being shut down, + or deadline is reached. + + Returns a grpc_event with type GRPC_QUEUE_TIMEOUT on timeout, + otherwise a grpc_event describing the event that occurred. + + Callers must not call grpc_completion_queue_next and + grpc_completion_queue_pluck simultaneously on the same completion queue. */ +grpc_event grpc_completion_queue_next(grpc_completion_queue *cq, + gpr_timespec deadline, void *reserved); + +/** Blocks until an event with tag 'tag' is available, the completion queue is + being shutdown or deadline is reached. + + Returns a grpc_event with type GRPC_QUEUE_TIMEOUT on timeout, + otherwise a grpc_event describing the event that occurred. + + Callers must not call grpc_completion_queue_next and + grpc_completion_queue_pluck simultaneously on the same completion queue. + + Completion queues support a maximum of GRPC_MAX_COMPLETION_QUEUE_PLUCKERS + concurrently executing plucks at any time. */ +grpc_event grpc_completion_queue_pluck(grpc_completion_queue *cq, void *tag, + gpr_timespec deadline, void *reserved); + +/** Maximum number of outstanding grpc_completion_queue_pluck executions per + completion queue */ +#define GRPC_MAX_COMPLETION_QUEUE_PLUCKERS 6 + +/** Begin destruction of a completion queue. Once all possible events are + drained then grpc_completion_queue_next will start to produce + GRPC_QUEUE_SHUTDOWN events only. At that point it's safe to call + grpc_completion_queue_destroy. + + After calling this function applications should ensure that no + NEW work is added to be published on this completion queue. */ +void grpc_completion_queue_shutdown(grpc_completion_queue *cq); + +/** Destroy a completion queue. The caller must ensure that the queue is + drained and no threads are executing grpc_completion_queue_next */ +void grpc_completion_queue_destroy(grpc_completion_queue *cq); + +/** Check the connectivity state of a channel. */ +grpc_connectivity_state grpc_channel_check_connectivity_state( + grpc_channel *channel, int try_to_connect); + +/** Watch for a change in connectivity state. + Once the channel connectivity state is different from last_observed_state, + tag will be enqueued on cq with success=1. + If deadline expires BEFORE the state is changed, tag will be enqueued on cq + with success=0. */ +void grpc_channel_watch_connectivity_state( + grpc_channel *channel, grpc_connectivity_state last_observed_state, + gpr_timespec deadline, grpc_completion_queue *cq, void *tag); + +/** Create a call given a grpc_channel, in order to call 'method'. All + completions are sent to 'completion_queue'. 'method' and 'host' need only + live through the invocation of this function. + If parent_call is non-NULL, it must be a server-side call. It will be used + to propagate properties from the server call to this new client call. + */ +grpc_call *grpc_channel_create_call(grpc_channel *channel, + grpc_call *parent_call, + gpr_uint32 propagation_mask, + grpc_completion_queue *completion_queue, + const char *method, const char *host, + gpr_timespec deadline, void *reserved); + +/** Pre-register a method/host pair on a channel. */ +void *grpc_channel_register_call(grpc_channel *channel, const char *method, + const char *host, void *reserved); + +/** Create a call given a handle returned from grpc_channel_register_call */ +grpc_call *grpc_channel_create_registered_call( + grpc_channel *channel, grpc_call *parent_call, gpr_uint32 propagation_mask, + grpc_completion_queue *completion_queue, void *registered_call_handle, + gpr_timespec deadline, void *reserved); + +/** Start a batch of operations defined in the array ops; when complete, post a + completion of type 'tag' to the completion queue bound to the call. + The order of ops specified in the batch has no significance. + Only one operation of each type can be active at once in any given + batch. You must call grpc_completion_queue_next or + grpc_completion_queue_pluck on the completion queue associated with 'call' + for work to be performed. + THREAD SAFETY: access to grpc_call_start_batch in multi-threaded environment + needs to be synchronized. As an optimization, you may synchronize batches + containing just send operations independently from batches containing just + receive operations. */ +grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops, + size_t nops, void *tag, void *reserved); + +/** Returns a newly allocated string representing the endpoint to which this + call is communicating with. The string is in the uri format accepted by + grpc_channel_create. + The returned string should be disposed of with gpr_free(). + + WARNING: this value is never authenticated or subject to any security + related code. It must not be used for any authentication related + functionality. Instead, use grpc_auth_context. */ +char *grpc_call_get_peer(grpc_call *call); + +struct census_context; + +/* Set census context for a call; Must be called before first call to + grpc_call_start_batch(). */ +void grpc_census_call_set_context(grpc_call *call, + struct census_context *context); + +/* Retrieve the calls current census context. */ +struct census_context *grpc_census_call_get_context(grpc_call *call); + +/** Return a newly allocated string representing the target a channel was + created for. */ +char *grpc_channel_get_target(grpc_channel *channel); + +/** Create a client channel to 'target'. Additional channel level configuration + MAY be provided by grpc_channel_args, though the expectation is that most + clients will want to simply pass NULL. See grpc_channel_args definition for + more on this. The data in 'args' need only live through the invocation of + this function. */ +grpc_channel *grpc_insecure_channel_create(const char *target, + const grpc_channel_args *args, + void *reserved); + +/** Create a lame client: this client fails every operation attempted on it. */ +grpc_channel *grpc_lame_client_channel_create(const char *target, + grpc_status_code error_code, + const char *error_message); + +/** Close and destroy a grpc channel */ +void grpc_channel_destroy(grpc_channel *channel); + +/* Error handling for grpc_call + Most grpc_call functions return a grpc_error. If the error is not GRPC_OK + then the operation failed due to some unsatisfied precondition. + If a grpc_call fails, it's guaranteed that no change to the call state + has been made. */ + +/** Called by clients to cancel an RPC on the server. + Can be called multiple times, from any thread. + THREAD-SAFETY grpc_call_cancel and grpc_call_cancel_with_status + are thread-safe, and can be called at any point before grpc_call_destroy + is called.*/ +grpc_call_error grpc_call_cancel(grpc_call *call, void *reserved); + +/** Called by clients to cancel an RPC on the server. + Can be called multiple times, from any thread. + If a status has not been received for the call, set it to the status code + and description passed in. + Importantly, this function does not send status nor description to the + remote endpoint. */ +grpc_call_error grpc_call_cancel_with_status(grpc_call *call, + grpc_status_code status, + const char *description, + void *reserved); + +/** Destroy a call. + THREAD SAFETY: grpc_call_destroy is thread-compatible */ +void grpc_call_destroy(grpc_call *call); + +/** Request notification of a new call. + Once a call is received, a notification tagged with \a tag_new is added to + \a cq_for_notification. \a call, \a details and \a request_metadata are + updated with the appropriate call information. \a cq_bound_to_call is bound + to \a call, and batch operation notifications for that call will be posted + to \a cq_bound_to_call. + Note that \a cq_for_notification must have been registered to the server via + \a grpc_server_register_completion_queue. */ +grpc_call_error grpc_server_request_call( + grpc_server *server, grpc_call **call, grpc_call_details *details, + grpc_metadata_array *request_metadata, + grpc_completion_queue *cq_bound_to_call, + grpc_completion_queue *cq_for_notification, void *tag_new); + +/** Registers a method in the server. + Methods to this (host, method) pair will not be reported by + grpc_server_request_call, but instead be reported by + grpc_server_request_registered_call when passed the appropriate + registered_method (as returned by this function). + Must be called before grpc_server_start. + Returns NULL on failure. */ +void *grpc_server_register_method(grpc_server *server, const char *method, + const char *host); + +/** Request notification of a new pre-registered call. 'cq_for_notification' + must have been registered to the server via + grpc_server_register_completion_queue. */ +grpc_call_error grpc_server_request_registered_call( + grpc_server *server, void *registered_method, grpc_call **call, + gpr_timespec *deadline, grpc_metadata_array *request_metadata, + grpc_byte_buffer **optional_payload, + grpc_completion_queue *cq_bound_to_call, + grpc_completion_queue *cq_for_notification, void *tag_new); + +/** Create a server. Additional configuration for each incoming channel can + be specified with args. If no additional configuration is needed, args can + be NULL. See grpc_channel_args for more. The data in 'args' need only live + through the invocation of this function. */ +grpc_server *grpc_server_create(const grpc_channel_args *args, void *reserved); + +/** Register a completion queue with the server. Must be done for any + notification completion queue that is passed to grpc_server_request_*_call + and to grpc_server_shutdown_and_notify. Must be performed prior to + grpc_server_start. */ +void grpc_server_register_completion_queue(grpc_server *server, + grpc_completion_queue *cq, + void *reserved); + +/** Add a HTTP2 over plaintext over tcp listener. + Returns bound port number on success, 0 on failure. + REQUIRES: server not started */ +int grpc_server_add_insecure_http2_port(grpc_server *server, const char *addr); + +/** Start a server - tells all listeners to start listening */ +void grpc_server_start(grpc_server *server); + +/** Begin shutting down a server. + After completion, no new calls or connections will be admitted. + Existing calls will be allowed to complete. + Send a GRPC_OP_COMPLETE event when there are no more calls being serviced. + Shutdown is idempotent, and all tags will be notified at once if multiple + grpc_server_shutdown_and_notify calls are made. 'cq' must have been + registered to this server via grpc_server_register_completion_queue. */ +void grpc_server_shutdown_and_notify(grpc_server *server, + grpc_completion_queue *cq, void *tag); + +/** Cancel all in-progress calls. + Only usable after shutdown. */ +void grpc_server_cancel_all_calls(grpc_server *server); + +/** Destroy a server. + Shutdown must have completed beforehand (i.e. all tags generated by + grpc_server_shutdown_and_notify must have been received, and at least + one call to grpc_server_shutdown_and_notify must have been made). */ +void grpc_server_destroy(grpc_server *server); + +/** Enable or disable a tracer. + + Tracers (usually controlled by the environment variable GRPC_TRACE) + allow printf-style debugging on GRPC internals, and are useful for + tracking down problems in the field. + + Use of this function is not strictly thread-safe, but the + thread-safety issues raised by it should not be of concern. */ +int grpc_tracer_set_enabled(const char *name, int enabled); + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_GRPC_H */ diff --git a/include/grpc/grpc_security.h b/include/grpc/grpc_security.h new file mode 100644 index 00000000..87bc2504 --- /dev/null +++ b/include/grpc/grpc_security.h @@ -0,0 +1,294 @@ +/* + * + * Copyright 2015, 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_GRPC_SECURITY_H +#define GRPC_GRPC_SECURITY_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* --- grpc_credentials object. --- + + A credentials object represents a way to authenticate a client. */ + +typedef struct grpc_credentials grpc_credentials; + +/* Releases a credentials object. + The creator of the credentials object is responsible for its release. */ +void grpc_credentials_release(grpc_credentials *creds); + +/* Environment variable that points to the google default application + credentials json key or refresh token. Used in the + grpc_google_default_credentials_create function. */ +#define GRPC_GOOGLE_CREDENTIALS_ENV_VAR "GOOGLE_APPLICATION_CREDENTIALS" + +/* Creates default credentials to connect to a google gRPC service. + WARNING: Do NOT use this credentials to connect to a non-google service as + this could result in an oauth2 token leak. */ +grpc_credentials *grpc_google_default_credentials_create(void); + +/* Environment variable that points to the default SSL roots file. This file + must be a PEM encoded file with all the roots such as the one that can be + downloaded from https://pki.google.com/roots.pem. */ +#define GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR \ + "GRPC_DEFAULT_SSL_ROOTS_FILE_PATH" + +/* Object that holds a private key / certificate chain pair in PEM format. */ +typedef struct { + /* private_key is the NULL-terminated string containing the PEM encoding of + the client's private key. */ + const char *private_key; + + /* cert_chain is the NULL-terminated string containing the PEM encoding of + the client's certificate chain. */ + const char *cert_chain; +} grpc_ssl_pem_key_cert_pair; + +/* Creates an SSL credentials object. + - pem_roots_cert is the NULL-terminated string containing the PEM encoding + of the server root certificates. If this parameter is NULL, the + implementation will first try to dereference the file pointed by the + GRPC_DEFAULT_SSL_ROOTS_FILE_PATH environment variable, and if that fails, + get the roots from a well-known place on disk (in the grpc install + directory). + - pem_key_cert_pair is a pointer on the object containing client's private + key and certificate chain. This parameter can be NULL if the client does + not have such a key/cert pair. */ +grpc_credentials *grpc_ssl_credentials_create( + const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pair, + void *reserved); + +/* Creates a composite credentials object. */ +grpc_credentials *grpc_composite_credentials_create(grpc_credentials *creds1, + grpc_credentials *creds2, + void *reserved); + +/* Creates a compute engine credentials object for connecting to Google. + WARNING: Do NOT use this credentials to connect to a non-google service as + this could result in an oauth2 token leak. */ +grpc_credentials *grpc_google_compute_engine_credentials_create(void *reserved); + +extern const gpr_timespec grpc_max_auth_token_lifetime; + +/* Creates a JWT credentials object. May return NULL if the input is invalid. + - json_key is the JSON key string containing the client's private key. + - token_lifetime is the lifetime of each Json Web Token (JWT) created with + this credentials. It should not exceed grpc_max_auth_token_lifetime or + will be cropped to this value. */ +grpc_credentials *grpc_service_account_jwt_access_credentials_create( + const char *json_key, gpr_timespec token_lifetime, void *reserved); + +/* Creates an Oauth2 Refresh Token credentials object for connecting to Google. + May return NULL if the input is invalid. + WARNING: Do NOT use this credentials to connect to a non-google service as + this could result in an oauth2 token leak. + - json_refresh_token is the JSON string containing the refresh token itself + along with a client_id and client_secret. */ +grpc_credentials *grpc_google_refresh_token_credentials_create( + const char *json_refresh_token, void *reserved); + +/* Creates an Oauth2 Access Token credentials with an access token that was + aquired by an out of band mechanism. */ +grpc_credentials *grpc_access_token_credentials_create( + const char *access_token, void *reserved); + +/* Creates an IAM credentials object for connecting to Google. */ +grpc_credentials *grpc_google_iam_credentials_create( + const char *authorization_token, const char *authority_selector, + void *reserved); + +/* --- Secure channel creation. --- */ + +/* Creates a secure channel using the passed-in credentials. */ +grpc_channel *grpc_secure_channel_create(grpc_credentials *creds, + const char *target, + const grpc_channel_args *args, + void *reserved); + +/* --- grpc_server_credentials object. --- + + A server credentials object represents a way to authenticate a server. */ + +typedef struct grpc_server_credentials grpc_server_credentials; + +/* Releases a server_credentials object. + The creator of the server_credentials object is responsible for its release. + */ +void grpc_server_credentials_release(grpc_server_credentials *creds); + +/* Creates an SSL server_credentials object. + - pem_roots_cert is the NULL-terminated string containing the PEM encoding of + the client root certificates. This parameter may be NULL if the server does + not want the client to be authenticated with SSL. + - pem_key_cert_pairs is an array private key / certificate chains of the + server. This parameter cannot be NULL. + - num_key_cert_pairs indicates the number of items in the private_key_files + and cert_chain_files parameters. It should be at least 1. + - force_client_auth, if set to non-zero will force the client to authenticate + with an SSL cert. Note that this option is ignored if pem_root_certs is + NULL. */ +grpc_server_credentials *grpc_ssl_server_credentials_create( + const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs, + size_t num_key_cert_pairs, int force_client_auth, void *reserved); + +/* --- Server-side secure ports. --- */ + +/* Add a HTTP2 over an encrypted link over tcp listener. + Returns bound port number on success, 0 on failure. + REQUIRES: server not started */ +int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr, + grpc_server_credentials *creds); + +/* --- Call specific credentials. --- */ + +/* Sets a credentials to a call. Can only be called on the client side before + grpc_call_start_batch. */ +grpc_call_error grpc_call_set_credentials(grpc_call *call, + grpc_credentials *creds); + +/* --- Authentication Context. --- */ + +#define GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME "transport_security_type" +#define GRPC_SSL_TRANSPORT_SECURITY_TYPE "ssl" + +#define GRPC_X509_CN_PROPERTY_NAME "x509_common_name" +#define GRPC_X509_SAN_PROPERTY_NAME "x509_subject_alternative_name" + +typedef struct grpc_auth_context grpc_auth_context; + +typedef struct grpc_auth_property_iterator { + const grpc_auth_context *ctx; + size_t index; + const char *name; +} grpc_auth_property_iterator; + +/* value, if not NULL, is guaranteed to be NULL terminated. */ +typedef struct grpc_auth_property { + char *name; + char *value; + size_t value_length; +} grpc_auth_property; + +/* Returns NULL when the iterator is at the end. */ +const grpc_auth_property *grpc_auth_property_iterator_next( + grpc_auth_property_iterator *it); + +/* Iterates over the auth context. */ +grpc_auth_property_iterator grpc_auth_context_property_iterator( + const grpc_auth_context *ctx); + +/* Gets the peer identity. Returns an empty iterator (first _next will return + NULL) if the peer is not authenticated. */ +grpc_auth_property_iterator grpc_auth_context_peer_identity( + const grpc_auth_context *ctx); + +/* Finds a property in the context. May return an empty iterator (first _next + will return NULL) if no property with this name was found in the context. */ +grpc_auth_property_iterator grpc_auth_context_find_properties_by_name( + const grpc_auth_context *ctx, const char *name); + +/* Gets the name of the property that indicates the peer identity. Will return + NULL if the peer is not authenticated. */ +const char *grpc_auth_context_peer_identity_property_name( + const grpc_auth_context *ctx); + +/* Returns 1 if the peer is authenticated, 0 otherwise. */ +int grpc_auth_context_peer_is_authenticated(const grpc_auth_context *ctx); + +/* Gets the auth context from the call. Caller needs to call + grpc_auth_context_release on the returned context. */ +grpc_auth_context *grpc_call_auth_context(grpc_call *call); + +/* Releases the auth context returned from grpc_call_auth_context. */ +void grpc_auth_context_release(grpc_auth_context *context); + +/* -- + The following auth context methods should only be called by a server metadata + processor to set properties extracted from auth metadata. + -- */ + +/* Add a property. */ +void grpc_auth_context_add_property(grpc_auth_context *ctx, const char *name, + const char *value, size_t value_length); + +/* Add a C string property. */ +void grpc_auth_context_add_cstring_property(grpc_auth_context *ctx, + const char *name, + const char *value); + +/* Sets the property name. Returns 1 if successful or 0 in case of failure + (which means that no property with this name exists). */ +int grpc_auth_context_set_peer_identity_property_name(grpc_auth_context *ctx, + const char *name); + +/* --- Auth Metadata Processing --- */ + +/* Callback function that is called when the metadata processing is done. + - Consumed metadata will be removed from the set of metadata available on the + call. consumed_md may be NULL if no metadata has been consumed. + - Response metadata will be set on the response. response_md may be NULL. + - status is GRPC_STATUS_OK for success or a specific status for an error. + Common error status for auth metadata processing is either + GRPC_STATUS_UNAUTHENTICATED in case of an authentication failure or + GRPC_STATUS PERMISSION_DENIED in case of an authorization failure. + - error_details gives details about the error. May be NULL. */ +typedef void (*grpc_process_auth_metadata_done_cb)( + void *user_data, const grpc_metadata *consumed_md, size_t num_consumed_md, + const grpc_metadata *response_md, size_t num_response_md, + grpc_status_code status, const char *error_details); + +/* Pluggable server-side metadata processor object. */ +typedef struct { + /* The context object is read/write: it contains the properties of the + channel peer and it is the job of the process function to augment it with + properties derived from the passed-in metadata. + The lifetime of these objects is guaranteed until cb is invoked. */ + void (*process)(void *state, grpc_auth_context *context, + const grpc_metadata *md, size_t num_md, + grpc_process_auth_metadata_done_cb cb, void *user_data); + void (*destroy)(void *state); + void *state; +} grpc_auth_metadata_processor; + +void grpc_server_credentials_set_auth_metadata_processor( + grpc_server_credentials *creds, grpc_auth_metadata_processor processor); + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_GRPC_SECURITY_H */ diff --git a/include/grpc/grpc_zookeeper.h b/include/grpc/grpc_zookeeper.h new file mode 100644 index 00000000..2b195c18 --- /dev/null +++ b/include/grpc/grpc_zookeeper.h @@ -0,0 +1,59 @@ +/* + * + * Copyright 2015, 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. + * + */ + +/** Support zookeeper as alternative name system in addition to DNS + * Zookeeper name in gRPC is represented as a URI: + * zookeeper://host:port/path/service/instance + * + * Where zookeeper is the name system scheme + * host:port is the address of a zookeeper server + * /path/service/instance is the zookeeper name to be resolved + * + * Refer doc/naming.md for more details + */ + +#ifndef GRPC_GRPC_ZOOKEEPER_H +#define GRPC_GRPC_ZOOKEEPER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** Register zookeeper name resolver in grpc */ +void grpc_zookeeper_register(); + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_GRPC_ZOOKEEPER_H */ diff --git a/include/grpc/status.h b/include/grpc/status.h new file mode 100644 index 00000000..65ce4102 --- /dev/null +++ b/include/grpc/status.h @@ -0,0 +1,163 @@ +/* + * + * Copyright 2015, 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_STATUS_H +#define GRPC_STATUS_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + /* Not an error; returned on success */ + GRPC_STATUS_OK = 0, + + /* The operation was cancelled (typically by the caller). */ + GRPC_STATUS_CANCELLED = 1, + + /* Unknown error. An example of where this error may be returned is + if a Status value received from another address space belongs to + an error-space that is not known in this address space. Also + errors raised by APIs that do not return enough error information + may be converted to this error. */ + GRPC_STATUS_UNKNOWN = 2, + + /* Client specified an invalid argument. Note that this differs + from FAILED_PRECONDITION. INVALID_ARGUMENT indicates arguments + that are problematic regardless of the state of the system + (e.g., a malformed file name). */ + GRPC_STATUS_INVALID_ARGUMENT = 3, + + /* Deadline expired before operation could complete. For operations + that change the state of the system, this error may be returned + even if the operation has completed successfully. For example, a + successful response from a server could have been delayed long + enough for the deadline to expire. */ + GRPC_STATUS_DEADLINE_EXCEEDED = 4, + + /* Some requested entity (e.g., file or directory) was not found. */ + GRPC_STATUS_NOT_FOUND = 5, + + /* Some entity that we attempted to create (e.g., file or directory) + already exists. */ + GRPC_STATUS_ALREADY_EXISTS = 6, + + /* The caller does not have permission to execute the specified + operation. PERMISSION_DENIED must not be used for rejections + caused by exhausting some resource (use RESOURCE_EXHAUSTED + instead for those errors). PERMISSION_DENIED must not be + used if the caller can not be identified (use UNAUTHENTICATED + instead for those errors). */ + GRPC_STATUS_PERMISSION_DENIED = 7, + + /* The request does not have valid authentication credentials for the + operation. */ + GRPC_STATUS_UNAUTHENTICATED = 16, + + /* Some resource has been exhausted, perhaps a per-user quota, or + perhaps the entire file system is out of space. */ + GRPC_STATUS_RESOURCE_EXHAUSTED = 8, + + /* Operation was rejected because the system is not in a state + required for the operation's execution. For example, directory + to be deleted may be non-empty, an rmdir operation is applied to + a non-directory, etc. + + A litmus test that may help a service implementor in deciding + between FAILED_PRECONDITION, ABORTED, and UNAVAILABLE: + (a) Use UNAVAILABLE if the client can retry just the failing call. + (b) Use ABORTED if the client should retry at a higher-level + (e.g., restarting a read-modify-write sequence). + (c) Use FAILED_PRECONDITION if the client should not retry until + the system state has been explicitly fixed. E.g., if an "rmdir" + fails because the directory is non-empty, FAILED_PRECONDITION + should be returned since the client should not retry unless + they have first fixed up the directory by deleting files from it. + (d) Use FAILED_PRECONDITION if the client performs conditional + REST Get/Update/Delete on a resource and the resource on the + server does not match the condition. E.g., conflicting + read-modify-write on the same resource. */ + GRPC_STATUS_FAILED_PRECONDITION = 9, + + /* The operation was aborted, typically due to a concurrency issue + like sequencer check failures, transaction aborts, etc. + + See litmus test above for deciding between FAILED_PRECONDITION, + ABORTED, and UNAVAILABLE. */ + GRPC_STATUS_ABORTED = 10, + + /* Operation was attempted past the valid range. E.g., seeking or + reading past end of file. + + Unlike INVALID_ARGUMENT, this error indicates a problem that may + be fixed if the system state changes. For example, a 32-bit file + system will generate INVALID_ARGUMENT if asked to read at an + offset that is not in the range [0,2^32-1], but it will generate + OUT_OF_RANGE if asked to read from an offset past the current + file size. + + There is a fair bit of overlap between FAILED_PRECONDITION and + OUT_OF_RANGE. We recommend using OUT_OF_RANGE (the more specific + error) when it applies so that callers who are iterating through + a space can easily look for an OUT_OF_RANGE error to detect when + they are done. */ + GRPC_STATUS_OUT_OF_RANGE = 11, + + /* Operation is not implemented or not supported/enabled in this service. */ + GRPC_STATUS_UNIMPLEMENTED = 12, + + /* Internal errors. Means some invariants expected by underlying + system has been broken. If you see one of these errors, + something is very broken. */ + GRPC_STATUS_INTERNAL = 13, + + /* The service is currently unavailable. This is a most likely a + transient condition and may be corrected by retrying with + a backoff. + + See litmus test above for deciding between FAILED_PRECONDITION, + ABORTED, and UNAVAILABLE. */ + GRPC_STATUS_UNAVAILABLE = 14, + + /* Unrecoverable data loss or corruption. */ + GRPC_STATUS_DATA_LOSS = 15, + + /* Force users to include a default branch: */ + GRPC_STATUS__DO_NOT_USE = -1 +} grpc_status_code; + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_STATUS_H */ diff --git a/include/grpc/support/alloc.h b/include/grpc/support/alloc.h new file mode 100644 index 00000000..9d4e743d --- /dev/null +++ b/include/grpc/support/alloc.h @@ -0,0 +1,58 @@ +/* + * + * Copyright 2015, 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_SUPPORT_ALLOC_H +#define GRPC_SUPPORT_ALLOC_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* malloc, never returns NULL */ +void *gpr_malloc(size_t size); +/* free */ +void gpr_free(void *ptr); +/* realloc, never returns NULL */ +void *gpr_realloc(void *p, size_t size); +/* aligned malloc, never returns NULL, will align to 1 << alignment_log */ +void *gpr_malloc_aligned(size_t size, size_t alignment_log); +/* free memory allocated by gpr_malloc_aligned */ +void gpr_free_aligned(void *ptr); + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_SUPPORT_ALLOC_H */ diff --git a/include/grpc/support/atm.h b/include/grpc/support/atm.h new file mode 100644 index 00000000..3f88e2e1 --- /dev/null +++ b/include/grpc/support/atm.h @@ -0,0 +1,92 @@ +/* + * + * Copyright 2015, 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_SUPPORT_ATM_H +#define GRPC_SUPPORT_ATM_H + +/* This interface provides atomic operations and barriers. + It is internal to gpr support code and should not be used outside it. + + If an operation with acquire semantics precedes another memory access by the + same thread, the operation will precede that other access as seen by other + threads. + + If an operation with release semantics follows another memory access by the + same thread, the operation will follow that other access as seen by other + threads. + + Routines with "acq" or "full" in the name have acquire semantics. Routines + with "rel" or "full" in the name have release semantics. Routines with + "no_barrier" in the name have neither acquire not release semantics. + + The routines may be implemented as macros. + + // Atomic operations act on an intergral_type gpr_atm that is guaranteed to + // be the same size as a pointer. + typedef gpr_intptr gpr_atm; + + // A memory barrier, providing both acquire and release semantics, but not + // otherwise acting on memory. + void gpr_atm_full_barrier(void); + + // Atomically return *p, with acquire semantics. + gpr_atm gpr_atm_acq_load(gpr_atm *p); + + // Atomically set *p = value, with release semantics. + void gpr_atm_rel_store(gpr_atm *p, gpr_atm value); + + // Atomically add delta to *p, and return the old value of *p, with + // the barriers specified. + gpr_atm gpr_atm_no_barrier_fetch_add(gpr_atm *p, gpr_atm delta); + gpr_atm gpr_atm_full_fetch_add(gpr_atm *p, gpr_atm delta); + + // Atomically, if *p==o, set *p=n and return non-zero otherwise return 0, + // with the barriers specified if the operation succeeds. + int gpr_atm_no_barrier_cas(gpr_atm *p, gpr_atm o, gpr_atm n); + int gpr_atm_acq_cas(gpr_atm *p, gpr_atm o, gpr_atm n); + int gpr_atm_rel_cas(gpr_atm *p, gpr_atm o, gpr_atm n); +*/ + +#include + +#if defined(GPR_GCC_ATOMIC) +#include +#elif defined(GPR_GCC_SYNC) +#include +#elif defined(GPR_WIN32_ATOMIC) +#include +#else +#error could not determine platform for atm +#endif + +#endif /* GRPC_SUPPORT_ATM_H */ diff --git a/include/grpc/support/atm_gcc_atomic.h b/include/grpc/support/atm_gcc_atomic.h new file mode 100644 index 00000000..104e1d51 --- /dev/null +++ b/include/grpc/support/atm_gcc_atomic.h @@ -0,0 +1,72 @@ +/* + * + * Copyright 2015, 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_SUPPORT_ATM_GCC_ATOMIC_H +#define GRPC_SUPPORT_ATM_GCC_ATOMIC_H + +/* atm_platform.h for gcc and gcc-like compilers with the + __atomic_* interface. */ +#include + +typedef gpr_intptr gpr_atm; + +#define gpr_atm_full_barrier() (__atomic_thread_fence(__ATOMIC_SEQ_CST)) + +#define gpr_atm_acq_load(p) (__atomic_load_n((p), __ATOMIC_ACQUIRE)) +#define gpr_atm_no_barrier_load(p) (__atomic_load_n((p), __ATOMIC_RELAXED)) +#define gpr_atm_rel_store(p, value) \ + (__atomic_store_n((p), (gpr_intptr)(value), __ATOMIC_RELEASE)) +#define gpr_atm_no_barrier_store(p, value) \ + (__atomic_store_n((p), (gpr_intptr)(value), __ATOMIC_RELAXED)) + +#define gpr_atm_no_barrier_fetch_add(p, delta) \ + (__atomic_fetch_add((p), (gpr_intptr)(delta), __ATOMIC_RELAXED)) +#define gpr_atm_full_fetch_add(p, delta) \ + (__atomic_fetch_add((p), (gpr_intptr)(delta), __ATOMIC_ACQ_REL)) + +static __inline int gpr_atm_no_barrier_cas(gpr_atm *p, gpr_atm o, gpr_atm n) { + return __atomic_compare_exchange_n(p, &o, n, 0, __ATOMIC_RELAXED, + __ATOMIC_RELAXED); +} + +static __inline int gpr_atm_acq_cas(gpr_atm *p, gpr_atm o, gpr_atm n) { + return __atomic_compare_exchange_n(p, &o, n, 0, __ATOMIC_ACQUIRE, + __ATOMIC_RELAXED); +} + +static __inline int gpr_atm_rel_cas(gpr_atm *p, gpr_atm o, gpr_atm n) { + return __atomic_compare_exchange_n(p, &o, n, 0, __ATOMIC_RELEASE, + __ATOMIC_RELAXED); +} + +#endif /* GRPC_SUPPORT_ATM_GCC_ATOMIC_H */ diff --git a/include/grpc/support/atm_gcc_sync.h b/include/grpc/support/atm_gcc_sync.h new file mode 100644 index 00000000..241ae76c --- /dev/null +++ b/include/grpc/support/atm_gcc_sync.h @@ -0,0 +1,87 @@ +/* + * + * Copyright 2015, 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_SUPPORT_ATM_GCC_SYNC_H +#define GRPC_SUPPORT_ATM_GCC_SYNC_H + +/* variant of atm_platform.h for gcc and gcc-like compiers with __sync_* + interface */ +#include + +typedef gpr_intptr gpr_atm; + +#define GPR_ATM_COMPILE_BARRIER_() __asm__ __volatile__("" : : : "memory") + +#if defined(__i386) || defined(__x86_64__) +/* All loads are acquire loads and all stores are release stores. */ +#define GPR_ATM_LS_BARRIER_() GPR_ATM_COMPILE_BARRIER_() +#else +#define GPR_ATM_LS_BARRIER_() gpr_atm_full_barrier() +#endif + +#define gpr_atm_full_barrier() (__sync_synchronize()) + +static __inline gpr_atm gpr_atm_acq_load(const gpr_atm *p) { + gpr_atm value = *p; + GPR_ATM_LS_BARRIER_(); + return value; +} + +static __inline gpr_atm gpr_atm_no_barrier_load(const gpr_atm *p) { + gpr_atm value = *p; + GPR_ATM_COMPILE_BARRIER_(); + return value; +} + +static __inline void gpr_atm_rel_store(gpr_atm *p, gpr_atm value) { + GPR_ATM_LS_BARRIER_(); + *p = value; +} + +static __inline void gpr_atm_no_barrier_store(gpr_atm *p, gpr_atm value) { + GPR_ATM_COMPILE_BARRIER_(); + *p = value; +} + +#undef GPR_ATM_LS_BARRIER_ +#undef GPR_ATM_COMPILE_BARRIER_ + +#define gpr_atm_no_barrier_fetch_add(p, delta) \ + gpr_atm_full_fetch_add((p), (delta)) +#define gpr_atm_full_fetch_add(p, delta) (__sync_fetch_and_add((p), (delta))) + +#define gpr_atm_no_barrier_cas(p, o, n) gpr_atm_acq_cas((p), (o), (n)) +#define gpr_atm_acq_cas(p, o, n) (__sync_bool_compare_and_swap((p), (o), (n))) +#define gpr_atm_rel_cas(p, o, n) gpr_atm_acq_cas((p), (o), (n)) + +#endif /* GRPC_SUPPORT_ATM_GCC_SYNC_H */ diff --git a/include/grpc/support/atm_win32.h b/include/grpc/support/atm_win32.h new file mode 100644 index 00000000..cc016e5c --- /dev/null +++ b/include/grpc/support/atm_win32.h @@ -0,0 +1,125 @@ +/* + * + * Copyright 2015, 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_SUPPORT_ATM_WIN32_H +#define GRPC_SUPPORT_ATM_WIN32_H + +/* Win32 variant of atm_platform.h */ +#include + +typedef gpr_intptr gpr_atm; + +#define gpr_atm_full_barrier MemoryBarrier + +static __inline gpr_atm gpr_atm_acq_load(const gpr_atm *p) { + gpr_atm result = *p; + gpr_atm_full_barrier(); + return result; +} + +static __inline gpr_atm gpr_atm_no_barrier_load(const gpr_atm *p) { + /* TODO(dklempner): Can we implement something better here? */ + return gpr_atm_acq_load(p); +} + +static __inline void gpr_atm_rel_store(gpr_atm *p, gpr_atm value) { + gpr_atm_full_barrier(); + *p = value; +} + +static __inline void gpr_atm_no_barrier_store(gpr_atm *p, gpr_atm value) { + /* TODO(ctiller): Can we implement something better here? */ + gpr_atm_rel_store(p, value); +} + +static __inline int gpr_atm_no_barrier_cas(gpr_atm *p, gpr_atm o, gpr_atm n) { +/* InterlockedCompareExchangePointerNoFence() not available on vista or + windows7 */ +#ifdef GPR_ARCH_64 + return o == (gpr_atm)InterlockedCompareExchangeAcquire64( + (volatile LONGLONG *)p, (LONGLONG)n, (LONGLONG)o); +#else + return o == (gpr_atm)InterlockedCompareExchangeAcquire((volatile LONG *)p, + (LONG)n, (LONG)o); +#endif +} + +static __inline int gpr_atm_acq_cas(gpr_atm *p, gpr_atm o, gpr_atm n) { +#ifdef GPR_ARCH_64 + return o == (gpr_atm)InterlockedCompareExchangeAcquire64( + (volatile LONGLONG *)p, (LONGLONG)n, (LONGLONG)o); +#else + return o == (gpr_atm)InterlockedCompareExchangeAcquire((volatile LONG *)p, + (LONG)n, (LONG)o); +#endif +} + +static __inline int gpr_atm_rel_cas(gpr_atm *p, gpr_atm o, gpr_atm n) { +#ifdef GPR_ARCH_64 + return o == (gpr_atm)InterlockedCompareExchangeRelease64( + (volatile LONGLONG *)p, (LONGLONG)n, (LONGLONG)o); +#else + return o == (gpr_atm)InterlockedCompareExchangeRelease((volatile LONG *)p, + (LONG)n, (LONG)o); +#endif +} + +static __inline gpr_atm gpr_atm_no_barrier_fetch_add(gpr_atm *p, + gpr_atm delta) { + /* Use the CAS operation to get pointer-sized fetch and add */ + gpr_atm old; + do { + old = *p; + } while (!gpr_atm_no_barrier_cas(p, old, old + delta)); + return old; +} + +static __inline gpr_atm gpr_atm_full_fetch_add(gpr_atm *p, gpr_atm delta) { + /* Use a CAS operation to get pointer-sized fetch and add */ + gpr_atm old; +#ifdef GPR_ARCH_64 + do { + old = *p; + } while (old != (gpr_atm)InterlockedCompareExchange64((volatile LONGLONG *)p, + (LONGLONG)old + delta, + (LONGLONG)old)); +#else + do { + old = *p; + } while (old != (gpr_atm)InterlockedCompareExchange( + (volatile LONG *)p, (LONG)old + delta, (LONG)old)); +#endif + return old; +} + +#endif /* GRPC_SUPPORT_ATM_WIN32_H */ diff --git a/include/grpc/support/cmdline.h b/include/grpc/support/cmdline.h new file mode 100644 index 00000000..028dac29 --- /dev/null +++ b/include/grpc/support/cmdline.h @@ -0,0 +1,97 @@ +/* + * + * Copyright 2015, 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_SUPPORT_CMDLINE_H +#define GRPC_SUPPORT_CMDLINE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Simple command line parser. + + Supports flags that can be specified as -foo, --foo, --no-foo, -no-foo, etc + And integers, strings that can be specified as -foo=4, -foo blah, etc + + No support for short command line options (but we may get that in the + future.) + + Usage (for a program with a single flag argument 'foo'): + + int main(int argc, char **argv) { + gpr_cmdline *cl; + int verbose = 0; + + cl = gpr_cmdline_create("My cool tool"); + gpr_cmdline_add_int(cl, "verbose", "Produce verbose output?", &verbose); + gpr_cmdline_parse(cl, argc, argv); + gpr_cmdline_destroy(cl); + + if (verbose) { + gpr_log(GPR_INFO, "Goodbye cruel world!"); + } + + return 0; + } */ + +typedef struct gpr_cmdline gpr_cmdline; + +/* Construct a command line parser: takes a short description of the tool + doing the parsing */ +gpr_cmdline *gpr_cmdline_create(const char *description); +/* Add an integer parameter, with a name (used on the command line) and some + helpful text (used in the command usage) */ +void gpr_cmdline_add_int(gpr_cmdline *cl, const char *name, const char *help, + int *value); +/* The same, for a boolean flag */ +void gpr_cmdline_add_flag(gpr_cmdline *cl, const char *name, const char *help, + int *value); +/* And for a string */ +void gpr_cmdline_add_string(gpr_cmdline *cl, const char *name, const char *help, + char **value); +/* Set a callback for non-named arguments */ +void gpr_cmdline_on_extra_arg( + gpr_cmdline *cl, const char *name, const char *help, + void (*on_extra_arg)(void *user_data, const char *arg), void *user_data); +/* Parse the command line */ +void gpr_cmdline_parse(gpr_cmdline *cl, int argc, char **argv); +/* Destroy the parser */ +void gpr_cmdline_destroy(gpr_cmdline *cl); +/* Get a string describing usage */ +char *gpr_cmdline_usage_string(gpr_cmdline *cl, const char *argv0); + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_SUPPORT_CMDLINE_H */ diff --git a/include/grpc/support/cpu.h b/include/grpc/support/cpu.h new file mode 100644 index 00000000..7d8af599 --- /dev/null +++ b/include/grpc/support/cpu.h @@ -0,0 +1,57 @@ +/* + * + * Copyright 2015, 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_SUPPORT_CPU_H +#define GRPC_SUPPORT_CPU_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Interface providing CPU information for currently running system */ + +/* Return the number of CPU cores on the current system. Will return 0 if + the information is not available. */ +unsigned gpr_cpu_num_cores(void); + +/* Return the CPU on which the current thread is executing; N.B. This should + be considered advisory only - it is possible that the thread is switched + to a different CPU at any time. Returns a value in range + [0, gpr_cpu_num_cores() - 1] */ +unsigned gpr_cpu_current_cpu(void); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* GRPC_SUPPORT_CPU_H */ diff --git a/include/grpc/support/histogram.h b/include/grpc/support/histogram.h new file mode 100644 index 00000000..2fd10842 --- /dev/null +++ b/include/grpc/support/histogram.h @@ -0,0 +1,76 @@ +/* + * + * Copyright 2015, 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_SUPPORT_HISTOGRAM_H +#define GRPC_SUPPORT_HISTOGRAM_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct gpr_histogram gpr_histogram; + +gpr_histogram *gpr_histogram_create(double resolution, double max_bucket_start); +void gpr_histogram_destroy(gpr_histogram *h); +void gpr_histogram_add(gpr_histogram *h, double x); + +/* The following merges the second histogram into the first. It only works + if they have the same buckets and resolution. Returns 0 on failure, 1 + on success */ +int gpr_histogram_merge(gpr_histogram *dst, gpr_histogram *src); + +double gpr_histogram_percentile(gpr_histogram *histogram, double percentile); +double gpr_histogram_mean(gpr_histogram *histogram); +double gpr_histogram_stddev(gpr_histogram *histogram); +double gpr_histogram_variance(gpr_histogram *histogram); +double gpr_histogram_maximum(gpr_histogram *histogram); +double gpr_histogram_minimum(gpr_histogram *histogram); +double gpr_histogram_count(gpr_histogram *histogram); +double gpr_histogram_sum(gpr_histogram *histogram); +double gpr_histogram_sum_of_squares(gpr_histogram *histogram); + +const gpr_uint32 *gpr_histogram_get_contents(gpr_histogram *histogram, + size_t *count); +void gpr_histogram_merge_contents(gpr_histogram *histogram, + const gpr_uint32 *data, size_t data_count, + double min_seen, double max_seen, double sum, + double sum_of_squares, double count); + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_SUPPORT_HISTOGRAM_H */ diff --git a/include/grpc/support/host_port.h b/include/grpc/support/host_port.h new file mode 100644 index 00000000..375d1774 --- /dev/null +++ b/include/grpc/support/host_port.h @@ -0,0 +1,64 @@ +/* + * + * Copyright 2015, 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_SUPPORT_HOST_PORT_H +#define GRPC_SUPPORT_HOST_PORT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Given a host and port, creates a newly-allocated string of the form + "host:port" or "[ho:st]:port", depending on whether the host contains colons + like an IPv6 literal. If the host is already bracketed, then additional + brackets will not be added. + + Usage is similar to gpr_asprintf: returns the number of bytes written + (excluding the final '\0'), and *out points to a string which must later be + destroyed using gpr_free(). + + In the unlikely event of an error, returns -1 and sets *out to NULL. */ +int gpr_join_host_port(char **out, const char *host, int port); + +/* Given a name in the form "host:port" or "[ho:st]:port", split into hostname + and port number, into newly allocated strings, which must later be + destroyed using gpr_free(). + Return 1 on success, 0 on failure. Guarantees *host and *port == NULL on + failure. */ +int gpr_split_host_port(const char *name, char **host, char **port); + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_SUPPORT_HOST_PORT_H */ diff --git a/include/grpc/support/log.h b/include/grpc/support/log.h new file mode 100644 index 00000000..59db4ba1 --- /dev/null +++ b/include/grpc/support/log.h @@ -0,0 +1,108 @@ +/* + * + * Copyright 2015, 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_SUPPORT_LOG_H +#define GRPC_SUPPORT_LOG_H + +#include /* for abort() */ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* GPR log API. + + Usage (within grpc): + + int argument1 = 3; + char* argument2 = "hello"; + gpr_log(GPR_DEBUG, "format string %d", argument1); + gpr_log(GPR_INFO, "hello world"); + gpr_log(GPR_ERROR, "%d %s!!", argument1, argument2); */ + +/* The severity of a log message - use the #defines below when calling into + gpr_log to additionally supply file and line data */ +typedef enum gpr_log_severity { + GPR_LOG_SEVERITY_DEBUG, + GPR_LOG_SEVERITY_INFO, + GPR_LOG_SEVERITY_ERROR +} gpr_log_severity; + +/* Returns a string representation of the log severity */ +const char *gpr_log_severity_string(gpr_log_severity severity); + +/* Macros to build log contexts at various severity levels */ +#define GPR_DEBUG __FILE__, __LINE__, GPR_LOG_SEVERITY_DEBUG +#define GPR_INFO __FILE__, __LINE__, GPR_LOG_SEVERITY_INFO +#define GPR_ERROR __FILE__, __LINE__, GPR_LOG_SEVERITY_ERROR + +/* Log a message. It's advised to use GPR_xxx above to generate the context + * for each message */ +void gpr_log(const char *file, int line, gpr_log_severity severity, + const char *format, ...); + +void gpr_log_message(const char *file, int line, gpr_log_severity severity, + const char *message); + +/* Log overrides: applications can use this API to intercept logging calls + and use their own implementations */ + +typedef struct { + const char *file; + int line; + gpr_log_severity severity; + const char *message; +} gpr_log_func_args; + +typedef void (*gpr_log_func)(gpr_log_func_args *args); +void gpr_set_log_function(gpr_log_func func); + +/* abort() the process if x is zero, having written a line to the log. + + Intended for internal invariants. If the error can be recovered from, + without the possibility of corruption, or might best be reflected via + an exception in a higher-level language, consider returning error code. */ +#define GPR_ASSERT(x) \ + do { \ + if (!(x)) { \ + gpr_log(GPR_ERROR, "assertion failed: %s", #x); \ + abort(); \ + } \ + } while (0) + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_SUPPORT_LOG_H */ diff --git a/include/grpc/support/log_win32.h b/include/grpc/support/log_win32.h new file mode 100644 index 00000000..ea6b16dd --- /dev/null +++ b/include/grpc/support/log_win32.h @@ -0,0 +1,51 @@ +/* + * + * Copyright 2015, 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_SUPPORT_LOG_WIN32_H +#define GRPC_SUPPORT_LOG_WIN32_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Returns a string allocated with gpr_malloc that contains a UTF-8 + * formatted error message, corresponding to the error messageid. + * Use in conjunction with GetLastError() et al. + */ +char *gpr_format_message(DWORD messageid); + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_SUPPORT_LOG_WIN32_H */ diff --git a/include/grpc/support/port_platform.h b/include/grpc/support/port_platform.h new file mode 100644 index 00000000..d0981555 --- /dev/null +++ b/include/grpc/support/port_platform.h @@ -0,0 +1,334 @@ +/* + * + * Copyright 2015, 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_SUPPORT_PORT_PLATFORM_H +#define GRPC_SUPPORT_PORT_PLATFORM_H + +/* Get windows.h included everywhere (we need it) */ +#if defined(_WIN64) || defined(WIN64) || defined(_WIN32) || defined(WIN32) +#ifndef WIN32_LEAN_AND_MEAN +#define GRPC_WIN32_LEAN_AND_MEAN_WAS_NOT_DEFINED +#define WIN32_LEAN_AND_MEAN +#endif /* WIN32_LEAN_AND_MEAN */ + +#ifndef NOMINMAX +#define GRPC_NOMINMX_WAS_NOT_DEFINED +#define NOMINMAX +#endif /* NOMINMAX */ + +#if defined(_WIN32_WINNT) +#if _WIN32_WINNT < 0x0600 +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0600 +#endif /* _WIN32_WINNT < 0x0600 */ +#endif /* defined(_WIN32_WINNT) */ + +#include + +#ifdef GRPC_WIN32_LEAN_AND_MEAN_WAS_NOT_DEFINED +#undef GRPC_WIN32_LEAN_AND_MEAN_WAS_NOT_DEFINED +#undef WIN32_LEAN_AND_MEAN +#endif /* GRPC_WIN32_LEAN_AND_MEAN_WAS_NOT_DEFINED */ + +#ifdef GRPC_NOMINMAX_WAS_NOT_DEFINED +#undef GRPC_NOMINMAX_WAS_NOT_DEFINED +#undef NOMINMAX +#endif /* GRPC_WIN32_LEAN_AND_MEAN_WAS_NOT_DEFINED */ +#endif /* defined(_WIN64) || defined(WIN64) || defined(_WIN32) || \ + defined(WIN32) */ + +/* Override this file with one for your platform if you need to redefine + things. */ + +#if !defined(GPR_NO_AUTODETECT_PLATFORM) +#if defined(_WIN64) || defined(WIN64) +#define GPR_PLATFORM_STRING "windows" +#define GPR_WIN32 1 +#define GPR_ARCH_64 1 +#define GPR_GETPID_IN_PROCESS_H 1 +#define GPR_WINSOCK_SOCKET 1 +#ifdef __GNUC__ +#define GPR_GCC_ATOMIC 1 +#define GPR_GCC_TLS 1 +#else +#define GPR_WIN32_ATOMIC 1 +#define GPR_MSVC_TLS 1 +#endif +#define GPR_WINDOWS_CRASH_HANDLER 1 +#elif defined(_WIN32) || defined(WIN32) +#define GPR_PLATFORM_STRING "windows" +#define GPR_ARCH_32 1 +#define GPR_WIN32 1 +#define GPR_GETPID_IN_PROCESS_H 1 +#define GPR_WINSOCK_SOCKET 1 +#ifdef __GNUC__ +#define GPR_GCC_ATOMIC 1 +#define GPR_GCC_TLS 1 +#else +#define GPR_WIN32_ATOMIC 1 +#define GPR_MSVC_TLS 1 +#endif +#define GPR_WINDOWS_CRASH_HANDLER 1 +#elif defined(ANDROID) || defined(__ANDROID__) +#define GPR_PLATFORM_STRING "android" +#define GPR_ANDROID 1 +#define GPR_ARCH_32 1 +#define GPR_CPU_LINUX 1 +#define GPR_GCC_SYNC 1 +#define GPR_GCC_TLS 1 +#define GPR_POSIX_MULTIPOLL_WITH_POLL 1 +#define GPR_POSIX_WAKEUP_FD 1 +#define GPR_LINUX_EVENTFD 1 +#define GPR_POSIX_SOCKET 1 +#define GPR_POSIX_SOCKETADDR 1 +#define GPR_POSIX_SOCKETUTILS 1 +#define GPR_POSIX_ENV 1 +#define GPR_POSIX_FILE 1 +#define GPR_POSIX_STRING 1 +#define GPR_POSIX_SUBPROCESS 1 +#define GPR_POSIX_SYNC 1 +#define GPR_POSIX_TIME 1 +#define GPR_GETPID_IN_UNISTD_H 1 +#define GPR_HAVE_MSG_NOSIGNAL 1 +#elif defined(__linux__) +#define GPR_PLATFORM_STRING "linux" +#ifndef _BSD_SOURCE +#define _BSD_SOURCE +#endif +#ifndef _DEFAULT_SOURCE +#define _DEFAULT_SOURCE +#endif +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include +#define GPR_CPU_LINUX 1 +#define GPR_GCC_ATOMIC 1 +#define GPR_GCC_TLS 1 +#define GPR_LINUX 1 +#define GPR_LINUX_MULTIPOLL_WITH_EPOLL 1 +#define GPR_POSIX_WAKEUP_FD 1 +#define GPR_POSIX_SOCKET 1 +#define GPR_POSIX_SOCKETADDR 1 +#ifdef __GLIBC_PREREQ +#if __GLIBC_PREREQ(2, 9) +#define GPR_LINUX_EVENTFD 1 +#endif +#if __GLIBC_PREREQ(2, 10) +#define GPR_LINUX_SOCKETUTILS 1 +#endif +#if __GLIBC_PREREQ(2, 17) +#define GPR_LINUX_ENV 1 +#endif +#endif +#ifndef GPR_LINUX_EVENTFD +#define GPR_POSIX_NO_SPECIAL_WAKEUP_FD 1 +#endif +#ifndef GPR_LINUX_SOCKETUTILS +#define GPR_POSIX_SOCKETUTILS +#endif +#ifndef GPR_LINUX_ENV +#define GPR_POSIX_ENV 1 +#endif +#define GPR_POSIX_FILE 1 +#define GPR_POSIX_STRING 1 +#define GPR_POSIX_SUBPROCESS 1 +#define GPR_POSIX_SYNC 1 +#define GPR_POSIX_TIME 1 +#define GPR_GETPID_IN_UNISTD_H 1 +#define GPR_HAVE_MSG_NOSIGNAL 1 +#ifdef _LP64 +#define GPR_ARCH_64 1 +#else /* _LP64 */ +#define GPR_ARCH_32 1 +#endif /* _LP64 */ +#elif defined(__APPLE__) +#include +/* Provides IPV6_RECVPKTINFO */ +#define __APPLE_USE_RFC_3542 +#ifndef _BSD_SOURCE +#define _BSD_SOURCE +#endif +#if TARGET_OS_IPHONE +#define GPR_PLATFORM_STRING "ios" +#define GPR_CPU_IPHONE 1 +#define GPR_PTHREAD_TLS 1 +#else /* TARGET_OS_IPHONE */ +#define GPR_PLATFORM_STRING "osx" +#define GPR_CPU_POSIX 1 +#define GPR_GCC_TLS 1 +#endif +#define GPR_GCC_ATOMIC 1 +#define GPR_POSIX_LOG 1 +#define GPR_POSIX_MULTIPOLL_WITH_POLL 1 +#define GPR_POSIX_WAKEUP_FD 1 +#define GPR_POSIX_NO_SPECIAL_WAKEUP_FD 1 +#define GPR_POSIX_SOCKET 1 +#define GPR_POSIX_SOCKETADDR 1 +#define GPR_POSIX_SOCKETUTILS 1 +#define GPR_POSIX_ENV 1 +#define GPR_POSIX_FILE 1 +#define GPR_POSIX_STRING 1 +#define GPR_POSIX_SUBPROCESS 1 +#define GPR_POSIX_SYNC 1 +#define GPR_POSIX_TIME 1 +#define GPR_GETPID_IN_UNISTD_H 1 +#define GPR_HAVE_SO_NOSIGPIPE 1 +#ifdef _LP64 +#define GPR_ARCH_64 1 +#else /* _LP64 */ +#define GPR_ARCH_32 1 +#endif /* _LP64 */ +#elif defined(__FreeBSD__) +#define GPR_PLATFORM_STRING "freebsd" +#ifndef _BSD_SOURCE +#define _BSD_SOURCE +#endif +#define GPR_CPU_POSIX 1 +#define GPR_GCC_ATOMIC 1 +#define GPR_GCC_TLS 1 +#define GPR_POSIX_LOG 1 +#define GPR_POSIX_MULTIPOLL_WITH_POLL 1 +#define GPR_POSIX_WAKEUP_FD 1 +#define GPR_POSIX_NO_SPECIAL_WAKEUP_FD 1 +#define GPR_POSIX_SOCKET 1 +#define GPR_POSIX_SOCKETADDR 1 +#define GPR_POSIX_SOCKETUTILS 1 +#define GPR_POSIX_ENV 1 +#define GPR_POSIX_FILE 1 +#define GPR_POSIX_STRING 1 +#define GPR_POSIX_SUBPROCESS 1 +#define GPR_POSIX_SYNC 1 +#define GPR_POSIX_TIME 1 +#define GPR_GETPID_IN_UNISTD_H 1 +#define GPR_HAVE_SO_NOSIGPIPE 1 +#ifdef _LP64 +#define GPR_ARCH_64 1 +#else /* _LP64 */ +#define GPR_ARCH_32 1 +#endif /* _LP64 */ +#else +#error Could not auto-detect platform +#endif +#endif /* GPR_NO_AUTODETECT_PLATFORM */ + +#ifndef GPR_PLATFORM_STRING +#warning "GPR_PLATFORM_STRING not auto-detected" +#define GPR_PLATFORM_STRING "unknown" +#endif + +/* For a common case, assume that the platform has a C99-like stdint.h */ + +#include + +/* Cache line alignment */ +#ifndef GPR_CACHELINE_SIZE_LOG +#if defined(__i386__) || defined(__x86_64__) +#define GPR_CACHELINE_SIZE_LOG 6 +#endif +#ifndef GPR_CACHELINE_SIZE_LOG +/* A reasonable default guess. Note that overestimates tend to waste more + space, while underestimates tend to waste more time. */ +#define GPR_CACHELINE_SIZE_LOG 6 +#endif /* GPR_CACHELINE_SIZE_LOG */ +#endif /* GPR_CACHELINE_SIZE_LOG */ + +#define GPR_CACHELINE_SIZE (1 << GPR_CACHELINE_SIZE_LOG) + +/* scrub GCC_ATOMIC if it's not available on this compiler */ +#if defined(GPR_GCC_ATOMIC) && !defined(__ATOMIC_RELAXED) +#undef GPR_GCC_ATOMIC +#define GPR_GCC_SYNC 1 +#endif + +/* Validate platform combinations */ +#if defined(GPR_GCC_ATOMIC) + defined(GPR_GCC_SYNC) + \ + defined(GPR_WIN32_ATOMIC) != \ + 1 +#error Must define exactly one of GPR_GCC_ATOMIC, GPR_GCC_SYNC, GPR_WIN32_ATOMIC +#endif + +#if defined(GPR_ARCH_32) + defined(GPR_ARCH_64) != 1 +#error Must define exactly one of GPR_ARCH_32, GPR_ARCH_64 +#endif + +#if defined(GPR_CPU_LINUX) + defined(GPR_CPU_POSIX) + defined(GPR_WIN32) + \ + defined(GPR_CPU_IPHONE) + defined(GPR_CPU_CUSTOM) != \ + 1 +#error Must define exactly one of GPR_CPU_LINUX, GPR_CPU_POSIX, GPR_WIN32, GPR_CPU_IPHONE, GPR_CPU_CUSTOM +#endif + +#if defined(GPR_POSIX_MULTIPOLL_WITH_POLL) && !defined(GPR_POSIX_SOCKET) +#error Must define GPR_POSIX_SOCKET to use GPR_POSIX_MULTIPOLL_WITH_POLL +#endif + +#if defined(GPR_POSIX_SOCKET) + defined(GPR_WINSOCK_SOCKET) + \ + defined(GPR_CUSTOM_SOCKET) != \ + 1 +#error Must define exactly one of GPR_POSIX_SOCKET, GPR_WINSOCK_SOCKET, GPR_CUSTOM_SOCKET +#endif + +#if defined(GPR_MSVC_TLS) + defined(GPR_GCC_TLS) + defined(GPR_PTHREAD_TLS) + \ + defined(GPR_CUSTOM_TLS) != \ + 1 +#error Must define exactly one of GPR_MSVC_TLS, GPR_GCC_TLS, GPR_PTHREAD_TLS, GPR_CUSTOM_TLS +#endif + +typedef int16_t gpr_int16; +typedef int32_t gpr_int32; +typedef int64_t gpr_int64; +typedef uint8_t gpr_uint8; +typedef uint16_t gpr_uint16; +typedef uint32_t gpr_uint32; +typedef uint64_t gpr_uint64; +typedef intmax_t gpr_intmax; +typedef intptr_t gpr_intptr; +typedef uintmax_t gpr_uintmax; +typedef uintptr_t gpr_uintptr; + +/* INT64_MAX is unavailable on some platforms. */ +#define GPR_INT64_MAX (gpr_int64)(~(gpr_uint64)0 >> 1) + +/* maximum alignment needed for any type on this platform, rounded up to a + power of two */ +#define GPR_MAX_ALIGNMENT 16 + +#ifndef GRPC_MUST_USE_RESULT +#ifdef __GNUC__ +#define GRPC_MUST_USE_RESULT __attribute__((warn_unused_result)) +#else +#define GRPC_MUST_USE_RESULT +#endif +#endif + +#endif /* GRPC_SUPPORT_PORT_PLATFORM_H */ diff --git a/include/grpc/support/slice.h b/include/grpc/support/slice.h new file mode 100644 index 00000000..3abb1b7c --- /dev/null +++ b/include/grpc/support/slice.h @@ -0,0 +1,179 @@ +/* + * + * Copyright 2015, 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_SUPPORT_SLICE_H +#define GRPC_SUPPORT_SLICE_H + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Slice API + + A slice represents a contiguous reference counted array of bytes. + It is cheap to take references to a slice, and it is cheap to create a + slice pointing to a subset of another slice. + + The data-structure for slices is exposed here to allow non-gpr code to + build slices from whatever data they have available. + + When defining interfaces that handle slices, care should be taken to define + reference ownership semantics (who should call unref?) and mutability + constraints (is the callee allowed to modify the slice?) */ + +/* Reference count container for gpr_slice. Contains function pointers to + increment and decrement reference counts. Implementations should cleanup + when the reference count drops to zero. + Typically client code should not touch this, and use gpr_slice_malloc, + gpr_slice_new, or gpr_slice_new_with_len instead. */ +typedef struct gpr_slice_refcount { + void (*ref)(void *); + void (*unref)(void *); +} gpr_slice_refcount; + +#define GPR_SLICE_INLINED_SIZE (sizeof(size_t) + sizeof(gpr_uint8 *) - 1) + +/* A gpr_slice s, if initialized, represents the byte range + s.bytes[0..s.length-1]. + + It can have an associated ref count which has a destruction routine to be run + when the ref count reaches zero (see gpr_slice_new() and grp_slice_unref()). + Multiple gpr_slice values may share a ref count. + + If the slice does not have a refcount, it represents an inlined small piece + of data that is copied by value. */ +typedef struct gpr_slice { + struct gpr_slice_refcount *refcount; + union { + struct { + gpr_uint8 *bytes; + size_t length; + } refcounted; + struct { + gpr_uint8 length; + gpr_uint8 bytes[GPR_SLICE_INLINED_SIZE]; + } inlined; + } data; +} gpr_slice; + +#define GPR_SLICE_START_PTR(slice) \ + ((slice).refcount ? (slice).data.refcounted.bytes \ + : (slice).data.inlined.bytes) +#define GPR_SLICE_LENGTH(slice) \ + ((slice).refcount ? (slice).data.refcounted.length \ + : (slice).data.inlined.length) +#define GPR_SLICE_SET_LENGTH(slice, newlen) \ + ((slice).refcount ? ((slice).data.refcounted.length = (size_t)(newlen)) \ + : ((slice).data.inlined.length = (gpr_uint8)(newlen))) +#define GPR_SLICE_END_PTR(slice) \ + GPR_SLICE_START_PTR(slice) + GPR_SLICE_LENGTH(slice) +#define GPR_SLICE_IS_EMPTY(slice) (GPR_SLICE_LENGTH(slice) == 0) + +/* Increment the refcount of s. Requires slice is initialized. + Returns s. */ +gpr_slice gpr_slice_ref(gpr_slice s); + +/* Decrement the ref count of s. If the ref count of s reaches zero, all + slices sharing the ref count are destroyed, and considered no longer + initialized. If s is ultimately derived from a call to gpr_slice_new(start, + len, dest) where dest!=NULL , then (*dest)(start) is called, else if s is + ultimately derived from a call to gpr_slice_new_with_len(start, len, dest) + where dest!=NULL , then (*dest)(start, len). Requires s initialized. */ +void gpr_slice_unref(gpr_slice s); + +/* Create a slice pointing at some data. Calls malloc to allocate a refcount + for the object, and arranges that destroy will be called with the pointer + passed in at destruction. */ +gpr_slice gpr_slice_new(void *p, size_t len, void (*destroy)(void *)); + +/* Equivalent to gpr_slice_new, but with a two argument destroy function that + also takes the slice length. */ +gpr_slice gpr_slice_new_with_len(void *p, size_t len, + void (*destroy)(void *, size_t)); + +/* Equivalent to gpr_slice_new(malloc(len), len, free), but saves one malloc() + call. + Aborts if malloc() fails. */ +gpr_slice gpr_slice_malloc(size_t length); + +/* Create a slice by copying a string. + Does not preserve null terminators. + Equivalent to: + size_t len = strlen(source); + gpr_slice slice = gpr_slice_malloc(len); + memcpy(slice->data, source, len); */ +gpr_slice gpr_slice_from_copied_string(const char *source); + +/* Create a slice by copying a buffer. + Equivalent to: + gpr_slice slice = gpr_slice_malloc(len); + memcpy(slice->data, source, len); */ +gpr_slice gpr_slice_from_copied_buffer(const char *source, size_t len); + +/* Return a result slice derived from s, which shares a ref count with s, where + result.data==s.data+begin, and result.length==end-begin. + The ref count of s is increased by one. + Requires s initialized, begin <= end, begin <= s.length, and + end <= source->length. */ +gpr_slice gpr_slice_sub(gpr_slice s, size_t begin, size_t end); + +/* The same as gpr_slice_sub, but without altering the ref count */ +gpr_slice gpr_slice_sub_no_ref(gpr_slice s, size_t begin, size_t end); + +/* Splits s into two: modifies s to be s[0:split], and returns a new slice, + sharing a refcount with s, that contains s[split:s.length]. + Requires s intialized, split <= s.length */ +gpr_slice gpr_slice_split_tail(gpr_slice *s, size_t split); + +/* Splits s into two: modifies s to be s[split:s.length], and returns a new + slice, sharing a refcount with s, that contains s[0:split]. + Requires s intialized, split <= s.length */ +gpr_slice gpr_slice_split_head(gpr_slice *s, size_t split); + +gpr_slice gpr_empty_slice(void); + +/* Returns <0 if a < b, ==0 if a == b, >0 if a > b + The order is arbitrary, and is not guaranteed to be stable across different + versions of the API. */ +int gpr_slice_cmp(gpr_slice a, gpr_slice b); +int gpr_slice_str_cmp(gpr_slice a, const char *b); + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_SUPPORT_SLICE_H */ diff --git a/include/grpc/support/slice_buffer.h b/include/grpc/support/slice_buffer.h new file mode 100644 index 00000000..04db003a --- /dev/null +++ b/include/grpc/support/slice_buffer.h @@ -0,0 +1,96 @@ +/* + * + * Copyright 2015, 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_SUPPORT_SLICE_BUFFER_H +#define GRPC_SUPPORT_SLICE_BUFFER_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define GRPC_SLICE_BUFFER_INLINE_ELEMENTS 8 + +/* Represents an expandable array of slices, to be interpreted as a single item + TODO(ctiller): inline some small number of elements into the struct, to + avoid per-call allocations */ +typedef struct { + /* slices in the array */ + gpr_slice *slices; + /* the number of slices in the array */ + size_t count; + /* the number of slices allocated in the array */ + size_t capacity; + /* the combined length of all slices in the array */ + size_t length; + /* inlined elements to avoid allocations */ + gpr_slice inlined[GRPC_SLICE_BUFFER_INLINE_ELEMENTS]; +} gpr_slice_buffer; + +/* initialize a slice buffer */ +void gpr_slice_buffer_init(gpr_slice_buffer *sb); +/* destroy a slice buffer - unrefs any held elements */ +void gpr_slice_buffer_destroy(gpr_slice_buffer *sb); +/* Add an element to a slice buffer - takes ownership of the slice. + This function is allowed to concatenate the passed in slice to the end of + some other slice if desired by the slice buffer. */ +void gpr_slice_buffer_add(gpr_slice_buffer *sb, gpr_slice slice); +/* add an element to a slice buffer - takes ownership of the slice and returns + the index of the slice. + Guarantees that the slice will not be concatenated at the end of another + slice (i.e. the data for this slice will begin at the first byte of the + slice at the returned index in sb->slices) + The implementation MAY decide to concatenate data at the end of a small + slice added in this fashion. */ +size_t gpr_slice_buffer_add_indexed(gpr_slice_buffer *sb, gpr_slice slice); +void gpr_slice_buffer_addn(gpr_slice_buffer *sb, gpr_slice *slices, size_t n); +/* add a very small (less than 8 bytes) amount of data to the end of a slice + buffer: returns a pointer into which to add the data */ +gpr_uint8 *gpr_slice_buffer_tiny_add(gpr_slice_buffer *sb, unsigned len); +/* pop the last buffer, but don't unref it */ +void gpr_slice_buffer_pop(gpr_slice_buffer *sb); +/* clear a slice buffer, unref all elements */ +void gpr_slice_buffer_reset_and_unref(gpr_slice_buffer *sb); +/* swap the contents of two slice buffers */ +void gpr_slice_buffer_swap(gpr_slice_buffer *a, gpr_slice_buffer *b); +/* move all of the elements of src into dst */ +void gpr_slice_buffer_move_into(gpr_slice_buffer *src, gpr_slice_buffer *dst); +/* remove n bytes from the end of a slice buffer */ +void gpr_slice_buffer_trim_end(gpr_slice_buffer *src, size_t n); + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_SUPPORT_SLICE_BUFFER_H */ diff --git a/include/grpc/support/string_util.h b/include/grpc/support/string_util.h new file mode 100644 index 00000000..109f9ffd --- /dev/null +++ b/include/grpc/support/string_util.h @@ -0,0 +1,61 @@ +/* + * + * Copyright 2015, 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_SUPPORT_STRING_UTIL_H +#define GRPC_SUPPORT_STRING_UTIL_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* String utility functions */ + +/* Returns a copy of src that can be passed to gpr_free(). + If allocation fails or if src is NULL, returns NULL. */ +char *gpr_strdup(const char *src); + +/* printf to a newly-allocated string. The set of supported formats may vary + between platforms. + + On success, returns the number of bytes printed (excluding the final '\0'), + and *strp points to a string which must later be destroyed with gpr_free(). + + On error, returns -1 and sets *strp to NULL. If the format string is bad, + the result is undefined. */ +int gpr_asprintf(char **strp, const char *format, ...); + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_SUPPORT_STRING_UTIL_H */ diff --git a/include/grpc/support/subprocess.h b/include/grpc/support/subprocess.h new file mode 100644 index 00000000..654623fd --- /dev/null +++ b/include/grpc/support/subprocess.h @@ -0,0 +1,57 @@ +/* + * + * Copyright 2015, 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_SUPPORT_SUBPROCESS_H +#define GRPC_SUPPORT_SUBPROCESS_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct gpr_subprocess gpr_subprocess; + +/* .exe on windows, empty on unices */ +const char *gpr_subprocess_binary_extension(); + +gpr_subprocess *gpr_subprocess_create(int argc, const char **argv); +/* if subprocess has not been joined, kill it */ +void gpr_subprocess_destroy(gpr_subprocess *p); +/* returns exit status; can be called at most once */ +int gpr_subprocess_join(gpr_subprocess *p); +void gpr_subprocess_interrupt(gpr_subprocess *p); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif diff --git a/include/grpc/support/sync.h b/include/grpc/support/sync.h new file mode 100644 index 00000000..172aea02 --- /dev/null +++ b/include/grpc/support/sync.h @@ -0,0 +1,315 @@ +/* + * + * Copyright 2015, 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_SUPPORT_SYNC_H +#define GRPC_SUPPORT_SYNC_H +/* Synchronization primitives for GPR. + + The type gpr_mu provides a non-reentrant mutex (lock). + + The type gpr_cv provides a condition variable. + + The type gpr_once provides for one-time initialization. + + The type gpr_event provides one-time-setting, reading, and + waiting of a void*, with memory barriers. + + The type gpr_refcount provides an object reference counter, + with memory barriers suitable to control + object lifetimes. + + The type gpr_stats_counter provides an atomic statistics counter. It + provides no memory barriers. + */ + +/* Platform-specific type declarations of gpr_mu and gpr_cv. */ +#include +#include + +#if defined(GPR_POSIX_SYNC) +#include +#elif defined(GPR_WIN32) +#include +#elif !defined(GPR_CUSTOM_SYNC) +#error Unable to determine platform for sync +#endif + +#include /* for gpr_timespec */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* --- Mutex interface --- + + At most one thread may hold an exclusive lock on a mutex at any given time. + Actions taken by a thread that holds a mutex exclusively happen after + actions taken by all previous holders of the mutex. Variables of type + gpr_mu are uninitialized when first declared. */ + +/* Initialize *mu. Requires: *mu uninitialized. */ +void gpr_mu_init(gpr_mu *mu); + +/* Cause *mu no longer to be initialized, freeing any memory in use. Requires: + *mu initialized; no other concurrent operation on *mu. */ +void gpr_mu_destroy(gpr_mu *mu); + +/* Wait until no thread has a lock on *mu, cause the calling thread to own an + exclusive lock on *mu, then return. May block indefinitely or crash if the + calling thread has a lock on *mu. Requires: *mu initialized. */ +void gpr_mu_lock(gpr_mu *mu); + +/* Release an exclusive lock on *mu held by the calling thread. Requires: *mu + initialized; the calling thread holds an exclusive lock on *mu. */ +void gpr_mu_unlock(gpr_mu *mu); + +/* Without blocking, attempt to acquire an exclusive lock on *mu for the + calling thread, then return non-zero iff success. Fail, if any thread holds + the lock; succeeds with high probability if no thread holds the lock. + Requires: *mu initialized. */ +int gpr_mu_trylock(gpr_mu *mu); + +/* --- Condition variable interface --- + + A while-loop should be used with gpr_cv_wait() when waiting for conditions + to become true. See the example below. Variables of type gpr_cv are + uninitialized when first declared. */ + +/* Initialize *cv. Requires: *cv uninitialized. */ +void gpr_cv_init(gpr_cv *cv); + +/* Cause *cv no longer to be initialized, freeing any memory in use. Requires: + *cv initialized; no other concurrent operation on *cv.*/ +void gpr_cv_destroy(gpr_cv *cv); + +/* Atomically release *mu and wait on *cv. When the calling thread is woken + from *cv or the deadline abs_deadline is exceeded, execute gpr_mu_lock(mu) + and return whether the deadline was exceeded. Use + abs_deadline==gpr_inf_future for no deadline. May return even when not + woken explicitly. Requires: *mu and *cv initialized; the calling thread + holds an exclusive lock on *mu. */ +int gpr_cv_wait(gpr_cv *cv, gpr_mu *mu, gpr_timespec abs_deadline); + +/* If any threads are waiting on *cv, wake at least one. + Clients may treat this as an optimization of gpr_cv_broadcast() + for use in the case where waking more than one waiter is not useful. + Requires: *cv initialized. */ +void gpr_cv_signal(gpr_cv *cv); + +/* Wake all threads waiting on *cv. Requires: *cv initialized. */ +void gpr_cv_broadcast(gpr_cv *cv); + +/* --- One-time initialization --- + + gpr_once must be declared with static storage class, and initialized with + GPR_ONCE_INIT. e.g., + static gpr_once once_var = GPR_ONCE_INIT; */ + +/* Ensure that (*init_routine)() has been called exactly once (for the + specified gpr_once instance) and then return. + If multiple threads call gpr_once() on the same gpr_once instance, one of + them will call (*init_routine)(), and the others will block until that call + finishes.*/ +void gpr_once_init(gpr_once *once, void (*init_routine)(void)); + +/* --- One-time event notification --- + + These operations act on a gpr_event, which should be initialized with + gpr_ev_init(), or with GPR_EVENT_INIT if static, e.g., + static gpr_event event_var = GPR_EVENT_INIT; + It requires no destruction. */ + +/* Initialize *ev. */ +void gpr_event_init(gpr_event *ev); + +/* Set *ev so that gpr_event_get() and gpr_event_wait() will return value. + Requires: *ev initialized; value != NULL; no prior or concurrent calls to + gpr_event_set(ev, ...) since initialization. */ +void gpr_event_set(gpr_event *ev, void *value); + +/* Return the value set by gpr_event_set(ev, ...), or NULL if no such call has + completed. If the result is non-NULL, all operations that occurred prior to + the gpr_event_set(ev, ...) set will be visible after this call returns. + Requires: *ev initialized. This operation is faster than acquiring a mutex + on most platforms. */ +void *gpr_event_get(gpr_event *ev); + +/* Wait until *ev is set by gpr_event_set(ev, ...), or abs_deadline is + exceeded, then return gpr_event_get(ev). Requires: *ev initialized. Use + abs_deadline==gpr_inf_future for no deadline. When the event has been + signalled before the call, this operation is faster than acquiring a mutex + on most platforms. */ +void *gpr_event_wait(gpr_event *ev, gpr_timespec abs_deadline); + +/* --- Reference counting --- + + These calls act on the type gpr_refcount. It requires no destruction. */ + +/* Initialize *r to value n. */ +void gpr_ref_init(gpr_refcount *r, int n); + +/* Increment the reference count *r. Requires *r initialized. */ +void gpr_ref(gpr_refcount *r); + +/* Increment the reference count *r by n. Requires *r initialized, n > 0. */ +void gpr_refn(gpr_refcount *r, int n); + +/* Decrement the reference count *r and return non-zero iff it has reached + zero. . Requires *r initialized. */ +int gpr_unref(gpr_refcount *r); + +/* --- Stats counters --- + + These calls act on the integral type gpr_stats_counter. It requires no + destruction. Static instances may be initialized with + gpr_stats_counter c = GPR_STATS_INIT; + Beware: These operations do not imply memory barriers. Do not use them to + synchronize other events. */ + +/* Initialize *c to the value n. */ +void gpr_stats_init(gpr_stats_counter *c, gpr_intptr n); + +/* *c += inc. Requires: *c initialized. */ +void gpr_stats_inc(gpr_stats_counter *c, gpr_intptr inc); + +/* Return *c. Requires: *c initialized. */ +gpr_intptr gpr_stats_read(const gpr_stats_counter *c); + +/* ==================Example use of interface=================== + A producer-consumer queue of up to N integers, + illustrating the use of the calls in this interface. */ +#if 0 + +#define N 4 + + typedef struct queue { + gpr_cv non_empty; /* Signalled when length becomes non-zero. */ + gpr_cv non_full; /* Signalled when length becomes non-N. */ + gpr_mu mu; /* Protects all fields below. + (That is, except during initialization or + destruction, the fields below should be accessed + only by a thread that holds mu.) */ + int head; /* Index of head of queue 0..N-1. */ + int length; /* Number of valid elements in queue 0..N. */ + int elem[N]; /* elem[head .. head+length-1] are queue elements. */ + } queue; + + /* Initialize *q. */ + void queue_init(queue *q) { + gpr_mu_init(&q->mu); + gpr_cv_init(&q->non_empty); + gpr_cv_init(&q->non_full); + q->head = 0; + q->length = 0; + } + + /* Free storage associated with *q. */ + void queue_destroy(queue *q) { + gpr_mu_destroy(&q->mu); + gpr_cv_destroy(&q->non_empty); + gpr_cv_destroy(&q->non_full); + } + + /* Wait until there is room in *q, then append x to *q. */ + void queue_append(queue *q, int x) { + gpr_mu_lock(&q->mu); + /* To wait for a predicate without a deadline, loop on the negation of the + predicate, and use gpr_cv_wait(..., gpr_inf_future) inside the loop + to release the lock, wait, and reacquire on each iteration. Code that + makes the condition true should use gpr_cv_broadcast() on the + corresponding condition variable. The predicate must be on state + protected by the lock. */ + while (q->length == N) { + gpr_cv_wait(&q->non_full, &q->mu, gpr_inf_future); + } + if (q->length == 0) { /* Wake threads blocked in queue_remove(). */ + /* It's normal to use gpr_cv_broadcast() or gpr_signal() while + holding the lock. */ + gpr_cv_broadcast(&q->non_empty); + } + q->elem[(q->head + q->length) % N] = x; + q->length++; + gpr_mu_unlock(&q->mu); + } + + /* If it can be done without blocking, append x to *q and return non-zero. + Otherwise return 0. */ + int queue_try_append(queue *q, int x) { + int result = 0; + if (gpr_mu_trylock(&q->mu)) { + if (q->length != N) { + if (q->length == 0) { /* Wake threads blocked in queue_remove(). */ + gpr_cv_broadcast(&q->non_empty); + } + q->elem[(q->head + q->length) % N] = x; + q->length++; + result = 1; + } + gpr_mu_unlock(&q->mu); + } + return result; + } + + /* Wait until the *q is non-empty or deadline abs_deadline passes. If the + queue is non-empty, remove its head entry, place it in *head, and return + non-zero. Otherwise return 0. */ + int queue_remove(queue *q, int *head, gpr_timespec abs_deadline) { + int result = 0; + gpr_mu_lock(&q->mu); + /* To wait for a predicate with a deadline, loop on the negation of the + predicate or until gpr_cv_wait() returns true. Code that makes + the condition true should use gpr_cv_broadcast() on the corresponding + condition variable. The predicate must be on state protected by the + lock. */ + while (q->length == 0 && + !gpr_cv_wait(&q->non_empty, &q->mu, abs_deadline)) { + } + if (q->length != 0) { /* Queue is non-empty. */ + result = 1; + if (q->length == N) { /* Wake threads blocked in queue_append(). */ + gpr_cv_broadcast(&q->non_full); + } + *head = q->elem[q->head]; + q->head = (q->head + 1) % N; + q->length--; + } /* else deadline exceeded */ + gpr_mu_unlock(&q->mu); + return result; + } +#endif /* 0 */ + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_SUPPORT_SYNC_H */ diff --git a/include/grpc/support/sync_generic.h b/include/grpc/support/sync_generic.h new file mode 100644 index 00000000..fd55e02e --- /dev/null +++ b/include/grpc/support/sync_generic.h @@ -0,0 +1,55 @@ +/* + * + * Copyright 2015, 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_SUPPORT_SYNC_GENERIC_H +#define GRPC_SUPPORT_SYNC_GENERIC_H +/* Generic type defintions for gpr_sync. */ + +#include + +/* gpr_event */ +typedef struct { gpr_atm state; } gpr_event; + +#define GPR_EVENT_INIT \ + { 0 } + +/* gpr_refcount */ +typedef struct { gpr_atm count; } gpr_refcount; + +/* gpr_stats_counter */ +typedef struct { gpr_atm value; } gpr_stats_counter; + +#define GPR_STATS_INIT \ + { 0 } + +#endif /* GRPC_SUPPORT_SYNC_GENERIC_H */ diff --git a/include/grpc/support/sync_posix.h b/include/grpc/support/sync_posix.h new file mode 100644 index 00000000..81ffa259 --- /dev/null +++ b/include/grpc/support/sync_posix.h @@ -0,0 +1,47 @@ +/* + * + * Copyright 2015, 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_SUPPORT_SYNC_POSIX_H +#define GRPC_SUPPORT_SYNC_POSIX_H + +#include + +#include + +typedef pthread_mutex_t gpr_mu; +typedef pthread_cond_t gpr_cv; +typedef pthread_once_t gpr_once; + +#define GPR_ONCE_INIT PTHREAD_ONCE_INIT + +#endif /* GRPC_SUPPORT_SYNC_POSIX_H */ diff --git a/include/grpc/support/sync_win32.h b/include/grpc/support/sync_win32.h new file mode 100644 index 00000000..8ddbeaab --- /dev/null +++ b/include/grpc/support/sync_win32.h @@ -0,0 +1,49 @@ +/* + * + * Copyright 2015, 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_SUPPORT_SYNC_WIN32_H +#define GRPC_SUPPORT_SYNC_WIN32_H + +#include + +typedef struct { + CRITICAL_SECTION cs; /* Not an SRWLock until Vista is unsupported */ + int locked; +} gpr_mu; + +typedef CONDITION_VARIABLE gpr_cv; + +typedef INIT_ONCE gpr_once; +#define GPR_ONCE_INIT INIT_ONCE_STATIC_INIT + +#endif /* GRPC_SUPPORT_SYNC_WIN32_H */ diff --git a/include/grpc/support/thd.h b/include/grpc/support/thd.h new file mode 100644 index 00000000..d3265f25 --- /dev/null +++ b/include/grpc/support/thd.h @@ -0,0 +1,91 @@ +/* + * + * Copyright 2015, 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_SUPPORT_THD_H +#define GRPC_SUPPORT_THD_H +/* Thread interface for GPR. + + Types + gpr_thd_id a thread identifier. + (Currently no calls take a thread identifier. + It exists for future extensibility.) + gpr_thd_options options used when creating a thread + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef gpr_uint64 gpr_thd_id; + +/* Thread creation options. */ +typedef struct { + int flags; /* Opaque field. Get and set with accessors below. */ +} gpr_thd_options; + +/* Create a new thread running (*thd_body)(arg) and place its thread identifier + in *t, and return true. If there are insufficient resources, return false. + If options==NULL, default options are used. + The thread is immediately runnable, and exits when (*thd_body)() returns. */ +int gpr_thd_new(gpr_thd_id *t, void (*thd_body)(void *arg), void *arg, + const gpr_thd_options *options); + +/* Return a gpr_thd_options struct with all fields set to defaults. */ +gpr_thd_options gpr_thd_options_default(void); + +/* Set the thread to become detached on startup - this is the default. */ +void gpr_thd_options_set_detached(gpr_thd_options *options); + +/* Set the thread to become joinable - mutually exclusive with detached. */ +void gpr_thd_options_set_joinable(gpr_thd_options *options); + +/* Returns non-zero if the option detached is set. */ +int gpr_thd_options_is_detached(const gpr_thd_options *options); + +/* Returns non-zero if the option joinable is set. */ +int gpr_thd_options_is_joinable(const gpr_thd_options *options); + +/* Returns the identifier of the current thread. */ +gpr_thd_id gpr_thd_currentid(void); + +/* Blocks until the specified thread properly terminates. + Calling this on a detached thread has unpredictable results. */ +void gpr_thd_join(gpr_thd_id t); + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_SUPPORT_THD_H */ diff --git a/include/grpc/support/time.h b/include/grpc/support/time.h new file mode 100644 index 00000000..f8ed893b --- /dev/null +++ b/include/grpc/support/time.h @@ -0,0 +1,128 @@ +/* + * + * Copyright 2015, 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_SUPPORT_TIME_H +#define GRPC_SUPPORT_TIME_H +/* Time support. + We use gpr_timespec, which is analogous to struct timespec. On some + machines, absolute times may be in local time. */ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* The clocks we support. */ +typedef enum { + /* Monotonic clock. Epoch undefined. Always moves forwards. */ + GPR_CLOCK_MONOTONIC = 0, + /* Realtime clock. May jump forwards or backwards. Settable by + the system administrator. Has its epoch at 0:00:00 UTC 1 Jan 1970. */ + GPR_CLOCK_REALTIME, + /* CPU cycle time obtained by rdtsc instruction on x86 platforms. Epoch + undefined. Degrades to GPR_CLOCK_REALTIME on other platforms. */ + GPR_CLOCK_PRECISE, + /* Unmeasurable clock type: no base, created by taking the difference + between two times */ + GPR_TIMESPAN +} gpr_clock_type; + +typedef struct gpr_timespec { + time_t tv_sec; + int tv_nsec; + /** Against which clock was this time measured? (or GPR_TIMESPAN if + this is a relative time meaure) */ + gpr_clock_type clock_type; +} gpr_timespec; + +/* Time constants. */ +gpr_timespec gpr_time_0(gpr_clock_type type); /* The zero time interval. */ +gpr_timespec gpr_inf_future(gpr_clock_type type); /* The far future */ +gpr_timespec gpr_inf_past(gpr_clock_type type); /* The far past. */ + +#define GPR_MS_PER_SEC 1000 +#define GPR_US_PER_SEC 1000000 +#define GPR_NS_PER_SEC 1000000000 +#define GPR_NS_PER_MS 1000000 +#define GPR_NS_PER_US 1000 +#define GPR_US_PER_MS 1000 + +/* initialize time subsystem */ +void gpr_time_init(void); + +/* Return the current time measured from the given clocks epoch. */ +gpr_timespec gpr_now(gpr_clock_type clock); + +/* Convert a timespec from one clock to another */ +gpr_timespec gpr_convert_clock_type(gpr_timespec t, + gpr_clock_type target_clock); + +/* Return -ve, 0, or +ve according to whether a < b, a == b, or a > b + respectively. */ +int gpr_time_cmp(gpr_timespec a, gpr_timespec b); + +gpr_timespec gpr_time_max(gpr_timespec a, gpr_timespec b); +gpr_timespec gpr_time_min(gpr_timespec a, gpr_timespec b); + +/* Add and subtract times. Calculations saturate at infinities. */ +gpr_timespec gpr_time_add(gpr_timespec a, gpr_timespec b); +gpr_timespec gpr_time_sub(gpr_timespec a, gpr_timespec b); + +/* Return a timespec representing a given number of time units. LONG_MIN is + interpreted as gpr_inf_past, and LONG_MAX as gpr_inf_future. */ +gpr_timespec gpr_time_from_micros(long x, gpr_clock_type clock_type); +gpr_timespec gpr_time_from_nanos(long x, gpr_clock_type clock_type); +gpr_timespec gpr_time_from_millis(long x, gpr_clock_type clock_type); +gpr_timespec gpr_time_from_seconds(long x, gpr_clock_type clock_type); +gpr_timespec gpr_time_from_minutes(long x, gpr_clock_type clock_type); +gpr_timespec gpr_time_from_hours(long x, gpr_clock_type clock_type); + +gpr_int32 gpr_time_to_millis(gpr_timespec timespec); + +/* Return 1 if two times are equal or within threshold of each other, + 0 otherwise */ +int gpr_time_similar(gpr_timespec a, gpr_timespec b, gpr_timespec threshold); + +/* Sleep until at least 'until' - an absolute timeout */ +void gpr_sleep_until(gpr_timespec until); + +double gpr_timespec_to_micros(gpr_timespec t); + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_SUPPORT_TIME_H */ diff --git a/include/grpc/support/tls.h b/include/grpc/support/tls.h new file mode 100644 index 00000000..a4ebac56 --- /dev/null +++ b/include/grpc/support/tls.h @@ -0,0 +1,77 @@ +/* + * + * Copyright 2015, 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_SUPPORT_TLS_H +#define GRPC_SUPPORT_TLS_H + +#include + +/* Thread local storage. + + A minimal wrapper that should be implementable across many compilers, + and implementable efficiently across most modern compilers. + + Thread locals have type gpr_intptr. + + Declaring a thread local variable 'foo': + GPR_TLS_DECL(foo); + Thread locals always have static scope. + + Initializing a thread local (must be done at library initialization + time): + gpr_tls_init(&foo); + + Destroying a thread local: + gpr_tls_destroy(&foo); + + Setting a thread local (returns new_value): + gpr_tls_set(&foo, new_value); + + Accessing a thread local: + current_value = gpr_tls_get(&foo, value); + + ALL functions here may be implemented as macros. */ + +#ifdef GPR_GCC_TLS +#include +#endif + +#ifdef GPR_MSVC_TLS +#include +#endif + +#ifdef GPR_PTHREAD_TLS +#include +#endif + +#endif diff --git a/include/grpc/support/tls_gcc.h b/include/grpc/support/tls_gcc.h new file mode 100644 index 00000000..1a02fb22 --- /dev/null +++ b/include/grpc/support/tls_gcc.h @@ -0,0 +1,56 @@ +/* + * + * Copyright 2015, 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_SUPPORT_TLS_GCC_H +#define GRPC_SUPPORT_TLS_GCC_H + +/* Thread local storage based on gcc compiler primitives. + #include tls.h to use this - and see that file for documentation */ + +struct gpr_gcc_thread_local { + gpr_intptr value; +}; + +#define GPR_TLS_DECL(name) \ + static __thread struct gpr_gcc_thread_local name = {0} + +#define gpr_tls_init(tls) \ + do { \ + } while (0) +#define gpr_tls_destroy(tls) \ + do { \ + } while (0) +#define gpr_tls_set(tls, new_value) (((tls)->value) = (new_value)) +#define gpr_tls_get(tls) ((tls)->value) + +#endif diff --git a/include/grpc/support/tls_msvc.h b/include/grpc/support/tls_msvc.h new file mode 100644 index 00000000..9997f8e4 --- /dev/null +++ b/include/grpc/support/tls_msvc.h @@ -0,0 +1,56 @@ +/* + * + * Copyright 2015, 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_SUPPORT_TLS_GCC_H +#define GRPC_SUPPORT_TLS_GCC_H + +/* Thread local storage based on ms visual c compiler primitives. + #include tls.h to use this - and see that file for documentation */ + +struct gpr_msvc_thread_local { + gpr_intptr value; +}; + +#define GPR_TLS_DECL(name) \ + static __declspec(thread) struct gpr_msvc_thread_local name = {0} + +#define gpr_tls_init(tls) \ + do { \ + } while (0) +#define gpr_tls_destroy(tls) \ + do { \ + } while (0) +#define gpr_tls_set(tls, new_value) (((tls)->value) = (new_value)) +#define gpr_tls_get(tls) ((tls)->value) + +#endif diff --git a/include/grpc/support/tls_pthread.h b/include/grpc/support/tls_pthread.h new file mode 100644 index 00000000..50e55d36 --- /dev/null +++ b/include/grpc/support/tls_pthread.h @@ -0,0 +1,60 @@ +/* + * + * Copyright 2015, 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_SUPPORT_TLS_PTHREAD_H +#define GRPC_SUPPORT_TLS_PTHREAD_H + +#include /* for GPR_ASSERT */ +#include + +/* Thread local storage based on pthread library calls. + #include tls.h to use this - and see that file for documentation */ + +struct gpr_pthread_thread_local { + pthread_key_t key; +}; + +#define GPR_TLS_DECL(name) static struct gpr_pthread_thread_local name = {0} + +#define gpr_tls_init(tls) GPR_ASSERT(0 == pthread_key_create(&(tls)->key, NULL)) +#define gpr_tls_destroy(tls) pthread_key_delete((tls)->key) +#define gpr_tls_get(tls) ((gpr_intptr)pthread_getspecific((tls)->key)) +#ifdef __cplusplus +extern "C" { +#endif +gpr_intptr gpr_tls_set(struct gpr_pthread_thread_local *tls, gpr_intptr value); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/grpc/support/useful.h b/include/grpc/support/useful.h new file mode 100644 index 00000000..9f08d788 --- /dev/null +++ b/include/grpc/support/useful.h @@ -0,0 +1,75 @@ +/* + * + * Copyright 2015, 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_SUPPORT_USEFUL_H +#define GRPC_SUPPORT_USEFUL_H + +/* useful macros that don't belong anywhere else */ + +#define GPR_MIN(a, b) ((a) < (b) ? (a) : (b)) +#define GPR_MAX(a, b) ((a) > (b) ? (a) : (b)) +#define GPR_CLAMP(a, min, max) ((a) < (min) ? (min) : (a) > (max) ? (max) : (a)) +/* rotl, rotr assume x is unsigned */ +#define GPR_ROTL(x, n) (((x) << (n)) | ((x) >> (sizeof(x) * 8 - (n)))) +#define GPR_ROTR(x, n) (((x) >> (n)) | ((x) << (sizeof(x) * 8 - (n)))) + +#define GPR_ARRAY_SIZE(array) (sizeof(array) / sizeof(*(array))) + +#define GPR_SWAP(type, a, b) \ + do { \ + type x = a; \ + a = b; \ + b = x; \ + } while (0) + +/** Set the \a n-th bit of \a i (a mutable pointer). */ +#define GPR_BITSET(i, n) ((*(i)) |= (1u << (n))) + +/** Clear the \a n-th bit of \a i (a mutable pointer). */ +#define GPR_BITCLEAR(i, n) ((*(i)) &= ~(1u << (n))) + +/** Get the \a n-th bit of \a i */ +#define GPR_BITGET(i, n) (((i) & (1u << (n))) != 0) + +#define GPR_INTERNAL_HEXDIGIT_BITCOUNT(x) \ + ((x) - (((x) >> 1) & 0x77777777) - (((x) >> 2) & 0x33333333) - \ + (((x) >> 3) & 0x11111111)) + +/** Returns number of bits set in bitset \a i */ +#define GPR_BITCOUNT(i) \ + (((GPR_INTERNAL_HEXDIGIT_BITCOUNT(i) + \ + (GPR_INTERNAL_HEXDIGIT_BITCOUNT(i) >> 4)) & \ + 0x0f0f0f0f) % \ + 255) + +#endif /* GRPC_SUPPORT_USEFUL_H */ diff --git a/src/compiler/config.h b/src/compiler/config.h new file mode 100644 index 00000000..fea976c3 --- /dev/null +++ b/src/compiler/config.h @@ -0,0 +1,93 @@ +/* + * + * Copyright 2015, 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 SRC_COMPILER_CONFIG_H +#define SRC_COMPILER_CONFIG_H + +#include +#include + +#ifndef GRPC_CUSTOM_DESCRIPTOR +#include +#include +#define GRPC_CUSTOM_DESCRIPTOR ::google::protobuf::Descriptor +#define GRPC_CUSTOM_FILEDESCRIPTOR ::google::protobuf::FileDescriptor +#define GRPC_CUSTOM_METHODDESCRIPTOR ::google::protobuf::MethodDescriptor +#define GRPC_CUSTOM_SERVICEDESCRIPTOR ::google::protobuf::ServiceDescriptor +#endif + +#ifndef GRPC_CUSTOM_CODEGENERATOR +#include +#define GRPC_CUSTOM_CODEGENERATOR ::google::protobuf::compiler::CodeGenerator +#define GRPC_CUSTOM_GENERATORCONTEXT \ + ::google::protobuf::compiler::GeneratorContext +#endif + +#ifndef GRPC_CUSTOM_PRINTER +#include +#include +#include +#define GRPC_CUSTOM_PRINTER ::google::protobuf::io::Printer +#define GRPC_CUSTOM_CODEDOUTPUTSTREAM ::google::protobuf::io::CodedOutputStream +#define GRPC_CUSTOM_STRINGOUTPUTSTREAM \ + ::google::protobuf::io::StringOutputStream +#endif + +#ifndef GRPC_CUSTOM_PLUGINMAIN +#include +#define GRPC_CUSTOM_PLUGINMAIN ::google::protobuf::compiler::PluginMain +#endif + +namespace grpc { +namespace protobuf { +typedef GRPC_CUSTOM_DESCRIPTOR Descriptor; +typedef GRPC_CUSTOM_FILEDESCRIPTOR FileDescriptor; +typedef GRPC_CUSTOM_METHODDESCRIPTOR MethodDescriptor; +typedef GRPC_CUSTOM_SERVICEDESCRIPTOR ServiceDescriptor; +namespace compiler { +typedef GRPC_CUSTOM_CODEGENERATOR CodeGenerator; +typedef GRPC_CUSTOM_GENERATORCONTEXT GeneratorContext; +static inline int PluginMain(int argc, char* argv[], + const CodeGenerator* generator) { + return GRPC_CUSTOM_PLUGINMAIN(argc, argv, generator); +} +} // namespace compiler +namespace io { +typedef GRPC_CUSTOM_PRINTER Printer; +typedef GRPC_CUSTOM_CODEDOUTPUTSTREAM CodedOutputStream; +typedef GRPC_CUSTOM_STRINGOUTPUTSTREAM StringOutputStream; +} // namespace io +} // namespace protobuf +} // namespace grpc + +#endif // SRC_COMPILER_CONFIG_H diff --git a/src/compiler/cpp_generator.cc b/src/compiler/cpp_generator.cc new file mode 100644 index 00000000..1bf2b16e --- /dev/null +++ b/src/compiler/cpp_generator.cc @@ -0,0 +1,1134 @@ +/* + * + * Copyright 2015, 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 + +#include "src/compiler/cpp_generator.h" +#include "src/compiler/cpp_generator_helpers.h" + +#include "src/compiler/config.h" + +#include + +namespace grpc_cpp_generator { +namespace { + +template +grpc::string as_string(T x) { + std::ostringstream out; + out << x; + return out.str(); +} + +bool NoStreaming(const grpc::protobuf::MethodDescriptor *method) { + return !method->client_streaming() && !method->server_streaming(); +} + +bool ClientOnlyStreaming(const grpc::protobuf::MethodDescriptor *method) { + return method->client_streaming() && !method->server_streaming(); +} + +bool ServerOnlyStreaming(const grpc::protobuf::MethodDescriptor *method) { + return !method->client_streaming() && method->server_streaming(); +} + +bool BidiStreaming(const grpc::protobuf::MethodDescriptor *method) { + return method->client_streaming() && method->server_streaming(); +} + +grpc::string FilenameIdentifier(const grpc::string &filename) { + grpc::string result; + for (unsigned i = 0; i < filename.size(); i++) { + char c = filename[i]; + if (isalnum(c)) { + result.push_back(c); + } else { + static char hex[] = "0123456789abcdef"; + result.push_back('_'); + result.push_back(hex[(c >> 4) & 0xf]); + result.push_back(hex[c & 0xf]); + } + } + return result; +} +} // namespace + +grpc::string GetHeaderPrologue(const grpc::protobuf::FileDescriptor *file, + const Parameters ¶ms) { + grpc::string output; + { + // Scope the output stream so it closes and finalizes output to the string. + grpc::protobuf::io::StringOutputStream output_stream(&output); + grpc::protobuf::io::Printer printer(&output_stream, '$'); + std::map vars; + + vars["filename"] = file->name(); + vars["filename_identifier"] = FilenameIdentifier(file->name()); + vars["filename_base"] = grpc_generator::StripProto(file->name()); + + printer.Print(vars, "// Generated by the gRPC protobuf plugin.\n"); + printer.Print(vars, + "// If you make any local change, they will be lost.\n"); + printer.Print(vars, "// source: $filename$\n"); + printer.Print(vars, "#ifndef GRPC_$filename_identifier$__INCLUDED\n"); + printer.Print(vars, "#define GRPC_$filename_identifier$__INCLUDED\n"); + printer.Print(vars, "\n"); + printer.Print(vars, "#include \"$filename_base$.pb.h\"\n"); + printer.Print(vars, "\n"); + } + return output; +} + +grpc::string GetHeaderIncludes(const grpc::protobuf::FileDescriptor *file, + const Parameters ¶ms) { + grpc::string temp = + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "\n" + "namespace grpc {\n" + "class CompletionQueue;\n" + "class Channel;\n" + "class RpcService;\n" + "class ServerCompletionQueue;\n" + "class ServerContext;\n" + "} // namespace grpc\n\n"; + + if (!file->package().empty()) { + std::vector parts = + grpc_generator::tokenize(file->package(), "."); + + for (auto part = parts.begin(); part != parts.end(); part++) { + temp.append("namespace "); + temp.append(*part); + temp.append(" {\n"); + } + temp.append("\n"); + } + + return temp; +} + +void PrintHeaderClientMethodInterfaces( + grpc::protobuf::io::Printer *printer, + const grpc::protobuf::MethodDescriptor *method, + std::map *vars, bool is_public) { + (*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 (is_public) { + if (NoStreaming(method)) { + printer->Print( + *vars, + "virtual ::grpc::Status $Method$(::grpc::ClientContext* context, " + "const $Request$& request, $Response$* response) = 0;\n"); + printer->Print(*vars, + "std::unique_ptr< " + "::grpc::ClientAsyncResponseReaderInterface< $Response$>> " + "Async$Method$(::grpc::ClientContext* context, " + "const $Request$& request, " + "::grpc::CompletionQueue* cq) {\n"); + printer->Indent(); + printer->Print(*vars, + "return std::unique_ptr< " + "::grpc::ClientAsyncResponseReaderInterface< $Response$>>(" + "Async$Method$Raw(context, request, cq));\n"); + printer->Outdent(); + printer->Print("}\n"); + } else if (ClientOnlyStreaming(method)) { + printer->Print( + *vars, + "std::unique_ptr< ::grpc::ClientWriterInterface< $Request$>>" + " $Method$(" + "::grpc::ClientContext* context, $Response$* response) {\n"); + printer->Indent(); + printer->Print( + *vars, + "return std::unique_ptr< ::grpc::ClientWriterInterface< $Request$>>" + "($Method$Raw(context, response));\n"); + printer->Outdent(); + printer->Print("}\n"); + printer->Print( + *vars, + "std::unique_ptr< ::grpc::ClientAsyncWriterInterface< $Request$>>" + " Async$Method$(::grpc::ClientContext* context, $Response$* " + "response, " + "::grpc::CompletionQueue* cq, void* tag) {\n"); + printer->Indent(); + printer->Print(*vars, + "return std::unique_ptr< " + "::grpc::ClientAsyncWriterInterface< $Request$>>(" + "Async$Method$Raw(context, response, cq, tag));\n"); + printer->Outdent(); + printer->Print("}\n"); + } else if (ServerOnlyStreaming(method)) { + printer->Print( + *vars, + "std::unique_ptr< ::grpc::ClientReaderInterface< $Response$>>" + " $Method$(::grpc::ClientContext* context, const $Request$& request)" + " {\n"); + printer->Indent(); + printer->Print( + *vars, + "return std::unique_ptr< ::grpc::ClientReaderInterface< $Response$>>" + "($Method$Raw(context, request));\n"); + printer->Outdent(); + printer->Print("}\n"); + printer->Print( + *vars, + "std::unique_ptr< ::grpc::ClientAsyncReaderInterface< $Response$>> " + "Async$Method$(" + "::grpc::ClientContext* context, const $Request$& request, " + "::grpc::CompletionQueue* cq, void* tag) {\n"); + printer->Indent(); + printer->Print(*vars, + "return std::unique_ptr< " + "::grpc::ClientAsyncReaderInterface< $Response$>>(" + "Async$Method$Raw(context, request, cq, tag));\n"); + printer->Outdent(); + printer->Print("}\n"); + } else if (BidiStreaming(method)) { + printer->Print(*vars, + "std::unique_ptr< ::grpc::ClientReaderWriterInterface< " + "$Request$, $Response$>> " + "$Method$(::grpc::ClientContext* context) {\n"); + printer->Indent(); + printer->Print( + *vars, + "return std::unique_ptr< " + "::grpc::ClientReaderWriterInterface< $Request$, $Response$>>(" + "$Method$Raw(context));\n"); + printer->Outdent(); + printer->Print("}\n"); + printer->Print( + *vars, + "std::unique_ptr< " + "::grpc::ClientAsyncReaderWriterInterface< $Request$, $Response$>> " + "Async$Method$(::grpc::ClientContext* context, " + "::grpc::CompletionQueue* cq, void* tag) {\n"); + printer->Indent(); + printer->Print( + *vars, + "return std::unique_ptr< " + "::grpc::ClientAsyncReaderWriterInterface< $Request$, $Response$>>(" + "Async$Method$Raw(context, cq, tag));\n"); + printer->Outdent(); + printer->Print("}\n"); + } + } else { + if (NoStreaming(method)) { + printer->Print( + *vars, + "virtual ::grpc::ClientAsyncResponseReaderInterface< $Response$>* " + "Async$Method$Raw(::grpc::ClientContext* context, " + "const $Request$& request, " + "::grpc::CompletionQueue* cq) = 0;\n"); + } else if (ClientOnlyStreaming(method)) { + printer->Print( + *vars, + "virtual ::grpc::ClientWriterInterface< $Request$>*" + " $Method$Raw(" + "::grpc::ClientContext* context, $Response$* response) = 0;\n"); + printer->Print(*vars, + "virtual ::grpc::ClientAsyncWriterInterface< $Request$>*" + " Async$Method$Raw(::grpc::ClientContext* context, " + "$Response$* response, " + "::grpc::CompletionQueue* cq, void* tag) = 0;\n"); + } else if (ServerOnlyStreaming(method)) { + printer->Print( + *vars, + "virtual ::grpc::ClientReaderInterface< $Response$>* $Method$Raw(" + "::grpc::ClientContext* context, const $Request$& request) = 0;\n"); + printer->Print( + *vars, + "virtual ::grpc::ClientAsyncReaderInterface< $Response$>* " + "Async$Method$Raw(" + "::grpc::ClientContext* context, const $Request$& request, " + "::grpc::CompletionQueue* cq, void* tag) = 0;\n"); + } else if (BidiStreaming(method)) { + printer->Print(*vars, + "virtual ::grpc::ClientReaderWriterInterface< $Request$, " + "$Response$>* " + "$Method$Raw(::grpc::ClientContext* context) = 0;\n"); + printer->Print(*vars, + "virtual ::grpc::ClientAsyncReaderWriterInterface< " + "$Request$, $Response$>* " + "Async$Method$Raw(::grpc::ClientContext* context, " + "::grpc::CompletionQueue* cq, void* tag) = 0;\n"); + } + } +} + +void PrintHeaderClientMethod(grpc::protobuf::io::Printer *printer, + const grpc::protobuf::MethodDescriptor *method, + std::map *vars, + bool is_public) { + (*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 (is_public) { + if (NoStreaming(method)) { + printer->Print( + *vars, + "::grpc::Status $Method$(::grpc::ClientContext* context, " + "const $Request$& request, $Response$* response) GRPC_OVERRIDE;\n"); + printer->Print( + *vars, + "std::unique_ptr< ::grpc::ClientAsyncResponseReader< $Response$>> " + "Async$Method$(::grpc::ClientContext* context, " + "const $Request$& request, " + "::grpc::CompletionQueue* cq) {\n"); + printer->Indent(); + printer->Print(*vars, + "return std::unique_ptr< " + "::grpc::ClientAsyncResponseReader< $Response$>>(" + "Async$Method$Raw(context, request, cq));\n"); + printer->Outdent(); + printer->Print("}\n"); + } else if (ClientOnlyStreaming(method)) { + printer->Print( + *vars, + "std::unique_ptr< ::grpc::ClientWriter< $Request$>>" + " $Method$(" + "::grpc::ClientContext* context, $Response$* response) {\n"); + printer->Indent(); + printer->Print(*vars, + "return std::unique_ptr< ::grpc::ClientWriter< $Request$>>" + "($Method$Raw(context, response));\n"); + printer->Outdent(); + printer->Print("}\n"); + printer->Print(*vars, + "std::unique_ptr< ::grpc::ClientAsyncWriter< $Request$>>" + " Async$Method$(::grpc::ClientContext* context, " + "$Response$* response, " + "::grpc::CompletionQueue* cq, void* tag) {\n"); + printer->Indent(); + printer->Print( + *vars, + "return std::unique_ptr< ::grpc::ClientAsyncWriter< $Request$>>(" + "Async$Method$Raw(context, response, cq, tag));\n"); + printer->Outdent(); + printer->Print("}\n"); + } else if (ServerOnlyStreaming(method)) { + printer->Print( + *vars, + "std::unique_ptr< ::grpc::ClientReader< $Response$>>" + " $Method$(::grpc::ClientContext* context, const $Request$& request)" + " {\n"); + printer->Indent(); + printer->Print( + *vars, + "return std::unique_ptr< ::grpc::ClientReader< $Response$>>" + "($Method$Raw(context, request));\n"); + printer->Outdent(); + printer->Print("}\n"); + printer->Print( + *vars, + "std::unique_ptr< ::grpc::ClientAsyncReader< $Response$>> " + "Async$Method$(" + "::grpc::ClientContext* context, const $Request$& request, " + "::grpc::CompletionQueue* cq, void* tag) {\n"); + printer->Indent(); + printer->Print( + *vars, + "return std::unique_ptr< ::grpc::ClientAsyncReader< $Response$>>(" + "Async$Method$Raw(context, request, cq, tag));\n"); + printer->Outdent(); + printer->Print("}\n"); + } else if (BidiStreaming(method)) { + printer->Print( + *vars, + "std::unique_ptr< ::grpc::ClientReaderWriter< $Request$, $Response$>>" + " $Method$(::grpc::ClientContext* context) {\n"); + printer->Indent(); + printer->Print(*vars, + "return std::unique_ptr< " + "::grpc::ClientReaderWriter< $Request$, $Response$>>(" + "$Method$Raw(context));\n"); + printer->Outdent(); + printer->Print("}\n"); + printer->Print(*vars, + "std::unique_ptr< ::grpc::ClientAsyncReaderWriter< " + "$Request$, $Response$>> " + "Async$Method$(::grpc::ClientContext* context, " + "::grpc::CompletionQueue* cq, void* tag) {\n"); + printer->Indent(); + printer->Print(*vars, + "return std::unique_ptr< " + "::grpc::ClientAsyncReaderWriter< $Request$, $Response$>>(" + "Async$Method$Raw(context, cq, tag));\n"); + printer->Outdent(); + printer->Print("}\n"); + } + } else { + if (NoStreaming(method)) { + printer->Print(*vars, + "::grpc::ClientAsyncResponseReader< $Response$>* " + "Async$Method$Raw(::grpc::ClientContext* context, " + "const $Request$& request, " + "::grpc::CompletionQueue* cq) GRPC_OVERRIDE;\n"); + } else if (ClientOnlyStreaming(method)) { + printer->Print(*vars, + "::grpc::ClientWriter< $Request$>* $Method$Raw(" + "::grpc::ClientContext* context, $Response$* response) " + "GRPC_OVERRIDE;\n"); + printer->Print( + *vars, + "::grpc::ClientAsyncWriter< $Request$>* Async$Method$Raw(" + "::grpc::ClientContext* context, $Response$* response, " + "::grpc::CompletionQueue* cq, void* tag) GRPC_OVERRIDE;\n"); + } else if (ServerOnlyStreaming(method)) { + printer->Print(*vars, + "::grpc::ClientReader< $Response$>* $Method$Raw(" + "::grpc::ClientContext* context, const $Request$& request)" + " GRPC_OVERRIDE;\n"); + printer->Print( + *vars, + "::grpc::ClientAsyncReader< $Response$>* Async$Method$Raw(" + "::grpc::ClientContext* context, const $Request$& request, " + "::grpc::CompletionQueue* cq, void* tag) GRPC_OVERRIDE;\n"); + } else if (BidiStreaming(method)) { + printer->Print( + *vars, + "::grpc::ClientReaderWriter< $Request$, $Response$>* " + "$Method$Raw(::grpc::ClientContext* context) GRPC_OVERRIDE;\n"); + printer->Print( + *vars, + "::grpc::ClientAsyncReaderWriter< $Request$, $Response$>* " + "Async$Method$Raw(::grpc::ClientContext* context, " + "::grpc::CompletionQueue* cq, void* tag) GRPC_OVERRIDE;\n"); + } + } +} + +void PrintHeaderClientMethodData(grpc::protobuf::io::Printer *printer, + const grpc::protobuf::MethodDescriptor *method, + std::map *vars) { + (*vars)["Method"] = method->name(); + printer->Print(*vars, "const ::grpc::RpcMethod rpcmethod_$Method$_;\n"); +} + +void PrintHeaderServerMethodSync(grpc::protobuf::io::Printer *printer, + const grpc::protobuf::MethodDescriptor *method, + std::map *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, + "virtual ::grpc::Status $Method$(" + "::grpc::ServerContext* context, const $Request$* request, " + "$Response$* response);\n"); + } else if (ClientOnlyStreaming(method)) { + printer->Print(*vars, + "virtual ::grpc::Status $Method$(" + "::grpc::ServerContext* context, " + "::grpc::ServerReader< $Request$>* reader, " + "$Response$* response);\n"); + } else if (ServerOnlyStreaming(method)) { + printer->Print(*vars, + "virtual ::grpc::Status $Method$(" + "::grpc::ServerContext* context, const $Request$* request, " + "::grpc::ServerWriter< $Response$>* writer);\n"); + } else if (BidiStreaming(method)) { + printer->Print( + *vars, + "virtual ::grpc::Status $Method$(" + "::grpc::ServerContext* context, " + "::grpc::ServerReaderWriter< $Response$, $Request$>* stream);" + "\n"); + } +} + +void PrintHeaderServerMethodAsync( + grpc::protobuf::io::Printer *printer, + const grpc::protobuf::MethodDescriptor *method, + std::map *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 Request$Method$(" + "::grpc::ServerContext* context, $Request$* request, " + "::grpc::ServerAsyncResponseWriter< $Response$>* response, " + "::grpc::CompletionQueue* new_call_cq, " + "::grpc::ServerCompletionQueue* notification_cq, void *tag);\n"); + } else if (ClientOnlyStreaming(method)) { + 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"); + } else if (ServerOnlyStreaming(method)) { + 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"); + } else if (BidiStreaming(method)) { + 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"); + } +} + +void PrintHeaderService(grpc::protobuf::io::Printer *printer, + const grpc::protobuf::ServiceDescriptor *service, + std::map *vars) { + (*vars)["Service"] = service->name(); + + printer->Print(*vars, + "class $Service$ GRPC_FINAL {\n" + " public:\n"); + printer->Indent(); + + // Client side + printer->Print( + "class StubInterface {\n" + " public:\n"); + printer->Indent(); + printer->Print("virtual ~StubInterface() {}\n"); + for (int i = 0; i < service->method_count(); ++i) { + PrintHeaderClientMethodInterfaces(printer, service->method(i), vars, true); + } + printer->Outdent(); + printer->Print("private:\n"); + printer->Indent(); + for (int i = 0; i < service->method_count(); ++i) { + PrintHeaderClientMethodInterfaces(printer, service->method(i), vars, false); + } + printer->Outdent(); + printer->Print("};\n"); + printer->Print( + "class Stub GRPC_FINAL : public StubInterface" + " {\n public:\n"); + printer->Indent(); + printer->Print("Stub(const std::shared_ptr< ::grpc::Channel>& channel);\n"); + for (int i = 0; i < service->method_count(); ++i) { + PrintHeaderClientMethod(printer, service->method(i), vars, true); + } + printer->Outdent(); + printer->Print("\n private:\n"); + printer->Indent(); + printer->Print("std::shared_ptr< ::grpc::Channel> channel_;\n"); + for (int i = 0; i < service->method_count(); ++i) { + PrintHeaderClientMethod(printer, service->method(i), vars, false); + } + for (int i = 0; i < service->method_count(); ++i) { + PrintHeaderClientMethodData(printer, service->method(i), vars); + } + printer->Outdent(); + printer->Print("};\n"); + printer->Print( + "static std::unique_ptr NewStub(const std::shared_ptr< " + "::grpc::Channel>& channel, " + "const ::grpc::StubOptions& options = ::grpc::StubOptions());\n"); + + printer->Print("\n"); + + // Server side - Synchronous + printer->Print( + "class Service : public ::grpc::SynchronousService {\n" + " public:\n"); + printer->Indent(); + printer->Print("Service() : service_(nullptr) {}\n"); + printer->Print("virtual ~Service();\n"); + 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" + " ::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) { + PrintHeaderServerMethodAsync(printer, service->method(i), vars); + } + printer->Outdent(); + printer->Print("};\n"); + + printer->Outdent(); + printer->Print("};\n"); +} + +grpc::string GetHeaderServices(const grpc::protobuf::FileDescriptor *file, + const Parameters ¶ms) { + grpc::string output; + { + // Scope the output stream so it closes and finalizes output to the string. + grpc::protobuf::io::StringOutputStream output_stream(&output); + grpc::protobuf::io::Printer printer(&output_stream, '$'); + std::map vars; + + if (!params.services_namespace.empty()) { + vars["services_namespace"] = params.services_namespace; + printer.Print(vars, "\nnamespace $services_namespace$ {\n\n"); + } + + for (int i = 0; i < file->service_count(); ++i) { + PrintHeaderService(&printer, file->service(i), &vars); + printer.Print("\n"); + } + + if (!params.services_namespace.empty()) { + printer.Print(vars, "} // namespace $services_namespace$\n\n"); + } + } + return output; +} + +grpc::string GetHeaderEpilogue(const grpc::protobuf::FileDescriptor *file, + const Parameters ¶ms) { + grpc::string output; + { + // Scope the output stream so it closes and finalizes output to the string. + grpc::protobuf::io::StringOutputStream output_stream(&output); + grpc::protobuf::io::Printer printer(&output_stream, '$'); + std::map vars; + + vars["filename"] = file->name(); + vars["filename_identifier"] = FilenameIdentifier(file->name()); + + if (!file->package().empty()) { + std::vector parts = + grpc_generator::tokenize(file->package(), "."); + + for (auto part = parts.rbegin(); part != parts.rend(); part++) { + vars["part"] = *part; + printer.Print(vars, "} // namespace $part$\n"); + } + printer.Print(vars, "\n"); + } + + printer.Print(vars, "\n"); + printer.Print(vars, "#endif // GRPC_$filename_identifier$__INCLUDED\n"); + } + return output; +} + +grpc::string GetSourcePrologue(const grpc::protobuf::FileDescriptor *file, + const Parameters ¶ms) { + grpc::string output; + { + // Scope the output stream so it closes and finalizes output to the string. + grpc::protobuf::io::StringOutputStream output_stream(&output); + grpc::protobuf::io::Printer printer(&output_stream, '$'); + std::map vars; + + vars["filename"] = file->name(); + vars["filename_base"] = grpc_generator::StripProto(file->name()); + + printer.Print(vars, "// Generated by the gRPC protobuf plugin.\n"); + printer.Print(vars, + "// If you make any local change, they will be lost.\n"); + printer.Print(vars, "// source: $filename$\n\n"); + printer.Print(vars, "#include \"$filename_base$.pb.h\"\n"); + printer.Print(vars, "#include \"$filename_base$.grpc.pb.h\"\n"); + printer.Print(vars, "\n"); + } + return output; +} + +grpc::string GetSourceIncludes(const grpc::protobuf::FileDescriptor *file, + const Parameters ¶m) { + grpc::string output; + { + // Scope the output stream so it closes and finalizes output to the string. + grpc::protobuf::io::StringOutputStream output_stream(&output); + grpc::protobuf::io::Printer printer(&output_stream, '$'); + std::map vars; + + printer.Print(vars, "#include \n"); + printer.Print(vars, "#include \n"); + printer.Print(vars, "#include \n"); + printer.Print(vars, "#include \n"); + printer.Print(vars, "#include \n"); + printer.Print(vars, "#include \n"); + printer.Print(vars, "#include \n"); + + if (!file->package().empty()) { + std::vector parts = + grpc_generator::tokenize(file->package(), "."); + + for (auto part = parts.begin(); part != parts.end(); part++) { + vars["part"] = *part; + printer.Print(vars, "namespace $part$ {\n"); + } + } + + printer.Print(vars, "\n"); + } + return output; +} + +void PrintSourceClientMethod(grpc::protobuf::io::Printer *printer, + const grpc::protobuf::MethodDescriptor *method, + std::map *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, + "::grpc::Status $ns$$Service$::Stub::$Method$(" + "::grpc::ClientContext* context, " + "const $Request$& request, $Response$* response) {\n"); + printer->Print(*vars, + " return ::grpc::BlockingUnaryCall(channel_.get(), " + "rpcmethod_$Method$_, " + "context, request, response);\n" + "}\n\n"); + printer->Print( + *vars, + "::grpc::ClientAsyncResponseReader< $Response$>* " + "$ns$$Service$::Stub::Async$Method$Raw(::grpc::ClientContext* context, " + "const $Request$& request, " + "::grpc::CompletionQueue* cq) {\n"); + printer->Print(*vars, + " return new " + "::grpc::ClientAsyncResponseReader< $Response$>(" + "channel_.get(), cq, " + "rpcmethod_$Method$_, " + "context, request);\n" + "}\n\n"); + } else if (ClientOnlyStreaming(method)) { + printer->Print(*vars, + "::grpc::ClientWriter< $Request$>* " + "$ns$$Service$::Stub::$Method$Raw(" + "::grpc::ClientContext* context, $Response$* response) {\n"); + printer->Print(*vars, + " return new ::grpc::ClientWriter< $Request$>(" + "channel_.get(), " + "rpcmethod_$Method$_, " + "context, response);\n" + "}\n\n"); + printer->Print(*vars, + "::grpc::ClientAsyncWriter< $Request$>* " + "$ns$$Service$::Stub::Async$Method$Raw(" + "::grpc::ClientContext* context, $Response$* response, " + "::grpc::CompletionQueue* cq, void* tag) {\n"); + printer->Print(*vars, + " return new ::grpc::ClientAsyncWriter< $Request$>(" + "channel_.get(), cq, " + "rpcmethod_$Method$_, " + "context, response, tag);\n" + "}\n\n"); + } else if (ServerOnlyStreaming(method)) { + printer->Print( + *vars, + "::grpc::ClientReader< $Response$>* " + "$ns$$Service$::Stub::$Method$Raw(" + "::grpc::ClientContext* context, const $Request$& request) {\n"); + printer->Print(*vars, + " return new ::grpc::ClientReader< $Response$>(" + "channel_.get(), " + "rpcmethod_$Method$_, " + "context, request);\n" + "}\n\n"); + printer->Print(*vars, + "::grpc::ClientAsyncReader< $Response$>* " + "$ns$$Service$::Stub::Async$Method$Raw(" + "::grpc::ClientContext* context, const $Request$& request, " + "::grpc::CompletionQueue* cq, void* tag) {\n"); + printer->Print(*vars, + " return new ::grpc::ClientAsyncReader< $Response$>(" + "channel_.get(), cq, " + "rpcmethod_$Method$_, " + "context, request, tag);\n" + "}\n\n"); + } else if (BidiStreaming(method)) { + printer->Print( + *vars, + "::grpc::ClientReaderWriter< $Request$, $Response$>* " + "$ns$$Service$::Stub::$Method$Raw(::grpc::ClientContext* context) {\n"); + printer->Print(*vars, + " return new ::grpc::ClientReaderWriter< " + "$Request$, $Response$>(" + "channel_.get(), " + "rpcmethod_$Method$_, " + "context);\n" + "}\n\n"); + printer->Print( + *vars, + "::grpc::ClientAsyncReaderWriter< $Request$, $Response$>* " + "$ns$$Service$::Stub::Async$Method$Raw(::grpc::ClientContext* context, " + "::grpc::CompletionQueue* cq, void* tag) {\n"); + printer->Print(*vars, + " return new " + "::grpc::ClientAsyncReaderWriter< $Request$, $Response$>(" + "channel_.get(), cq, " + "rpcmethod_$Method$_, " + "context, tag);\n" + "}\n\n"); + } +} + +void PrintSourceServerMethod(grpc::protobuf::io::Printer *printer, + const grpc::protobuf::MethodDescriptor *method, + std::map *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, + "::grpc::Status $ns$$Service$::Service::$Method$(" + "::grpc::ServerContext* context, " + "const $Request$* request, $Response$* response) {\n"); + printer->Print(" (void) context;\n"); + printer->Print(" (void) request;\n"); + printer->Print(" (void) response;\n"); + printer->Print( + " return ::grpc::Status(" + "::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"); + printer->Print("}\n\n"); + } else if (ClientOnlyStreaming(method)) { + printer->Print(*vars, + "::grpc::Status $ns$$Service$::Service::$Method$(" + "::grpc::ServerContext* context, " + "::grpc::ServerReader< $Request$>* reader, " + "$Response$* response) {\n"); + printer->Print(" (void) context;\n"); + printer->Print(" (void) reader;\n"); + printer->Print(" (void) response;\n"); + printer->Print( + " return ::grpc::Status(" + "::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"); + printer->Print("}\n\n"); + } else if (ServerOnlyStreaming(method)) { + printer->Print(*vars, + "::grpc::Status $ns$$Service$::Service::$Method$(" + "::grpc::ServerContext* context, " + "const $Request$* request, " + "::grpc::ServerWriter< $Response$>* writer) {\n"); + printer->Print(" (void) context;\n"); + printer->Print(" (void) request;\n"); + printer->Print(" (void) writer;\n"); + printer->Print( + " return ::grpc::Status(" + "::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"); + printer->Print("}\n\n"); + } else if (BidiStreaming(method)) { + printer->Print(*vars, + "::grpc::Status $ns$$Service$::Service::$Method$(" + "::grpc::ServerContext* context, " + "::grpc::ServerReaderWriter< $Response$, $Request$>* " + "stream) {\n"); + printer->Print(" (void) context;\n"); + printer->Print(" (void) stream;\n"); + printer->Print( + " return ::grpc::Status(" + "::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"); + printer->Print("}\n\n"); + } +} + +void PrintSourceServerAsyncMethod( + grpc::protobuf::io::Printer *printer, + const grpc::protobuf::MethodDescriptor *method, + std::map *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 *vars) { + (*vars)["Service"] = service->name(); + + printer->Print(*vars, + "static const char* $prefix$$Service$_method_names[] = {\n"); + for (int i = 0; i < service->method_count(); ++i) { + (*vars)["Method"] = service->method(i)->name(); + printer->Print(*vars, " \"/$Package$$Service$/$Method$\",\n"); + } + printer->Print(*vars, "};\n\n"); + + printer->Print(*vars, + "std::unique_ptr< $ns$$Service$::Stub> $ns$$Service$::NewStub(" + "const std::shared_ptr< ::grpc::Channel>& channel, " + "const ::grpc::StubOptions& options) {\n" + " std::unique_ptr< $ns$$Service$::Stub> stub(new " + "$ns$$Service$::Stub(channel));\n" + " return stub;\n" + "}\n\n"); + printer->Print(*vars, + "$ns$$Service$::Stub::Stub(const std::shared_ptr< " + "::grpc::Channel>& channel)\n"); + printer->Indent(); + printer->Print(": channel_(channel)"); + for (int i = 0; i < service->method_count(); ++i) { + const grpc::protobuf::MethodDescriptor *method = service->method(i); + (*vars)["Method"] = method->name(); + (*vars)["Idx"] = as_string(i); + if (NoStreaming(method)) { + (*vars)["StreamingType"] = "NORMAL_RPC"; + } else if (ClientOnlyStreaming(method)) { + (*vars)["StreamingType"] = "CLIENT_STREAMING"; + } else if (ServerOnlyStreaming(method)) { + (*vars)["StreamingType"] = "SERVER_STREAMING"; + } else { + (*vars)["StreamingType"] = "BIDI_STREAMING"; + } + printer->Print(*vars, + ", rpcmethod_$Method$_(" + "$prefix$$Service$_method_names[$Idx$], " + "::grpc::RpcMethod::$StreamingType$, " + "channel" + ")\n"); + } + printer->Print("{}\n\n"); + printer->Outdent(); + + for (int i = 0; i < service->method_count(); ++i) { + (*vars)["Idx"] = as_string(i); + 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" + " delete 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->Indent(); + printer->Print( + "if (service_ != nullptr) {\n" + " return service_;\n" + "}\n"); + printer->Print("service_ = 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); + (*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, + "service_->AddMethod(new ::grpc::RpcServiceMethod(\n" + " $prefix$$Service$_method_names[$Idx$],\n" + " ::grpc::RpcMethod::NORMAL_RPC,\n" + " new ::grpc::RpcMethodHandler< $ns$$Service$::Service, " + "$Request$, " + "$Response$>(\n" + " std::mem_fn(&$ns$$Service$::Service::$Method$), this)));\n"); + } else if (ClientOnlyStreaming(method)) { + printer->Print( + *vars, + "service_->AddMethod(new ::grpc::RpcServiceMethod(\n" + " $prefix$$Service$_method_names[$Idx$],\n" + " ::grpc::RpcMethod::CLIENT_STREAMING,\n" + " new ::grpc::ClientStreamingHandler< " + "$ns$$Service$::Service, $Request$, $Response$>(\n" + " std::mem_fn(&$ns$$Service$::Service::$Method$), this)));\n"); + } else if (ServerOnlyStreaming(method)) { + printer->Print( + *vars, + "service_->AddMethod(new ::grpc::RpcServiceMethod(\n" + " $prefix$$Service$_method_names[$Idx$],\n" + " ::grpc::RpcMethod::SERVER_STREAMING,\n" + " new ::grpc::ServerStreamingHandler< " + "$ns$$Service$::Service, $Request$, $Response$>(\n" + " std::mem_fn(&$ns$$Service$::Service::$Method$), this)));\n"); + } else if (BidiStreaming(method)) { + printer->Print( + *vars, + "service_->AddMethod(new ::grpc::RpcServiceMethod(\n" + " $prefix$$Service$_method_names[$Idx$],\n" + " ::grpc::RpcMethod::BIDI_STREAMING,\n" + " new ::grpc::BidiStreamingHandler< " + "$ns$$Service$::Service, $Request$, $Response$>(\n" + " std::mem_fn(&$ns$$Service$::Service::$Method$), this)));\n"); + } + } + printer->Print("return service_;\n"); + printer->Outdent(); + printer->Print("}\n\n"); +} + +grpc::string GetSourceServices(const grpc::protobuf::FileDescriptor *file, + const Parameters ¶ms) { + grpc::string output; + { + // Scope the output stream so it closes and finalizes output to the string. + grpc::protobuf::io::StringOutputStream output_stream(&output); + grpc::protobuf::io::Printer printer(&output_stream, '$'); + std::map 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["ns"] = params.services_namespace + "::"; + vars["prefix"] = params.services_namespace; + } else { + vars["ns"] = ""; + vars["prefix"] = ""; + } + + for (int i = 0; i < file->service_count(); ++i) { + PrintSourceService(&printer, file->service(i), &vars); + printer.Print("\n"); + } + } + return output; +} + +grpc::string GetSourceEpilogue(const grpc::protobuf::FileDescriptor *file, + const Parameters ¶ms) { + grpc::string temp; + + if (!file->package().empty()) { + std::vector parts = + grpc_generator::tokenize(file->package(), "."); + + for (auto part = parts.begin(); part != parts.end(); part++) { + temp.append("} // namespace "); + temp.append(*part); + temp.append("\n"); + } + temp.append("\n"); + } + + return temp; +} + +} // namespace grpc_cpp_generator diff --git a/src/compiler/cpp_generator.h b/src/compiler/cpp_generator.h new file mode 100644 index 00000000..70c2e985 --- /dev/null +++ b/src/compiler/cpp_generator.h @@ -0,0 +1,81 @@ +/* + * + * Copyright 2015, 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_INTERNAL_COMPILER_CPP_GENERATOR_H +#define GRPC_INTERNAL_COMPILER_CPP_GENERATOR_H + +#include "src/compiler/config.h" + +namespace grpc_cpp_generator { + +// Contains all the parameters that are parsed from the command line. +struct Parameters { + // Puts the service into a namespace + grpc::string services_namespace; +}; + +// Return the prologue of the generated header file. +grpc::string GetHeaderPrologue(const grpc::protobuf::FileDescriptor *file, + const Parameters ¶ms); + +// Return the includes needed for generated header file. +grpc::string GetHeaderIncludes(const grpc::protobuf::FileDescriptor *file, + const Parameters ¶ms); + +// Return the includes needed for generated source file. +grpc::string GetSourceIncludes(const grpc::protobuf::FileDescriptor *file, + const Parameters ¶ms); + +// Return the epilogue of the generated header file. +grpc::string GetHeaderEpilogue(const grpc::protobuf::FileDescriptor *file, + const Parameters ¶ms); + +// Return the prologue of the generated source file. +grpc::string GetSourcePrologue(const grpc::protobuf::FileDescriptor *file, + const Parameters ¶ms); + +// Return the services for generated header file. +grpc::string GetHeaderServices(const grpc::protobuf::FileDescriptor *file, + const Parameters ¶ms); + +// Return the services for generated source file. +grpc::string GetSourceServices(const grpc::protobuf::FileDescriptor *file, + const Parameters ¶ms); + +// Return the epilogue of the generated source file. +grpc::string GetSourceEpilogue(const grpc::protobuf::FileDescriptor *file, + const Parameters ¶ms); + +} // namespace grpc_cpp_generator + +#endif // GRPC_INTERNAL_COMPILER_CPP_GENERATOR_H diff --git a/src/compiler/cpp_generator_helpers.h b/src/compiler/cpp_generator_helpers.h new file mode 100644 index 00000000..be68cbe6 --- /dev/null +++ b/src/compiler/cpp_generator_helpers.h @@ -0,0 +1,70 @@ +/* + * + * Copyright 2015, 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_INTERNAL_COMPILER_CPP_GENERATOR_HELPERS_H +#define GRPC_INTERNAL_COMPILER_CPP_GENERATOR_HELPERS_H + +#include +#include "src/compiler/config.h" +#include "src/compiler/generator_helpers.h" + +namespace grpc_cpp_generator { + +inline grpc::string DotsToColons(const grpc::string &name) { + return grpc_generator::StringReplace(name, ".", "::"); +} + +inline grpc::string DotsToUnderscores(const grpc::string &name) { + return grpc_generator::StringReplace(name, ".", "_"); +} + +inline grpc::string ClassName(const grpc::protobuf::Descriptor *descriptor, + bool qualified) { + // Find "outer", the descriptor of the top-level message in which + // "descriptor" is embedded. + const grpc::protobuf::Descriptor *outer = descriptor; + while (outer->containing_type() != NULL) outer = outer->containing_type(); + + const grpc::string &outer_name = outer->full_name(); + grpc::string inner_name = descriptor->full_name().substr(outer_name.size()); + + if (qualified) { + return "::" + DotsToColons(outer_name) + DotsToUnderscores(inner_name); + } else { + return outer->name() + DotsToUnderscores(inner_name); + } +} + +} // namespace grpc_cpp_generator + +#endif // GRPC_INTERNAL_COMPILER_CPP_GENERATOR_HELPERS_H diff --git a/src/compiler/cpp_plugin.cc b/src/compiler/cpp_plugin.cc new file mode 100644 index 00000000..88c70494 --- /dev/null +++ b/src/compiler/cpp_plugin.cc @@ -0,0 +1,122 @@ +/* + * + * Copyright 2015, 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. + * + */ + +// Generates cpp gRPC service interface out of Protobuf IDL. +// + +#include + +#include "src/compiler/config.h" + +#include "src/compiler/cpp_generator.h" +#include "src/compiler/cpp_generator_helpers.h" + +class CppGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator { + public: + CppGrpcGenerator() {} + virtual ~CppGrpcGenerator() {} + + virtual bool Generate(const grpc::protobuf::FileDescriptor *file, + const grpc::string ¶meter, + grpc::protobuf::compiler::GeneratorContext *context, + grpc::string *error) const { + if (file->options().cc_generic_services()) { + *error = + "cpp grpc proto compiler plugin does not work with generic " + "services. To generate cpp grpc APIs, please set \"" + "cc_generic_service = false\"."; + return false; + } + + grpc_cpp_generator::Parameters generator_parameters; + + if (!parameter.empty()) { + std::vector parameters_list = + grpc_generator::tokenize(parameter, ","); + for (auto parameter_string = parameters_list.begin(); + parameter_string != parameters_list.end(); + parameter_string++) { + std::vector param = + grpc_generator::tokenize(*parameter_string, "="); + if (param[0] == "services_namespace") { + generator_parameters.services_namespace = param[1]; + } else { + *error = grpc::string("Unknown parameter: ") + *parameter_string; + return false; + } + } + } + + grpc::string file_name = grpc_generator::StripProto(file->name()); + + grpc::string header_code = + grpc_cpp_generator::GetHeaderPrologue(file, generator_parameters) + + grpc_cpp_generator::GetHeaderIncludes(file, generator_parameters) + + grpc_cpp_generator::GetHeaderServices(file, generator_parameters) + + grpc_cpp_generator::GetHeaderEpilogue(file, generator_parameters); + std::unique_ptr header_output( + context->Open(file_name + ".grpc.pb.h")); + grpc::protobuf::io::CodedOutputStream header_coded_out( + header_output.get()); + header_coded_out.WriteRaw(header_code.data(), header_code.size()); + + grpc::string source_code = + grpc_cpp_generator::GetSourcePrologue(file, generator_parameters) + + grpc_cpp_generator::GetSourceIncludes(file, generator_parameters) + + grpc_cpp_generator::GetSourceServices(file, generator_parameters) + + grpc_cpp_generator::GetSourceEpilogue(file, generator_parameters); + std::unique_ptr source_output( + context->Open(file_name + ".grpc.pb.cc")); + grpc::protobuf::io::CodedOutputStream source_coded_out( + source_output.get()); + source_coded_out.WriteRaw(source_code.data(), source_code.size()); + + return true; + } + + private: + // Insert the given code into the given file at the given insertion point. + void Insert(grpc::protobuf::compiler::GeneratorContext *context, + const grpc::string &filename, const grpc::string &insertion_point, + const grpc::string &code) const { + std::unique_ptr output( + context->OpenForInsert(filename, insertion_point)); + grpc::protobuf::io::CodedOutputStream coded_out(output.get()); + coded_out.WriteRaw(code.data(), code.size()); + } +}; + +int main(int argc, char *argv[]) { + CppGrpcGenerator generator; + return grpc::protobuf::compiler::PluginMain(argc, argv, &generator); +} diff --git a/src/compiler/csharp_generator.cc b/src/compiler/csharp_generator.cc new file mode 100644 index 00000000..7b497df7 --- /dev/null +++ b/src/compiler/csharp_generator.cc @@ -0,0 +1,539 @@ +/* + * + * Copyright 2015, 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 +#include +#include +#include + +#include "src/compiler/csharp_generator.h" +#include "src/compiler/config.h" +#include "src/compiler/csharp_generator_helpers.h" +#include "src/compiler/csharp_generator.h" + + +using google::protobuf::compiler::csharp::GetFileNamespace; +using google::protobuf::compiler::csharp::GetClassName; +using google::protobuf::compiler::csharp::GetUmbrellaClassName; +using grpc::protobuf::FileDescriptor; +using grpc::protobuf::Descriptor; +using grpc::protobuf::ServiceDescriptor; +using grpc::protobuf::MethodDescriptor; +using grpc::protobuf::io::Printer; +using grpc::protobuf::io::StringOutputStream; +using grpc_generator::MethodType; +using grpc_generator::GetMethodType; +using grpc_generator::METHODTYPE_NO_STREAMING; +using grpc_generator::METHODTYPE_CLIENT_STREAMING; +using grpc_generator::METHODTYPE_SERVER_STREAMING; +using grpc_generator::METHODTYPE_BIDI_STREAMING; +using grpc_generator::StringReplace; +using std::map; +using std::vector; + + +namespace grpc_csharp_generator { +namespace { + +std::string GetServiceClassName(const ServiceDescriptor* service) { + return service->name(); +} + +std::string GetClientInterfaceName(const ServiceDescriptor* service) { + return "I" + service->name() + "Client"; +} + +std::string GetClientClassName(const ServiceDescriptor* service) { + return service->name() + "Client"; +} + +std::string GetServerInterfaceName(const ServiceDescriptor* service) { + return "I" + service->name(); +} + +std::string GetCSharpMethodType(MethodType method_type) { + switch (method_type) { + case METHODTYPE_NO_STREAMING: + return "MethodType.Unary"; + case METHODTYPE_CLIENT_STREAMING: + return "MethodType.ClientStreaming"; + case METHODTYPE_SERVER_STREAMING: + return "MethodType.ServerStreaming"; + case METHODTYPE_BIDI_STREAMING: + return "MethodType.DuplexStreaming"; + } + GOOGLE_LOG(FATAL)<< "Can't get here."; + return ""; +} + +std::string GetServiceNameFieldName() { + return "__ServiceName"; +} + +std::string GetMarshallerFieldName(const Descriptor *message) { + return "__Marshaller_" + message->name(); +} + +std::string GetMethodFieldName(const MethodDescriptor *method) { + return "__Method_" + method->name(); +} + +std::string GetMethodRequestParamMaybe(const MethodDescriptor *method) { + if (method->client_streaming()) { + return ""; + } + return GetClassName(method->input_type()) + " request, "; +} + +std::string GetMethodReturnTypeClient(const MethodDescriptor *method) { + switch (GetMethodType(method)) { + case METHODTYPE_NO_STREAMING: + return "AsyncUnaryCall<" + GetClassName(method->output_type()) + ">"; + case METHODTYPE_CLIENT_STREAMING: + return "AsyncClientStreamingCall<" + GetClassName(method->input_type()) + + ", " + GetClassName(method->output_type()) + ">"; + case METHODTYPE_SERVER_STREAMING: + return "AsyncServerStreamingCall<" + GetClassName(method->output_type()) + + ">"; + case METHODTYPE_BIDI_STREAMING: + return "AsyncDuplexStreamingCall<" + GetClassName(method->input_type()) + + ", " + GetClassName(method->output_type()) + ">"; + } + GOOGLE_LOG(FATAL)<< "Can't get here."; + return ""; +} + +std::string GetMethodRequestParamServer(const MethodDescriptor *method) { + switch (GetMethodType(method)) { + case METHODTYPE_NO_STREAMING: + case METHODTYPE_SERVER_STREAMING: + return GetClassName(method->input_type()) + " request"; + case METHODTYPE_CLIENT_STREAMING: + case METHODTYPE_BIDI_STREAMING: + return "IAsyncStreamReader<" + GetClassName(method->input_type()) + + "> requestStream"; + } + GOOGLE_LOG(FATAL)<< "Can't get here."; + return ""; +} + +std::string GetMethodReturnTypeServer(const MethodDescriptor *method) { + switch (GetMethodType(method)) { + case METHODTYPE_NO_STREAMING: + case METHODTYPE_CLIENT_STREAMING: + return "Task<" + GetClassName(method->output_type()) + ">"; + case METHODTYPE_SERVER_STREAMING: + case METHODTYPE_BIDI_STREAMING: + return "Task"; + } + GOOGLE_LOG(FATAL)<< "Can't get here."; + return ""; +} + +std::string GetMethodResponseStreamMaybe(const MethodDescriptor *method) { + switch (GetMethodType(method)) { + case METHODTYPE_NO_STREAMING: + case METHODTYPE_CLIENT_STREAMING: + return ""; + case METHODTYPE_SERVER_STREAMING: + case METHODTYPE_BIDI_STREAMING: + return ", IServerStreamWriter<" + GetClassName(method->output_type()) + + "> responseStream"; + } + GOOGLE_LOG(FATAL)<< "Can't get here."; + return ""; +} + +// Gets vector of all messages used as input or output types. +std::vector GetUsedMessages( + const ServiceDescriptor *service) { + std::set descriptor_set; + std::vector result; // vector is to maintain stable ordering + for (int i = 0; i < service->method_count(); i++) { + const MethodDescriptor *method = service->method(i); + if (descriptor_set.find(method->input_type()) == descriptor_set.end()) { + descriptor_set.insert(method->input_type()); + result.push_back(method->input_type()); + } + if (descriptor_set.find(method->output_type()) == descriptor_set.end()) { + descriptor_set.insert(method->output_type()); + result.push_back(method->output_type()); + } + } + return result; +} + +void GenerateMarshallerFields(Printer* out, const ServiceDescriptor *service) { + std::vector used_messages = GetUsedMessages(service); + for (size_t i = 0; i < used_messages.size(); i++) { + const Descriptor *message = used_messages[i]; + out->Print( + "static readonly Marshaller<$type$> $fieldname$ = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), $type$.Parser.ParseFrom);\n", + "fieldname", GetMarshallerFieldName(message), "type", + GetClassName(message)); + } + out->Print("\n"); +} + +void GenerateStaticMethodField(Printer* out, const MethodDescriptor *method) { + out->Print( + "static readonly Method<$request$, $response$> $fieldname$ = new Method<$request$, $response$>(\n", + "fieldname", GetMethodFieldName(method), "request", + GetClassName(method->input_type()), "response", + GetClassName(method->output_type())); + out->Indent(); + out->Indent(); + out->Print("$methodtype$,\n", "methodtype", + GetCSharpMethodType(GetMethodType(method))); + out->Print("$servicenamefield$,\n", "servicenamefield", + GetServiceNameFieldName()); + out->Print("\"$methodname$\",\n", "methodname", method->name()); + out->Print("$requestmarshaller$,\n", "requestmarshaller", + GetMarshallerFieldName(method->input_type())); + out->Print("$responsemarshaller$);\n", "responsemarshaller", + GetMarshallerFieldName(method->output_type())); + out->Print("\n"); + out->Outdent(); + out->Outdent(); +} + +void GenerateServiceDescriptorProperty(Printer* out, const ServiceDescriptor *service) { + std::ostringstream index; + index << service->index(); + out->Print("// service descriptor\n"); + out->Print("public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor\n"); + out->Print("{\n"); + out->Print(" get { return $umbrella$.Descriptor.Services[$index$]; }\n", + "umbrella", GetUmbrellaClassName(service->file()), "index", + index.str()); + out->Print("}\n"); + out->Print("\n"); +} + +void GenerateClientInterface(Printer* out, const ServiceDescriptor *service) { + out->Print("// client interface\n"); + out->Print("public interface $name$\n", "name", + GetClientInterfaceName(service)); + out->Print("{\n"); + out->Indent(); + for (int i = 0; i < service->method_count(); i++) { + const MethodDescriptor *method = service->method(i); + MethodType method_type = GetMethodType(method); + + if (method_type == METHODTYPE_NO_STREAMING) { + // unary calls have an extra synchronous stub method + out->Print( + "$response$ $methodname$($request$ request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));\n", + "methodname", method->name(), "request", + GetClassName(method->input_type()), "response", + GetClassName(method->output_type())); + + // overload taking CallOptions as a param + out->Print( + "$response$ $methodname$($request$ request, CallOptions options);\n", + "methodname", method->name(), "request", + GetClassName(method->input_type()), "response", + GetClassName(method->output_type())); + } + + std::string method_name = method->name(); + if (method_type == METHODTYPE_NO_STREAMING) { + method_name += "Async"; // prevent name clash with synchronous method. + } + out->Print( + "$returntype$ $methodname$($request_maybe$Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));\n", + "methodname", method_name, "request_maybe", + GetMethodRequestParamMaybe(method), "returntype", + GetMethodReturnTypeClient(method)); + + // overload taking CallOptions as a param + out->Print( + "$returntype$ $methodname$($request_maybe$CallOptions options);\n", + "methodname", method_name, "request_maybe", + GetMethodRequestParamMaybe(method), "returntype", + GetMethodReturnTypeClient(method)); + } + out->Outdent(); + out->Print("}\n"); + out->Print("\n"); +} + +void GenerateServerInterface(Printer* out, const ServiceDescriptor *service) { + out->Print("// server-side interface\n"); + out->Print("public interface $name$\n", "name", + GetServerInterfaceName(service)); + out->Print("{\n"); + out->Indent(); + for (int i = 0; i < service->method_count(); i++) { + const MethodDescriptor *method = service->method(i); + out->Print( + "$returntype$ $methodname$($request$$response_stream_maybe$, " + "ServerCallContext context);\n", + "methodname", method->name(), "returntype", + GetMethodReturnTypeServer(method), "request", + GetMethodRequestParamServer(method), "response_stream_maybe", + GetMethodResponseStreamMaybe(method)); + } + out->Outdent(); + out->Print("}\n"); + out->Print("\n"); +} + +void GenerateClientStub(Printer* out, const ServiceDescriptor *service) { + out->Print("// client stub\n"); + out->Print( + "public class $name$ : ClientBase, $interface$\n", + "name", GetClientClassName(service), "interface", + GetClientInterfaceName(service)); + out->Print("{\n"); + out->Indent(); + + // constructors + out->Print( + "public $name$(Channel channel) : base(channel)\n", + "name", GetClientClassName(service)); + out->Print("{\n"); + out->Print("}\n"); + + for (int i = 0; i < service->method_count(); i++) { + const MethodDescriptor *method = service->method(i); + MethodType method_type = GetMethodType(method); + + if (method_type == METHODTYPE_NO_STREAMING) { + // unary calls have an extra synchronous stub method + out->Print( + "public $response$ $methodname$($request$ request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))\n", + "methodname", method->name(), "request", + GetClassName(method->input_type()), "response", + GetClassName(method->output_type())); + out->Print("{\n"); + out->Indent(); + out->Print("var call = CreateCall($methodfield$, new CallOptions(headers, deadline, cancellationToken));\n", + "methodfield", GetMethodFieldName(method)); + out->Print("return Calls.BlockingUnaryCall(call, request);\n"); + out->Outdent(); + out->Print("}\n"); + + // overload taking CallOptions as a param + out->Print( + "public $response$ $methodname$($request$ request, CallOptions options)\n", + "methodname", method->name(), "request", + GetClassName(method->input_type()), "response", + GetClassName(method->output_type())); + out->Print("{\n"); + out->Indent(); + out->Print("var call = CreateCall($methodfield$, options);\n", + "methodfield", GetMethodFieldName(method)); + out->Print("return Calls.BlockingUnaryCall(call, request);\n"); + out->Outdent(); + out->Print("}\n"); + } + + std::string method_name = method->name(); + if (method_type == METHODTYPE_NO_STREAMING) { + method_name += "Async"; // prevent name clash with synchronous method. + } + out->Print( + "public $returntype$ $methodname$($request_maybe$Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))\n", + "methodname", method_name, "request_maybe", + GetMethodRequestParamMaybe(method), "returntype", + GetMethodReturnTypeClient(method)); + out->Print("{\n"); + out->Indent(); + out->Print("var call = CreateCall($methodfield$, new CallOptions(headers, deadline, cancellationToken));\n", + "methodfield", GetMethodFieldName(method)); + switch (GetMethodType(method)) { + case METHODTYPE_NO_STREAMING: + out->Print("return Calls.AsyncUnaryCall(call, request);\n"); + break; + case METHODTYPE_CLIENT_STREAMING: + out->Print("return Calls.AsyncClientStreamingCall(call);\n"); + break; + case METHODTYPE_SERVER_STREAMING: + out->Print( + "return Calls.AsyncServerStreamingCall(call, request);\n"); + break; + case METHODTYPE_BIDI_STREAMING: + out->Print("return Calls.AsyncDuplexStreamingCall(call);\n"); + break; + default: + GOOGLE_LOG(FATAL)<< "Can't get here."; + } + out->Outdent(); + out->Print("}\n"); + + // overload taking CallOptions as a param + out->Print( + "public $returntype$ $methodname$($request_maybe$CallOptions options)\n", + "methodname", method_name, "request_maybe", + GetMethodRequestParamMaybe(method), "returntype", + GetMethodReturnTypeClient(method)); + out->Print("{\n"); + out->Indent(); + out->Print("var call = CreateCall($methodfield$, options);\n", + "methodfield", GetMethodFieldName(method)); + switch (GetMethodType(method)) { + case METHODTYPE_NO_STREAMING: + out->Print("return Calls.AsyncUnaryCall(call, request);\n"); + break; + case METHODTYPE_CLIENT_STREAMING: + out->Print("return Calls.AsyncClientStreamingCall(call);\n"); + break; + case METHODTYPE_SERVER_STREAMING: + out->Print( + "return Calls.AsyncServerStreamingCall(call, request);\n"); + break; + case METHODTYPE_BIDI_STREAMING: + out->Print("return Calls.AsyncDuplexStreamingCall(call);\n"); + break; + default: + GOOGLE_LOG(FATAL)<< "Can't get here."; + } + out->Outdent(); + out->Print("}\n"); + } + out->Outdent(); + out->Print("}\n"); + out->Print("\n"); +} + +void GenerateBindServiceMethod(Printer* out, const ServiceDescriptor *service) { + out->Print( + "// creates service definition that can be registered with a server\n"); + out->Print( + "public static ServerServiceDefinition BindService($interface$ serviceImpl)\n", + "interface", GetServerInterfaceName(service)); + out->Print("{\n"); + out->Indent(); + + out->Print( + "return ServerServiceDefinition.CreateBuilder($servicenamefield$)\n", + "servicenamefield", GetServiceNameFieldName()); + out->Indent(); + out->Indent(); + for (int i = 0; i < service->method_count(); i++) { + const MethodDescriptor *method = service->method(i); + out->Print(".AddMethod($methodfield$, serviceImpl.$methodname$)", + "methodfield", GetMethodFieldName(method), "methodname", + method->name()); + if (i == service->method_count() - 1) { + out->Print(".Build();"); + } + out->Print("\n"); + } + out->Outdent(); + out->Outdent(); + + out->Outdent(); + out->Print("}\n"); + out->Print("\n"); +} + +void GenerateNewStubMethods(Printer* out, const ServiceDescriptor *service) { + out->Print("// creates a new client\n"); + out->Print("public static $classname$ NewClient(Channel channel)\n", + "classname", GetClientClassName(service)); + out->Print("{\n"); + out->Indent(); + out->Print("return new $classname$(channel);\n", "classname", + GetClientClassName(service)); + out->Outdent(); + out->Print("}\n"); + out->Print("\n"); +} + +void GenerateService(Printer* out, const ServiceDescriptor *service) { + out->Print("public static class $classname$\n", "classname", + GetServiceClassName(service)); + out->Print("{\n"); + out->Indent(); + out->Print("static readonly string $servicenamefield$ = \"$servicename$\";\n", + "servicenamefield", GetServiceNameFieldName(), "servicename", + service->full_name()); + out->Print("\n"); + + GenerateMarshallerFields(out, service); + for (int i = 0; i < service->method_count(); i++) { + GenerateStaticMethodField(out, service->method(i)); + } + GenerateServiceDescriptorProperty(out, service); + GenerateClientInterface(out, service); + GenerateServerInterface(out, service); + GenerateClientStub(out, service); + GenerateBindServiceMethod(out, service); + GenerateNewStubMethods(out, service); + + out->Outdent(); + out->Print("}\n"); +} + +} // anonymous namespace + +grpc::string GetServices(const FileDescriptor *file) { + grpc::string output; + { + // Scope the output stream so it closes and finalizes output to the string. + + StringOutputStream output_stream(&output); + Printer out(&output_stream, '$'); + + // Don't write out any output if there no services, to avoid empty service + // files being generated for proto files that don't declare any. + if (file->service_count() == 0) { + return output; + } + + // Write out a file header. + out.Print("// Generated by the protocol buffer compiler. DO NOT EDIT!\n"); + out.Print("// source: $filename$\n", "filename", file->name()); + out.Print("#region Designer generated code\n"); + out.Print("\n"); + out.Print("using System;\n"); + out.Print("using System.Threading;\n"); + out.Print("using System.Threading.Tasks;\n"); + out.Print("using Grpc.Core;\n"); + out.Print("\n"); + + out.Print("namespace $namespace$ {\n", "namespace", GetFileNamespace(file)); + out.Indent(); + for (int i = 0; i < file->service_count(); i++) { + GenerateService(&out, file->service(i)); + } + out.Outdent(); + out.Print("}\n"); + out.Print("#endregion\n"); + } + return output; +} + +} // namespace grpc_csharp_generator diff --git a/src/compiler/csharp_generator.h b/src/compiler/csharp_generator.h new file mode 100644 index 00000000..90eb7e29 --- /dev/null +++ b/src/compiler/csharp_generator.h @@ -0,0 +1,47 @@ +/* + * + * Copyright 2015, 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_INTERNAL_COMPILER_CSHARP_GENERATOR_H +#define GRPC_INTERNAL_COMPILER_CSHARP_GENERATOR_H + +#include "src/compiler/config.h" + +#include + +namespace grpc_csharp_generator { + +grpc::string GetServices(const grpc::protobuf::FileDescriptor *file); + +} // namespace grpc_csharp_generator + +#endif // GRPC_INTERNAL_COMPILER_CSHARP_GENERATOR_H diff --git a/src/compiler/csharp_generator_helpers.h b/src/compiler/csharp_generator_helpers.h new file mode 100644 index 00000000..5639ea05 --- /dev/null +++ b/src/compiler/csharp_generator_helpers.h @@ -0,0 +1,50 @@ +/* + * + * Copyright 2015, 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_INTERNAL_COMPILER_CSHARP_GENERATOR_HELPERS_H +#define GRPC_INTERNAL_COMPILER_CSHARP_GENERATOR_HELPERS_H + +#include "src/compiler/config.h" +#include "src/compiler/generator_helpers.h" + +namespace grpc_csharp_generator { + +inline bool ServicesFilename(const grpc::protobuf::FileDescriptor *file, + grpc::string *file_name_or_error) { + *file_name_or_error = grpc_generator::FileNameInUpperCamel(file, false) + "Grpc.cs"; + return true; +} + +} // namespace grpc_csharp_generator + +#endif // GRPC_INTERNAL_COMPILER_CSHARP_GENERATOR_HELPERS_H diff --git a/src/compiler/csharp_plugin.cc b/src/compiler/csharp_plugin.cc new file mode 100644 index 00000000..8b9395f9 --- /dev/null +++ b/src/compiler/csharp_plugin.cc @@ -0,0 +1,72 @@ +/* + * + * Copyright 2015, 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. + * + */ + +// Generates C# gRPC service interface out of Protobuf IDL. + +#include + +#include "src/compiler/config.h" +#include "src/compiler/csharp_generator.h" +#include "src/compiler/csharp_generator_helpers.h" + +class CSharpGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator { + public: + CSharpGrpcGenerator() {} + ~CSharpGrpcGenerator() {} + + bool Generate(const grpc::protobuf::FileDescriptor *file, + const grpc::string ¶meter, + grpc::protobuf::compiler::GeneratorContext *context, + grpc::string *error) const { + grpc::string code = grpc_csharp_generator::GetServices(file); + if (code.size() == 0) { + return true; // don't generate a file if there are no services + } + + // Get output file name. + grpc::string file_name; + if (!grpc_csharp_generator::ServicesFilename(file, &file_name)) { + return false; + } + std::unique_ptr output( + context->Open(file_name)); + grpc::protobuf::io::CodedOutputStream coded_out(output.get()); + coded_out.WriteRaw(code.data(), code.size()); + return true; + } +}; + +int main(int argc, char *argv[]) { + CSharpGrpcGenerator generator; + return grpc::protobuf::compiler::PluginMain(argc, argv, &generator); +} diff --git a/src/compiler/generator_helpers.h b/src/compiler/generator_helpers.h new file mode 100644 index 00000000..e1bb66a8 --- /dev/null +++ b/src/compiler/generator_helpers.h @@ -0,0 +1,170 @@ +/* + * + * Copyright 2015, 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_INTERNAL_COMPILER_GENERATOR_HELPERS_H +#define GRPC_INTERNAL_COMPILER_GENERATOR_HELPERS_H + +#include + +#include "src/compiler/config.h" + +namespace grpc_generator { + +inline bool StripSuffix(grpc::string *filename, const grpc::string &suffix) { + if (filename->length() >= suffix.length()) { + size_t suffix_pos = filename->length() - suffix.length(); + if (filename->compare(suffix_pos, grpc::string::npos, suffix) == 0) { + filename->resize(filename->size() - suffix.size()); + return true; + } + } + + return false; +} + +inline grpc::string StripProto(grpc::string filename) { + if (!StripSuffix(&filename, ".protodevel")) { + StripSuffix(&filename, ".proto"); + } + return filename; +} + +inline grpc::string StringReplace(grpc::string str, const grpc::string &from, + const grpc::string &to, bool replace_all) { + size_t pos = 0; + + do { + pos = str.find(from, pos); + if (pos == grpc::string::npos) { + break; + } + str.replace(pos, from.length(), to); + pos += to.length(); + } while(replace_all); + + return str; +} + +inline grpc::string StringReplace(grpc::string str, const grpc::string &from, + const grpc::string &to) { + return StringReplace(str, from, to, true); +} + +inline std::vector tokenize(const grpc::string &input, + const grpc::string &delimiters) { + std::vector tokens; + size_t pos, last_pos = 0; + + for (;;) { + bool done = false; + pos = input.find_first_of(delimiters, last_pos); + if (pos == grpc::string::npos) { + done = true; + pos = input.length(); + } + + tokens.push_back(input.substr(last_pos, pos - last_pos)); + if (done) return tokens; + + last_pos = pos + 1; + } +} + +inline grpc::string CapitalizeFirstLetter(grpc::string s) { + if (s.empty()) { + return s; + } + s[0] = ::toupper(s[0]); + return s; +} + +inline grpc::string LowercaseFirstLetter(grpc::string s) { + if (s.empty()) { + return s; + } + s[0] = ::tolower(s[0]); + return s; +} + +inline grpc::string LowerUnderscoreToUpperCamel(grpc::string str) { + std::vector tokens = tokenize(str, "_"); + grpc::string result = ""; + for (unsigned int i = 0; i < tokens.size(); i++) { + result += CapitalizeFirstLetter(tokens[i]); + } + return result; +} + +inline grpc::string FileNameInUpperCamel(const grpc::protobuf::FileDescriptor *file, + bool include_package_path) { + std::vector tokens = tokenize(StripProto(file->name()), "/"); + grpc::string result = ""; + if (include_package_path) { + for (unsigned int i = 0; i < tokens.size() - 1; i++) { + result += tokens[i] + "/"; + } + } + result += LowerUnderscoreToUpperCamel(tokens.back()); + return result; +} + +inline grpc::string FileNameInUpperCamel(const grpc::protobuf::FileDescriptor *file) { + return FileNameInUpperCamel(file, true); +} + +enum MethodType { + METHODTYPE_NO_STREAMING, + METHODTYPE_CLIENT_STREAMING, + METHODTYPE_SERVER_STREAMING, + METHODTYPE_BIDI_STREAMING +}; + +inline MethodType GetMethodType(const grpc::protobuf::MethodDescriptor *method) { + if (method->client_streaming()) { + if (method->server_streaming()) { + return METHODTYPE_BIDI_STREAMING; + } else { + return METHODTYPE_CLIENT_STREAMING; + } + } else { + if (method->server_streaming()) { + return METHODTYPE_SERVER_STREAMING; + } else { + return METHODTYPE_NO_STREAMING; + } + } +} + +} // namespace grpc_generator + +#endif // GRPC_INTERNAL_COMPILER_GENERATOR_HELPERS_H diff --git a/src/compiler/objective_c_generator.cc b/src/compiler/objective_c_generator.cc new file mode 100644 index 00000000..a3157db0 --- /dev/null +++ b/src/compiler/objective_c_generator.cc @@ -0,0 +1,253 @@ +/* + * + * Copyright 2015, 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 +#include + +#include "src/compiler/config.h" +#include "src/compiler/objective_c_generator.h" +#include "src/compiler/objective_c_generator_helpers.h" + +#include + +using ::google::protobuf::compiler::objectivec::ClassName; +using ::grpc::protobuf::io::Printer; +using ::grpc::protobuf::MethodDescriptor; +using ::grpc::protobuf::ServiceDescriptor; +using ::std::map; + +namespace grpc_objective_c_generator { +namespace { + +void PrintProtoRpcDeclarationAsPragma(Printer *printer, + const MethodDescriptor *method, + map< ::grpc::string, ::grpc::string> vars) { + vars["client_stream"] = method->client_streaming() ? "stream " : ""; + vars["server_stream"] = method->server_streaming() ? "stream " : ""; + + printer->Print(vars, + "#pragma mark $method_name$($client_stream$$request_type$)" + " returns ($server_stream$$response_type$)\n\n"); +} + +void PrintMethodSignature(Printer *printer, const MethodDescriptor *method, + const map< ::grpc::string, ::grpc::string> &vars) { + // TODO(jcanizales): Print method comments. + + printer->Print(vars, "- ($return_type$)$method_name$With"); + if (method->client_streaming()) { + printer->Print("RequestsWriter:(GRXWriter *)requestWriter"); + } else { + printer->Print(vars, "Request:($request_class$ *)request"); + } + + // TODO(jcanizales): Put this on a new line and align colons. + if (method->server_streaming()) { + printer->Print(vars, + " eventHandler:(void(^)(BOOL done, " + "$response_class$ *response, NSError *error))eventHandler"); + } else { + printer->Print(vars, + " handler:(void(^)($response_class$ *response, " + "NSError *error))handler"); + } +} + +void PrintSimpleSignature(Printer *printer, const MethodDescriptor *method, + map< ::grpc::string, ::grpc::string> vars) { + vars["method_name"] = + grpc_generator::LowercaseFirstLetter(vars["method_name"]); + vars["return_type"] = "void"; + PrintMethodSignature(printer, method, vars); +} + +void PrintAdvancedSignature(Printer *printer, const MethodDescriptor *method, + map< ::grpc::string, ::grpc::string> vars) { + vars["method_name"] = "RPCTo" + vars["method_name"]; + vars["return_type"] = "ProtoRPC *"; + PrintMethodSignature(printer, method, vars); +} + +inline map< ::grpc::string, ::grpc::string> GetMethodVars(const MethodDescriptor *method) { + map< ::grpc::string, ::grpc::string> res; + res["method_name"] = method->name(); + res["request_type"] = method->input_type()->name(); + res["response_type"] = method->output_type()->name(); + res["request_class"] = ClassName(method->input_type()); + res["response_class"] = ClassName(method->output_type()); + return res; +} + +void PrintMethodDeclarations(Printer *printer, const MethodDescriptor *method) { + map< ::grpc::string, ::grpc::string> vars = GetMethodVars(method); + + PrintProtoRpcDeclarationAsPragma(printer, method, vars); + + PrintSimpleSignature(printer, method, vars); + printer->Print(";\n\n"); + PrintAdvancedSignature(printer, method, vars); + printer->Print(";\n\n\n"); +} + +void PrintSimpleImplementation(Printer *printer, const MethodDescriptor *method, + map< ::grpc::string, ::grpc::string> vars) { + printer->Print("{\n"); + printer->Print(vars, " [[self RPCTo$method_name$With"); + if (method->client_streaming()) { + printer->Print("RequestsWriter:requestWriter"); + } else { + printer->Print("Request:request"); + } + if (method->server_streaming()) { + printer->Print(" eventHandler:eventHandler] start];\n"); + } else { + printer->Print(" handler:handler] start];\n"); + } + printer->Print("}\n"); +} + +void PrintAdvancedImplementation(Printer *printer, + const MethodDescriptor *method, + map< ::grpc::string, ::grpc::string> vars) { + printer->Print("{\n"); + printer->Print(vars, " return [self RPCToMethod:@\"$method_name$\"\n"); + + printer->Print(" requestsWriter:"); + if (method->client_streaming()) { + printer->Print("requestWriter\n"); + } else { + printer->Print("[GRXWriter writerWithValue:request]\n"); + } + + printer->Print(vars, " responseClass:[$response_class$ class]\n"); + + printer->Print(" responsesWriteable:[GRXWriteable "); + if (method->server_streaming()) { + printer->Print("writeableWithEventHandler:eventHandler]];\n"); + } else { + printer->Print("writeableWithSingleHandler:handler]];\n"); + } + + printer->Print("}\n"); +} + +void PrintMethodImplementations(Printer *printer, + const MethodDescriptor *method) { + map< ::grpc::string, ::grpc::string> vars = GetMethodVars(method); + + PrintProtoRpcDeclarationAsPragma(printer, method, vars); + + // TODO(jcanizales): Print documentation from the method. + PrintSimpleSignature(printer, method, vars); + PrintSimpleImplementation(printer, method, vars); + + printer->Print("// Returns a not-yet-started RPC object.\n"); + PrintAdvancedSignature(printer, method, vars); + PrintAdvancedImplementation(printer, method, vars); +} + +} // namespace + +::grpc::string GetHeader(const ServiceDescriptor *service) { + ::grpc::string output; + { + // Scope the output stream so it closes and finalizes output to the string. + grpc::protobuf::io::StringOutputStream output_stream(&output); + Printer printer(&output_stream, '$'); + + map< ::grpc::string, ::grpc::string> vars = {{"service_class", ServiceClassName(service)}}; + + printer.Print(vars, "@protocol $service_class$ \n\n"); + + for (int i = 0; i < service->method_count(); i++) { + PrintMethodDeclarations(&printer, service->method(i)); + } + printer.Print("@end\n\n"); + + printer.Print( + "// Basic service implementation, over gRPC, that only does" + " marshalling and parsing.\n"); + printer.Print(vars, + "@interface $service_class$ :" + " ProtoService<$service_class$>\n"); + printer.Print( + "- (instancetype)initWithHost:(NSString *)host" + " NS_DESIGNATED_INITIALIZER;\n"); + printer.Print("@end\n"); + } + return output; +} + +::grpc::string GetSource(const ServiceDescriptor *service) { + ::grpc::string output; + { + // Scope the output stream so it closes and finalizes output to the string. + grpc::protobuf::io::StringOutputStream output_stream(&output); + Printer printer(&output_stream, '$'); + + map< ::grpc::string,::grpc::string> vars = {{"service_name", service->name()}, + {"service_class", ServiceClassName(service)}, + {"package", service->file()->package()}}; + + printer.Print(vars, + "static NSString *const kPackageName = @\"$package$\";\n"); + printer.Print( + vars, "static NSString *const kServiceName = @\"$service_name$\";\n\n"); + + printer.Print(vars, "@implementation $service_class$\n\n"); + + printer.Print("// Designated initializer\n"); + printer.Print("- (instancetype)initWithHost:(NSString *)host {\n"); + printer.Print( + " return (self = [super initWithHost:host" + " packageName:kPackageName serviceName:kServiceName]);\n"); + printer.Print("}\n\n"); + printer.Print( + "// Override superclass initializer to disallow different" + " package and service names.\n"); + printer.Print("- (instancetype)initWithHost:(NSString *)host\n"); + printer.Print(" packageName:(NSString *)packageName\n"); + printer.Print(" serviceName:(NSString *)serviceName {\n"); + printer.Print(" return [self initWithHost:host];\n"); + printer.Print("}\n\n\n"); + + for (int i = 0; i < service->method_count(); i++) { + PrintMethodImplementations(&printer, service->method(i)); + } + + printer.Print("@end\n"); + } + return output; +} + +} // namespace grpc_objective_c_generator diff --git a/src/compiler/objective_c_generator.h b/src/compiler/objective_c_generator.h new file mode 100644 index 00000000..40a0c87f --- /dev/null +++ b/src/compiler/objective_c_generator.h @@ -0,0 +1,54 @@ +/* + * + * Copyright 2015, 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_INTERNAL_COMPILER_OBJECTIVE_C_GENERATOR_H +#define GRPC_INTERNAL_COMPILER_OBJECTIVE_C_GENERATOR_H + +#include "src/compiler/config.h" + +namespace grpc_objective_c_generator { + +using ::grpc::protobuf::ServiceDescriptor; +using ::grpc::string; + +// Returns the content to be included in the "global_scope" insertion point of +// the generated header file. +string GetHeader(const ServiceDescriptor *service); + +// Returns the content to be included in the "global_scope" insertion point of +// the generated implementation file. +string GetSource(const ServiceDescriptor *service); + +} // namespace grpc_objective_c_generator + +#endif // GRPC_INTERNAL_COMPILER_OBJECTIVE_C_GENERATOR_H diff --git a/src/compiler/objective_c_generator_helpers.h b/src/compiler/objective_c_generator_helpers.h new file mode 100644 index 00000000..1f8c8001 --- /dev/null +++ b/src/compiler/objective_c_generator_helpers.h @@ -0,0 +1,58 @@ +/* + * + * Copyright 2015, 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_INTERNAL_COMPILER_OBJECTIVE_C_GENERATOR_HELPERS_H +#define GRPC_INTERNAL_COMPILER_OBJECTIVE_C_GENERATOR_HELPERS_H + +#include +#include "src/compiler/config.h" +#include "src/compiler/generator_helpers.h" + +namespace grpc_objective_c_generator { + +using ::grpc::protobuf::FileDescriptor; +using ::grpc::protobuf::ServiceDescriptor; +using ::grpc::string; + +inline string MessageHeaderName(const FileDescriptor *file) { + return grpc_generator::FileNameInUpperCamel(file) + ".pbobjc.h"; +} + +inline string ServiceClassName(const ServiceDescriptor *service) { + const FileDescriptor *file = service->file(); + string prefix = file->options().objc_class_prefix(); + return prefix + service->name(); +} + +} +#endif // GRPC_INTERNAL_COMPILER_OBJECTIVE_C_GENERATOR_HELPERS_H diff --git a/src/compiler/objective_c_plugin.cc b/src/compiler/objective_c_plugin.cc new file mode 100644 index 00000000..17440358 --- /dev/null +++ b/src/compiler/objective_c_plugin.cc @@ -0,0 +1,122 @@ +/* + * + * Copyright 2015, 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. + * + */ + +// Generates Objective C gRPC service interface out of Protobuf IDL. + +#include + +#include "src/compiler/config.h" +#include "src/compiler/objective_c_generator.h" +#include "src/compiler/objective_c_generator_helpers.h" + +class ObjectiveCGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator { + public: + ObjectiveCGrpcGenerator() {} + virtual ~ObjectiveCGrpcGenerator() {} + + virtual bool Generate(const grpc::protobuf::FileDescriptor *file, + const ::grpc::string ¶meter, + grpc::protobuf::compiler::GeneratorContext *context, + ::grpc::string *error) const { + + if (file->service_count() == 0) { + // No services. Do nothing. + return true; + } + + ::grpc::string file_name = grpc_generator::FileNameInUpperCamel(file); + ::grpc::string prefix = file->options().objc_class_prefix(); + + { + // Generate .pbrpc.h + + ::grpc::string imports = ::grpc::string("#import \"") + file_name + + ".pbobjc.h\"\n\n" + "#import \n" + "#import \n" + "#import \n"; + + // TODO(jcanizales): Instead forward-declare the input and output types + // and import the files in the .pbrpc.m + ::grpc::string proto_imports; + for (int i = 0; i < file->dependency_count(); i++) { + ::grpc::string header = grpc_objective_c_generator::MessageHeaderName( + file->dependency(i)); + proto_imports += ::grpc::string("#import \"") + header + "\"\n"; + } + + ::grpc::string declarations; + for (int i = 0; i < file->service_count(); i++) { + const grpc::protobuf::ServiceDescriptor *service = file->service(i); + declarations += grpc_objective_c_generator::GetHeader(service); + } + + Write(context, file_name + ".pbrpc.h", + imports + '\n' + proto_imports + '\n' + declarations); + } + + { + // Generate .pbrpc.m + + ::grpc::string imports = ::grpc::string("#import \"") + file_name + + ".pbrpc.h\"\n\n" + "#import \n" + "#import \n"; + + ::grpc::string definitions; + for (int i = 0; i < file->service_count(); i++) { + const grpc::protobuf::ServiceDescriptor *service = file->service(i); + definitions += grpc_objective_c_generator::GetSource(service); + } + + Write(context, file_name + ".pbrpc.m", imports + '\n' + definitions); + } + + return true; + } + + private: + // Write the given code into the given file. + void Write(grpc::protobuf::compiler::GeneratorContext *context, + const ::grpc::string &filename, const ::grpc::string &code) const { + std::unique_ptr output( + context->Open(filename)); + grpc::protobuf::io::CodedOutputStream coded_out(output.get()); + coded_out.WriteRaw(code.data(), code.size()); + } +}; + +int main(int argc, char *argv[]) { + ObjectiveCGrpcGenerator generator; + return grpc::protobuf::compiler::PluginMain(argc, argv, &generator); +} diff --git a/src/compiler/python_generator.cc b/src/compiler/python_generator.cc new file mode 100644 index 00000000..83133f2b --- /dev/null +++ b/src/compiler/python_generator.cc @@ -0,0 +1,749 @@ +/* + * + * Copyright 2015, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "src/compiler/config.h" +#include "src/compiler/generator_helpers.h" +#include "src/compiler/python_generator.h" + +using grpc_generator::StringReplace; +using grpc_generator::StripProto; +using grpc::protobuf::Descriptor; +using grpc::protobuf::FileDescriptor; +using grpc::protobuf::MethodDescriptor; +using grpc::protobuf::ServiceDescriptor; +using grpc::protobuf::compiler::GeneratorContext; +using grpc::protobuf::io::CodedOutputStream; +using grpc::protobuf::io::Printer; +using grpc::protobuf::io::StringOutputStream; +using grpc::protobuf::io::ZeroCopyOutputStream; +using std::initializer_list; +using std::make_pair; +using std::map; +using std::pair; +using std::replace; +using std::vector; + +namespace grpc_python_generator { + +PythonGrpcGenerator::PythonGrpcGenerator(const GeneratorConfiguration& config) + : config_(config) {} + +PythonGrpcGenerator::~PythonGrpcGenerator() {} + +bool PythonGrpcGenerator::Generate( + const FileDescriptor* file, const grpc::string& parameter, + GeneratorContext* context, grpc::string* error) const { + // Get output file name. + grpc::string file_name; + static const int proto_suffix_length = strlen(".proto"); + if (file->name().size() > static_cast(proto_suffix_length) && + file->name().find_last_of(".proto") == file->name().size() - 1) { + file_name = file->name().substr( + 0, file->name().size() - proto_suffix_length) + "_pb2.py"; + } else { + *error = "Invalid proto file name. Proto file must end with .proto"; + return false; + } + + std::unique_ptr output( + context->OpenForInsert(file_name, "module_scope")); + CodedOutputStream coded_out(output.get()); + bool success = false; + grpc::string code = ""; + tie(success, code) = grpc_python_generator::GetServices(file, config_); + if (success) { + coded_out.WriteRaw(code.data(), code.size()); + return true; + } else { + return false; + } +} + +namespace { +////////////////////////////////// +// BEGIN FORMATTING BOILERPLATE // +////////////////////////////////// + +// Converts an initializer list of the form { key0, value0, key1, value1, ... } +// into a map of key* to value*. Is merely a readability helper for later code. +map ListToDict( + const initializer_list& values) { + assert(values.size() % 2 == 0); + map value_map; + auto value_iter = values.begin(); + for (unsigned i = 0; i < values.size()/2; ++i) { + grpc::string key = *value_iter; + ++value_iter; + grpc::string value = *value_iter; + value_map[key] = value; + ++value_iter; + } + return value_map; +} + +// Provides RAII indentation handling. Use as: +// { +// IndentScope raii_my_indent_var_name_here(my_py_printer); +// // constructor indented my_py_printer +// ... +// // destructor called at end of scope, un-indenting my_py_printer +// } +class IndentScope { + public: + explicit IndentScope(Printer* printer) : printer_(printer) { + printer_->Indent(); + } + + ~IndentScope() { + printer_->Outdent(); + } + + private: + Printer* printer_; +}; + +//////////////////////////////// +// END FORMATTING BOILERPLATE // +//////////////////////////////// + +bool PrintAlphaServicer(const ServiceDescriptor* service, + Printer* out) { + grpc::string doc = ""; + map dict = ListToDict({ + "Service", service->name(), + "Documentation", doc, + }); + out->Print(dict, "class EarlyAdopter$Service$Servicer(object):\n"); + { + IndentScope raii_class_indent(out); + out->Print(dict, "\"\"\"$Documentation$\"\"\"\n"); + out->Print("__metaclass__ = abc.ABCMeta\n"); + for (int i = 0; i < service->method_count(); ++i) { + auto meth = service->method(i); + grpc::string arg_name = meth->client_streaming() ? + "request_iterator" : "request"; + out->Print("@abc.abstractmethod\n"); + out->Print("def $Method$(self, $ArgName$, context):\n", + "Method", meth->name(), "ArgName", arg_name); + { + IndentScope raii_method_indent(out); + out->Print("raise NotImplementedError()\n"); + } + } + } + return true; +} + +bool PrintAlphaServer(const ServiceDescriptor* service, Printer* out) { + grpc::string doc = ""; + map dict = ListToDict({ + "Service", service->name(), + "Documentation", doc, + }); + out->Print(dict, "class EarlyAdopter$Service$Server(object):\n"); + { + IndentScope raii_class_indent(out); + out->Print(dict, "\"\"\"$Documentation$\"\"\"\n"); + out->Print("__metaclass__ = abc.ABCMeta\n"); + out->Print("@abc.abstractmethod\n"); + out->Print("def start(self):\n"); + { + IndentScope raii_method_indent(out); + out->Print("raise NotImplementedError()\n"); + } + + out->Print("@abc.abstractmethod\n"); + out->Print("def stop(self):\n"); + { + IndentScope raii_method_indent(out); + out->Print("raise NotImplementedError()\n"); + } + } + return true; +} + +bool PrintAlphaStub(const ServiceDescriptor* service, + Printer* out) { + grpc::string doc = ""; + map dict = ListToDict({ + "Service", service->name(), + "Documentation", doc, + }); + out->Print(dict, "class EarlyAdopter$Service$Stub(object):\n"); + { + IndentScope raii_class_indent(out); + out->Print(dict, "\"\"\"$Documentation$\"\"\"\n"); + out->Print("__metaclass__ = abc.ABCMeta\n"); + for (int i = 0; i < service->method_count(); ++i) { + const MethodDescriptor* meth = service->method(i); + grpc::string arg_name = meth->client_streaming() ? + "request_iterator" : "request"; + auto methdict = ListToDict({"Method", meth->name(), "ArgName", arg_name}); + out->Print("@abc.abstractmethod\n"); + out->Print(methdict, "def $Method$(self, $ArgName$):\n"); + { + IndentScope raii_method_indent(out); + out->Print("raise NotImplementedError()\n"); + } + out->Print(methdict, "$Method$.async = None\n"); + } + } + return true; +} + +// TODO(protobuf team): Export `ModuleName` from protobuf's +// `src/google/protobuf/compiler/python/python_generator.cc` file. +grpc::string ModuleName(const grpc::string& filename) { + grpc::string basename = StripProto(filename); + basename = StringReplace(basename, "-", "_"); + basename = StringReplace(basename, "/", "."); + return basename + "_pb2"; +} + +bool GetModuleAndMessagePath(const Descriptor* type, + pair* out) { + const Descriptor* path_elem_type = type; + vector message_path; + do { + message_path.push_back(path_elem_type); + path_elem_type = path_elem_type->containing_type(); + } while (path_elem_type); // implicit nullptr comparison; don't be explicit + grpc::string file_name = type->file()->name(); + static const int proto_suffix_length = strlen(".proto"); + if (!(file_name.size() > static_cast(proto_suffix_length) && + file_name.find_last_of(".proto") == file_name.size() - 1)) { + return false; + } + grpc::string module = ModuleName(file_name); + grpc::string message_type; + for (auto path_iter = message_path.rbegin(); + path_iter != message_path.rend(); ++path_iter) { + message_type += (*path_iter)->name() + "."; + } + // no pop_back prior to C++11 + message_type.resize(message_type.size() - 1); + *out = make_pair(module, message_type); + return true; +} + +bool PrintAlphaServerFactory(const grpc::string& package_qualified_service_name, + const ServiceDescriptor* service, Printer* out) { + out->Print("def early_adopter_create_$Service$_server(servicer, port, " + "private_key=None, certificate_chain=None):\n", + "Service", service->name()); + { + IndentScope raii_create_server_indent(out); + map method_description_constructors; + map> + input_message_modules_and_classes; + map> + output_message_modules_and_classes; + for (int i = 0; i < service->method_count(); ++i) { + const MethodDescriptor* method = service->method(i); + const grpc::string method_description_constructor = + grpc::string(method->client_streaming() ? "stream_" : "unary_") + + grpc::string(method->server_streaming() ? "stream_" : "unary_") + + "service_description"; + pair input_message_module_and_class; + if (!GetModuleAndMessagePath(method->input_type(), + &input_message_module_and_class)) { + return false; + } + pair output_message_module_and_class; + if (!GetModuleAndMessagePath(method->output_type(), + &output_message_module_and_class)) { + return false; + } + // Import the modules that define the messages used in RPCs. + out->Print("import $Module$\n", "Module", + input_message_module_and_class.first); + out->Print("import $Module$\n", "Module", + output_message_module_and_class.first); + method_description_constructors.insert( + make_pair(method->name(), method_description_constructor)); + input_message_modules_and_classes.insert( + make_pair(method->name(), input_message_module_and_class)); + output_message_modules_and_classes.insert( + make_pair(method->name(), output_message_module_and_class)); + } + out->Print("method_service_descriptions = {\n"); + for (auto name_and_description_constructor = + method_description_constructors.begin(); + name_and_description_constructor != + method_description_constructors.end(); + name_and_description_constructor++) { + IndentScope raii_descriptions_indent(out); + const grpc::string method_name = name_and_description_constructor->first; + auto input_message_module_and_class = + input_message_modules_and_classes.find(method_name); + auto output_message_module_and_class = + output_message_modules_and_classes.find(method_name); + out->Print("\"$Method$\": alpha_utilities.$Constructor$(\n", "Method", + method_name, "Constructor", + name_and_description_constructor->second); + { + IndentScope raii_description_arguments_indent(out); + out->Print("servicer.$Method$,\n", "Method", method_name); + out->Print( + "$InputTypeModule$.$InputTypeClass$.FromString,\n", + "InputTypeModule", input_message_module_and_class->second.first, + "InputTypeClass", input_message_module_and_class->second.second); + out->Print( + "$OutputTypeModule$.$OutputTypeClass$.SerializeToString,\n", + "OutputTypeModule", output_message_module_and_class->second.first, + "OutputTypeClass", output_message_module_and_class->second.second); + } + out->Print("),\n"); + } + out->Print("}\n"); + out->Print( + "return early_adopter_implementations.server(" + "\"$PackageQualifiedServiceName$\"," + " method_service_descriptions, port, private_key=private_key," + " certificate_chain=certificate_chain)\n", + "PackageQualifiedServiceName", package_qualified_service_name); + } + return true; +} + +bool PrintAlphaStubFactory(const grpc::string& package_qualified_service_name, + const ServiceDescriptor* service, Printer* out) { + map dict = ListToDict({ + "Service", service->name(), + }); + out->Print(dict, "def early_adopter_create_$Service$_stub(host, port," + " metadata_transformer=None," + " secure=False, root_certificates=None, private_key=None," + " certificate_chain=None, server_host_override=None):\n"); + { + IndentScope raii_create_server_indent(out); + map method_description_constructors; + map> + input_message_modules_and_classes; + map> + output_message_modules_and_classes; + for (int i = 0; i < service->method_count(); ++i) { + const MethodDescriptor* method = service->method(i); + const grpc::string method_description_constructor = + grpc::string(method->client_streaming() ? "stream_" : "unary_") + + grpc::string(method->server_streaming() ? "stream_" : "unary_") + + "invocation_description"; + pair input_message_module_and_class; + if (!GetModuleAndMessagePath(method->input_type(), + &input_message_module_and_class)) { + return false; + } + pair output_message_module_and_class; + if (!GetModuleAndMessagePath(method->output_type(), + &output_message_module_and_class)) { + return false; + } + // Import the modules that define the messages used in RPCs. + out->Print("import $Module$\n", "Module", + input_message_module_and_class.first); + out->Print("import $Module$\n", "Module", + output_message_module_and_class.first); + method_description_constructors.insert( + make_pair(method->name(), method_description_constructor)); + input_message_modules_and_classes.insert( + make_pair(method->name(), input_message_module_and_class)); + output_message_modules_and_classes.insert( + make_pair(method->name(), output_message_module_and_class)); + } + out->Print("method_invocation_descriptions = {\n"); + for (auto name_and_description_constructor = + method_description_constructors.begin(); + name_and_description_constructor != + method_description_constructors.end(); + name_and_description_constructor++) { + IndentScope raii_descriptions_indent(out); + const grpc::string method_name = name_and_description_constructor->first; + auto input_message_module_and_class = + input_message_modules_and_classes.find(method_name); + auto output_message_module_and_class = + output_message_modules_and_classes.find(method_name); + out->Print("\"$Method$\": alpha_utilities.$Constructor$(\n", "Method", + method_name, "Constructor", + name_and_description_constructor->second); + { + IndentScope raii_description_arguments_indent(out); + out->Print( + "$InputTypeModule$.$InputTypeClass$.SerializeToString,\n", + "InputTypeModule", input_message_module_and_class->second.first, + "InputTypeClass", input_message_module_and_class->second.second); + out->Print( + "$OutputTypeModule$.$OutputTypeClass$.FromString,\n", + "OutputTypeModule", output_message_module_and_class->second.first, + "OutputTypeClass", output_message_module_and_class->second.second); + } + out->Print("),\n"); + } + out->Print("}\n"); + out->Print( + "return early_adopter_implementations.stub(" + "\"$PackageQualifiedServiceName$\"," + " method_invocation_descriptions, host, port," + " metadata_transformer=metadata_transformer, secure=secure," + " root_certificates=root_certificates, private_key=private_key," + " certificate_chain=certificate_chain," + " server_host_override=server_host_override)\n", + "PackageQualifiedServiceName", package_qualified_service_name); + } + return true; +} + +bool PrintBetaServicer(const ServiceDescriptor* service, + Printer* out) { + grpc::string doc = ""; + map dict = ListToDict({ + "Service", service->name(), + "Documentation", doc, + }); + out->Print("\n"); + out->Print(dict, "class Beta$Service$Servicer(object):\n"); + { + IndentScope raii_class_indent(out); + out->Print(dict, "\"\"\"$Documentation$\"\"\"\n"); + out->Print("__metaclass__ = abc.ABCMeta\n"); + for (int i = 0; i < service->method_count(); ++i) { + auto meth = service->method(i); + grpc::string arg_name = meth->client_streaming() ? + "request_iterator" : "request"; + out->Print("@abc.abstractmethod\n"); + out->Print("def $Method$(self, $ArgName$, context):\n", + "Method", meth->name(), "ArgName", arg_name); + { + IndentScope raii_method_indent(out); + out->Print("raise NotImplementedError()\n"); + } + } + } + return true; +} + +bool PrintBetaStub(const ServiceDescriptor* service, + Printer* out) { + grpc::string doc = "The interface to which stubs will conform."; + map dict = ListToDict({ + "Service", service->name(), + "Documentation", doc, + }); + out->Print("\n"); + out->Print(dict, "class Beta$Service$Stub(object):\n"); + { + IndentScope raii_class_indent(out); + out->Print(dict, "\"\"\"$Documentation$\"\"\"\n"); + out->Print("__metaclass__ = abc.ABCMeta\n"); + for (int i = 0; i < service->method_count(); ++i) { + const MethodDescriptor* meth = service->method(i); + grpc::string arg_name = meth->client_streaming() ? + "request_iterator" : "request"; + auto methdict = ListToDict({"Method", meth->name(), "ArgName", arg_name}); + out->Print("@abc.abstractmethod\n"); + out->Print(methdict, "def $Method$(self, $ArgName$, timeout):\n"); + { + IndentScope raii_method_indent(out); + out->Print("raise NotImplementedError()\n"); + } + if (!meth->server_streaming()) { + out->Print(methdict, "$Method$.future = None\n"); + } + } + } + return true; +} + +bool PrintBetaServerFactory(const grpc::string& package_qualified_service_name, + const ServiceDescriptor* service, Printer* out) { + out->Print("\n"); + out->Print("def beta_create_$Service$_server(servicer, pool=None, " + "pool_size=None, default_timeout=None, maximum_timeout=None):\n", + "Service", service->name()); + { + IndentScope raii_create_server_indent(out); + map method_implementation_constructors; + map> + input_message_modules_and_classes; + map> + output_message_modules_and_classes; + for (int i = 0; i < service->method_count(); ++i) { + const MethodDescriptor* method = service->method(i); + const grpc::string method_implementation_constructor = + grpc::string(method->client_streaming() ? "stream_" : "unary_") + + grpc::string(method->server_streaming() ? "stream_" : "unary_") + + "inline"; + pair input_message_module_and_class; + if (!GetModuleAndMessagePath(method->input_type(), + &input_message_module_and_class)) { + return false; + } + pair output_message_module_and_class; + if (!GetModuleAndMessagePath(method->output_type(), + &output_message_module_and_class)) { + return false; + } + // Import the modules that define the messages used in RPCs. + out->Print("import $Module$\n", "Module", + input_message_module_and_class.first); + out->Print("import $Module$\n", "Module", + output_message_module_and_class.first); + method_implementation_constructors.insert( + make_pair(method->name(), method_implementation_constructor)); + input_message_modules_and_classes.insert( + make_pair(method->name(), input_message_module_and_class)); + output_message_modules_and_classes.insert( + make_pair(method->name(), output_message_module_and_class)); + } + out->Print("request_deserializers = {\n"); + for (auto name_and_input_module_class_pair = + input_message_modules_and_classes.begin(); + name_and_input_module_class_pair != + input_message_modules_and_classes.end(); + name_and_input_module_class_pair++) { + IndentScope raii_indent(out); + out->Print("(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): " + "$InputTypeModule$.$InputTypeClass$.FromString,\n", + "PackageQualifiedServiceName", package_qualified_service_name, + "MethodName", name_and_input_module_class_pair->first, + "InputTypeModule", + name_and_input_module_class_pair->second.first, + "InputTypeClass", + name_and_input_module_class_pair->second.second); + } + out->Print("}\n"); + out->Print("response_serializers = {\n"); + for (auto name_and_output_module_class_pair = + output_message_modules_and_classes.begin(); + name_and_output_module_class_pair != + output_message_modules_and_classes.end(); + name_and_output_module_class_pair++) { + IndentScope raii_indent(out); + out->Print("(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): " + "$OutputTypeModule$.$OutputTypeClass$.SerializeToString,\n", + "PackageQualifiedServiceName", package_qualified_service_name, + "MethodName", name_and_output_module_class_pair->first, + "OutputTypeModule", + name_and_output_module_class_pair->second.first, + "OutputTypeClass", + name_and_output_module_class_pair->second.second); + } + out->Print("}\n"); + out->Print("method_implementations = {\n"); + for (auto name_and_implementation_constructor = + method_implementation_constructors.begin(); + name_and_implementation_constructor != + method_implementation_constructors.end(); + name_and_implementation_constructor++) { + IndentScope raii_descriptions_indent(out); + const grpc::string method_name = + name_and_implementation_constructor->first; + out->Print("(\'$PackageQualifiedServiceName$\', \'$Method$\'): " + "face_utilities.$Constructor$(servicer.$Method$),\n", + "PackageQualifiedServiceName", package_qualified_service_name, + "Method", name_and_implementation_constructor->first, + "Constructor", name_and_implementation_constructor->second); + } + out->Print("}\n"); + out->Print("server_options = beta_implementations.server_options(" + "request_deserializers=request_deserializers, " + "response_serializers=response_serializers, " + "thread_pool=pool, thread_pool_size=pool_size, " + "default_timeout=default_timeout, " + "maximum_timeout=maximum_timeout)\n"); + out->Print("return beta_implementations.server(method_implementations, " + "options=server_options)\n"); + } + return true; +} + +bool PrintBetaStubFactory(const grpc::string& package_qualified_service_name, + const ServiceDescriptor* service, Printer* out) { + map dict = ListToDict({ + "Service", service->name(), + }); + out->Print("\n"); + out->Print(dict, "def beta_create_$Service$_stub(channel, host=None," + " metadata_transformer=None, pool=None, pool_size=None):\n"); + { + IndentScope raii_create_server_indent(out); + map method_cardinalities; + map> + input_message_modules_and_classes; + map> + output_message_modules_and_classes; + for (int i = 0; i < service->method_count(); ++i) { + const MethodDescriptor* method = service->method(i); + const grpc::string method_cardinality = + grpc::string(method->client_streaming() ? "STREAM" : "UNARY") + + "_" + + grpc::string(method->server_streaming() ? "STREAM" : "UNARY"); + pair input_message_module_and_class; + if (!GetModuleAndMessagePath(method->input_type(), + &input_message_module_and_class)) { + return false; + } + pair output_message_module_and_class; + if (!GetModuleAndMessagePath(method->output_type(), + &output_message_module_and_class)) { + return false; + } + // Import the modules that define the messages used in RPCs. + out->Print("import $Module$\n", "Module", + input_message_module_and_class.first); + out->Print("import $Module$\n", "Module", + output_message_module_and_class.first); + method_cardinalities.insert( + make_pair(method->name(), method_cardinality)); + input_message_modules_and_classes.insert( + make_pair(method->name(), input_message_module_and_class)); + output_message_modules_and_classes.insert( + make_pair(method->name(), output_message_module_and_class)); + } + out->Print("request_serializers = {\n"); + for (auto name_and_input_module_class_pair = + input_message_modules_and_classes.begin(); + name_and_input_module_class_pair != + input_message_modules_and_classes.end(); + name_and_input_module_class_pair++) { + IndentScope raii_indent(out); + out->Print("(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): " + "$InputTypeModule$.$InputTypeClass$.SerializeToString,\n", + "PackageQualifiedServiceName", package_qualified_service_name, + "MethodName", name_and_input_module_class_pair->first, + "InputTypeModule", + name_and_input_module_class_pair->second.first, + "InputTypeClass", + name_and_input_module_class_pair->second.second); + } + out->Print("}\n"); + out->Print("response_deserializers = {\n"); + for (auto name_and_output_module_class_pair = + output_message_modules_and_classes.begin(); + name_and_output_module_class_pair != + output_message_modules_and_classes.end(); + name_and_output_module_class_pair++) { + IndentScope raii_indent(out); + out->Print("(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): " + "$OutputTypeModule$.$OutputTypeClass$.FromString,\n", + "PackageQualifiedServiceName", package_qualified_service_name, + "MethodName", name_and_output_module_class_pair->first, + "OutputTypeModule", + name_and_output_module_class_pair->second.first, + "OutputTypeClass", + name_and_output_module_class_pair->second.second); + } + out->Print("}\n"); + out->Print("cardinalities = {\n"); + for (auto name_and_cardinality = method_cardinalities.begin(); + name_and_cardinality != method_cardinalities.end(); + name_and_cardinality++) { + IndentScope raii_descriptions_indent(out); + out->Print("\'$Method$\': cardinality.Cardinality.$Cardinality$,\n", + "Method", name_and_cardinality->first, + "Cardinality", name_and_cardinality->second); + } + out->Print("}\n"); + out->Print("stub_options = beta_implementations.stub_options(" + "host=host, metadata_transformer=metadata_transformer, " + "request_serializers=request_serializers, " + "response_deserializers=response_deserializers, " + "thread_pool=pool, thread_pool_size=pool_size)\n"); + out->Print( + "return beta_implementations.dynamic_stub(channel, \'$PackageQualifiedServiceName$\', " + "cardinalities, options=stub_options)\n", + "PackageQualifiedServiceName", package_qualified_service_name); + } + return true; +} + +bool PrintPreamble(const FileDescriptor* file, + const GeneratorConfiguration& config, Printer* out) { + out->Print("import abc\n"); + out->Print("from $Package$ import implementations as beta_implementations\n", + "Package", config.beta_package_root); + out->Print("from $Package$ import implementations as early_adopter_implementations\n", + "Package", config.early_adopter_package_root); + out->Print("from grpc.framework.alpha import utilities as alpha_utilities\n"); + out->Print("from grpc.framework.common import cardinality\n"); + out->Print("from grpc.framework.interfaces.face import utilities as face_utilities\n"); + return true; +} + +} // namespace + +pair GetServices(const FileDescriptor* file, + const GeneratorConfiguration& config) { + grpc::string output; + { + // Scope the output stream so it closes and finalizes output to the string. + StringOutputStream output_stream(&output); + Printer out(&output_stream, '$'); + if (!PrintPreamble(file, config, &out)) { + return make_pair(false, ""); + } + auto package = file->package(); + if (!package.empty()) { + package = package.append("."); + } + for (int i = 0; i < file->service_count(); ++i) { + auto service = file->service(i); + auto package_qualified_service_name = package + service->name(); + if (!(PrintAlphaServicer(service, &out) && + PrintAlphaServer(service, &out) && + PrintAlphaStub(service, &out) && + PrintAlphaServerFactory(package_qualified_service_name, service, &out) && + PrintAlphaStubFactory(package_qualified_service_name, service, &out) && + PrintBetaServicer(service, &out) && + PrintBetaStub(service, &out) && + PrintBetaServerFactory(package_qualified_service_name, service, &out) && + PrintBetaStubFactory(package_qualified_service_name, service, &out))) { + return make_pair(false, ""); + } + } + } + return make_pair(true, std::move(output)); +} + +} // namespace grpc_python_generator diff --git a/src/compiler/python_generator.h b/src/compiler/python_generator.h new file mode 100644 index 00000000..44ed4b3f --- /dev/null +++ b/src/compiler/python_generator.h @@ -0,0 +1,69 @@ +/* + * + * Copyright 2015, 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_INTERNAL_COMPILER_PYTHON_GENERATOR_H +#define GRPC_INTERNAL_COMPILER_PYTHON_GENERATOR_H + +#include + +#include "src/compiler/config.h" + +namespace grpc_python_generator { + +// Data pertaining to configuration of the generator with respect to anything +// that may be used internally at Google. +struct GeneratorConfiguration { + grpc::string early_adopter_package_root; + grpc::string beta_package_root; +}; + +class PythonGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator { + public: + PythonGrpcGenerator(const GeneratorConfiguration& config); + ~PythonGrpcGenerator(); + + bool Generate(const grpc::protobuf::FileDescriptor* file, + const grpc::string& parameter, + grpc::protobuf::compiler::GeneratorContext* context, + grpc::string* error) const; + private: + GeneratorConfiguration config_; +}; + +std::pair GetServices( + const grpc::protobuf::FileDescriptor* file, + const GeneratorConfiguration& config); + +} // namespace grpc_python_generator + +#endif // GRPC_INTERNAL_COMPILER_PYTHON_GENERATOR_H diff --git a/src/compiler/python_plugin.cc b/src/compiler/python_plugin.cc new file mode 100644 index 00000000..c7cef549 --- /dev/null +++ b/src/compiler/python_plugin.cc @@ -0,0 +1,45 @@ +/* + * + * Copyright 2015, 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. + * + */ + +// Generates a Python gRPC service interface out of Protobuf IDL. + +#include "src/compiler/config.h" +#include "src/compiler/python_generator.h" + +int main(int argc, char* argv[]) { + grpc_python_generator::GeneratorConfiguration config; + config.early_adopter_package_root = "grpc.early_adopter"; + config.beta_package_root = "grpc.beta"; + grpc_python_generator::PythonGrpcGenerator generator(config); + return grpc::protobuf::compiler::PluginMain(argc, argv, &generator); +} diff --git a/src/compiler/ruby_generator.cc b/src/compiler/ruby_generator.cc new file mode 100644 index 00000000..29913751 --- /dev/null +++ b/src/compiler/ruby_generator.cc @@ -0,0 +1,171 @@ +/* + * + * Copyright 2015, 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 +#include +#include + +#include "src/compiler/config.h" +#include "src/compiler/ruby_generator.h" +#include "src/compiler/ruby_generator_helpers-inl.h" +#include "src/compiler/ruby_generator_map-inl.h" +#include "src/compiler/ruby_generator_string-inl.h" + +using grpc::protobuf::FileDescriptor; +using grpc::protobuf::ServiceDescriptor; +using grpc::protobuf::MethodDescriptor; +using grpc::protobuf::io::Printer; +using grpc::protobuf::io::StringOutputStream; +using std::map; +using std::vector; + +namespace grpc_ruby_generator { +namespace { + +// Prints out the method using the ruby gRPC DSL. +void PrintMethod(const MethodDescriptor *method, const grpc::string &package, + Printer *out) { + grpc::string input_type = RubyTypeOf(method->input_type()->name(), package); + if (method->client_streaming()) { + input_type = "stream(" + input_type + ")"; + } + grpc::string output_type = RubyTypeOf(method->output_type()->name(), package); + if (method->server_streaming()) { + output_type = "stream(" + output_type + ")"; + } + std::map method_vars = + ListToDict({"mth.name", method->name(), "input.type", input_type, + "output.type", output_type, }); + out->Print(method_vars, "rpc :$mth.name$, $input.type$, $output.type$\n"); +} + +// Prints out the service using the ruby gRPC DSL. +void PrintService(const ServiceDescriptor *service, const grpc::string &package, + Printer *out) { + if (service->method_count() == 0) { + return; + } + + // Begin the service module + std::map module_vars = + ListToDict({"module.name", CapitalizeFirst(service->name()), }); + out->Print(module_vars, "module $module.name$\n"); + out->Indent(); + + // TODO(temiola): add documentation + grpc::string doc = "TODO: add proto service documentation here"; + std::map template_vars = + ListToDict({"Documentation", doc, }); + out->Print("\n"); + out->Print(template_vars, "# $Documentation$\n"); + out->Print("class Service\n"); + + // Write the indented class body. + out->Indent(); + out->Print("\n"); + out->Print("include GRPC::GenericService\n"); + out->Print("\n"); + out->Print("self.marshal_class_method = :encode\n"); + out->Print("self.unmarshal_class_method = :decode\n"); + std::map pkg_vars = + ListToDict({"service.name", service->name(), "pkg.name", package, }); + out->Print(pkg_vars, "self.service_name = '$pkg.name$.$service.name$'\n"); + out->Print("\n"); + for (int i = 0; i < service->method_count(); ++i) { + PrintMethod(service->method(i), package, out); + } + out->Outdent(); + + out->Print("end\n"); + out->Print("\n"); + out->Print("Stub = Service.rpc_stub_class\n"); + + // End the service module + out->Outdent(); + out->Print("end\n"); +} + +} // namespace + +grpc::string GetServices(const FileDescriptor *file) { + grpc::string output; + { + // Scope the output stream so it closes and finalizes output to the string. + + StringOutputStream output_stream(&output); + Printer out(&output_stream, '$'); + + // Don't write out any output if there no services, to avoid empty service + // files being generated for proto files that don't declare any. + if (file->service_count() == 0) { + return output; + } + + // Write out a file header. + std::map header_comment_vars = ListToDict( + {"file.name", file->name(), "file.package", file->package(), }); + out.Print("# Generated by the protocol buffer compiler. DO NOT EDIT!\n"); + out.Print(header_comment_vars, + "# Source: $file.name$ for package '$file.package$'\n"); + + out.Print("\n"); + out.Print("require 'grpc'\n"); + // Write out require statemment to import the separately generated file + // that defines the messages used by the service. This is generated by the + // main ruby plugin. + std::map dep_vars = + ListToDict({"dep.name", MessagesRequireName(file), }); + out.Print(dep_vars, "require '$dep.name$'\n"); + + // Write out services within the modules + out.Print("\n"); + std::vector modules = Split(file->package(), '.'); + for (size_t i = 0; i < modules.size(); ++i) { + std::map module_vars = + ListToDict({"module.name", CapitalizeFirst(modules[i]), }); + out.Print(module_vars, "module $module.name$\n"); + out.Indent(); + } + for (int i = 0; i < file->service_count(); ++i) { + auto service = file->service(i); + PrintService(service, file->package(), &out); + } + for (size_t i = 0; i < modules.size(); ++i) { + out.Outdent(); + out.Print("end\n"); + } + } + return output; +} + +} // namespace grpc_ruby_generator diff --git a/src/compiler/ruby_generator.h b/src/compiler/ruby_generator.h new file mode 100644 index 00000000..a2ab36d4 --- /dev/null +++ b/src/compiler/ruby_generator.h @@ -0,0 +1,45 @@ +/* + * + * Copyright 2015, 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_INTERNAL_COMPILER_RUBY_GENERATOR_H +#define GRPC_INTERNAL_COMPILER_RUBY_GENERATOR_H + +#include "src/compiler/config.h" + +namespace grpc_ruby_generator { + +grpc::string GetServices(const grpc::protobuf::FileDescriptor *file); + +} // namespace grpc_ruby_generator + +#endif // GRPC_INTERNAL_COMPILER_RUBY_GENERATOR_H diff --git a/src/compiler/ruby_generator_helpers-inl.h b/src/compiler/ruby_generator_helpers-inl.h new file mode 100644 index 00000000..9da7cab3 --- /dev/null +++ b/src/compiler/ruby_generator_helpers-inl.h @@ -0,0 +1,65 @@ +/* + * + * Copyright 2015, 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_INTERNAL_COMPILER_RUBY_GENERATOR_HELPERS_INL_H +#define GRPC_INTERNAL_COMPILER_RUBY_GENERATOR_HELPERS_INL_H + +#include "src/compiler/config.h" +#include "src/compiler/ruby_generator_string-inl.h" + +namespace grpc_ruby_generator { + +inline bool ServicesFilename(const grpc::protobuf::FileDescriptor *file, + grpc::string *file_name_or_error) { + // Get output file name. + static const unsigned proto_suffix_length = 6; // length of ".proto" + if (file->name().size() > proto_suffix_length && + file->name().find_last_of(".proto") == file->name().size() - 1) { + *file_name_or_error = + file->name().substr(0, file->name().size() - proto_suffix_length) + + "_services.rb"; + return true; + } else { + *file_name_or_error = "Invalid proto file name: must end with .proto"; + return false; + } +} + +inline grpc::string MessagesRequireName( + const grpc::protobuf::FileDescriptor *file) { + return Replace(file->name(), ".proto", ""); +} + +} // namespace grpc_ruby_generator + +#endif // GRPC_INTERNAL_COMPILER_RUBY_GENERATOR_HELPERS_INL_H diff --git a/src/compiler/ruby_generator_map-inl.h b/src/compiler/ruby_generator_map-inl.h new file mode 100644 index 00000000..6b87774f --- /dev/null +++ b/src/compiler/ruby_generator_map-inl.h @@ -0,0 +1,73 @@ +/* + * + * Copyright 2015, 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_INTERNAL_COMPILER_RUBY_GENERATOR_MAP_INL_H +#define GRPC_INTERNAL_COMPILER_RUBY_GENERATOR_MAP_INL_H + +#include "src/compiler/config.h" + +#include +#include +#include +#include // NOLINT +#include + +using std::initializer_list; +using std::map; +using std::vector; + +namespace grpc_ruby_generator { + +// Converts an initializer list of the form { key0, value0, key1, value1, ... } +// into a map of key* to value*. Is merely a readability helper for later code. +inline std::map ListToDict( + const initializer_list &values) { + if (values.size() % 2 != 0) { + std::cerr << "Not every 'key' has a value in `values`." + << std::endl; + } + std::map value_map; + auto value_iter = values.begin(); + for (unsigned i = 0; i < values.size() / 2; ++i) { + grpc::string key = *value_iter; + ++value_iter; + grpc::string value = *value_iter; + value_map[key] = value; + ++value_iter; + } + return value_map; +} + +} // namespace grpc_ruby_generator + +#endif // GRPC_INTERNAL_COMPILER_RUBY_GENERATOR_MAP_INL_H diff --git a/src/compiler/ruby_generator_string-inl.h b/src/compiler/ruby_generator_string-inl.h new file mode 100644 index 00000000..8da3a88d --- /dev/null +++ b/src/compiler/ruby_generator_string-inl.h @@ -0,0 +1,134 @@ +/* + * + * Copyright 2015, 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_INTERNAL_COMPILER_RUBY_GENERATOR_STRING_INL_H +#define GRPC_INTERNAL_COMPILER_RUBY_GENERATOR_STRING_INL_H + +#include "src/compiler/config.h" + +#include +#include +#include + +using std::getline; +using std::transform; + +namespace grpc_ruby_generator { + +// Split splits a string using char into elems. +inline std::vector &Split(const grpc::string &s, char delim, + std::vector *elems) { + std::stringstream ss(s); + grpc::string item; + while (getline(ss, item, delim)) { + elems->push_back(item); + } + return *elems; +} + +// Split splits a string using char, returning the result in a vector. +inline std::vector Split(const grpc::string &s, char delim) { + std::vector elems; + Split(s, delim, &elems); + return elems; +} + +// Replace replaces from with to in s. +inline grpc::string Replace(grpc::string s, const grpc::string &from, + const grpc::string &to) { + size_t start_pos = s.find(from); + if (start_pos == grpc::string::npos) { + return s; + } + s.replace(start_pos, from.length(), to); + return s; +} + +// ReplaceAll replaces all instances of search with replace in s. +inline grpc::string ReplaceAll(grpc::string s, const grpc::string &search, + const grpc::string &replace) { + size_t pos = 0; + while ((pos = s.find(search, pos)) != grpc::string::npos) { + s.replace(pos, search.length(), replace); + pos += replace.length(); + } + return s; +} + +// ReplacePrefix replaces from with to in s if search is a prefix of s. +inline bool ReplacePrefix(grpc::string *s, const grpc::string &from, + const grpc::string &to) { + size_t start_pos = s->find(from); + if (start_pos == grpc::string::npos || start_pos != 0) { + return false; + } + s->replace(start_pos, from.length(), to); + return true; +} + +// CapitalizeFirst capitalizes the first char in a string. +inline grpc::string CapitalizeFirst(grpc::string s) { + if (s.empty()) { + return s; + } + s[0] = ::toupper(s[0]); + return s; +} + +// RubyTypeOf updates a proto type to the required ruby equivalent. +inline grpc::string RubyTypeOf(const grpc::string &a_type, + const grpc::string &package) { + grpc::string res(a_type); + ReplacePrefix(&res, package, ""); // remove the leading package if present + ReplacePrefix(&res, ".", ""); // remove the leading . (no package) + if (res.find('.') == grpc::string::npos) { + return res; + } else { + std::vector prefixes_and_type = Split(res, '.'); + for (unsigned int i = 0; i < prefixes_and_type.size(); ++i) { + if (i != 0) { + res += "::"; // switch '.' to the ruby module delim + } + if (i < prefixes_and_type.size() - 1) { + res += CapitalizeFirst(prefixes_and_type[i]); // capitalize pkgs + } else { + res += prefixes_and_type[i]; + } + } + return res; + } +} + +} // namespace grpc_ruby_generator + +#endif // GRPC_INTERNAL_COMPILER_RUBY_GENERATOR_STRING_INL_H diff --git a/src/compiler/ruby_plugin.cc b/src/compiler/ruby_plugin.cc new file mode 100644 index 00000000..bd10d46e --- /dev/null +++ b/src/compiler/ruby_plugin.cc @@ -0,0 +1,72 @@ +/* + * + * Copyright 2015, 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. + * + */ + +// Generates Ruby gRPC service interface out of Protobuf IDL. + +#include + +#include "src/compiler/config.h" +#include "src/compiler/ruby_generator.h" +#include "src/compiler/ruby_generator_helpers-inl.h" + +class RubyGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator { + public: + RubyGrpcGenerator() {} + ~RubyGrpcGenerator() {} + + bool Generate(const grpc::protobuf::FileDescriptor *file, + const grpc::string ¶meter, + grpc::protobuf::compiler::GeneratorContext *context, + grpc::string *error) const { + grpc::string code = grpc_ruby_generator::GetServices(file); + if (code.size() == 0) { + return true; // don't generate a file if there are no services + } + + // Get output file name. + grpc::string file_name; + if (!grpc_ruby_generator::ServicesFilename(file, &file_name)) { + return false; + } + std::unique_ptr output( + context->Open(file_name)); + grpc::protobuf::io::CodedOutputStream coded_out(output.get()); + coded_out.WriteRaw(code.data(), code.size()); + return true; + } +}; + +int main(int argc, char *argv[]) { + RubyGrpcGenerator generator; + return grpc::protobuf::compiler::PluginMain(argc, argv, &generator); +} diff --git a/src/core/README.md b/src/core/README.md new file mode 100644 index 00000000..407dc4f7 --- /dev/null +++ b/src/core/README.md @@ -0,0 +1,9 @@ +#Overview + +This directory contains source code for shared C library. Libraries in other languages in this repository (C++, Ruby, +Python, PHP, NodeJS, Objective-C) are layered on top of this library. + +#Status + +Alpha : Ready for early adopters + diff --git a/src/core/census/README.md b/src/core/census/README.md new file mode 100644 index 00000000..fb615a21 --- /dev/null +++ b/src/core/census/README.md @@ -0,0 +1,76 @@ + + +# Census - a resource measurement and tracing system + +This directory contains code for Census, which will ultimately provide the +following features for any gRPC-using system: +* A [dapper](http://research.google.com/pubs/pub36356.html)-like tracing + system, enabling tracing across a distributed infrastructure. +* RPC statistics and measurements for key metrics, such as latency, bytes + transferred, number of errors etc. +* Resource measurement framework which can be used for measuring custom + metrics. Through the use of [tags](#Tags), these can be broken down across + the entire distributed stack. +* Easy integration of the above with + [Google Cloud Trace](https://cloud.google.com/tools/cloud-trace) and + [Google Cloud Monitoring](https://cloud.google.com/monitoring/). + +## Concepts + +### Context + +### Operations + +### Tags + +### Metrics + +## API + +### Internal/RPC API + +### External/Client API + +### RPC API + +## Files in this directory + +Note that files and functions in this directory can be split into two +categories: +* Files that define core census library functions. Functions etc. in these + files are named census\_\*, and constitute the core census library + functionality. At some time in the future, these will become a standalone + library. +* Files that define functions etc. that provide a convenient interface between + grpc and the core census functionality. These files are all named + grpc\_\*.{c,h}, and define function names beginning with grpc\_census\_\*. + diff --git a/src/core/census/aggregation.h b/src/core/census/aggregation.h new file mode 100644 index 00000000..e9bc6ada --- /dev/null +++ b/src/core/census/aggregation.h @@ -0,0 +1,66 @@ +/* + * + * Copyright 2015, 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 + +#ifndef GRPC_INTERNAL_CORE_CENSUS_AGGREGATION_H +#define GRPC_INTERNAL_CORE_CENSUS_AGGREGATION_H + +/** Structure used to describe an aggregation type. */ +struct census_aggregation_ops { + /* Create a new aggregation. The pointer returned can be used in future calls + to clone(), free(), record(), data() and reset(). */ + void *(*create)(const void *create_arg); + /* Make a copy of an aggregation created by create() */ + void *(*clone)(const void *aggregation); + /* Destroy an aggregation created by create() */ + void (*free)(void *aggregation); + /* Record a new value against aggregation. */ + void (*record)(void *aggregation, double value); + /* Return current aggregation data. The caller must cast this object into + the correct type for the aggregation result. The object returned can be + freed by using free_data(). */ + void *(*data)(const void *aggregation); + /* free data returned by data() */ + void (*free_data)(void *data); + /* Reset an aggregation to default (zero) values. */ + void (*reset)(void *aggregation); + /* Merge 'from' aggregation into 'to'. Both aggregations must be compatible */ + void (*merge)(void *to, const void *from); + /* Fill buffer with printable string version of aggregation contents. For + debugging only. Returns the number of bytes added to buffer (a value == n + implies the buffer was of insufficient size). */ + size_t (*print)(const void *aggregation, char *buffer, size_t n); +}; + +#endif /* GRPC_INTERNAL_CORE_CENSUS_AGGREGATION_H */ diff --git a/src/core/census/context.c b/src/core/census/context.c new file mode 100644 index 00000000..cab58b65 --- /dev/null +++ b/src/core/census/context.c @@ -0,0 +1,46 @@ +/* + * + * Copyright 2015, 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 "src/core/census/context.h" + +#include +#include +#include + +/* Placeholder implementation only. */ + +size_t census_context_serialize(const census_context *context, char *buffer, + size_t buf_size) { + /* TODO(aveitch): implement serialization */ + return 0; +} diff --git a/src/core/census/context.h b/src/core/census/context.h new file mode 100644 index 00000000..d43a69f7 --- /dev/null +++ b/src/core/census/context.h @@ -0,0 +1,49 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_CENSUS_CONTEXT_H +#define GRPC_INTERNAL_CORE_CENSUS_CONTEXT_H + +#include + +/* census_context is the in-memory representation of information needed to + * maintain tracing, RPC statistics and resource usage information. */ +struct census_context { + gpr_uint64 op_id; /* Operation identifier - unique per-context */ + gpr_uint64 trace_id; /* Globally unique trace identifier */ + /* TODO(aveitch) Add census tags: + const census_tag_set *tags; + */ +}; + +#endif /* GRPC_INTERNAL_CORE_CENSUS_CONTEXT_H */ diff --git a/src/core/census/grpc_context.c b/src/core/census/grpc_context.c new file mode 100644 index 00000000..429f3ec9 --- /dev/null +++ b/src/core/census/grpc_context.c @@ -0,0 +1,49 @@ +/* + * + * Copyright 2015, 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 +#include +#include "src/core/surface/call.h" + +void grpc_census_call_set_context(grpc_call *call, census_context *context) { + if (census_enabled() == CENSUS_FEATURE_NONE) { + return; + } + if (context != NULL) { + grpc_call_context_set(call, GRPC_CONTEXT_TRACING, context, NULL); + } +} + +census_context *grpc_census_call_get_context(grpc_call *call) { + return (census_context *)grpc_call_context_get(call, GRPC_CONTEXT_TRACING); +} diff --git a/src/core/census/grpc_filter.c b/src/core/census/grpc_filter.c new file mode 100644 index 00000000..8b6ba1d4 --- /dev/null +++ b/src/core/census/grpc_filter.c @@ -0,0 +1,186 @@ +/* + * + * Copyright 2015, 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 "src/core/census/grpc_filter.h" + +#include +#include + +#include "src/core/channel/channel_stack.h" +#include "src/core/channel/noop_filter.h" +#include "src/core/statistics/census_interface.h" +#include "src/core/statistics/census_rpc_stats.h" +#include +#include +#include +#include +#include + +typedef struct call_data { + census_op_id op_id; + census_context* ctxt; + gpr_timespec start_ts; + int error; + + /* recv callback */ + grpc_stream_op_buffer* recv_ops; + grpc_iomgr_closure* on_done_recv; +} call_data; + +typedef struct channel_data { + grpc_mdstr* path_str; /* pointer to meta data str with key == ":path" */ +} channel_data; + +static void extract_and_annotate_method_tag(grpc_stream_op_buffer* sopb, + call_data* calld, + channel_data* chand) { + grpc_linked_mdelem* m; + size_t i; + for (i = 0; i < sopb->nops; i++) { + grpc_stream_op* op = &sopb->ops[i]; + if (op->type != GRPC_OP_METADATA) continue; + for (m = op->data.metadata.list.head; m != NULL; m = m->next) { + if (m->md->key == chand->path_str) { + gpr_log(GPR_DEBUG, "%s", + (const char*)GPR_SLICE_START_PTR(m->md->value->slice)); + /* Add method tag here */ + } + } + } +} + +static void client_mutate_op(grpc_call_element* elem, + grpc_transport_stream_op* op) { + call_data* calld = elem->call_data; + channel_data* chand = elem->channel_data; + if (op->send_ops) { + extract_and_annotate_method_tag(op->send_ops, calld, chand); + } +} + +static void client_start_transport_op(grpc_call_element* elem, + grpc_transport_stream_op* op) { + client_mutate_op(elem, op); + grpc_call_next_op(elem, op); +} + +static void server_on_done_recv(void* ptr, int success) { + grpc_call_element* elem = ptr; + call_data* calld = elem->call_data; + channel_data* chand = elem->channel_data; + if (success) { + extract_and_annotate_method_tag(calld->recv_ops, calld, chand); + } + calld->on_done_recv->cb(calld->on_done_recv->cb_arg, success); +} + +static void server_mutate_op(grpc_call_element* elem, + grpc_transport_stream_op* op) { + call_data* calld = elem->call_data; + if (op->recv_ops) { + /* substitute our callback for the op callback */ + calld->recv_ops = op->recv_ops; + calld->on_done_recv = op->on_done_recv; + op->on_done_recv = calld->on_done_recv; + } +} + +static void server_start_transport_op(grpc_call_element* elem, + grpc_transport_stream_op* op) { + call_data* calld = elem->call_data; + GPR_ASSERT((calld->op_id.upper != 0) || (calld->op_id.lower != 0)); + server_mutate_op(elem, op); + grpc_call_next_op(elem, op); +} + +static void client_init_call_elem(grpc_call_element* elem, + const void* server_transport_data, + grpc_transport_stream_op* initial_op) { + call_data* d = elem->call_data; + GPR_ASSERT(d != NULL); + d->start_ts = gpr_now(GPR_CLOCK_REALTIME); + if (initial_op) client_mutate_op(elem, initial_op); +} + +static void client_destroy_call_elem(grpc_call_element* elem) { + call_data* d = elem->call_data; + GPR_ASSERT(d != NULL); + /* TODO(hongyu): record rpc client stats and census_rpc_end_op here */ +} + +static void server_init_call_elem(grpc_call_element* elem, + const void* server_transport_data, + grpc_transport_stream_op* initial_op) { + call_data* d = elem->call_data; + GPR_ASSERT(d != NULL); + d->start_ts = gpr_now(GPR_CLOCK_REALTIME); + /* TODO(hongyu): call census_tracing_start_op here. */ + grpc_iomgr_closure_init(d->on_done_recv, server_on_done_recv, elem); + if (initial_op) server_mutate_op(elem, initial_op); +} + +static void server_destroy_call_elem(grpc_call_element* elem) { + call_data* d = elem->call_data; + GPR_ASSERT(d != NULL); + /* TODO(hongyu): record rpc server stats and census_tracing_end_op here */ +} + +static void init_channel_elem(grpc_channel_element* elem, grpc_channel* master, + const grpc_channel_args* args, grpc_mdctx* mdctx, + int is_first, int is_last) { + channel_data* chand = elem->channel_data; + GPR_ASSERT(chand != NULL); + chand->path_str = grpc_mdstr_from_string(mdctx, ":path", 0); +} + +static void destroy_channel_elem(grpc_channel_element* elem) { + channel_data* chand = elem->channel_data; + GPR_ASSERT(chand != NULL); + if (chand->path_str != NULL) { + GRPC_MDSTR_UNREF(chand->path_str); + } +} + +const grpc_channel_filter grpc_client_census_filter = { + client_start_transport_op, grpc_channel_next_op, + sizeof(call_data), client_init_call_elem, + client_destroy_call_elem, sizeof(channel_data), + init_channel_elem, destroy_channel_elem, + grpc_call_next_get_peer, "census-client"}; + +const grpc_channel_filter grpc_server_census_filter = { + server_start_transport_op, grpc_channel_next_op, + sizeof(call_data), server_init_call_elem, + server_destroy_call_elem, sizeof(channel_data), + init_channel_elem, destroy_channel_elem, + grpc_call_next_get_peer, "census-server"}; diff --git a/src/core/census/grpc_filter.h b/src/core/census/grpc_filter.h new file mode 100644 index 00000000..b3de3adc --- /dev/null +++ b/src/core/census/grpc_filter.h @@ -0,0 +1,44 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_CENSUS_GRPC_FILTER_H +#define GRPC_INTERNAL_CORE_CENSUS_GRPC_FILTER_H + +#include "src/core/channel/channel_stack.h" + +/* Census filters: provides tracing and stats collection functionalities. It + needs to reside right below the surface filter in the channel stack. */ +extern const grpc_channel_filter grpc_client_census_filter; +extern const grpc_channel_filter grpc_server_census_filter; + +#endif /* GRPC_INTERNAL_CORE_CENSUS_GRPC_FILTER_H */ diff --git a/src/core/census/initialize.c b/src/core/census/initialize.c new file mode 100644 index 00000000..8d60f790 --- /dev/null +++ b/src/core/census/initialize.c @@ -0,0 +1,57 @@ +/* + * + * Copyright 2015, 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 + +static int features_enabled = CENSUS_FEATURE_NONE; + +int census_initialize(int features) { + if (features_enabled != CENSUS_FEATURE_NONE) { + return 1; + } + if (features != CENSUS_FEATURE_NONE) { + return 1; + } else { + features_enabled = features; + return 0; + } +} + +void census_shutdown(void) { features_enabled = CENSUS_FEATURE_NONE; } + +int census_supported(void) { + /* TODO(aveitch): improve this as we implement features... */ + return CENSUS_FEATURE_NONE; +} + +int census_enabled(void) { return features_enabled; } diff --git a/src/core/census/operation.c b/src/core/census/operation.c new file mode 100644 index 00000000..118eb0a4 --- /dev/null +++ b/src/core/census/operation.c @@ -0,0 +1,63 @@ +/* + * Copyright 2015, 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 + +/* TODO(aveitch): These are all placeholder implementations. */ + +census_timestamp census_start_rpc_op_timestamp(void) { + census_timestamp ct; + /* TODO(aveitch): assumes gpr_timespec implementation of census_timestamp. */ + ct.ts = gpr_now(GPR_CLOCK_MONOTONIC); + return ct; +} + +census_context *census_start_client_rpc_op( + const census_context *context, gpr_int64 rpc_name_id, + const census_rpc_name_info *rpc_name_info, const char *peer, int trace_mask, + const census_timestamp *start_time) { + return NULL; +} + +census_context *census_start_server_rpc_op( + const char *buffer, gpr_int64 rpc_name_id, + const census_rpc_name_info *rpc_name_info, const char *peer, int trace_mask, + census_timestamp *start_time) { + return NULL; +} + +census_context *census_start_op(census_context *context, const char *family, + const char *name, int trace_mask) { + return NULL; +} + +void census_end_op(census_context *context, int status) {} diff --git a/src/core/census/rpc_metric_id.h b/src/core/census/rpc_metric_id.h new file mode 100644 index 00000000..1d8e8806 --- /dev/null +++ b/src/core/census/rpc_metric_id.h @@ -0,0 +1,51 @@ +/* + * + * Copyright 2015, 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 CENSUS_RPC_METRIC_ID_H +#define CENSUS_RPC_METRIC_ID_H + +/* Metric ID's used for RPC measurements. */ +/* Count of client requests sent. */ +#define CENSUS_METRIC_RPC_CLIENT_REQUESTS ((gpr_uint32)0) +/* Count of server requests sent. */ +#define CENSUS_METRIC_RPC_SERVER_REQUESTS ((gpr_uint32)1) +/* Client error counts. */ +#define CENSUS_METRIC_RPC_CLIENT_ERRORS ((gpr_uint32)2) +/* Server error counts. */ +#define CENSUS_METRIC_RPC_SERVER_ERRORS ((gpr_uint32)3) +/* Client side request latency. */ +#define CENSUS_METRIC_RPC_CLIENT_LATENCY ((gpr_uint32)4) +/* Server side request latency. */ +#define CENSUS_METRIC_RPC_SERVER_LATENCY ((gpr_uint32)5) + +#endif /* CENSUS_RPC_METRIC_ID_H */ diff --git a/src/core/census/tracing.c b/src/core/census/tracing.c new file mode 100644 index 00000000..ae38773c --- /dev/null +++ b/src/core/census/tracing.c @@ -0,0 +1,45 @@ +/* + * + * Copyright 2015, 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 + +/* TODO(aveitch): These are all placeholder implementations. */ + +int census_trace_mask(const census_context *context) { + return CENSUS_TRACE_MASK_NONE; +} + +void census_set_trace_mask(int trace_mask) {} + +void census_trace_print(census_context *context, gpr_uint32 type, + const char *buffer, size_t n) {} diff --git a/src/core/channel/channel_args.c b/src/core/channel/channel_args.c new file mode 100644 index 00000000..54ee75af --- /dev/null +++ b/src/core/channel/channel_args.c @@ -0,0 +1,211 @@ +/* + * + * Copyright 2015, 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 +#include "src/core/channel/channel_args.h" +#include "src/core/support/string.h" + +#include +#include +#include + +#include + +static grpc_arg copy_arg(const grpc_arg *src) { + grpc_arg dst; + dst.type = src->type; + dst.key = gpr_strdup(src->key); + switch (dst.type) { + case GRPC_ARG_STRING: + dst.value.string = gpr_strdup(src->value.string); + break; + case GRPC_ARG_INTEGER: + dst.value.integer = src->value.integer; + break; + case GRPC_ARG_POINTER: + dst.value.pointer = src->value.pointer; + dst.value.pointer.p = src->value.pointer.copy + ? src->value.pointer.copy(src->value.pointer.p) + : src->value.pointer.p; + break; + } + return dst; +} + +grpc_channel_args *grpc_channel_args_copy_and_add(const grpc_channel_args *src, + const grpc_arg *to_add, + size_t num_to_add) { + grpc_channel_args *dst = gpr_malloc(sizeof(grpc_channel_args)); + size_t i; + size_t src_num_args = (src == NULL) ? 0 : src->num_args; + if (!src && !to_add) { + dst->num_args = 0; + dst->args = NULL; + return dst; + } + dst->num_args = src_num_args + num_to_add; + dst->args = gpr_malloc(sizeof(grpc_arg) * dst->num_args); + for (i = 0; i < src_num_args; i++) { + dst->args[i] = copy_arg(&src->args[i]); + } + for (i = 0; i < num_to_add; i++) { + dst->args[i + src_num_args] = copy_arg(&to_add[i]); + } + return dst; +} + +grpc_channel_args *grpc_channel_args_copy(const grpc_channel_args *src) { + return grpc_channel_args_copy_and_add(src, NULL, 0); +} + +grpc_channel_args *grpc_channel_args_merge(const grpc_channel_args *a, + const grpc_channel_args *b) { + return grpc_channel_args_copy_and_add(a, b->args, b->num_args); +} + +void grpc_channel_args_destroy(grpc_channel_args *a) { + size_t i; + for (i = 0; i < a->num_args; i++) { + switch (a->args[i].type) { + case GRPC_ARG_STRING: + gpr_free(a->args[i].value.string); + break; + case GRPC_ARG_INTEGER: + break; + case GRPC_ARG_POINTER: + if (a->args[i].value.pointer.destroy) { + a->args[i].value.pointer.destroy(a->args[i].value.pointer.p); + } + break; + } + gpr_free(a->args[i].key); + } + gpr_free(a->args); + gpr_free(a); +} + +int grpc_channel_args_is_census_enabled(const grpc_channel_args *a) { + size_t i; + if (a == NULL) return 0; + for (i = 0; i < a->num_args; i++) { + if (0 == strcmp(a->args[i].key, GRPC_ARG_ENABLE_CENSUS)) { + return a->args[i].value.integer != 0; + } + } + return 0; +} + +grpc_compression_algorithm grpc_channel_args_get_compression_algorithm( + const grpc_channel_args *a) { + size_t i; + if (a == NULL) return 0; + for (i = 0; i < a->num_args; ++i) { + if (a->args[i].type == GRPC_ARG_INTEGER && + !strcmp(GRPC_COMPRESSION_ALGORITHM_ARG, a->args[i].key)) { + return a->args[i].value.integer; + break; + } + } + return GRPC_COMPRESS_NONE; +} + +grpc_channel_args *grpc_channel_args_set_compression_algorithm( + grpc_channel_args *a, grpc_compression_algorithm algorithm) { + grpc_arg tmp; + tmp.type = GRPC_ARG_INTEGER; + tmp.key = GRPC_COMPRESSION_ALGORITHM_ARG; + tmp.value.integer = algorithm; + return grpc_channel_args_copy_and_add(a, &tmp, 1); +} + +/** Returns 1 if the argument for compression algorithm's enabled states bitset + * was found in \a a, returning the arg's value in \a states. Otherwise, returns + * 0. */ +static int find_compression_algorithm_states_bitset( + const grpc_channel_args *a, int **states_arg) { + if (a != NULL) { + size_t i; + for (i = 0; i < a->num_args; ++i) { + if (a->args[i].type == GRPC_ARG_INTEGER && + !strcmp(GRPC_COMPRESSION_ALGORITHM_STATE_ARG, a->args[i].key)) { + *states_arg = &a->args[i].value.integer; + return 1; /* GPR_TRUE */ + } + } + } + return 0; /* GPR_FALSE */ +} + +grpc_channel_args *grpc_channel_args_compression_algorithm_set_state( + grpc_channel_args **a, + grpc_compression_algorithm algorithm, + int state) { + int *states_arg; + grpc_channel_args *result = *a; + const int states_arg_found = + find_compression_algorithm_states_bitset(*a, &states_arg); + + if (states_arg_found) { + if (state != 0) { + GPR_BITSET(states_arg, algorithm); + } else { + GPR_BITCLEAR(states_arg, algorithm); + } + } else { + /* create a new arg */ + grpc_arg tmp; + tmp.type = GRPC_ARG_INTEGER; + tmp.key = GRPC_COMPRESSION_ALGORITHM_STATE_ARG; + /* all enabled by default */ + tmp.value.integer = (1u << GRPC_COMPRESS_ALGORITHMS_COUNT) - 1; + if (state != 0) { + GPR_BITSET(&tmp.value.integer, algorithm); + } else { + GPR_BITCLEAR(&tmp.value.integer, algorithm); + } + result = grpc_channel_args_copy_and_add(*a, &tmp, 1); + grpc_channel_args_destroy(*a); + *a = result; + } + return result; +} + +int grpc_channel_args_compression_algorithm_get_states( + const grpc_channel_args *a) { + int *states_arg; + if (find_compression_algorithm_states_bitset(a, &states_arg)) { + return *states_arg; + } else { + return (1u << GRPC_COMPRESS_ALGORITHMS_COUNT) - 1; /* All algs. enabled */ + } +} diff --git a/src/core/channel/channel_args.h b/src/core/channel/channel_args.h new file mode 100644 index 00000000..06a6012d --- /dev/null +++ b/src/core/channel/channel_args.h @@ -0,0 +1,90 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_CHANNEL_CHANNEL_ARGS_H +#define GRPC_INTERNAL_CORE_CHANNEL_CHANNEL_ARGS_H + +#include +#include + +/* Copy some arguments */ +grpc_channel_args *grpc_channel_args_copy(const grpc_channel_args *src); + +/** Copy some arguments and add the to_add parameter in the end. + If to_add is NULL, it is equivalent to call grpc_channel_args_copy. */ +grpc_channel_args *grpc_channel_args_copy_and_add(const grpc_channel_args *src, + const grpc_arg *to_add, + size_t num_to_add); + +/** Copy args from a then args from b into a new channel args */ +grpc_channel_args *grpc_channel_args_merge(const grpc_channel_args *a, + const grpc_channel_args *b); + +/** Destroy arguments created by grpc_channel_args_copy */ +void grpc_channel_args_destroy(grpc_channel_args *a); + +/** Reads census_enabled settings from channel args. Returns 1 if census_enabled + * is specified in channel args, otherwise returns 0. */ +int grpc_channel_args_is_census_enabled(const grpc_channel_args *a); + +/** Returns the compression algorithm set in \a a. */ +grpc_compression_algorithm grpc_channel_args_get_compression_algorithm( + const grpc_channel_args *a); + +/** Returns a channel arg instance with compression enabled. If \a a is + * non-NULL, its args are copied. N.B. GRPC_COMPRESS_NONE disables compression + * for the channel. */ +grpc_channel_args *grpc_channel_args_set_compression_algorithm( + grpc_channel_args *a, grpc_compression_algorithm algorithm); + +/** Sets the support for the given compression algorithm. By default, all + * compression algorithms are enabled. It's an error to disable an algorithm set + * by grpc_channel_args_set_compression_algorithm. + * + * Returns an instance will the updated algorithm states. The \a a pointer is + * modified to point to the returned instance (which may be different from the + * input value of \a a). */ +grpc_channel_args *grpc_channel_args_compression_algorithm_set_state( + grpc_channel_args **a, + grpc_compression_algorithm algorithm, + int enabled); + +/** Returns the bitset representing the support state (true for enabled, false + * for disabled) for compression algorithms. + * + * The i-th bit of the returned bitset corresponds to the i-th entry in the + * grpc_compression_algorithm enum. */ +int grpc_channel_args_compression_algorithm_get_states( + const grpc_channel_args *a); + +#endif /* GRPC_INTERNAL_CORE_CHANNEL_CHANNEL_ARGS_H */ diff --git a/src/core/channel/channel_stack.c b/src/core/channel/channel_stack.c new file mode 100644 index 00000000..cd7c182e --- /dev/null +++ b/src/core/channel/channel_stack.c @@ -0,0 +1,220 @@ +/* + * + * Copyright 2015, 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 "src/core/channel/channel_stack.h" +#include + +#include +#include + +int grpc_trace_channel = 0; + +/* Memory layouts. + + Channel stack is laid out as: { + grpc_channel_stack stk; + padding to GPR_MAX_ALIGNMENT + grpc_channel_element[stk.count]; + per-filter memory, aligned to GPR_MAX_ALIGNMENT + } + + Call stack is laid out as: { + grpc_call_stack stk; + padding to GPR_MAX_ALIGNMENT + grpc_call_element[stk.count]; + per-filter memory, aligned to GPR_MAX_ALIGNMENT + } */ + +/* Given a size, round up to the next multiple of sizeof(void*) */ +#define ROUND_UP_TO_ALIGNMENT_SIZE(x) \ + (((x) + GPR_MAX_ALIGNMENT - 1) & ~(GPR_MAX_ALIGNMENT - 1)) + +size_t grpc_channel_stack_size(const grpc_channel_filter **filters, + size_t filter_count) { + /* always need the header, and size for the channel elements */ + size_t size = + ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_channel_stack)) + + ROUND_UP_TO_ALIGNMENT_SIZE(filter_count * sizeof(grpc_channel_element)); + size_t i; + + GPR_ASSERT((GPR_MAX_ALIGNMENT & (GPR_MAX_ALIGNMENT - 1)) == 0 && + "GPR_MAX_ALIGNMENT must be a power of two"); + + /* add the size for each filter */ + for (i = 0; i < filter_count; i++) { + size += ROUND_UP_TO_ALIGNMENT_SIZE(filters[i]->sizeof_channel_data); + } + + return size; +} + +#define CHANNEL_ELEMS_FROM_STACK(stk) \ + ((grpc_channel_element *)((char *)(stk) + ROUND_UP_TO_ALIGNMENT_SIZE( \ + sizeof(grpc_channel_stack)))) + +#define CALL_ELEMS_FROM_STACK(stk) \ + ((grpc_call_element *)((char *)(stk) + \ + ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_call_stack)))) + +grpc_channel_element *grpc_channel_stack_element( + grpc_channel_stack *channel_stack, size_t index) { + return CHANNEL_ELEMS_FROM_STACK(channel_stack) + index; +} + +grpc_channel_element *grpc_channel_stack_last_element( + grpc_channel_stack *channel_stack) { + return grpc_channel_stack_element(channel_stack, channel_stack->count - 1); +} + +grpc_call_element *grpc_call_stack_element(grpc_call_stack *call_stack, + size_t index) { + return CALL_ELEMS_FROM_STACK(call_stack) + index; +} + +void grpc_channel_stack_init(const grpc_channel_filter **filters, + size_t filter_count, grpc_channel *master, + const grpc_channel_args *args, + grpc_mdctx *metadata_context, + grpc_channel_stack *stack) { + size_t call_size = + ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_call_stack)) + + ROUND_UP_TO_ALIGNMENT_SIZE(filter_count * sizeof(grpc_call_element)); + grpc_channel_element *elems; + char *user_data; + size_t i; + + stack->count = filter_count; + elems = CHANNEL_ELEMS_FROM_STACK(stack); + user_data = + ((char *)elems) + + ROUND_UP_TO_ALIGNMENT_SIZE(filter_count * sizeof(grpc_channel_element)); + + /* init per-filter data */ + for (i = 0; i < filter_count; i++) { + elems[i].filter = filters[i]; + elems[i].channel_data = user_data; + elems[i].filter->init_channel_elem(&elems[i], master, args, + metadata_context, i == 0, + i == (filter_count - 1)); + user_data += ROUND_UP_TO_ALIGNMENT_SIZE(filters[i]->sizeof_channel_data); + call_size += ROUND_UP_TO_ALIGNMENT_SIZE(filters[i]->sizeof_call_data); + } + + GPR_ASSERT(user_data > (char *)stack); + GPR_ASSERT((gpr_uintptr)(user_data - (char *)stack) == + grpc_channel_stack_size(filters, filter_count)); + + stack->call_stack_size = call_size; +} + +void grpc_channel_stack_destroy(grpc_channel_stack *stack) { + grpc_channel_element *channel_elems = CHANNEL_ELEMS_FROM_STACK(stack); + size_t count = stack->count; + size_t i; + + /* destroy per-filter data */ + for (i = 0; i < count; i++) { + channel_elems[i].filter->destroy_channel_elem(&channel_elems[i]); + } +} + +void grpc_call_stack_init(grpc_channel_stack *channel_stack, + const void *transport_server_data, + grpc_transport_stream_op *initial_op, + grpc_call_stack *call_stack) { + grpc_channel_element *channel_elems = CHANNEL_ELEMS_FROM_STACK(channel_stack); + size_t count = channel_stack->count; + grpc_call_element *call_elems; + char *user_data; + size_t i; + + call_stack->count = count; + call_elems = CALL_ELEMS_FROM_STACK(call_stack); + user_data = ((char *)call_elems) + + ROUND_UP_TO_ALIGNMENT_SIZE(count * sizeof(grpc_call_element)); + + /* init per-filter data */ + for (i = 0; i < count; i++) { + call_elems[i].filter = channel_elems[i].filter; + call_elems[i].channel_data = channel_elems[i].channel_data; + call_elems[i].call_data = user_data; + call_elems[i].filter->init_call_elem(&call_elems[i], transport_server_data, + initial_op); + user_data += + ROUND_UP_TO_ALIGNMENT_SIZE(call_elems[i].filter->sizeof_call_data); + } +} + +void grpc_call_stack_destroy(grpc_call_stack *stack) { + grpc_call_element *elems = CALL_ELEMS_FROM_STACK(stack); + size_t count = stack->count; + size_t i; + + /* destroy per-filter data */ + for (i = 0; i < count; i++) { + elems[i].filter->destroy_call_elem(&elems[i]); + } +} + +void grpc_call_next_op(grpc_call_element *elem, grpc_transport_stream_op *op) { + grpc_call_element *next_elem = elem + 1; + next_elem->filter->start_transport_stream_op(next_elem, op); +} + +char *grpc_call_next_get_peer(grpc_call_element *elem) { + grpc_call_element *next_elem = elem + 1; + return next_elem->filter->get_peer(next_elem); +} + +void grpc_channel_next_op(grpc_channel_element *elem, grpc_transport_op *op) { + grpc_channel_element *next_elem = elem + 1; + next_elem->filter->start_transport_op(next_elem, op); +} + +grpc_channel_stack *grpc_channel_stack_from_top_element( + grpc_channel_element *elem) { + return (grpc_channel_stack *)((char *)(elem)-ROUND_UP_TO_ALIGNMENT_SIZE( + sizeof(grpc_channel_stack))); +} + +grpc_call_stack *grpc_call_stack_from_top_element(grpc_call_element *elem) { + return (grpc_call_stack *)((char *)(elem)-ROUND_UP_TO_ALIGNMENT_SIZE( + sizeof(grpc_call_stack))); +} + +void grpc_call_element_send_cancel(grpc_call_element *cur_elem) { + grpc_transport_stream_op op; + memset(&op, 0, sizeof(op)); + op.cancel_with_status = GRPC_STATUS_CANCELLED; + grpc_call_next_op(cur_elem, &op); +} diff --git a/src/core/channel/channel_stack.h b/src/core/channel/channel_stack.h new file mode 100644 index 00000000..4a608b95 --- /dev/null +++ b/src/core/channel/channel_stack.h @@ -0,0 +1,198 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_CHANNEL_CHANNEL_STACK_H +#define GRPC_INTERNAL_CORE_CHANNEL_CHANNEL_STACK_H + +/* A channel filter defines how operations on a channel are implemented. + Channel filters are chained together to create full channels, and if those + chains are linear, then channel stacks provide a mechanism to minimize + allocations for that chain. + Call stacks are created by channel stacks and represent the per-call data + for that stack. */ + +#include + +#include +#include +#include "src/core/debug/trace.h" +#include "src/core/transport/transport.h" + +typedef struct grpc_channel_element grpc_channel_element; +typedef struct grpc_call_element grpc_call_element; + +/* Channel filters specify: + 1. the amount of memory needed in the channel & call (via the sizeof_XXX + members) + 2. functions to initialize and destroy channel & call data + (init_XXX, destroy_XXX) + 3. functions to implement call operations and channel operations (call_op, + channel_op) + 4. a name, which is useful when debugging + + Members are laid out in approximate frequency of use order. */ +typedef struct { + /* Called to eg. send/receive data on a call. + See grpc_call_next_op on how to call the next element in the stack */ + void (*start_transport_stream_op)(grpc_call_element *elem, + grpc_transport_stream_op *op); + /* Called to handle channel level operations - e.g. new calls, or transport + closure. + See grpc_channel_next_op on how to call the next element in the stack */ + void (*start_transport_op)(grpc_channel_element *elem, grpc_transport_op *op); + + /* sizeof(per call data) */ + size_t sizeof_call_data; + /* Initialize per call data. + elem is initialized at the start of the call, and elem->call_data is what + needs initializing. + The filter does not need to do any chaining. + server_transport_data is an opaque pointer. If it is NULL, this call is + on a client; if it is non-NULL, then it points to memory owned by the + transport and is on the server. Most filters want to ignore this + argument.*/ + void (*init_call_elem)(grpc_call_element *elem, + const void *server_transport_data, + grpc_transport_stream_op *initial_op); + /* Destroy per call data. + The filter does not need to do any chaining */ + void (*destroy_call_elem)(grpc_call_element *elem); + + /* sizeof(per channel data) */ + size_t sizeof_channel_data; + /* Initialize per-channel data. + elem is initialized at the start of the call, and elem->channel_data is + what needs initializing. + is_first, is_last designate this elements position in the stack, and are + useful for asserting correct configuration by upper layer code. + The filter does not need to do any chaining */ + void (*init_channel_elem)(grpc_channel_element *elem, grpc_channel *master, + const grpc_channel_args *args, + grpc_mdctx *metadata_context, int is_first, + int is_last); + /* Destroy per channel data. + The filter does not need to do any chaining */ + void (*destroy_channel_elem)(grpc_channel_element *elem); + + /* Implement grpc_call_get_peer() */ + char *(*get_peer)(grpc_call_element *elem); + + /* The name of this filter */ + const char *name; +} grpc_channel_filter; + +/* A channel_element tracks its filter and the filter requested memory within + a channel allocation */ +struct grpc_channel_element { + const grpc_channel_filter *filter; + void *channel_data; +}; + +/* A call_element tracks its filter, the filter requested memory within + a channel allocation, and the filter requested memory within a call + allocation */ +struct grpc_call_element { + const grpc_channel_filter *filter; + void *channel_data; + void *call_data; +}; + +/* A channel stack tracks a set of related filters for one channel, and + guarantees they live within a single malloc() allocation */ +typedef struct { + size_t count; + /* Memory required for a call stack (computed at channel stack + initialization) */ + size_t call_stack_size; +} grpc_channel_stack; + +/* A call stack tracks a set of related filters for one call, and guarantees + they live within a single malloc() allocation */ +typedef struct { size_t count; } grpc_call_stack; + +/* Get a channel element given a channel stack and its index */ +grpc_channel_element *grpc_channel_stack_element(grpc_channel_stack *stack, + size_t i); +/* Get the last channel element in a channel stack */ +grpc_channel_element *grpc_channel_stack_last_element( + grpc_channel_stack *stack); +/* Get a call stack element given a call stack and an index */ +grpc_call_element *grpc_call_stack_element(grpc_call_stack *stack, size_t i); + +/* Determine memory required for a channel stack containing a set of filters */ +size_t grpc_channel_stack_size(const grpc_channel_filter **filters, + size_t filter_count); +/* Initialize a channel stack given some filters */ +void grpc_channel_stack_init(const grpc_channel_filter **filters, + size_t filter_count, grpc_channel *master, + const grpc_channel_args *args, + grpc_mdctx *metadata_context, + grpc_channel_stack *stack); +/* Destroy a channel stack */ +void grpc_channel_stack_destroy(grpc_channel_stack *stack); + +/* Initialize a call stack given a channel stack. transport_server_data is + expected to be NULL on a client, or an opaque transport owned pointer on the + server. */ +void grpc_call_stack_init(grpc_channel_stack *channel_stack, + const void *transport_server_data, + grpc_transport_stream_op *initial_op, + grpc_call_stack *call_stack); +/* Destroy a call stack */ +void grpc_call_stack_destroy(grpc_call_stack *stack); + +/* Call the next operation in a call stack */ +void grpc_call_next_op(grpc_call_element *elem, grpc_transport_stream_op *op); +/* Call the next operation (depending on call directionality) in a channel + stack */ +void grpc_channel_next_op(grpc_channel_element *elem, grpc_transport_op *op); +/* Pass through a request to get_peer to the next child element */ +char *grpc_call_next_get_peer(grpc_call_element *elem); + +/* Given the top element of a channel stack, get the channel stack itself */ +grpc_channel_stack *grpc_channel_stack_from_top_element( + grpc_channel_element *elem); +/* Given the top element of a call stack, get the call stack itself */ +grpc_call_stack *grpc_call_stack_from_top_element(grpc_call_element *elem); + +void grpc_call_log_op(char *file, int line, gpr_log_severity severity, + grpc_call_element *elem, grpc_transport_stream_op *op); + +void grpc_call_element_send_cancel(grpc_call_element *cur_elem); + +extern int grpc_trace_channel; + +#define GRPC_CALL_LOG_OP(sev, elem, op) \ + if (grpc_trace_channel) grpc_call_log_op(sev, elem, op) + +#endif /* GRPC_INTERNAL_CORE_CHANNEL_CHANNEL_STACK_H */ diff --git a/src/core/channel/client_channel.c b/src/core/channel/client_channel.c new file mode 100644 index 00000000..2e250338 --- /dev/null +++ b/src/core/channel/client_channel.c @@ -0,0 +1,773 @@ +/* + * + * Copyright 2015, 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 "src/core/channel/client_channel.h" + +#include +#include + +#include "src/core/channel/channel_args.h" +#include "src/core/channel/connected_channel.h" +#include "src/core/surface/channel.h" +#include "src/core/iomgr/iomgr.h" +#include "src/core/support/string.h" +#include "src/core/transport/connectivity_state.h" +#include +#include +#include +#include + +/* Client channel implementation */ + +typedef struct call_data call_data; + +typedef struct { + /** metadata context for this channel */ + grpc_mdctx *mdctx; + /** resolver for this channel */ + grpc_resolver *resolver; + /** have we started resolving this channel */ + int started_resolving; + /** master channel - the grpc_channel instance that ultimately owns + this channel_data via its channel stack. + We occasionally use this to bump the refcount on the master channel + to keep ourselves alive through an asynchronous operation. */ + grpc_channel *master; + + /** mutex protecting client configuration, including all + variables below in this data structure */ + gpr_mu mu_config; + /** currently active load balancer - guarded by mu_config */ + grpc_lb_policy *lb_policy; + /** incoming configuration - set by resolver.next + guarded by mu_config */ + grpc_client_config *incoming_configuration; + /** a list of closures that are all waiting for config to come in */ + grpc_iomgr_closure *waiting_for_config_closures; + /** resolver callback */ + grpc_iomgr_closure on_config_changed; + /** connectivity state being tracked */ + grpc_connectivity_state_tracker state_tracker; + /** when an lb_policy arrives, should we try to exit idle */ + int exit_idle_when_lb_policy_arrives; + /** pollset_set of interested parties in a new connection */ + grpc_pollset_set pollset_set; +} channel_data; + +/** We create one watcher for each new lb_policy that is returned from a + resolver, + to watch for state changes from the lb_policy. When a state change is seen, + we + update the channel, and create a new watcher */ +typedef struct { + channel_data *chand; + grpc_iomgr_closure on_changed; + grpc_connectivity_state state; + grpc_lb_policy *lb_policy; +} lb_policy_connectivity_watcher; + +typedef enum { + CALL_CREATED, + CALL_WAITING_FOR_SEND, + CALL_WAITING_FOR_CONFIG, + CALL_WAITING_FOR_PICK, + CALL_WAITING_FOR_CALL, + CALL_ACTIVE, + CALL_CANCELLED +} call_state; + +struct call_data { + /* owning element */ + grpc_call_element *elem; + + gpr_mu mu_state; + + call_state state; + gpr_timespec deadline; + grpc_subchannel *picked_channel; + grpc_iomgr_closure async_setup_task; + grpc_transport_stream_op waiting_op; + /* our child call stack */ + grpc_subchannel_call *subchannel_call; + grpc_linked_mdelem status; + grpc_linked_mdelem details; +}; + +static grpc_iomgr_closure *merge_into_waiting_op( + grpc_call_element *elem, + grpc_transport_stream_op *new_op) GRPC_MUST_USE_RESULT; + +static void handle_op_after_cancellation(grpc_call_element *elem, + grpc_transport_stream_op *op) { + call_data *calld = elem->call_data; + channel_data *chand = elem->channel_data; + if (op->send_ops) { + grpc_stream_ops_unref_owned_objects(op->send_ops->ops, op->send_ops->nops); + op->on_done_send->cb(op->on_done_send->cb_arg, 0); + } + if (op->recv_ops) { + char status[GPR_LTOA_MIN_BUFSIZE]; + grpc_metadata_batch mdb; + gpr_ltoa(GRPC_STATUS_CANCELLED, status); + calld->status.md = + grpc_mdelem_from_strings(chand->mdctx, "grpc-status", status); + calld->details.md = + grpc_mdelem_from_strings(chand->mdctx, "grpc-message", "Cancelled"); + calld->status.prev = calld->details.next = NULL; + calld->status.next = &calld->details; + calld->details.prev = &calld->status; + mdb.list.head = &calld->status; + mdb.list.tail = &calld->details; + mdb.garbage.head = mdb.garbage.tail = NULL; + mdb.deadline = gpr_inf_future(GPR_CLOCK_REALTIME); + grpc_sopb_add_metadata(op->recv_ops, mdb); + *op->recv_state = GRPC_STREAM_CLOSED; + op->on_done_recv->cb(op->on_done_recv->cb_arg, 1); + } + if (op->on_consumed) { + op->on_consumed->cb(op->on_consumed->cb_arg, 0); + } +} + +typedef struct { + grpc_iomgr_closure closure; + grpc_call_element *elem; +} waiting_call; + +static void perform_transport_stream_op(grpc_call_element *elem, + grpc_transport_stream_op *op, + int continuation); + +static void continue_with_pick(void *arg, int iomgr_success) { + waiting_call *wc = arg; + call_data *calld = wc->elem->call_data; + perform_transport_stream_op(wc->elem, &calld->waiting_op, 1); + gpr_free(wc); +} + +static void add_to_lb_policy_wait_queue_locked_state_config( + grpc_call_element *elem) { + channel_data *chand = elem->channel_data; + waiting_call *wc = gpr_malloc(sizeof(*wc)); + grpc_iomgr_closure_init(&wc->closure, continue_with_pick, wc); + wc->elem = elem; + wc->closure.next = chand->waiting_for_config_closures; + chand->waiting_for_config_closures = &wc->closure; +} + +static int is_empty(void *p, int len) { + char *ptr = p; + int i; + for (i = 0; i < len; i++) { + if (ptr[i] != 0) return 0; + } + return 1; +} + +static void started_call(void *arg, int iomgr_success) { + call_data *calld = arg; + grpc_transport_stream_op op; + int have_waiting; + + gpr_mu_lock(&calld->mu_state); + if (calld->state == CALL_CANCELLED && calld->subchannel_call != NULL) { + memset(&op, 0, sizeof(op)); + op.cancel_with_status = GRPC_STATUS_CANCELLED; + gpr_mu_unlock(&calld->mu_state); + grpc_subchannel_call_process_op(calld->subchannel_call, &op); + } else if (calld->state == CALL_WAITING_FOR_CALL) { + have_waiting = !is_empty(&calld->waiting_op, sizeof(calld->waiting_op)); + if (calld->subchannel_call != NULL) { + calld->state = CALL_ACTIVE; + gpr_mu_unlock(&calld->mu_state); + if (have_waiting) { + grpc_subchannel_call_process_op(calld->subchannel_call, + &calld->waiting_op); + } + } else { + calld->state = CALL_CANCELLED; + gpr_mu_unlock(&calld->mu_state); + if (have_waiting) { + handle_op_after_cancellation(calld->elem, &calld->waiting_op); + } + } + } else { + GPR_ASSERT(calld->state == CALL_CANCELLED); + gpr_mu_unlock(&calld->mu_state); + } +} + +static void picked_target(void *arg, int iomgr_success) { + call_data *calld = arg; + grpc_pollset *pollset; + + if (calld->picked_channel == NULL) { + /* treat this like a cancellation */ + calld->waiting_op.cancel_with_status = GRPC_STATUS_UNAVAILABLE; + perform_transport_stream_op(calld->elem, &calld->waiting_op, 1); + } else { + gpr_mu_lock(&calld->mu_state); + if (calld->state == CALL_CANCELLED) { + gpr_mu_unlock(&calld->mu_state); + handle_op_after_cancellation(calld->elem, &calld->waiting_op); + } else { + GPR_ASSERT(calld->state == CALL_WAITING_FOR_PICK); + calld->state = CALL_WAITING_FOR_CALL; + pollset = calld->waiting_op.bind_pollset; + gpr_mu_unlock(&calld->mu_state); + grpc_iomgr_closure_init(&calld->async_setup_task, started_call, calld); + grpc_subchannel_create_call(calld->picked_channel, pollset, + &calld->subchannel_call, + &calld->async_setup_task); + } + } +} + +static grpc_iomgr_closure *merge_into_waiting_op( + grpc_call_element *elem, grpc_transport_stream_op *new_op) { + call_data *calld = elem->call_data; + grpc_iomgr_closure *consumed_op = NULL; + grpc_transport_stream_op *waiting_op = &calld->waiting_op; + GPR_ASSERT((waiting_op->send_ops != NULL) + (new_op->send_ops != NULL) <= 1); + GPR_ASSERT((waiting_op->recv_ops != NULL) + (new_op->recv_ops != NULL) <= 1); + if (new_op->send_ops != NULL) { + waiting_op->send_ops = new_op->send_ops; + waiting_op->is_last_send = new_op->is_last_send; + waiting_op->on_done_send = new_op->on_done_send; + } + if (new_op->recv_ops != NULL) { + waiting_op->recv_ops = new_op->recv_ops; + waiting_op->recv_state = new_op->recv_state; + waiting_op->on_done_recv = new_op->on_done_recv; + } + if (new_op->on_consumed != NULL) { + if (waiting_op->on_consumed != NULL) { + consumed_op = waiting_op->on_consumed; + } + waiting_op->on_consumed = new_op->on_consumed; + } + if (new_op->cancel_with_status != GRPC_STATUS_OK) { + waiting_op->cancel_with_status = new_op->cancel_with_status; + } + return consumed_op; +} + +static char *cc_get_peer(grpc_call_element *elem) { + call_data *calld = elem->call_data; + channel_data *chand = elem->channel_data; + grpc_subchannel_call *subchannel_call; + char *result; + + gpr_mu_lock(&calld->mu_state); + if (calld->state == CALL_ACTIVE) { + subchannel_call = calld->subchannel_call; + GRPC_SUBCHANNEL_CALL_REF(subchannel_call, "get_peer"); + gpr_mu_unlock(&calld->mu_state); + result = grpc_subchannel_call_get_peer(subchannel_call); + GRPC_SUBCHANNEL_CALL_UNREF(subchannel_call, "get_peer"); + return result; + } else { + gpr_mu_unlock(&calld->mu_state); + return grpc_channel_get_target(chand->master); + } +} + +static void perform_transport_stream_op(grpc_call_element *elem, + grpc_transport_stream_op *op, + int continuation) { + call_data *calld = elem->call_data; + channel_data *chand = elem->channel_data; + grpc_subchannel_call *subchannel_call; + grpc_lb_policy *lb_policy; + grpc_transport_stream_op op2; + grpc_iomgr_closure *consumed_op = NULL; + GPR_ASSERT(elem->filter == &grpc_client_channel_filter); + GRPC_CALL_LOG_OP(GPR_INFO, elem, op); + + gpr_mu_lock(&calld->mu_state); + switch (calld->state) { + case CALL_ACTIVE: + GPR_ASSERT(!continuation); + subchannel_call = calld->subchannel_call; + gpr_mu_unlock(&calld->mu_state); + grpc_subchannel_call_process_op(subchannel_call, op); + break; + case CALL_CANCELLED: + gpr_mu_unlock(&calld->mu_state); + handle_op_after_cancellation(elem, op); + break; + case CALL_WAITING_FOR_SEND: + GPR_ASSERT(!continuation); + consumed_op = merge_into_waiting_op(elem, op); + if (!calld->waiting_op.send_ops && + calld->waiting_op.cancel_with_status == GRPC_STATUS_OK) { + gpr_mu_unlock(&calld->mu_state); + break; + } + *op = calld->waiting_op; + memset(&calld->waiting_op, 0, sizeof(calld->waiting_op)); + continuation = 1; + /* fall through */ + case CALL_WAITING_FOR_CONFIG: + case CALL_WAITING_FOR_PICK: + case CALL_WAITING_FOR_CALL: + if (!continuation) { + if (op->cancel_with_status != GRPC_STATUS_OK) { + calld->state = CALL_CANCELLED; + op2 = calld->waiting_op; + memset(&calld->waiting_op, 0, sizeof(calld->waiting_op)); + if (op->on_consumed) { + calld->waiting_op.on_consumed = op->on_consumed; + op->on_consumed = NULL; + } else if (op2.on_consumed) { + calld->waiting_op.on_consumed = op2.on_consumed; + op2.on_consumed = NULL; + } + gpr_mu_unlock(&calld->mu_state); + handle_op_after_cancellation(elem, op); + handle_op_after_cancellation(elem, &op2); + } else { + consumed_op = merge_into_waiting_op(elem, op); + gpr_mu_unlock(&calld->mu_state); + } + break; + } + /* fall through */ + case CALL_CREATED: + if (op->cancel_with_status != GRPC_STATUS_OK) { + calld->state = CALL_CANCELLED; + gpr_mu_unlock(&calld->mu_state); + handle_op_after_cancellation(elem, op); + } else { + calld->waiting_op = *op; + + if (op->send_ops == NULL) { + /* need to have some send ops before we can select the + lb target */ + calld->state = CALL_WAITING_FOR_SEND; + gpr_mu_unlock(&calld->mu_state); + } else { + gpr_mu_lock(&chand->mu_config); + lb_policy = chand->lb_policy; + if (lb_policy) { + grpc_transport_stream_op *op = &calld->waiting_op; + grpc_pollset *bind_pollset = op->bind_pollset; + grpc_metadata_batch *initial_metadata = + &op->send_ops->ops[0].data.metadata; + GRPC_LB_POLICY_REF(lb_policy, "pick"); + gpr_mu_unlock(&chand->mu_config); + calld->state = CALL_WAITING_FOR_PICK; + + GPR_ASSERT(op->bind_pollset); + GPR_ASSERT(op->send_ops); + GPR_ASSERT(op->send_ops->nops >= 1); + GPR_ASSERT(op->send_ops->ops[0].type == GRPC_OP_METADATA); + gpr_mu_unlock(&calld->mu_state); + + grpc_iomgr_closure_init(&calld->async_setup_task, picked_target, + calld); + grpc_lb_policy_pick(lb_policy, bind_pollset, initial_metadata, + &calld->picked_channel, + &calld->async_setup_task); + + GRPC_LB_POLICY_UNREF(lb_policy, "pick"); + } else if (chand->resolver != NULL) { + calld->state = CALL_WAITING_FOR_CONFIG; + add_to_lb_policy_wait_queue_locked_state_config(elem); + if (!chand->started_resolving && chand->resolver != NULL) { + GRPC_CHANNEL_INTERNAL_REF(chand->master, "resolver"); + chand->started_resolving = 1; + grpc_resolver_next(chand->resolver, + &chand->incoming_configuration, + &chand->on_config_changed); + } + gpr_mu_unlock(&chand->mu_config); + gpr_mu_unlock(&calld->mu_state); + } else { + calld->state = CALL_CANCELLED; + gpr_mu_unlock(&chand->mu_config); + gpr_mu_unlock(&calld->mu_state); + handle_op_after_cancellation(elem, op); + } + } + } + break; + } + + if (consumed_op != NULL) { + consumed_op->cb(consumed_op->cb_arg, 1); + } +} + +static void cc_start_transport_stream_op(grpc_call_element *elem, + grpc_transport_stream_op *op) { + perform_transport_stream_op(elem, op, 0); +} + +static void watch_lb_policy(channel_data *chand, grpc_lb_policy *lb_policy, + grpc_connectivity_state current_state); + +static void on_lb_policy_state_changed(void *arg, int iomgr_success) { + lb_policy_connectivity_watcher *w = arg; + + gpr_mu_lock(&w->chand->mu_config); + /* check if the notification is for a stale policy */ + if (w->lb_policy == w->chand->lb_policy) { + grpc_connectivity_state_set(&w->chand->state_tracker, w->state, + "lb_changed"); + if (w->state != GRPC_CHANNEL_FATAL_FAILURE) { + watch_lb_policy(w->chand, w->lb_policy, w->state); + } + } + gpr_mu_unlock(&w->chand->mu_config); + + GRPC_CHANNEL_INTERNAL_UNREF(w->chand->master, "watch_lb_policy"); + gpr_free(w); +} + +static void watch_lb_policy(channel_data *chand, grpc_lb_policy *lb_policy, + grpc_connectivity_state current_state) { + lb_policy_connectivity_watcher *w = gpr_malloc(sizeof(*w)); + GRPC_CHANNEL_INTERNAL_REF(chand->master, "watch_lb_policy"); + + w->chand = chand; + grpc_iomgr_closure_init(&w->on_changed, on_lb_policy_state_changed, w); + w->state = current_state; + w->lb_policy = lb_policy; + grpc_lb_policy_notify_on_state_change(lb_policy, &w->state, &w->on_changed); +} + +static void cc_on_config_changed(void *arg, int iomgr_success) { + channel_data *chand = arg; + grpc_lb_policy *lb_policy = NULL; + grpc_lb_policy *old_lb_policy; + grpc_resolver *old_resolver; + grpc_iomgr_closure *wakeup_closures = NULL; + grpc_connectivity_state state = GRPC_CHANNEL_TRANSIENT_FAILURE; + int exit_idle = 0; + + if (chand->incoming_configuration != NULL) { + lb_policy = grpc_client_config_get_lb_policy(chand->incoming_configuration); + if (lb_policy != NULL) { + GRPC_LB_POLICY_REF(lb_policy, "channel"); + GRPC_LB_POLICY_REF(lb_policy, "config_change"); + state = grpc_lb_policy_check_connectivity(lb_policy); + } + + grpc_client_config_unref(chand->incoming_configuration); + } + + chand->incoming_configuration = NULL; + + gpr_mu_lock(&chand->mu_config); + old_lb_policy = chand->lb_policy; + chand->lb_policy = lb_policy; + if (lb_policy != NULL || chand->resolver == NULL /* disconnected */) { + wakeup_closures = chand->waiting_for_config_closures; + chand->waiting_for_config_closures = NULL; + } + if (lb_policy != NULL && chand->exit_idle_when_lb_policy_arrives) { + GRPC_LB_POLICY_REF(lb_policy, "exit_idle"); + exit_idle = 1; + chand->exit_idle_when_lb_policy_arrives = 0; + } + + if (iomgr_success && chand->resolver) { + grpc_resolver *resolver = chand->resolver; + GRPC_RESOLVER_REF(resolver, "channel-next"); + grpc_connectivity_state_set(&chand->state_tracker, state, + "new_lb+resolver"); + gpr_mu_unlock(&chand->mu_config); + GRPC_CHANNEL_INTERNAL_REF(chand->master, "resolver"); + grpc_resolver_next(resolver, &chand->incoming_configuration, + &chand->on_config_changed); + GRPC_RESOLVER_UNREF(resolver, "channel-next"); + if (lb_policy != NULL) { + watch_lb_policy(chand, lb_policy, state); + } + } else { + old_resolver = chand->resolver; + chand->resolver = NULL; + grpc_connectivity_state_set(&chand->state_tracker, + GRPC_CHANNEL_FATAL_FAILURE, "resolver_gone"); + gpr_mu_unlock(&chand->mu_config); + if (old_resolver != NULL) { + grpc_resolver_shutdown(old_resolver); + GRPC_RESOLVER_UNREF(old_resolver, "channel"); + } + } + + if (exit_idle) { + grpc_lb_policy_exit_idle(lb_policy); + GRPC_LB_POLICY_UNREF(lb_policy, "exit_idle"); + } + + if (old_lb_policy != NULL) { + grpc_lb_policy_shutdown(old_lb_policy); + GRPC_LB_POLICY_UNREF(old_lb_policy, "channel"); + } + + while (wakeup_closures) { + grpc_iomgr_closure *next = wakeup_closures->next; + wakeup_closures->cb(wakeup_closures->cb_arg, 1); + wakeup_closures = next; + } + + if (lb_policy != NULL) { + GRPC_LB_POLICY_UNREF(lb_policy, "config_change"); + } + GRPC_CHANNEL_INTERNAL_UNREF(chand->master, "resolver"); +} + +static void cc_start_transport_op(grpc_channel_element *elem, + grpc_transport_op *op) { + grpc_lb_policy *lb_policy = NULL; + channel_data *chand = elem->channel_data; + grpc_resolver *destroy_resolver = NULL; + grpc_iomgr_closure *on_consumed = op->on_consumed; + op->on_consumed = NULL; + + GPR_ASSERT(op->set_accept_stream == NULL); + GPR_ASSERT(op->bind_pollset == NULL); + + gpr_mu_lock(&chand->mu_config); + if (op->on_connectivity_state_change != NULL) { + grpc_connectivity_state_notify_on_state_change( + &chand->state_tracker, op->connectivity_state, + op->on_connectivity_state_change); + op->on_connectivity_state_change = NULL; + op->connectivity_state = NULL; + } + + if (!is_empty(op, sizeof(*op))) { + lb_policy = chand->lb_policy; + if (lb_policy) { + GRPC_LB_POLICY_REF(lb_policy, "broadcast"); + } + } + + if (op->disconnect && chand->resolver != NULL) { + grpc_connectivity_state_set(&chand->state_tracker, + GRPC_CHANNEL_FATAL_FAILURE, "disconnect"); + destroy_resolver = chand->resolver; + chand->resolver = NULL; + if (chand->lb_policy != NULL) { + grpc_lb_policy_shutdown(chand->lb_policy); + GRPC_LB_POLICY_UNREF(chand->lb_policy, "channel"); + chand->lb_policy = NULL; + } + } + gpr_mu_unlock(&chand->mu_config); + + if (destroy_resolver) { + grpc_resolver_shutdown(destroy_resolver); + GRPC_RESOLVER_UNREF(destroy_resolver, "channel"); + } + + if (lb_policy) { + grpc_lb_policy_broadcast(lb_policy, op); + GRPC_LB_POLICY_UNREF(lb_policy, "broadcast"); + } + + if (on_consumed) { + grpc_iomgr_add_callback(on_consumed); + } +} + +/* Constructor for call_data */ +static void init_call_elem(grpc_call_element *elem, + const void *server_transport_data, + grpc_transport_stream_op *initial_op) { + call_data *calld = elem->call_data; + + /* TODO(ctiller): is there something useful we can do here? */ + GPR_ASSERT(initial_op == NULL); + + GPR_ASSERT(elem->filter == &grpc_client_channel_filter); + GPR_ASSERT(server_transport_data == NULL); + gpr_mu_init(&calld->mu_state); + calld->elem = elem; + calld->state = CALL_CREATED; + calld->deadline = gpr_inf_future(GPR_CLOCK_REALTIME); +} + +/* Destructor for call_data */ +static void destroy_call_elem(grpc_call_element *elem) { + call_data *calld = elem->call_data; + grpc_subchannel_call *subchannel_call; + + /* if the call got activated, we need to destroy the child stack also, and + remove it from the in-flight requests tracked by the child_entry we + picked */ + gpr_mu_lock(&calld->mu_state); + switch (calld->state) { + case CALL_ACTIVE: + subchannel_call = calld->subchannel_call; + gpr_mu_unlock(&calld->mu_state); + GRPC_SUBCHANNEL_CALL_UNREF(subchannel_call, "client_channel"); + break; + case CALL_CREATED: + case CALL_CANCELLED: + gpr_mu_unlock(&calld->mu_state); + break; + case CALL_WAITING_FOR_PICK: + case CALL_WAITING_FOR_CONFIG: + case CALL_WAITING_FOR_CALL: + case CALL_WAITING_FOR_SEND: + gpr_log(GPR_ERROR, "should never reach here"); + abort(); + break; + } +} + +/* Constructor for channel_data */ +static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, + const grpc_channel_args *args, + grpc_mdctx *metadata_context, int is_first, + int is_last) { + channel_data *chand = elem->channel_data; + + memset(chand, 0, sizeof(*chand)); + + GPR_ASSERT(is_last); + GPR_ASSERT(elem->filter == &grpc_client_channel_filter); + + gpr_mu_init(&chand->mu_config); + chand->mdctx = metadata_context; + chand->master = master; + grpc_pollset_set_init(&chand->pollset_set); + grpc_iomgr_closure_init(&chand->on_config_changed, cc_on_config_changed, + chand); + + grpc_connectivity_state_init(&chand->state_tracker, GRPC_CHANNEL_IDLE, + "client_channel"); +} + +/* Destructor for channel_data */ +static void destroy_channel_elem(grpc_channel_element *elem) { + channel_data *chand = elem->channel_data; + + if (chand->resolver != NULL) { + grpc_resolver_shutdown(chand->resolver); + GRPC_RESOLVER_UNREF(chand->resolver, "channel"); + } + if (chand->lb_policy != NULL) { + GRPC_LB_POLICY_UNREF(chand->lb_policy, "channel"); + } + grpc_connectivity_state_destroy(&chand->state_tracker); + grpc_pollset_set_destroy(&chand->pollset_set); + gpr_mu_destroy(&chand->mu_config); +} + +const grpc_channel_filter grpc_client_channel_filter = { + cc_start_transport_stream_op, + cc_start_transport_op, + sizeof(call_data), + init_call_elem, + destroy_call_elem, + sizeof(channel_data), + init_channel_elem, + destroy_channel_elem, + cc_get_peer, + "client-channel", +}; + +void grpc_client_channel_set_resolver(grpc_channel_stack *channel_stack, + grpc_resolver *resolver) { + /* post construction initialization: set the transport setup pointer */ + grpc_channel_element *elem = grpc_channel_stack_last_element(channel_stack); + channel_data *chand = elem->channel_data; + gpr_mu_lock(&chand->mu_config); + GPR_ASSERT(!chand->resolver); + chand->resolver = resolver; + GRPC_RESOLVER_REF(resolver, "channel"); + if (chand->waiting_for_config_closures != NULL || + chand->exit_idle_when_lb_policy_arrives) { + chand->started_resolving = 1; + GRPC_CHANNEL_INTERNAL_REF(chand->master, "resolver"); + grpc_resolver_next(resolver, &chand->incoming_configuration, + &chand->on_config_changed); + } + gpr_mu_unlock(&chand->mu_config); +} + +grpc_connectivity_state grpc_client_channel_check_connectivity_state( + grpc_channel_element *elem, int try_to_connect) { + channel_data *chand = elem->channel_data; + grpc_connectivity_state out; + gpr_mu_lock(&chand->mu_config); + out = grpc_connectivity_state_check(&chand->state_tracker); + if (out == GRPC_CHANNEL_IDLE && try_to_connect) { + if (chand->lb_policy != NULL) { + grpc_lb_policy_exit_idle(chand->lb_policy); + } else { + chand->exit_idle_when_lb_policy_arrives = 1; + if (!chand->started_resolving && chand->resolver != NULL) { + GRPC_CHANNEL_INTERNAL_REF(chand->master, "resolver"); + chand->started_resolving = 1; + grpc_resolver_next(chand->resolver, &chand->incoming_configuration, + &chand->on_config_changed); + } + } + } + gpr_mu_unlock(&chand->mu_config); + return out; +} + +void grpc_client_channel_watch_connectivity_state( + grpc_channel_element *elem, grpc_connectivity_state *state, + grpc_iomgr_closure *on_complete) { + channel_data *chand = elem->channel_data; + gpr_mu_lock(&chand->mu_config); + grpc_connectivity_state_notify_on_state_change(&chand->state_tracker, state, + on_complete); + gpr_mu_unlock(&chand->mu_config); +} + +grpc_pollset_set *grpc_client_channel_get_connecting_pollset_set( + grpc_channel_element *elem) { + channel_data *chand = elem->channel_data; + return &chand->pollset_set; +} + +void grpc_client_channel_add_interested_party(grpc_channel_element *elem, + grpc_pollset *pollset) { + channel_data *chand = elem->channel_data; + grpc_pollset_set_add_pollset(&chand->pollset_set, pollset); +} + +void grpc_client_channel_del_interested_party(grpc_channel_element *elem, + grpc_pollset *pollset) { + channel_data *chand = elem->channel_data; + grpc_pollset_set_del_pollset(&chand->pollset_set, pollset); +} diff --git a/src/core/channel/client_channel.h b/src/core/channel/client_channel.h new file mode 100644 index 00000000..13681e39 --- /dev/null +++ b/src/core/channel/client_channel.h @@ -0,0 +1,70 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_CHANNEL_CLIENT_CHANNEL_H +#define GRPC_INTERNAL_CORE_CHANNEL_CLIENT_CHANNEL_H + +#include "src/core/channel/channel_stack.h" +#include "src/core/client_config/resolver.h" + +/* A client channel is a channel that begins disconnected, and can connect + to some endpoint on demand. If that endpoint disconnects, it will be + connected to again later. + + Calls on a disconnected client channel are queued until a connection is + established. */ + +extern const grpc_channel_filter grpc_client_channel_filter; + +/* post-construction initializer to let the client channel know which + transport setup it should cancel upon destruction, or initiate when it needs + a connection */ +void grpc_client_channel_set_resolver(grpc_channel_stack *channel_stack, + grpc_resolver *resolver); + +grpc_connectivity_state grpc_client_channel_check_connectivity_state( + grpc_channel_element *elem, int try_to_connect); + +void grpc_client_channel_watch_connectivity_state( + grpc_channel_element *elem, grpc_connectivity_state *state, + grpc_iomgr_closure *on_complete); + +grpc_pollset_set *grpc_client_channel_get_connecting_pollset_set( + grpc_channel_element *elem); + +void grpc_client_channel_add_interested_party(grpc_channel_element *channel, + grpc_pollset *pollset); +void grpc_client_channel_del_interested_party(grpc_channel_element *channel, + grpc_pollset *pollset); + +#endif /* GRPC_INTERNAL_CORE_CHANNEL_CLIENT_CHANNEL_H */ diff --git a/src/core/channel/compress_filter.c b/src/core/channel/compress_filter.c new file mode 100644 index 00000000..762a4edc --- /dev/null +++ b/src/core/channel/compress_filter.c @@ -0,0 +1,363 @@ +/* + * + * Copyright 2015, 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 +#include + +#include +#include +#include +#include + +#include "src/core/channel/compress_filter.h" +#include "src/core/channel/channel_args.h" +#include "src/core/compression/message_compress.h" +#include "src/core/support/string.h" + +typedef struct call_data { + gpr_slice_buffer slices; /**< Buffers up input slices to be compressed */ + grpc_linked_mdelem compression_algorithm_storage; + grpc_linked_mdelem accept_encoding_storage; + int remaining_slice_bytes; /**< Input data to be read, as per BEGIN_MESSAGE */ + int written_initial_metadata; /**< Already processed initial md? */ + /** Compression algorithm we'll try to use. It may be given by incoming + * metadata, or by the channel's default compression settings. */ + grpc_compression_algorithm compression_algorithm; + /** If true, contents of \a compression_algorithm are authoritative */ + int has_compression_algorithm; +} call_data; + +typedef struct channel_data { + /** Metadata key for the incoming (requested) compression algorithm */ + grpc_mdstr *mdstr_request_compression_algorithm_key; + /** Metadata key for the outgoing (used) compression algorithm */ + grpc_mdstr *mdstr_outgoing_compression_algorithm_key; + /** Metadata key for the accepted encodings */ + grpc_mdstr *mdstr_compression_capabilities_key; + /** Precomputed metadata elements for all available compression algorithms */ + grpc_mdelem *mdelem_compression_algorithms[GRPC_COMPRESS_ALGORITHMS_COUNT]; + /** Precomputed metadata elements for the accepted encodings */ + grpc_mdelem *mdelem_accept_encoding; + /** The default, channel-level, compression algorithm */ + grpc_compression_algorithm default_compression_algorithm; +} channel_data; + +/** Compress \a slices in place using \a algorithm. Returns 1 if compression did + * actually happen, 0 otherwise (for example if the compressed output size was + * larger than the raw input). + * + * Returns 1 if the data was actually compress and 0 otherwise. */ +static int compress_send_sb(grpc_compression_algorithm algorithm, + gpr_slice_buffer *slices) { + int did_compress; + gpr_slice_buffer tmp; + gpr_slice_buffer_init(&tmp); + did_compress = grpc_msg_compress(algorithm, slices, &tmp); + if (did_compress) { + gpr_slice_buffer_swap(slices, &tmp); + } + gpr_slice_buffer_destroy(&tmp); + return did_compress; +} + +/** For each \a md element from the incoming metadata, filter out the entry for + * "grpc-encoding", using its value to populate the call data's + * compression_algorithm field. */ +static grpc_mdelem *compression_md_filter(void *user_data, grpc_mdelem *md) { + grpc_call_element *elem = user_data; + call_data *calld = elem->call_data; + channel_data *channeld = elem->channel_data; + + if (md->key == channeld->mdstr_request_compression_algorithm_key) { + const char *md_c_str = grpc_mdstr_as_c_string(md->value); + if (!grpc_compression_algorithm_parse(md_c_str, strlen(md_c_str), + &calld->compression_algorithm)) { + gpr_log(GPR_ERROR, "Invalid compression algorithm: '%s'. Ignoring.", + md_c_str); + calld->compression_algorithm = GRPC_COMPRESS_NONE; + } + calld->has_compression_algorithm = 1; + return NULL; + } + + return md; +} + +static int skip_compression(channel_data *channeld, call_data *calld) { + if (calld->has_compression_algorithm) { + if (calld->compression_algorithm == GRPC_COMPRESS_NONE) { + return 1; + } + return 0; /* we have an actual call-specific algorithm */ + } + /* no per-call compression override */ + return channeld->default_compression_algorithm == GRPC_COMPRESS_NONE; +} + +/** Assembles a new grpc_stream_op_buffer with the compressed slices, modifying + * the associated GRPC_OP_BEGIN_MESSAGE accordingly (new compressed length, + * flags indicating compression is in effect) and replaces \a send_ops with it. + * */ +static void finish_compressed_sopb(grpc_stream_op_buffer *send_ops, + grpc_call_element *elem) { + size_t i; + call_data *calld = elem->call_data; + int new_slices_added = 0; /* GPR_FALSE */ + grpc_metadata_batch metadata; + grpc_stream_op_buffer new_send_ops; + grpc_sopb_init(&new_send_ops); + + for (i = 0; i < send_ops->nops; i++) { + grpc_stream_op *sop = &send_ops->ops[i]; + switch (sop->type) { + case GRPC_OP_BEGIN_MESSAGE: + grpc_sopb_add_begin_message( + &new_send_ops, calld->slices.length, + sop->data.begin_message.flags | GRPC_WRITE_INTERNAL_COMPRESS); + break; + case GRPC_OP_SLICE: + /* Once we reach the slices section of the original buffer, simply add + * all the new (compressed) slices. We obviously want to do this only + * once, hence the "new_slices_added" guard. */ + if (!new_slices_added) { + size_t j; + for (j = 0; j < calld->slices.count; ++j) { + grpc_sopb_add_slice(&new_send_ops, + gpr_slice_ref(calld->slices.slices[j])); + } + new_slices_added = 1; /* GPR_TRUE */ + } + break; + case GRPC_OP_METADATA: + /* move the metadata to the new buffer. */ + grpc_metadata_batch_move(&metadata, &sop->data.metadata); + grpc_sopb_add_metadata(&new_send_ops, metadata); + break; + case GRPC_NO_OP: + break; + } + } + grpc_sopb_swap(send_ops, &new_send_ops); + grpc_sopb_destroy(&new_send_ops); +} + +/** Filter's "main" function, called for any incoming grpc_transport_stream_op + * instance that holds a non-zero number of send operations, accesible to this + * function in \a send_ops. */ +static void process_send_ops(grpc_call_element *elem, + grpc_stream_op_buffer *send_ops) { + call_data *calld = elem->call_data; + channel_data *channeld = elem->channel_data; + size_t i; + int did_compress = 0; + + /* In streaming calls, we need to reset the previously accumulated slices */ + gpr_slice_buffer_reset_and_unref(&calld->slices); + for (i = 0; i < send_ops->nops; ++i) { + grpc_stream_op *sop = &send_ops->ops[i]; + switch (sop->type) { + case GRPC_OP_BEGIN_MESSAGE: + /* buffer up slices until we've processed all the expected ones (as + * given by GRPC_OP_BEGIN_MESSAGE) */ + calld->remaining_slice_bytes = sop->data.begin_message.length; + if (sop->data.begin_message.flags & GRPC_WRITE_NO_COMPRESS) { + calld->has_compression_algorithm = 1; /* GPR_TRUE */ + calld->compression_algorithm = GRPC_COMPRESS_NONE; + } + break; + case GRPC_OP_METADATA: + if (!calld->written_initial_metadata) { + /* Parse incoming request for compression. If any, it'll be available + * at calld->compression_algorithm */ + grpc_metadata_batch_filter(&(sop->data.metadata), + compression_md_filter, elem); + if (!calld->has_compression_algorithm) { + /* If no algorithm was found in the metadata and we aren't + * exceptionally skipping compression, fall back to the channel + * default */ + calld->compression_algorithm = + channeld->default_compression_algorithm; + calld->has_compression_algorithm = 1; /* GPR_TRUE */ + } + /* hint compression algorithm */ + grpc_metadata_batch_add_tail( + &(sop->data.metadata), &calld->compression_algorithm_storage, + GRPC_MDELEM_REF(channeld->mdelem_compression_algorithms + [calld->compression_algorithm])); + + /* convey supported compression algorithms */ + grpc_metadata_batch_add_tail( + &(sop->data.metadata), &calld->accept_encoding_storage, + GRPC_MDELEM_REF(channeld->mdelem_accept_encoding)); + + calld->written_initial_metadata = 1; /* GPR_TRUE */ + } + break; + case GRPC_OP_SLICE: + if (skip_compression(channeld, calld)) continue; + GPR_ASSERT(calld->remaining_slice_bytes > 0); + /* Increase input ref count, gpr_slice_buffer_add takes ownership. */ + gpr_slice_buffer_add(&calld->slices, gpr_slice_ref(sop->data.slice)); + calld->remaining_slice_bytes -= GPR_SLICE_LENGTH(sop->data.slice); + if (calld->remaining_slice_bytes == 0) { + did_compress = + compress_send_sb(calld->compression_algorithm, &calld->slices); + } + break; + case GRPC_NO_OP: + break; + } + } + + /* Modify the send_ops stream_op_buffer depending on whether compression was + * carried out */ + if (did_compress) { + finish_compressed_sopb(send_ops, elem); + } +} + +/* Called either: + - in response to an API call (or similar) from above, to send something + - a network event (or similar) from below, to receive something + op contains type and call direction information, in addition to the data + that is being sent or received. */ +static void compress_start_transport_stream_op(grpc_call_element *elem, + grpc_transport_stream_op *op) { + if (op->send_ops && op->send_ops->nops > 0) { + process_send_ops(elem, op->send_ops); + } + + /* pass control down the stack */ + grpc_call_next_op(elem, op); +} + +/* Constructor for call_data */ +static void init_call_elem(grpc_call_element *elem, + const void *server_transport_data, + grpc_transport_stream_op *initial_op) { + /* grab pointers to our data from the call element */ + call_data *calld = elem->call_data; + + /* initialize members */ + gpr_slice_buffer_init(&calld->slices); + calld->has_compression_algorithm = 0; + calld->written_initial_metadata = 0; /* GPR_FALSE */ + + if (initial_op) { + if (initial_op->send_ops && initial_op->send_ops->nops > 0) { + process_send_ops(elem, initial_op->send_ops); + } + } +} + +/* Destructor for call_data */ +static void destroy_call_elem(grpc_call_element *elem) { + /* grab pointers to our data from the call element */ + call_data *calld = elem->call_data; + gpr_slice_buffer_destroy(&calld->slices); +} + +/* Constructor for channel_data */ +static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, + const grpc_channel_args *args, grpc_mdctx *mdctx, + int is_first, int is_last) { + channel_data *channeld = elem->channel_data; + grpc_compression_algorithm algo_idx; + const char *supported_algorithms_names[GRPC_COMPRESS_ALGORITHMS_COUNT - 1]; + char *accept_encoding_str; + size_t accept_encoding_str_len; + + channeld->default_compression_algorithm = + grpc_channel_args_get_compression_algorithm(args); + + channeld->mdstr_request_compression_algorithm_key = + grpc_mdstr_from_string(mdctx, GRPC_COMPRESS_REQUEST_ALGORITHM_KEY, 0); + + channeld->mdstr_outgoing_compression_algorithm_key = + grpc_mdstr_from_string(mdctx, "grpc-encoding", 0); + + channeld->mdstr_compression_capabilities_key = + grpc_mdstr_from_string(mdctx, "grpc-accept-encoding", 0); + + for (algo_idx = 0; algo_idx < GRPC_COMPRESS_ALGORITHMS_COUNT; ++algo_idx) { + char *algorithm_name; + GPR_ASSERT(grpc_compression_algorithm_name(algo_idx, &algorithm_name) != 0); + channeld->mdelem_compression_algorithms[algo_idx] = + grpc_mdelem_from_metadata_strings( + mdctx, + GRPC_MDSTR_REF(channeld->mdstr_outgoing_compression_algorithm_key), + grpc_mdstr_from_string(mdctx, algorithm_name, 0)); + if (algo_idx > 0) { + supported_algorithms_names[algo_idx - 1] = algorithm_name; + } + } + + /* TODO(dgq): gpr_strjoin_sep could be made to work with statically allocated + * arrays, as to avoid the heap allocs */ + accept_encoding_str = gpr_strjoin_sep( + supported_algorithms_names, GPR_ARRAY_SIZE(supported_algorithms_names), + ", ", &accept_encoding_str_len); + + channeld->mdelem_accept_encoding = grpc_mdelem_from_metadata_strings( + mdctx, GRPC_MDSTR_REF(channeld->mdstr_compression_capabilities_key), + grpc_mdstr_from_string(mdctx, accept_encoding_str, 0)); + gpr_free(accept_encoding_str); + + GPR_ASSERT(!is_last); +} + +/* Destructor for channel data */ +static void destroy_channel_elem(grpc_channel_element *elem) { + channel_data *channeld = elem->channel_data; + grpc_compression_algorithm algo_idx; + + GRPC_MDSTR_UNREF(channeld->mdstr_request_compression_algorithm_key); + GRPC_MDSTR_UNREF(channeld->mdstr_outgoing_compression_algorithm_key); + GRPC_MDSTR_UNREF(channeld->mdstr_compression_capabilities_key); + for (algo_idx = 0; algo_idx < GRPC_COMPRESS_ALGORITHMS_COUNT; ++algo_idx) { + GRPC_MDELEM_UNREF(channeld->mdelem_compression_algorithms[algo_idx]); + } + GRPC_MDELEM_UNREF(channeld->mdelem_accept_encoding); +} + +const grpc_channel_filter grpc_compress_filter = { + compress_start_transport_stream_op, + grpc_channel_next_op, + sizeof(call_data), + init_call_elem, + destroy_call_elem, + sizeof(channel_data), + init_channel_elem, + destroy_channel_elem, + grpc_call_next_get_peer, + "compress"}; diff --git a/src/core/channel/compress_filter.h b/src/core/channel/compress_filter.h new file mode 100644 index 00000000..415459bc --- /dev/null +++ b/src/core/channel/compress_filter.h @@ -0,0 +1,65 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_CHANNEL_COMPRESS_FILTER_H +#define GRPC_INTERNAL_CORE_CHANNEL_COMPRESS_FILTER_H + +#include "src/core/channel/channel_stack.h" + +#define GRPC_COMPRESS_REQUEST_ALGORITHM_KEY "grpc-internal-encoding-request" + +/** Compression filter for outgoing data. + * + * See for the available compression settings. + * + * Compression settings may come from: + * - Channel configuration, as established at channel creation time. + * - The metadata accompanying the outgoing data to be compressed. This is + * taken as a request only. We may choose not to honor it. The metadata key + * is given by \a GRPC_COMPRESS_REQUEST_ALGORITHM_KEY. + * + * Compression can be disabled for concrete messages (for instance in order to + * prevent CRIME/BEAST type attacks) by having the GRPC_WRITE_NO_COMPRESS set in + * the BEGIN_MESSAGE flags. + * + * The attempted compression mechanism is added to the resulting initial + * metadata under the'grpc-encoding' key. + * + * If compression is actually performed, BEGIN_MESSAGE's flag is modified to + * incorporate GRPC_WRITE_INTERNAL_COMPRESS. Otherwise, and regardless of the + * aforementioned 'grpc-encoding' metadata value, data will pass through + * uncompressed. */ + +extern const grpc_channel_filter grpc_compress_filter; + +#endif /* GRPC_INTERNAL_CORE_CHANNEL_COMPRESS_FILTER_H */ diff --git a/src/core/channel/connected_channel.c b/src/core/channel/connected_channel.c new file mode 100644 index 00000000..b95ed06f --- /dev/null +++ b/src/core/channel/connected_channel.c @@ -0,0 +1,157 @@ +/* + * + * Copyright 2015, 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 "src/core/channel/connected_channel.h" + +#include +#include +#include + +#include "src/core/support/string.h" +#include "src/core/transport/transport.h" +#include +#include +#include +#include + +#define MAX_BUFFER_LENGTH 8192 + +typedef struct connected_channel_channel_data { + grpc_transport *transport; +} channel_data; + +typedef struct connected_channel_call_data { void *unused; } call_data; + +/* We perform a small hack to locate transport data alongside the connected + channel data in call allocations, to allow everything to be pulled in minimal + cache line requests */ +#define TRANSPORT_STREAM_FROM_CALL_DATA(calld) ((grpc_stream *)((calld) + 1)) +#define CALL_DATA_FROM_TRANSPORT_STREAM(transport_stream) \ + (((call_data *)(transport_stream)) - 1) + +/* Intercept a call operation and either push it directly up or translate it + into transport stream operations */ +static void con_start_transport_stream_op(grpc_call_element *elem, + grpc_transport_stream_op *op) { + call_data *calld = elem->call_data; + channel_data *chand = elem->channel_data; + GPR_ASSERT(elem->filter == &grpc_connected_channel_filter); + GRPC_CALL_LOG_OP(GPR_INFO, elem, op); + + grpc_transport_perform_stream_op(chand->transport, + TRANSPORT_STREAM_FROM_CALL_DATA(calld), op); +} + +static void con_start_transport_op(grpc_channel_element *elem, + grpc_transport_op *op) { + channel_data *chand = elem->channel_data; + grpc_transport_perform_op(chand->transport, op); +} + +/* Constructor for call_data */ +static void init_call_elem(grpc_call_element *elem, + const void *server_transport_data, + grpc_transport_stream_op *initial_op) { + call_data *calld = elem->call_data; + channel_data *chand = elem->channel_data; + int r; + + GPR_ASSERT(elem->filter == &grpc_connected_channel_filter); + r = grpc_transport_init_stream(chand->transport, + TRANSPORT_STREAM_FROM_CALL_DATA(calld), + server_transport_data, initial_op); + GPR_ASSERT(r == 0); +} + +/* Destructor for call_data */ +static void destroy_call_elem(grpc_call_element *elem) { + call_data *calld = elem->call_data; + channel_data *chand = elem->channel_data; + GPR_ASSERT(elem->filter == &grpc_connected_channel_filter); + grpc_transport_destroy_stream(chand->transport, + TRANSPORT_STREAM_FROM_CALL_DATA(calld)); +} + +/* Constructor for channel_data */ +static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, + const grpc_channel_args *args, grpc_mdctx *mdctx, + int is_first, int is_last) { + channel_data *cd = (channel_data *)elem->channel_data; + GPR_ASSERT(is_last); + GPR_ASSERT(elem->filter == &grpc_connected_channel_filter); + cd->transport = NULL; +} + +/* Destructor for channel_data */ +static void destroy_channel_elem(grpc_channel_element *elem) { + channel_data *cd = (channel_data *)elem->channel_data; + GPR_ASSERT(elem->filter == &grpc_connected_channel_filter); + grpc_transport_destroy(cd->transport); +} + +static char *con_get_peer(grpc_call_element *elem) { + channel_data *chand = elem->channel_data; + return grpc_transport_get_peer(chand->transport); +} + +const grpc_channel_filter grpc_connected_channel_filter = { + con_start_transport_stream_op, + con_start_transport_op, + sizeof(call_data), + init_call_elem, + destroy_call_elem, + sizeof(channel_data), + init_channel_elem, + destroy_channel_elem, + con_get_peer, + "connected", +}; + +void grpc_connected_channel_bind_transport(grpc_channel_stack *channel_stack, + grpc_transport *transport) { + /* Assumes that the connected channel filter is always the last filter + in a channel stack */ + grpc_channel_element *elem = grpc_channel_stack_last_element(channel_stack); + channel_data *cd = (channel_data *)elem->channel_data; + GPR_ASSERT(elem->filter == &grpc_connected_channel_filter); + GPR_ASSERT(cd->transport == NULL); + cd->transport = transport; + + /* HACK(ctiller): increase call stack size for the channel to make space + for channel data. We need a cleaner (but performant) way to do this, + and I'm not sure what that is yet. + This is only "safe" because call stacks place no additional data after + the last call element, and the last call element MUST be the connected + channel. */ + channel_stack->call_stack_size += grpc_transport_stream_size(transport); +} diff --git a/src/core/channel/connected_channel.h b/src/core/channel/connected_channel.h new file mode 100644 index 00000000..b615b0d3 --- /dev/null +++ b/src/core/channel/connected_channel.h @@ -0,0 +1,49 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_CHANNEL_CONNECTED_CHANNEL_H +#define GRPC_INTERNAL_CORE_CHANNEL_CONNECTED_CHANNEL_H + +#include "src/core/channel/channel_stack.h" + +/* A channel filter representing a channel that is on a connected transport. + This filter performs actual sending and receiving of messages. */ + +extern const grpc_channel_filter grpc_connected_channel_filter; + +/* Post construction fixup: set the transport in the connected channel. + Must be called before any call stack using this filter is used. */ +void grpc_connected_channel_bind_transport(grpc_channel_stack *channel_stack, + grpc_transport *transport); + +#endif /* GRPC_INTERNAL_CORE_CHANNEL_CONNECTED_CHANNEL_H */ diff --git a/src/core/channel/context.h b/src/core/channel/context.h new file mode 100644 index 00000000..ac5796b9 --- /dev/null +++ b/src/core/channel/context.h @@ -0,0 +1,49 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_CHANNEL_CONTEXT_H +#define GRPC_INTERNAL_CORE_CHANNEL_CONTEXT_H + +/* Call object context pointers */ +typedef enum { + GRPC_CONTEXT_SECURITY = 0, + GRPC_CONTEXT_TRACING, + GRPC_CONTEXT_COUNT +} grpc_context_index; + +typedef struct { + void *value; + void (*destroy)(void *); +} grpc_call_context_element; + +#endif /* GRPC_INTERNAL_CORE_CHANNEL_CONTEXT_H */ diff --git a/src/core/channel/http_client_filter.c b/src/core/channel/http_client_filter.c new file mode 100644 index 00000000..5f20f8c1 --- /dev/null +++ b/src/core/channel/http_client_filter.c @@ -0,0 +1,287 @@ +/* + * Copyright 2015, 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 "src/core/channel/http_client_filter.h" +#include +#include +#include +#include +#include "src/core/support/string.h" + +typedef struct call_data { + grpc_linked_mdelem method; + grpc_linked_mdelem scheme; + grpc_linked_mdelem authority; + grpc_linked_mdelem te_trailers; + grpc_linked_mdelem content_type; + grpc_linked_mdelem user_agent; + int sent_initial_metadata; + + int got_initial_metadata; + grpc_stream_op_buffer *recv_ops; + + /** Closure to call when finished with the hc_on_recv hook */ + grpc_iomgr_closure *on_done_recv; + /** Receive closures are chained: we inject this closure as the on_done_recv + up-call on transport_op, and remember to call our on_done_recv member + after handling it. */ + grpc_iomgr_closure hc_on_recv; +} call_data; + +typedef struct channel_data { + grpc_mdelem *te_trailers; + grpc_mdelem *method; + grpc_mdelem *scheme; + grpc_mdelem *content_type; + grpc_mdelem *status; + /** complete user agent mdelem */ + grpc_mdelem *user_agent; +} channel_data; + +/* used to silence 'variable not used' warnings */ +static void ignore_unused(void *ignored) {} + +static grpc_mdelem *client_recv_filter(void *user_data, grpc_mdelem *md) { + grpc_call_element *elem = user_data; + channel_data *channeld = elem->channel_data; + if (md == channeld->status) { + return NULL; + } else if (md->key == channeld->status->key) { + grpc_call_element_send_cancel(elem); + return NULL; + } else if (md->key == channeld->content_type->key) { + return NULL; + } + return md; +} + +static void hc_on_recv(void *user_data, int success) { + grpc_call_element *elem = user_data; + call_data *calld = elem->call_data; + size_t i; + size_t nops = calld->recv_ops->nops; + grpc_stream_op *ops = calld->recv_ops->ops; + for (i = 0; i < nops; i++) { + grpc_stream_op *op = &ops[i]; + if (op->type != GRPC_OP_METADATA) continue; + calld->got_initial_metadata = 1; + grpc_metadata_batch_filter(&op->data.metadata, client_recv_filter, elem); + } + calld->on_done_recv->cb(calld->on_done_recv->cb_arg, success); +} + + + +static grpc_mdelem *client_strip_filter(void *user_data, grpc_mdelem *md) { + grpc_call_element *elem = user_data; + channel_data *channeld = elem->channel_data; + /* eat the things we'd like to set ourselves */ + if (md->key == channeld->method->key) return NULL; + if (md->key == channeld->scheme->key) return NULL; + if (md->key == channeld->te_trailers->key) return NULL; + if (md->key == channeld->content_type->key) return NULL; + if (md->key == channeld->user_agent->key) return NULL; + return md; +} + +static void hc_mutate_op(grpc_call_element *elem, + grpc_transport_stream_op *op) { + /* grab pointers to our data from the call element */ + call_data *calld = elem->call_data; + channel_data *channeld = elem->channel_data; + size_t i; + if (op->send_ops && !calld->sent_initial_metadata) { + size_t nops = op->send_ops->nops; + grpc_stream_op *ops = op->send_ops->ops; + for (i = 0; i < nops; i++) { + grpc_stream_op *op = &ops[i]; + if (op->type != GRPC_OP_METADATA) continue; + calld->sent_initial_metadata = 1; + grpc_metadata_batch_filter(&op->data.metadata, client_strip_filter, elem); + /* Send : prefixed headers, which have to be before any application + layer headers. */ + grpc_metadata_batch_add_head(&op->data.metadata, &calld->method, + GRPC_MDELEM_REF(channeld->method)); + grpc_metadata_batch_add_head(&op->data.metadata, &calld->scheme, + GRPC_MDELEM_REF(channeld->scheme)); + grpc_metadata_batch_add_tail(&op->data.metadata, &calld->te_trailers, + GRPC_MDELEM_REF(channeld->te_trailers)); + grpc_metadata_batch_add_tail(&op->data.metadata, &calld->content_type, + GRPC_MDELEM_REF(channeld->content_type)); + grpc_metadata_batch_add_tail(&op->data.metadata, &calld->user_agent, + GRPC_MDELEM_REF(channeld->user_agent)); + break; + } + } + + if (op->recv_ops && !calld->got_initial_metadata) { + /* substitute our callback for the higher callback */ + calld->recv_ops = op->recv_ops; + calld->on_done_recv = op->on_done_recv; + op->on_done_recv = &calld->hc_on_recv; + } +} + +static void hc_start_transport_op(grpc_call_element *elem, + grpc_transport_stream_op *op) { + GRPC_CALL_LOG_OP(GPR_INFO, elem, op); + hc_mutate_op(elem, op); + grpc_call_next_op(elem, op); +} + +/* Constructor for call_data */ +static void init_call_elem(grpc_call_element *elem, + const void *server_transport_data, + grpc_transport_stream_op *initial_op) { + call_data *calld = elem->call_data; + calld->sent_initial_metadata = 0; + calld->got_initial_metadata = 0; + calld->on_done_recv = NULL; + grpc_iomgr_closure_init(&calld->hc_on_recv, hc_on_recv, elem); + if (initial_op) hc_mutate_op(elem, initial_op); +} + +/* Destructor for call_data */ +static void destroy_call_elem(grpc_call_element *elem) { + /* grab pointers to our data from the call element */ + call_data *calld = elem->call_data; + channel_data *channeld = elem->channel_data; + + ignore_unused(calld); + ignore_unused(channeld); +} + +static const char *scheme_from_args(const grpc_channel_args *args) { + unsigned i; + if (args != NULL) { + for (i = 0; i < args->num_args; ++i) { + if (args->args[i].type == GRPC_ARG_STRING && + strcmp(args->args[i].key, GRPC_ARG_HTTP2_SCHEME) == 0) { + return args->args[i].value.string; + } + } + } + return "http"; +} + +static grpc_mdstr *user_agent_from_args(grpc_mdctx *mdctx, + const grpc_channel_args *args) { + gpr_strvec v; + size_t i; + int is_first = 1; + char *tmp; + grpc_mdstr *result; + + gpr_strvec_init(&v); + + for (i = 0; args && i < args->num_args; i++) { + if (0 == strcmp(args->args[i].key, GRPC_ARG_PRIMARY_USER_AGENT_STRING)) { + if (args->args[i].type != GRPC_ARG_STRING) { + gpr_log(GPR_ERROR, "Channel argument '%s' should be a string", + GRPC_ARG_PRIMARY_USER_AGENT_STRING); + } else { + if (!is_first) gpr_strvec_add(&v, gpr_strdup(" ")); + is_first = 0; + gpr_strvec_add(&v, gpr_strdup(args->args[i].value.string)); + } + } + } + + gpr_asprintf(&tmp, "%sgrpc-c/%s (%s)", is_first ? "" : " ", + grpc_version_string(), GPR_PLATFORM_STRING); + is_first = 0; + gpr_strvec_add(&v, tmp); + + for (i = 0; args && i < args->num_args; i++) { + if (0 == strcmp(args->args[i].key, GRPC_ARG_SECONDARY_USER_AGENT_STRING)) { + if (args->args[i].type != GRPC_ARG_STRING) { + gpr_log(GPR_ERROR, "Channel argument '%s' should be a string", + GRPC_ARG_SECONDARY_USER_AGENT_STRING); + } else { + if (!is_first) gpr_strvec_add(&v, gpr_strdup(" ")); + is_first = 0; + gpr_strvec_add(&v, gpr_strdup(args->args[i].value.string)); + } + } + } + + tmp = gpr_strvec_flatten(&v, NULL); + gpr_strvec_destroy(&v); + result = grpc_mdstr_from_string(mdctx, tmp, 0); + gpr_free(tmp); + + return result; +} + +/* Constructor for channel_data */ +static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, + const grpc_channel_args *channel_args, + grpc_mdctx *mdctx, int is_first, int is_last) { + /* grab pointers to our data from the channel element */ + channel_data *channeld = elem->channel_data; + + /* The first and the last filters tend to be implemented differently to + handle the case that there's no 'next' filter to call on the up or down + path */ + GPR_ASSERT(!is_last); + + /* initialize members */ + channeld->te_trailers = grpc_mdelem_from_strings(mdctx, "te", "trailers"); + channeld->method = grpc_mdelem_from_strings(mdctx, ":method", "POST"); + channeld->scheme = grpc_mdelem_from_strings(mdctx, ":scheme", + scheme_from_args(channel_args)); + channeld->content_type = + grpc_mdelem_from_strings(mdctx, "content-type", "application/grpc"); + channeld->status = grpc_mdelem_from_strings(mdctx, ":status", "200"); + channeld->user_agent = grpc_mdelem_from_metadata_strings( + mdctx, grpc_mdstr_from_string(mdctx, "user-agent", 0), + user_agent_from_args(mdctx, channel_args)); +} + +/* Destructor for channel data */ +static void destroy_channel_elem(grpc_channel_element *elem) { + /* grab pointers to our data from the channel element */ + channel_data *channeld = elem->channel_data; + + GRPC_MDELEM_UNREF(channeld->te_trailers); + GRPC_MDELEM_UNREF(channeld->method); + GRPC_MDELEM_UNREF(channeld->scheme); + GRPC_MDELEM_UNREF(channeld->content_type); + GRPC_MDELEM_UNREF(channeld->status); + GRPC_MDELEM_UNREF(channeld->user_agent); +} + +const grpc_channel_filter grpc_http_client_filter = { + hc_start_transport_op, grpc_channel_next_op, sizeof(call_data), + init_call_elem, destroy_call_elem, sizeof(channel_data), + init_channel_elem, destroy_channel_elem, grpc_call_next_get_peer, + "http-client"}; diff --git a/src/core/channel/http_client_filter.h b/src/core/channel/http_client_filter.h new file mode 100644 index 00000000..21c66b9b --- /dev/null +++ b/src/core/channel/http_client_filter.h @@ -0,0 +1,44 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_CHANNEL_HTTP_CLIENT_FILTER_H +#define GRPC_INTERNAL_CORE_CHANNEL_HTTP_CLIENT_FILTER_H + +#include "src/core/channel/channel_stack.h" + +/* Processes metadata on the client side for HTTP2 transports */ +extern const grpc_channel_filter grpc_http_client_filter; + +#define GRPC_ARG_HTTP2_SCHEME "grpc.http2_scheme" + +#endif /* GRPC_INTERNAL_CORE_CHANNEL_HTTP_CLIENT_FILTER_H */ diff --git a/src/core/channel/http_server_filter.c b/src/core/channel/http_server_filter.c new file mode 100644 index 00000000..2f061946 --- /dev/null +++ b/src/core/channel/http_server_filter.c @@ -0,0 +1,295 @@ +/* + * + * Copyright 2015, 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 "src/core/channel/http_server_filter.h" + +#include +#include +#include + +typedef struct call_data { + gpr_uint8 got_initial_metadata; + gpr_uint8 seen_path; + gpr_uint8 seen_post; + gpr_uint8 sent_status; + gpr_uint8 seen_scheme; + gpr_uint8 seen_te_trailers; + gpr_uint8 seen_authority; + grpc_linked_mdelem status; + grpc_linked_mdelem content_type; + + grpc_stream_op_buffer *recv_ops; + /** Closure to call when finished with the hs_on_recv hook */ + grpc_iomgr_closure *on_done_recv; + /** Receive closures are chained: we inject this closure as the on_done_recv + up-call on transport_op, and remember to call our on_done_recv member + after handling it. */ + grpc_iomgr_closure hs_on_recv; +} call_data; + +typedef struct channel_data { + grpc_mdelem *te_trailers; + grpc_mdelem *method_post; + grpc_mdelem *http_scheme; + grpc_mdelem *https_scheme; + /* TODO(klempner): Remove this once we stop using it */ + grpc_mdelem *grpc_scheme; + grpc_mdelem *content_type; + grpc_mdelem *status_ok; + grpc_mdelem *status_not_found; + grpc_mdstr *path_key; + grpc_mdstr *authority_key; + grpc_mdstr *host_key; + + grpc_mdctx *mdctx; +} channel_data; + +static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) { + grpc_call_element *elem = user_data; + channel_data *channeld = elem->channel_data; + call_data *calld = elem->call_data; + + /* Check if it is one of the headers we care about. */ + if (md == channeld->te_trailers || md == channeld->method_post || + md == channeld->http_scheme || md == channeld->https_scheme || + md == channeld->grpc_scheme || md == channeld->content_type) { + /* swallow it */ + if (md == channeld->method_post) { + calld->seen_post = 1; + } else if (md->key == channeld->http_scheme->key) { + calld->seen_scheme = 1; + } else if (md == channeld->te_trailers) { + calld->seen_te_trailers = 1; + } + /* TODO(klempner): Track that we've seen all the headers we should + require */ + return NULL; + } else if (md->key == channeld->content_type->key) { + if (strncmp(grpc_mdstr_as_c_string(md->value), "application/grpc+", 17) == + 0) { + /* Although the C implementation doesn't (currently) generate them, + any custom +-suffix is explicitly valid. */ + /* TODO(klempner): We should consider preallocating common values such + as +proto or +json, or at least stashing them if we see them. */ + /* TODO(klempner): Should we be surfacing this to application code? */ + } else { + /* TODO(klempner): We're currently allowing this, but we shouldn't + see it without a proxy so log for now. */ + gpr_log(GPR_INFO, "Unexpected content-type %s", + channeld->content_type->key); + } + return NULL; + } else if (md->key == channeld->te_trailers->key || + md->key == channeld->method_post->key || + md->key == channeld->http_scheme->key || + md->key == channeld->content_type->key) { + gpr_log(GPR_ERROR, "Invalid %s: header: '%s'", + grpc_mdstr_as_c_string(md->key), grpc_mdstr_as_c_string(md->value)); + /* swallow it and error everything out. */ + /* TODO(klempner): We ought to generate more descriptive error messages + on the wire here. */ + grpc_call_element_send_cancel(elem); + return NULL; + } else if (md->key == channeld->path_key) { + if (calld->seen_path) { + gpr_log(GPR_ERROR, "Received :path twice"); + return NULL; + } + calld->seen_path = 1; + return md; + } else if (md->key == channeld->authority_key) { + calld->seen_authority = 1; + return md; + } else if (md->key == channeld->host_key) { + /* translate host to :authority since :authority may be + omitted */ + grpc_mdelem *authority = grpc_mdelem_from_metadata_strings( + channeld->mdctx, GRPC_MDSTR_REF(channeld->authority_key), + GRPC_MDSTR_REF(md->value)); + GRPC_MDELEM_UNREF(md); + calld->seen_authority = 1; + return authority; + } else { + return md; + } +} + +static void hs_on_recv(void *user_data, int success) { + grpc_call_element *elem = user_data; + call_data *calld = elem->call_data; + if (success) { + size_t i; + size_t nops = calld->recv_ops->nops; + grpc_stream_op *ops = calld->recv_ops->ops; + for (i = 0; i < nops; i++) { + grpc_stream_op *op = &ops[i]; + if (op->type != GRPC_OP_METADATA) continue; + calld->got_initial_metadata = 1; + grpc_metadata_batch_filter(&op->data.metadata, server_filter, elem); + /* Have we seen the required http2 transport headers? + (:method, :scheme, content-type, with :path and :authority covered + at the channel level right now) */ + if (calld->seen_post && calld->seen_scheme && calld->seen_te_trailers && + calld->seen_path && calld->seen_authority) { + /* do nothing */ + } else { + if (!calld->seen_path) { + gpr_log(GPR_ERROR, "Missing :path header"); + } + if (!calld->seen_authority) { + gpr_log(GPR_ERROR, "Missing :authority header"); + } + if (!calld->seen_post) { + gpr_log(GPR_ERROR, "Missing :method header"); + } + if (!calld->seen_scheme) { + gpr_log(GPR_ERROR, "Missing :scheme header"); + } + if (!calld->seen_te_trailers) { + gpr_log(GPR_ERROR, "Missing te trailers header"); + } + /* Error this call out */ + success = 0; + grpc_call_element_send_cancel(elem); + } + } + } + calld->on_done_recv->cb(calld->on_done_recv->cb_arg, success); +} + +static void hs_mutate_op(grpc_call_element *elem, + grpc_transport_stream_op *op) { + /* grab pointers to our data from the call element */ + call_data *calld = elem->call_data; + channel_data *channeld = elem->channel_data; + size_t i; + + if (op->send_ops && !calld->sent_status) { + size_t nops = op->send_ops->nops; + grpc_stream_op *ops = op->send_ops->ops; + for (i = 0; i < nops; i++) { + grpc_stream_op *op = &ops[i]; + if (op->type != GRPC_OP_METADATA) continue; + calld->sent_status = 1; + grpc_metadata_batch_add_head(&op->data.metadata, &calld->status, + GRPC_MDELEM_REF(channeld->status_ok)); + grpc_metadata_batch_add_tail(&op->data.metadata, &calld->content_type, + GRPC_MDELEM_REF(channeld->content_type)); + break; + } + } + + if (op->recv_ops && !calld->got_initial_metadata) { + /* substitute our callback for the higher callback */ + calld->recv_ops = op->recv_ops; + calld->on_done_recv = op->on_done_recv; + op->on_done_recv = &calld->hs_on_recv; + } +} + +static void hs_start_transport_op(grpc_call_element *elem, + grpc_transport_stream_op *op) { + GRPC_CALL_LOG_OP(GPR_INFO, elem, op); + hs_mutate_op(elem, op); + grpc_call_next_op(elem, op); +} + +/* Constructor for call_data */ +static void init_call_elem(grpc_call_element *elem, + const void *server_transport_data, + grpc_transport_stream_op *initial_op) { + /* grab pointers to our data from the call element */ + call_data *calld = elem->call_data; + /* initialize members */ + memset(calld, 0, sizeof(*calld)); + grpc_iomgr_closure_init(&calld->hs_on_recv, hs_on_recv, elem); + if (initial_op) hs_mutate_op(elem, initial_op); +} + +/* Destructor for call_data */ +static void destroy_call_elem(grpc_call_element *elem) {} + +/* Constructor for channel_data */ +static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, + const grpc_channel_args *args, grpc_mdctx *mdctx, + int is_first, int is_last) { + /* grab pointers to our data from the channel element */ + channel_data *channeld = elem->channel_data; + + /* The first and the last filters tend to be implemented differently to + handle the case that there's no 'next' filter to call on the up or down + path */ + GPR_ASSERT(!is_first); + GPR_ASSERT(!is_last); + + /* initialize members */ + channeld->te_trailers = grpc_mdelem_from_strings(mdctx, "te", "trailers"); + channeld->status_ok = grpc_mdelem_from_strings(mdctx, ":status", "200"); + channeld->status_not_found = + grpc_mdelem_from_strings(mdctx, ":status", "404"); + channeld->method_post = grpc_mdelem_from_strings(mdctx, ":method", "POST"); + channeld->http_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "http"); + channeld->https_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "https"); + channeld->grpc_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "grpc"); + channeld->path_key = grpc_mdstr_from_string(mdctx, ":path", 0); + channeld->authority_key = grpc_mdstr_from_string(mdctx, ":authority", 0); + channeld->host_key = grpc_mdstr_from_string(mdctx, "host", 0); + channeld->content_type = + grpc_mdelem_from_strings(mdctx, "content-type", "application/grpc"); + + channeld->mdctx = mdctx; +} + +/* Destructor for channel data */ +static void destroy_channel_elem(grpc_channel_element *elem) { + /* grab pointers to our data from the channel element */ + channel_data *channeld = elem->channel_data; + + GRPC_MDELEM_UNREF(channeld->te_trailers); + GRPC_MDELEM_UNREF(channeld->status_ok); + GRPC_MDELEM_UNREF(channeld->status_not_found); + GRPC_MDELEM_UNREF(channeld->method_post); + GRPC_MDELEM_UNREF(channeld->http_scheme); + GRPC_MDELEM_UNREF(channeld->https_scheme); + GRPC_MDELEM_UNREF(channeld->grpc_scheme); + GRPC_MDELEM_UNREF(channeld->content_type); + GRPC_MDSTR_UNREF(channeld->path_key); + GRPC_MDSTR_UNREF(channeld->authority_key); + GRPC_MDSTR_UNREF(channeld->host_key); +} + +const grpc_channel_filter grpc_http_server_filter = { + hs_start_transport_op, grpc_channel_next_op, sizeof(call_data), + init_call_elem, destroy_call_elem, sizeof(channel_data), + init_channel_elem, destroy_channel_elem, grpc_call_next_get_peer, + "http-server"}; diff --git a/src/core/channel/http_server_filter.h b/src/core/channel/http_server_filter.h new file mode 100644 index 00000000..f219d4e6 --- /dev/null +++ b/src/core/channel/http_server_filter.h @@ -0,0 +1,42 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_CHANNEL_HTTP_SERVER_FILTER_H +#define GRPC_INTERNAL_CORE_CHANNEL_HTTP_SERVER_FILTER_H + +#include "src/core/channel/channel_stack.h" + +/* Processes metadata on the client side for HTTP2 transports */ +extern const grpc_channel_filter grpc_http_server_filter; + +#endif /* GRPC_INTERNAL_CORE_CHANNEL_HTTP_SERVER_FILTER_H */ diff --git a/src/core/channel/noop_filter.c b/src/core/channel/noop_filter.c new file mode 100644 index 00000000..d631885a --- /dev/null +++ b/src/core/channel/noop_filter.c @@ -0,0 +1,131 @@ +/* + * + * Copyright 2015, 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 "src/core/channel/noop_filter.h" +#include + +typedef struct call_data { + int unused; /* C89 requires at least one struct element */ +} call_data; + +typedef struct channel_data { + int unused; /* C89 requires at least one struct element */ +} channel_data; + +/* used to silence 'variable not used' warnings */ +static void ignore_unused(void *ignored) {} + +static void noop_mutate_op(grpc_call_element *elem, + grpc_transport_stream_op *op) { + /* grab pointers to our data from the call element */ + call_data *calld = elem->call_data; + channel_data *channeld = elem->channel_data; + + ignore_unused(calld); + ignore_unused(channeld); + + /* do nothing */ +} + +/* Called either: + - in response to an API call (or similar) from above, to send something + - a network event (or similar) from below, to receive something + op contains type and call direction information, in addition to the data + that is being sent or received. */ +static void noop_start_transport_stream_op(grpc_call_element *elem, + grpc_transport_stream_op *op) { + noop_mutate_op(elem, op); + + /* pass control down the stack */ + grpc_call_next_op(elem, op); +} + +/* Constructor for call_data */ +static void init_call_elem(grpc_call_element *elem, + const void *server_transport_data, + grpc_transport_stream_op *initial_op) { + /* grab pointers to our data from the call element */ + call_data *calld = elem->call_data; + channel_data *channeld = elem->channel_data; + + /* initialize members */ + calld->unused = channeld->unused; + + if (initial_op) noop_mutate_op(elem, initial_op); +} + +/* Destructor for call_data */ +static void destroy_call_elem(grpc_call_element *elem) { + /* grab pointers to our data from the call element */ + call_data *calld = elem->call_data; + channel_data *channeld = elem->channel_data; + + ignore_unused(calld); + ignore_unused(channeld); +} + +/* Constructor for channel_data */ +static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, + const grpc_channel_args *args, grpc_mdctx *mdctx, + int is_first, int is_last) { + /* grab pointers to our data from the channel element */ + channel_data *channeld = elem->channel_data; + + /* The first and the last filters tend to be implemented differently to + handle the case that there's no 'next' filter to call on the up or down + path */ + GPR_ASSERT(!is_first); + GPR_ASSERT(!is_last); + + /* initialize members */ + channeld->unused = 0; +} + +/* Destructor for channel data */ +static void destroy_channel_elem(grpc_channel_element *elem) { + /* grab pointers to our data from the channel element */ + channel_data *channeld = elem->channel_data; + + ignore_unused(channeld); +} + +const grpc_channel_filter grpc_no_op_filter = {noop_start_transport_stream_op, + grpc_channel_next_op, + sizeof(call_data), + init_call_elem, + destroy_call_elem, + sizeof(channel_data), + init_channel_elem, + destroy_channel_elem, + grpc_call_next_get_peer, + "no-op"}; diff --git a/src/core/channel/noop_filter.h b/src/core/channel/noop_filter.h new file mode 100644 index 00000000..ded9b331 --- /dev/null +++ b/src/core/channel/noop_filter.h @@ -0,0 +1,44 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_CHANNEL_NOOP_FILTER_H +#define GRPC_INTERNAL_CORE_CHANNEL_NOOP_FILTER_H + +#include "src/core/channel/channel_stack.h" + +/* No-op filter: simply takes everything it's given, and passes it on to the + next filter. Exists simply as a starting point that others can take and + customize for their own filters */ +extern const grpc_channel_filter grpc_no_op_filter; + +#endif /* GRPC_INTERNAL_CORE_CHANNEL_NOOP_FILTER_H */ diff --git a/src/core/client_config/README.md b/src/core/client_config/README.md new file mode 100644 index 00000000..fff7a5af --- /dev/null +++ b/src/core/client_config/README.md @@ -0,0 +1,66 @@ +Client Configuration Support for GRPC +===================================== + +This library provides high level configuration machinery to construct client +channels and load balance between them. + +Each grpc_channel is created with a grpc_resolver. It is the resolver's duty +to resolve a name into configuration data for the channel. Such configuration +data might include: + +- a list of (ip, port) addresses to connect to +- a load balancing policy to decide which server to send a request to +- a set of filters to mutate outgoing requests (say, by adding metadata) + +The resolver provides this data as a stream of grpc_client_config objects to +the channel. We represent configuration as a stream so that it can be changed +by the resolver during execution, by reacting to external events (such as a +new configuration file being pushed to some store). + + +Load Balancing +-------------- + +Load balancing configuration is provided by a grpc_lb_policy object, stored as +part of grpc_client_config. + +The primary job of the load balancing policies is to pick a target server given only the +initial metadata for a request. It does this by providing a grpc_subchannel +object to the owning channel. + + +Sub-Channels +------------ + +A sub-channel provides a connection to a server for a client channel. It has a +connectivity state like a regular channel, and so can be connected or +disconnected. This connectivity state can be used to inform load balancing +decisions (for example, by avoiding disconnected backends). + +Configured sub-channels are fully setup to participate in the grpc data plane. +Their behavior is specified by a set of grpc channel filters defined at their +construction. To customize this behavior, resolvers build +grpc_subchannel_factory objects, which use the decorator pattern to customize +construction arguments for concrete grpc_subchannel instances. + + +Naming for GRPC +=============== + +Names in GRPC are represented by a URI (as defined in +[RFC 3986](https://tools.ietf.org/html/rfc3986)). + +The following schemes are currently supported: + +dns:///host:port - dns schemes are currently supported so long as authority is + empty (authority based dns resolution is expected in a future + release) + +unix:path - the unix scheme is used to create and connect to unix domain + sockets - the authority must be empty, and the path + represents the absolute or relative path to the desired + socket + +ipv4:host:port - a pre-resolved ipv4 dotted decimal address/port combination + +ipv6:[host]:port - a pre-resolved ipv6 address/port combination diff --git a/src/core/client_config/client_config.c b/src/core/client_config/client_config.c new file mode 100644 index 00000000..44538241 --- /dev/null +++ b/src/core/client_config/client_config.c @@ -0,0 +1,74 @@ +/* + * + * Copyright 2015, 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 "src/core/client_config/client_config.h" + +#include + +#include + +struct grpc_client_config { + gpr_refcount refs; + grpc_lb_policy *lb_policy; +}; + +grpc_client_config *grpc_client_config_create() { + grpc_client_config *c = gpr_malloc(sizeof(*c)); + memset(c, 0, sizeof(*c)); + gpr_ref_init(&c->refs, 1); + return c; +} + +void grpc_client_config_ref(grpc_client_config *c) { gpr_ref(&c->refs); } + +void grpc_client_config_unref(grpc_client_config *c) { + if (gpr_unref(&c->refs)) { + GRPC_LB_POLICY_UNREF(c->lb_policy, "client_config"); + gpr_free(c); + } +} + +void grpc_client_config_set_lb_policy(grpc_client_config *c, + grpc_lb_policy *lb_policy) { + if (lb_policy) { + GRPC_LB_POLICY_REF(lb_policy, "client_config"); + } + if (c->lb_policy) { + GRPC_LB_POLICY_UNREF(c->lb_policy, "client_config"); + } + c->lb_policy = lb_policy; +} + +grpc_lb_policy *grpc_client_config_get_lb_policy(grpc_client_config *c) { + return c->lb_policy; +} diff --git a/src/core/client_config/client_config.h b/src/core/client_config/client_config.h new file mode 100644 index 00000000..47612da4 --- /dev/null +++ b/src/core/client_config/client_config.h @@ -0,0 +1,52 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_CLIENT_CONFIG_CLIENT_CONFIG_H +#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_CLIENT_CONFIG_H + +#include "src/core/client_config/lb_policy.h" + +/** Total configuration for a client. Provided, and updated, by + grpc_resolver */ +typedef struct grpc_client_config grpc_client_config; + +grpc_client_config *grpc_client_config_create(); +void grpc_client_config_ref(grpc_client_config *client_config); +void grpc_client_config_unref(grpc_client_config *client_config); + +void grpc_client_config_set_lb_policy(grpc_client_config *client_config, + grpc_lb_policy *lb_policy); +grpc_lb_policy *grpc_client_config_get_lb_policy( + grpc_client_config *client_config); + +#endif /* GRPC_INTERNAL_CORE_CLIENT_CONFIG_CLIENT_CONFIG_H */ diff --git a/src/core/client_config/connector.c b/src/core/client_config/connector.c new file mode 100644 index 00000000..a8cd5fc1 --- /dev/null +++ b/src/core/client_config/connector.c @@ -0,0 +1,49 @@ +/* + * + * Copyright 2015, 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 "src/core/client_config/connector.h" + +void grpc_connector_ref(grpc_connector *connector) { + connector->vtable->ref(connector); +} + +void grpc_connector_unref(grpc_connector *connector) { + connector->vtable->unref(connector); +} + +void grpc_connector_connect(grpc_connector *connector, + const grpc_connect_in_args *in_args, + grpc_connect_out_args *out_args, + grpc_iomgr_closure *notify) { + connector->vtable->connect(connector, in_args, out_args, notify); +} diff --git a/src/core/client_config/connector.h b/src/core/client_config/connector.h new file mode 100644 index 00000000..edcb10a3 --- /dev/null +++ b/src/core/client_config/connector.h @@ -0,0 +1,85 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_CLIENT_CONFIG_CONNECTOR_H +#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_CONNECTOR_H + +#include "src/core/channel/channel_stack.h" +#include "src/core/iomgr/sockaddr.h" +#include "src/core/transport/transport.h" + +typedef struct grpc_connector grpc_connector; +typedef struct grpc_connector_vtable grpc_connector_vtable; + +struct grpc_connector { + const grpc_connector_vtable *vtable; +}; + +typedef struct { + /** set of pollsets interested in this connection */ + grpc_pollset_set *interested_parties; + /** address to connect to */ + const struct sockaddr *addr; + int addr_len; + /** deadline for connection */ + gpr_timespec deadline; + /** channel arguments (to be passed to transport) */ + const grpc_channel_args *channel_args; + /** metadata context */ + grpc_mdctx *metadata_context; +} grpc_connect_in_args; + +typedef struct { + /** the connected transport */ + grpc_transport *transport; + /** any additional filters (owned by the caller of connect) */ + const grpc_channel_filter **filters; + size_t num_filters; +} grpc_connect_out_args; + +struct grpc_connector_vtable { + void (*ref)(grpc_connector *connector); + void (*unref)(grpc_connector *connector); + void (*connect)(grpc_connector *connector, + const grpc_connect_in_args *in_args, + grpc_connect_out_args *out_args, grpc_iomgr_closure *notify); +}; + +void grpc_connector_ref(grpc_connector *connector); +void grpc_connector_unref(grpc_connector *connector); +void grpc_connector_connect(grpc_connector *connector, + const grpc_connect_in_args *in_args, + grpc_connect_out_args *out_args, + grpc_iomgr_closure *notify); + +#endif diff --git a/src/core/client_config/lb_policies/pick_first.c b/src/core/client_config/lb_policies/pick_first.c new file mode 100644 index 00000000..5ae2e0ea --- /dev/null +++ b/src/core/client_config/lb_policies/pick_first.c @@ -0,0 +1,332 @@ +/* + * + * Copyright 2015, 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 "src/core/client_config/lb_policies/pick_first.h" + +#include + +#include +#include "src/core/transport/connectivity_state.h" + +typedef struct pending_pick { + struct pending_pick *next; + grpc_pollset *pollset; + grpc_subchannel **target; + grpc_iomgr_closure *on_complete; +} pending_pick; + +typedef struct { + /** base policy: must be first */ + grpc_lb_policy base; + /** all our subchannels */ + grpc_subchannel **subchannels; + size_t num_subchannels; + + grpc_iomgr_closure connectivity_changed; + + /** mutex protecting remaining members */ + gpr_mu mu; + /** the selected channel + TODO(ctiller): this should be atomically set so we don't + need to take a mutex in the common case */ + grpc_subchannel *selected; + /** have we started picking? */ + int started_picking; + /** are we shut down? */ + int shutdown; + /** which subchannel are we watching? */ + size_t checking_subchannel; + /** what is the connectivity of that channel? */ + grpc_connectivity_state checking_connectivity; + /** list of picks that are waiting on connectivity */ + pending_pick *pending_picks; + + /** our connectivity state tracker */ + grpc_connectivity_state_tracker state_tracker; +} pick_first_lb_policy; + +static void del_interested_parties_locked(pick_first_lb_policy *p) { + pending_pick *pp; + for (pp = p->pending_picks; pp; pp = pp->next) { + grpc_subchannel_del_interested_party(p->subchannels[p->checking_subchannel], + pp->pollset); + } +} + +static void add_interested_parties_locked(pick_first_lb_policy *p) { + pending_pick *pp; + for (pp = p->pending_picks; pp; pp = pp->next) { + grpc_subchannel_add_interested_party(p->subchannels[p->checking_subchannel], + pp->pollset); + } +} + +void pf_destroy(grpc_lb_policy *pol) { + pick_first_lb_policy *p = (pick_first_lb_policy *)pol; + size_t i; + del_interested_parties_locked(p); + for (i = 0; i < p->num_subchannels; i++) { + GRPC_SUBCHANNEL_UNREF(p->subchannels[i], "pick_first"); + } + grpc_connectivity_state_destroy(&p->state_tracker); + gpr_free(p->subchannels); + gpr_mu_destroy(&p->mu); + gpr_free(p); +} + +void pf_shutdown(grpc_lb_policy *pol) { + pick_first_lb_policy *p = (pick_first_lb_policy *)pol; + pending_pick *pp; + gpr_mu_lock(&p->mu); + del_interested_parties_locked(p); + p->shutdown = 1; + while ((pp = p->pending_picks)) { + p->pending_picks = pp->next; + *pp->target = NULL; + grpc_iomgr_add_delayed_callback(pp->on_complete, 0); + gpr_free(pp); + } + grpc_connectivity_state_set(&p->state_tracker, GRPC_CHANNEL_FATAL_FAILURE, + "shutdown"); + gpr_mu_unlock(&p->mu); +} + +static void start_picking(pick_first_lb_policy *p) { + p->started_picking = 1; + p->checking_subchannel = 0; + p->checking_connectivity = GRPC_CHANNEL_IDLE; + GRPC_LB_POLICY_REF(&p->base, "pick_first_connectivity"); + grpc_subchannel_notify_on_state_change(p->subchannels[p->checking_subchannel], + &p->checking_connectivity, + &p->connectivity_changed); +} + +void pf_exit_idle(grpc_lb_policy *pol) { + pick_first_lb_policy *p = (pick_first_lb_policy *)pol; + gpr_mu_lock(&p->mu); + if (!p->started_picking) { + start_picking(p); + } + gpr_mu_unlock(&p->mu); +} + +void pf_pick(grpc_lb_policy *pol, grpc_pollset *pollset, + grpc_metadata_batch *initial_metadata, grpc_subchannel **target, + grpc_iomgr_closure *on_complete) { + pick_first_lb_policy *p = (pick_first_lb_policy *)pol; + pending_pick *pp; + gpr_mu_lock(&p->mu); + if (p->selected) { + gpr_mu_unlock(&p->mu); + *target = p->selected; + on_complete->cb(on_complete->cb_arg, 1); + } else { + if (!p->started_picking) { + start_picking(p); + } + grpc_subchannel_add_interested_party(p->subchannels[p->checking_subchannel], + pollset); + pp = gpr_malloc(sizeof(*pp)); + pp->next = p->pending_picks; + pp->pollset = pollset; + pp->target = target; + pp->on_complete = on_complete; + p->pending_picks = pp; + gpr_mu_unlock(&p->mu); + } +} + +static void pf_connectivity_changed(void *arg, int iomgr_success) { + pick_first_lb_policy *p = arg; + pending_pick *pp; + int unref = 0; + + gpr_mu_lock(&p->mu); + + if (p->shutdown) { + unref = 1; + } else if (p->selected != NULL) { + grpc_connectivity_state_set(&p->state_tracker, p->checking_connectivity, + "selected_changed"); + if (p->checking_connectivity != GRPC_CHANNEL_FATAL_FAILURE) { + grpc_subchannel_notify_on_state_change( + p->selected, &p->checking_connectivity, &p->connectivity_changed); + } else { + unref = 1; + } + } else { + loop: + switch (p->checking_connectivity) { + case GRPC_CHANNEL_READY: + grpc_connectivity_state_set(&p->state_tracker, GRPC_CHANNEL_READY, + "connecting_ready"); + p->selected = p->subchannels[p->checking_subchannel]; + while ((pp = p->pending_picks)) { + p->pending_picks = pp->next; + *pp->target = p->selected; + grpc_subchannel_del_interested_party(p->selected, pp->pollset); + grpc_iomgr_add_delayed_callback(pp->on_complete, 1); + gpr_free(pp); + } + grpc_subchannel_notify_on_state_change( + p->selected, &p->checking_connectivity, &p->connectivity_changed); + break; + case GRPC_CHANNEL_TRANSIENT_FAILURE: + grpc_connectivity_state_set(&p->state_tracker, + GRPC_CHANNEL_TRANSIENT_FAILURE, + "connecting_transient_failure"); + del_interested_parties_locked(p); + p->checking_subchannel = + (p->checking_subchannel + 1) % p->num_subchannels; + p->checking_connectivity = grpc_subchannel_check_connectivity( + p->subchannels[p->checking_subchannel]); + add_interested_parties_locked(p); + if (p->checking_connectivity == GRPC_CHANNEL_TRANSIENT_FAILURE) { + grpc_subchannel_notify_on_state_change( + p->subchannels[p->checking_subchannel], &p->checking_connectivity, + &p->connectivity_changed); + } else { + goto loop; + } + break; + case GRPC_CHANNEL_CONNECTING: + case GRPC_CHANNEL_IDLE: + grpc_connectivity_state_set(&p->state_tracker, p->checking_connectivity, + "connecting_changed"); + grpc_subchannel_notify_on_state_change( + p->subchannels[p->checking_subchannel], &p->checking_connectivity, + &p->connectivity_changed); + break; + case GRPC_CHANNEL_FATAL_FAILURE: + del_interested_parties_locked(p); + GPR_SWAP(grpc_subchannel *, p->subchannels[p->checking_subchannel], + p->subchannels[p->num_subchannels - 1]); + p->num_subchannels--; + GRPC_SUBCHANNEL_UNREF(p->subchannels[p->num_subchannels], "pick_first"); + if (p->num_subchannels == 0) { + grpc_connectivity_state_set(&p->state_tracker, + GRPC_CHANNEL_FATAL_FAILURE, + "no_more_channels"); + while ((pp = p->pending_picks)) { + p->pending_picks = pp->next; + *pp->target = NULL; + grpc_iomgr_add_delayed_callback(pp->on_complete, 1); + gpr_free(pp); + } + unref = 1; + } else { + grpc_connectivity_state_set(&p->state_tracker, + GRPC_CHANNEL_TRANSIENT_FAILURE, + "subchannel_failed"); + p->checking_subchannel %= p->num_subchannels; + p->checking_connectivity = grpc_subchannel_check_connectivity( + p->subchannels[p->checking_subchannel]); + add_interested_parties_locked(p); + goto loop; + } + } + } + + gpr_mu_unlock(&p->mu); + + if (unref) { + GRPC_LB_POLICY_UNREF(&p->base, "pick_first_connectivity"); + } +} + +static void pf_broadcast(grpc_lb_policy *pol, grpc_transport_op *op) { + pick_first_lb_policy *p = (pick_first_lb_policy *)pol; + size_t i; + size_t n; + grpc_subchannel **subchannels; + + gpr_mu_lock(&p->mu); + n = p->num_subchannels; + subchannels = gpr_malloc(n * sizeof(*subchannels)); + for (i = 0; i < n; i++) { + subchannels[i] = p->subchannels[i]; + GRPC_SUBCHANNEL_REF(subchannels[i], "pf_broadcast"); + } + gpr_mu_unlock(&p->mu); + + for (i = 0; i < n; i++) { + grpc_subchannel_process_transport_op(subchannels[i], op); + GRPC_SUBCHANNEL_UNREF(subchannels[i], "pf_broadcast"); + } + gpr_free(subchannels); +} + +static grpc_connectivity_state pf_check_connectivity(grpc_lb_policy *pol) { + pick_first_lb_policy *p = (pick_first_lb_policy *)pol; + grpc_connectivity_state st; + gpr_mu_lock(&p->mu); + st = grpc_connectivity_state_check(&p->state_tracker); + gpr_mu_unlock(&p->mu); + return st; +} + +static void pf_notify_on_state_change(grpc_lb_policy *pol, + grpc_connectivity_state *current, + grpc_iomgr_closure *notify) { + pick_first_lb_policy *p = (pick_first_lb_policy *)pol; + gpr_mu_lock(&p->mu); + grpc_connectivity_state_notify_on_state_change(&p->state_tracker, current, + notify); + gpr_mu_unlock(&p->mu); +} + +static const grpc_lb_policy_vtable pick_first_lb_policy_vtable = { + pf_destroy, + pf_shutdown, + pf_pick, + pf_exit_idle, + pf_broadcast, + pf_check_connectivity, + pf_notify_on_state_change}; + +grpc_lb_policy *grpc_create_pick_first_lb_policy(grpc_subchannel **subchannels, + size_t num_subchannels) { + pick_first_lb_policy *p = gpr_malloc(sizeof(*p)); + GPR_ASSERT(num_subchannels); + memset(p, 0, sizeof(*p)); + grpc_lb_policy_init(&p->base, &pick_first_lb_policy_vtable); + p->subchannels = gpr_malloc(sizeof(grpc_subchannel *) * num_subchannels); + p->num_subchannels = num_subchannels; + grpc_connectivity_state_init(&p->state_tracker, GRPC_CHANNEL_IDLE, + "pick_first"); + memcpy(p->subchannels, subchannels, + sizeof(grpc_subchannel *) * num_subchannels); + grpc_iomgr_closure_init(&p->connectivity_changed, pf_connectivity_changed, p); + gpr_mu_init(&p->mu); + return &p->base; +} diff --git a/src/core/client_config/lb_policies/pick_first.h b/src/core/client_config/lb_policies/pick_first.h new file mode 100644 index 00000000..31394985 --- /dev/null +++ b/src/core/client_config/lb_policies/pick_first.h @@ -0,0 +1,44 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_CLIENT_CONFIG_PICK_FIRST_H +#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_PICK_FIRST_H + +#include "src/core/client_config/lb_policy.h" + +/** Returns a load balancing policy instance that picks up the first subchannel + * from \a subchannels to succesfully connect */ +grpc_lb_policy *grpc_create_pick_first_lb_policy(grpc_subchannel **subchannels, + size_t num_subchannels); + +#endif diff --git a/src/core/client_config/lb_policy.c b/src/core/client_config/lb_policy.c new file mode 100644 index 00000000..90ec4443 --- /dev/null +++ b/src/core/client_config/lb_policy.c @@ -0,0 +1,94 @@ +/* + * + * Copyright 2015, 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 "src/core/client_config/lb_policy.h" + +void grpc_lb_policy_init(grpc_lb_policy *policy, + const grpc_lb_policy_vtable *vtable) { + policy->vtable = vtable; + gpr_ref_init(&policy->refs, 1); +} + +#ifdef GRPC_LB_POLICY_REFCOUNT_DEBUG +void grpc_lb_policy_ref(grpc_lb_policy *policy, const char *file, int line, + const char *reason) { + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "LB_POLICY:%p ref %d -> %d %s", + policy, (int)policy->refs.count, (int)policy->refs.count + 1, reason); +#else +void grpc_lb_policy_ref(grpc_lb_policy *policy) { +#endif + gpr_ref(&policy->refs); +} + +#ifdef GRPC_LB_POLICY_REFCOUNT_DEBUG +void grpc_lb_policy_unref(grpc_lb_policy *policy, const char *file, int line, + const char *reason) { + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "LB_POLICY:%p unref %d -> %d %s", + policy, (int)policy->refs.count, (int)policy->refs.count - 1, reason); +#else +void grpc_lb_policy_unref(grpc_lb_policy *policy) { +#endif + if (gpr_unref(&policy->refs)) { + policy->vtable->destroy(policy); + } +} + +void grpc_lb_policy_shutdown(grpc_lb_policy *policy) { + policy->vtable->shutdown(policy); +} + +void grpc_lb_policy_pick(grpc_lb_policy *policy, grpc_pollset *pollset, + grpc_metadata_batch *initial_metadata, + grpc_subchannel **target, + grpc_iomgr_closure *on_complete) { + policy->vtable->pick(policy, pollset, initial_metadata, target, on_complete); +} + +void grpc_lb_policy_broadcast(grpc_lb_policy *policy, grpc_transport_op *op) { + policy->vtable->broadcast(policy, op); +} + +void grpc_lb_policy_exit_idle(grpc_lb_policy *policy) { + policy->vtable->exit_idle(policy); +} + +void grpc_lb_policy_notify_on_state_change(grpc_lb_policy *policy, + grpc_connectivity_state *state, + grpc_iomgr_closure *closure) { + policy->vtable->notify_on_state_change(policy, state, closure); +} + +grpc_connectivity_state grpc_lb_policy_check_connectivity( + grpc_lb_policy *policy) { + return policy->vtable->check_connectivity(policy); +} diff --git a/src/core/client_config/lb_policy.h b/src/core/client_config/lb_policy.h new file mode 100644 index 00000000..3f7ca8f2 --- /dev/null +++ b/src/core/client_config/lb_policy.h @@ -0,0 +1,121 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_CLIENT_CONFIG_LB_POLICY_H +#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_LB_POLICY_H + +#include "src/core/client_config/subchannel.h" + +/** A load balancing policy: specified by a vtable and a struct (which + is expected to be extended to contain some parameters) */ +typedef struct grpc_lb_policy grpc_lb_policy; +typedef struct grpc_lb_policy_vtable grpc_lb_policy_vtable; + +typedef void (*grpc_lb_completion)(void *cb_arg, grpc_subchannel *subchannel, + grpc_status_code status, const char *errmsg); + +struct grpc_lb_policy { + const grpc_lb_policy_vtable *vtable; + gpr_refcount refs; +}; + +struct grpc_lb_policy_vtable { + void (*destroy)(grpc_lb_policy *policy); + + void (*shutdown)(grpc_lb_policy *policy); + + /** implement grpc_lb_policy_pick */ + void (*pick)(grpc_lb_policy *policy, grpc_pollset *pollset, + grpc_metadata_batch *initial_metadata, grpc_subchannel **target, + grpc_iomgr_closure *on_complete); + + /** try to enter a READY connectivity state */ + void (*exit_idle)(grpc_lb_policy *policy); + + /** broadcast a transport op to all subchannels */ + void (*broadcast)(grpc_lb_policy *policy, grpc_transport_op *op); + + /** check the current connectivity of the lb_policy */ + grpc_connectivity_state (*check_connectivity)(grpc_lb_policy *policy); + + /** call notify when the connectivity state of a channel changes from *state. + Updates *state with the new state of the policy */ + void (*notify_on_state_change)(grpc_lb_policy *policy, + grpc_connectivity_state *state, + grpc_iomgr_closure *closure); +}; + +#ifdef GRPC_LB_POLICY_REFCOUNT_DEBUG +#define GRPC_LB_POLICY_REF(p, r) \ + grpc_lb_policy_ref((p), __FILE__, __LINE__, (r)) +#define GRPC_LB_POLICY_UNREF(p, r) \ + grpc_lb_policy_unref((p), __FILE__, __LINE__, (r)) +void grpc_lb_policy_ref(grpc_lb_policy *policy, const char *file, int line, + const char *reason); +void grpc_lb_policy_unref(grpc_lb_policy *policy, const char *file, int line, + const char *reason); +#else +#define GRPC_LB_POLICY_REF(p, r) grpc_lb_policy_ref((p)) +#define GRPC_LB_POLICY_UNREF(p, r) grpc_lb_policy_unref((p)) +void grpc_lb_policy_ref(grpc_lb_policy *policy); +void grpc_lb_policy_unref(grpc_lb_policy *policy); +#endif + +/** called by concrete implementations to initialize the base struct */ +void grpc_lb_policy_init(grpc_lb_policy *policy, + const grpc_lb_policy_vtable *vtable); + +/** Start shutting down (fail any pending picks) */ +void grpc_lb_policy_shutdown(grpc_lb_policy *policy); + +/** Given initial metadata in \a initial_metadata, find an appropriate + target for this rpc, and 'return' it by calling \a on_complete after setting + \a target. + Picking can be asynchronous. Any IO should be done under \a pollset. */ +void grpc_lb_policy_pick(grpc_lb_policy *policy, grpc_pollset *pollset, + grpc_metadata_batch *initial_metadata, + grpc_subchannel **target, + grpc_iomgr_closure *on_complete); + +void grpc_lb_policy_broadcast(grpc_lb_policy *policy, grpc_transport_op *op); + +void grpc_lb_policy_exit_idle(grpc_lb_policy *policy); + +void grpc_lb_policy_notify_on_state_change(grpc_lb_policy *policy, + grpc_connectivity_state *state, + grpc_iomgr_closure *closure); + +grpc_connectivity_state grpc_lb_policy_check_connectivity( + grpc_lb_policy *policy); + +#endif /* GRPC_INTERNAL_CORE_CONFIG_LB_POLICY_H */ diff --git a/src/core/client_config/resolver.c b/src/core/client_config/resolver.c new file mode 100644 index 00000000..91e42bb6 --- /dev/null +++ b/src/core/client_config/resolver.c @@ -0,0 +1,83 @@ +/* + * + * Copyright 2015, 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 "src/core/client_config/resolver.h" + +void grpc_resolver_init(grpc_resolver *resolver, + const grpc_resolver_vtable *vtable) { + resolver->vtable = vtable; + gpr_ref_init(&resolver->refs, 1); +} + +#ifdef GRPC_RESOLVER_REFCOUNT_DEBUG +void grpc_resolver_ref(grpc_resolver *resolver, const char *file, int line, + const char *reason) { + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "RESOLVER:%p ref %d -> %d %s", + resolver, (int)resolver->refs.count, (int)resolver->refs.count + 1, + reason); +#else +void grpc_resolver_ref(grpc_resolver *resolver) { +#endif + gpr_ref(&resolver->refs); +} + +#ifdef GRPC_RESOLVER_REFCOUNT_DEBUG +void grpc_resolver_unref(grpc_resolver *resolver, const char *file, int line, + const char *reason) { + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "RESOLVER:%p unref %d -> %d %s", + resolver, (int)resolver->refs.count, (int)resolver->refs.count - 1, + reason); +#else +void grpc_resolver_unref(grpc_resolver *resolver) { +#endif + if (gpr_unref(&resolver->refs)) { + resolver->vtable->destroy(resolver); + } +} + +void grpc_resolver_shutdown(grpc_resolver *resolver) { + resolver->vtable->shutdown(resolver); +} + +void grpc_resolver_channel_saw_error(grpc_resolver *resolver, + struct sockaddr *failing_address, + int failing_address_len) { + resolver->vtable->channel_saw_error(resolver, failing_address, + failing_address_len); +} + +void grpc_resolver_next(grpc_resolver *resolver, + grpc_client_config **target_config, + grpc_iomgr_closure *on_complete) { + resolver->vtable->next(resolver, target_config, on_complete); +} diff --git a/src/core/client_config/resolver.h b/src/core/client_config/resolver.h new file mode 100644 index 00000000..8ad87d78 --- /dev/null +++ b/src/core/client_config/resolver.h @@ -0,0 +1,97 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_CLIENT_CONFIG_RESOLVER_H +#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_RESOLVER_H + +#include "src/core/client_config/client_config.h" +#include "src/core/iomgr/iomgr.h" +#include "src/core/iomgr/sockaddr.h" + +typedef struct grpc_resolver grpc_resolver; +typedef struct grpc_resolver_vtable grpc_resolver_vtable; + +/** grpc_resolver provides grpc_client_config objects to grpc_channel + objects */ +struct grpc_resolver { + const grpc_resolver_vtable *vtable; + gpr_refcount refs; +}; + +struct grpc_resolver_vtable { + void (*destroy)(grpc_resolver *resolver); + void (*shutdown)(grpc_resolver *resolver); + void (*channel_saw_error)(grpc_resolver *resolver, + struct sockaddr *failing_address, + int failing_address_len); + void (*next)(grpc_resolver *resolver, grpc_client_config **target_config, + grpc_iomgr_closure *on_complete); +}; + +#ifdef GRPC_RESOLVER_REFCOUNT_DEBUG +#define GRPC_RESOLVER_REF(p, r) grpc_resolver_ref((p), __FILE__, __LINE__, (r)) +#define GRPC_RESOLVER_UNREF(p, r) \ + grpc_resolver_unref((p), __FILE__, __LINE__, (r)) +void grpc_resolver_ref(grpc_resolver *policy, const char *file, int line, + const char *reason); +void grpc_resolver_unref(grpc_resolver *policy, const char *file, int line, + const char *reason); +#else +#define GRPC_RESOLVER_REF(p, r) grpc_resolver_ref((p)) +#define GRPC_RESOLVER_UNREF(p, r) grpc_resolver_unref((p)) +void grpc_resolver_ref(grpc_resolver *policy); +void grpc_resolver_unref(grpc_resolver *policy); +#endif + +void grpc_resolver_init(grpc_resolver *resolver, + const grpc_resolver_vtable *vtable); + +void grpc_resolver_shutdown(grpc_resolver *resolver); + +/** Notification that the channel has seen an error on some address. + Can be used as a hint that re-resolution is desirable soon. */ +void grpc_resolver_channel_saw_error(grpc_resolver *resolver, + struct sockaddr *failing_address, + int failing_address_len); + +/** Get the next client config. Called by the channel to fetch a new + configuration. Expected to set *target_config with a new configuration, + and then schedule on_complete for execution. + + If resolution is fatally broken, set *target_config to NULL and + schedule on_complete. */ +void grpc_resolver_next(grpc_resolver *resolver, + grpc_client_config **target_config, + grpc_iomgr_closure *on_complete); + +#endif /* GRPC_INTERNAL_CORE_CONFIG_RESOLVER_H */ diff --git a/src/core/client_config/resolver_factory.c b/src/core/client_config/resolver_factory.c new file mode 100644 index 00000000..5b859a8d --- /dev/null +++ b/src/core/client_config/resolver_factory.c @@ -0,0 +1,56 @@ +/* + * + * Copyright 2015, 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 "src/core/client_config/resolver_factory.h" + +void grpc_resolver_factory_ref(grpc_resolver_factory *factory) { + factory->vtable->ref(factory); +} + +void grpc_resolver_factory_unref(grpc_resolver_factory *factory) { + factory->vtable->unref(factory); +} + +/** Create a resolver instance for a name */ +grpc_resolver *grpc_resolver_factory_create_resolver( + grpc_resolver_factory *factory, grpc_uri *uri, + grpc_subchannel_factory *subchannel_factory) { + if (factory == NULL) return NULL; + return factory->vtable->create_resolver(factory, uri, subchannel_factory); +} + +char *grpc_resolver_factory_get_default_authority( + grpc_resolver_factory *factory, grpc_uri *uri) { + if (factory == NULL) return NULL; + return factory->vtable->get_default_authority(factory, uri); +} diff --git a/src/core/client_config/resolver_factory.h b/src/core/client_config/resolver_factory.h new file mode 100644 index 00000000..e243b239 --- /dev/null +++ b/src/core/client_config/resolver_factory.h @@ -0,0 +1,79 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_CLIENT_CONFIG_RESOLVER_FACTORY_H +#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_RESOLVER_FACTORY_H + +#include "src/core/client_config/resolver.h" +#include "src/core/client_config/subchannel_factory.h" +#include "src/core/client_config/uri_parser.h" + +typedef struct grpc_resolver_factory grpc_resolver_factory; +typedef struct grpc_resolver_factory_vtable grpc_resolver_factory_vtable; + +/** grpc_resolver provides grpc_client_config objects to grpc_channel + objects */ +struct grpc_resolver_factory { + const grpc_resolver_factory_vtable *vtable; +}; + +struct grpc_resolver_factory_vtable { + void (*ref)(grpc_resolver_factory *factory); + void (*unref)(grpc_resolver_factory *factory); + + /** Implementation of grpc_resolver_factory_create_resolver */ + grpc_resolver *(*create_resolver)( + grpc_resolver_factory *factory, grpc_uri *uri, + grpc_subchannel_factory *subchannel_factory); + + /** Implementation of grpc_resolver_factory_get_default_authority */ + char *(*get_default_authority)(grpc_resolver_factory *factory, grpc_uri *uri); + + /** URI scheme that this factory implements */ + const char *scheme; +}; + +void grpc_resolver_factory_ref(grpc_resolver_factory *resolver); +void grpc_resolver_factory_unref(grpc_resolver_factory *resolver); + +/** Create a resolver instance for a name */ +grpc_resolver *grpc_resolver_factory_create_resolver( + grpc_resolver_factory *factory, grpc_uri *uri, + grpc_subchannel_factory *subchannel_factory); + +/** Return a (freshly allocated with gpr_malloc) string representing + the default authority to use for this scheme. */ +char *grpc_resolver_factory_get_default_authority( + grpc_resolver_factory *factory, grpc_uri *uri); + +#endif /* GRPC_INTERNAL_CORE_CONFIG_RESOLVER_FACTORY_H */ diff --git a/src/core/client_config/resolver_registry.c b/src/core/client_config/resolver_registry.c new file mode 100644 index 00000000..37979b3b --- /dev/null +++ b/src/core/client_config/resolver_registry.c @@ -0,0 +1,133 @@ +/* + * + * Copyright 2015, 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 "src/core/client_config/resolver_registry.h" + +#include + +#include +#include +#include + +#define MAX_RESOLVERS 10 + +static grpc_resolver_factory *g_all_of_the_resolvers[MAX_RESOLVERS]; +static int g_number_of_resolvers = 0; + +static char *g_default_resolver_prefix; + +void grpc_resolver_registry_init(const char *default_resolver_prefix) { + g_number_of_resolvers = 0; + g_default_resolver_prefix = gpr_strdup(default_resolver_prefix); +} + +void grpc_resolver_registry_shutdown(void) { + int i; + for (i = 0; i < g_number_of_resolvers; i++) { + grpc_resolver_factory_unref(g_all_of_the_resolvers[i]); + } + gpr_free(g_default_resolver_prefix); +} + +void grpc_register_resolver_type(grpc_resolver_factory *factory) { + int i; + for (i = 0; i < g_number_of_resolvers; i++) { + GPR_ASSERT(0 != strcmp(factory->vtable->scheme, + g_all_of_the_resolvers[i]->vtable->scheme)); + } + GPR_ASSERT(g_number_of_resolvers != MAX_RESOLVERS); + grpc_resolver_factory_ref(factory); + g_all_of_the_resolvers[g_number_of_resolvers++] = factory; +} + +static grpc_resolver_factory *lookup_factory(grpc_uri *uri) { + int i; + + /* handling NULL uri's here simplifies grpc_resolver_create */ + if (!uri) return NULL; + + for (i = 0; i < g_number_of_resolvers; i++) { + if (0 == strcmp(uri->scheme, g_all_of_the_resolvers[i]->vtable->scheme)) { + return g_all_of_the_resolvers[i]; + } + } + + return NULL; +} + +static grpc_resolver_factory *resolve_factory(const char *target, + grpc_uri **uri) { + char *tmp; + grpc_resolver_factory *factory = NULL; + + GPR_ASSERT(uri != NULL); + *uri = grpc_uri_parse(target, 1); + factory = lookup_factory(*uri); + if (factory == NULL) { + if (g_default_resolver_prefix != NULL) { + grpc_uri_destroy(*uri); + gpr_asprintf(&tmp, "%s%s", g_default_resolver_prefix, target); + *uri = grpc_uri_parse(tmp, 1); + factory = lookup_factory(*uri); + if (factory == NULL) { + grpc_uri_destroy(grpc_uri_parse(target, 0)); + grpc_uri_destroy(grpc_uri_parse(tmp, 0)); + gpr_log(GPR_ERROR, "don't know how to resolve '%s' or '%s'", target, + tmp); + } + gpr_free(tmp); + } else { + grpc_uri_destroy(grpc_uri_parse(target, 0)); + gpr_log(GPR_ERROR, "don't know how to resolve '%s'", target); + } + } + return factory; +} + +grpc_resolver *grpc_resolver_create( + const char *target, grpc_subchannel_factory *subchannel_factory) { + grpc_uri *uri = NULL; + grpc_resolver_factory *factory = resolve_factory(target, &uri); + grpc_resolver *resolver = + grpc_resolver_factory_create_resolver(factory, uri, subchannel_factory); + grpc_uri_destroy(uri); + return resolver; +} + +char *grpc_get_default_authority(const char *target) { + grpc_uri *uri = NULL; + grpc_resolver_factory *factory = resolve_factory(target, &uri); + char *authority = grpc_resolver_factory_get_default_authority(factory, uri); + grpc_uri_destroy(uri); + return authority; +} diff --git a/src/core/client_config/resolver_registry.h b/src/core/client_config/resolver_registry.h new file mode 100644 index 00000000..5a7193b7 --- /dev/null +++ b/src/core/client_config/resolver_registry.h @@ -0,0 +1,65 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_CLIENT_CONFIG_RESOLVER_REGISTRY_H +#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_RESOLVER_REGISTRY_H + +#include "src/core/client_config/resolver_factory.h" + +void grpc_resolver_registry_init(const char *default_prefix); +void grpc_resolver_registry_shutdown(void); + +/** Register a resolver type. + URI's of \a scheme will be resolved with the given resolver. + If \a priority is greater than zero, then the resolver will be eligible + to resolve names that are passed in with no scheme. Higher priority + resolvers will be tried before lower priority schemes. */ +void grpc_register_resolver_type(grpc_resolver_factory *factory); + +/** Create a resolver given \a target. + First tries to parse \a target as a URI. If this succeeds, tries + to locate a registered resolver factory based on the URI scheme. + If parsing or location fails, prefixes default_prefix from + grpc_resolver_registry_init to target, and tries again (if default_prefix + was not NULL). + If a resolver factory was found, use it to instantiate a resolver and + return it. + If a resolver factory was not found, return NULL. */ +grpc_resolver *grpc_resolver_create( + const char *target, grpc_subchannel_factory *subchannel_factory); + +/** Given a target, return a (freshly allocated with gpr_malloc) string + representing the default authority to pass from a client. */ +char *grpc_get_default_authority(const char *target); + +#endif /* GRPC_INTERNAL_CORE_CLIENT_CONFIG_RESOLVER_REGISTRY_H */ diff --git a/src/core/client_config/resolvers/dns_resolver.c b/src/core/client_config/resolvers/dns_resolver.c new file mode 100644 index 00000000..84643c46 --- /dev/null +++ b/src/core/client_config/resolvers/dns_resolver.c @@ -0,0 +1,256 @@ +/* + * + * Copyright 2015, 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 "src/core/client_config/resolvers/dns_resolver.h" + +#include + +#include +#include +#include + +#include "src/core/client_config/lb_policies/pick_first.h" +#include "src/core/client_config/subchannel_factory_decorators/add_channel_arg.h" +#include "src/core/iomgr/resolve_address.h" +#include "src/core/support/string.h" + +typedef struct { + /** base class: must be first */ + grpc_resolver base; + /** refcount */ + gpr_refcount refs; + /** name to resolve */ + char *name; + /** default port to use */ + char *default_port; + /** subchannel factory */ + grpc_subchannel_factory *subchannel_factory; + /** load balancing policy factory */ + grpc_lb_policy *(*lb_policy_factory)(grpc_subchannel **subchannels, + size_t num_subchannels); + + /** mutex guarding the rest of the state */ + gpr_mu mu; + /** are we currently resolving? */ + int resolving; + /** which version of resolved_config have we published? */ + int published_version; + /** which version of resolved_config is current? */ + int resolved_version; + /** pending next completion, or NULL */ + grpc_iomgr_closure *next_completion; + /** target config address for next completion */ + grpc_client_config **target_config; + /** current (fully resolved) config */ + grpc_client_config *resolved_config; +} dns_resolver; + +static void dns_destroy(grpc_resolver *r); + +static void dns_start_resolving_locked(dns_resolver *r); +static void dns_maybe_finish_next_locked(dns_resolver *r); + +static void dns_shutdown(grpc_resolver *r); +static void dns_channel_saw_error(grpc_resolver *r, + struct sockaddr *failing_address, + int failing_address_len); +static void dns_next(grpc_resolver *r, grpc_client_config **target_config, + grpc_iomgr_closure *on_complete); + +static const grpc_resolver_vtable dns_resolver_vtable = { + dns_destroy, dns_shutdown, dns_channel_saw_error, dns_next}; + +static void dns_shutdown(grpc_resolver *resolver) { + dns_resolver *r = (dns_resolver *)resolver; + gpr_mu_lock(&r->mu); + if (r->next_completion != NULL) { + *r->target_config = NULL; + grpc_iomgr_add_callback(r->next_completion); + r->next_completion = NULL; + } + gpr_mu_unlock(&r->mu); +} + +static void dns_channel_saw_error(grpc_resolver *resolver, struct sockaddr *sa, + int len) { + dns_resolver *r = (dns_resolver *)resolver; + gpr_mu_lock(&r->mu); + if (!r->resolving) { + dns_start_resolving_locked(r); + } + gpr_mu_unlock(&r->mu); +} + +static void dns_next(grpc_resolver *resolver, + grpc_client_config **target_config, + grpc_iomgr_closure *on_complete) { + dns_resolver *r = (dns_resolver *)resolver; + gpr_mu_lock(&r->mu); + GPR_ASSERT(!r->next_completion); + r->next_completion = on_complete; + r->target_config = target_config; + if (r->resolved_version == 0 && !r->resolving) { + dns_start_resolving_locked(r); + } else { + dns_maybe_finish_next_locked(r); + } + gpr_mu_unlock(&r->mu); +} + +static void dns_on_resolved(void *arg, grpc_resolved_addresses *addresses) { + dns_resolver *r = arg; + grpc_client_config *config = NULL; + grpc_subchannel **subchannels; + grpc_subchannel_args args; + grpc_lb_policy *lb_policy; + size_t i; + if (addresses) { + config = grpc_client_config_create(); + subchannels = gpr_malloc(sizeof(grpc_subchannel *) * addresses->naddrs); + for (i = 0; i < addresses->naddrs; i++) { + memset(&args, 0, sizeof(args)); + args.addr = (struct sockaddr *)(addresses->addrs[i].addr); + args.addr_len = addresses->addrs[i].len; + subchannels[i] = grpc_subchannel_factory_create_subchannel( + r->subchannel_factory, &args); + } + lb_policy = r->lb_policy_factory(subchannels, addresses->naddrs); + grpc_client_config_set_lb_policy(config, lb_policy); + GRPC_LB_POLICY_UNREF(lb_policy, "construction"); + grpc_resolved_addresses_destroy(addresses); + gpr_free(subchannels); + } + gpr_mu_lock(&r->mu); + GPR_ASSERT(r->resolving); + r->resolving = 0; + if (r->resolved_config) { + grpc_client_config_unref(r->resolved_config); + } + r->resolved_config = config; + r->resolved_version++; + dns_maybe_finish_next_locked(r); + gpr_mu_unlock(&r->mu); + + GRPC_RESOLVER_UNREF(&r->base, "dns-resolving"); +} + +static void dns_start_resolving_locked(dns_resolver *r) { + GRPC_RESOLVER_REF(&r->base, "dns-resolving"); + GPR_ASSERT(!r->resolving); + r->resolving = 1; + grpc_resolve_address(r->name, r->default_port, dns_on_resolved, r); +} + +static void dns_maybe_finish_next_locked(dns_resolver *r) { + if (r->next_completion != NULL && + r->resolved_version != r->published_version) { + *r->target_config = r->resolved_config; + if (r->resolved_config) { + grpc_client_config_ref(r->resolved_config); + } + grpc_iomgr_add_callback(r->next_completion); + r->next_completion = NULL; + r->published_version = r->resolved_version; + } +} + +static void dns_destroy(grpc_resolver *gr) { + dns_resolver *r = (dns_resolver *)gr; + gpr_mu_destroy(&r->mu); + if (r->resolved_config) { + grpc_client_config_unref(r->resolved_config); + } + grpc_subchannel_factory_unref(r->subchannel_factory); + gpr_free(r->name); + gpr_free(r->default_port); + gpr_free(r); +} + +static grpc_resolver *dns_create( + grpc_uri *uri, const char *default_port, + grpc_lb_policy *(*lb_policy_factory)(grpc_subchannel **subchannels, + size_t num_subchannels), + grpc_subchannel_factory *subchannel_factory) { + dns_resolver *r; + const char *path = uri->path; + + if (0 != strcmp(uri->authority, "")) { + gpr_log(GPR_ERROR, "authority based uri's not supported"); + return NULL; + } + + if (path[0] == '/') ++path; + + r = gpr_malloc(sizeof(dns_resolver)); + memset(r, 0, sizeof(*r)); + gpr_ref_init(&r->refs, 1); + gpr_mu_init(&r->mu); + grpc_resolver_init(&r->base, &dns_resolver_vtable); + r->name = gpr_strdup(path); + r->default_port = gpr_strdup(default_port); + r->subchannel_factory = subchannel_factory; + grpc_subchannel_factory_ref(subchannel_factory); + r->lb_policy_factory = lb_policy_factory; + return &r->base; +} + +/* + * FACTORY + */ + +static void dns_factory_ref(grpc_resolver_factory *factory) {} + +static void dns_factory_unref(grpc_resolver_factory *factory) {} + +static grpc_resolver *dns_factory_create_resolver( + grpc_resolver_factory *factory, grpc_uri *uri, + grpc_subchannel_factory *subchannel_factory) { + return dns_create(uri, "https", grpc_create_pick_first_lb_policy, + subchannel_factory); +} + +char *dns_factory_get_default_host_name(grpc_resolver_factory *factory, + grpc_uri *uri) { + const char *path = uri->path; + if (path[0] == '/') ++path; + return gpr_strdup(path); +} + +static const grpc_resolver_factory_vtable dns_factory_vtable = { + dns_factory_ref, dns_factory_unref, dns_factory_create_resolver, + dns_factory_get_default_host_name, "dns"}; +static grpc_resolver_factory dns_resolver_factory = {&dns_factory_vtable}; + +grpc_resolver_factory *grpc_dns_resolver_factory_create() { + return &dns_resolver_factory; +} diff --git a/src/core/client_config/resolvers/dns_resolver.h b/src/core/client_config/resolvers/dns_resolver.h new file mode 100644 index 00000000..a3ef3161 --- /dev/null +++ b/src/core/client_config/resolvers/dns_resolver.h @@ -0,0 +1,42 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_CLIENT_CONFIG_RESOLVERS_DNS_RESOLVER_H +#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_RESOLVERS_DNS_RESOLVER_H + +#include "src/core/client_config/resolver_factory.h" + +/** Create a dns resolver factory */ +grpc_resolver_factory *grpc_dns_resolver_factory_create(void); + +#endif /* GRPC_INTERNAL_CORE_CLIENT_CONFIG_RESOLVERS_DNS_RESOLVER_H */ diff --git a/src/core/client_config/resolvers/sockaddr_resolver.c b/src/core/client_config/resolvers/sockaddr_resolver.c new file mode 100644 index 00000000..0d8540a5 --- /dev/null +++ b/src/core/client_config/resolvers/sockaddr_resolver.c @@ -0,0 +1,357 @@ +/* + * + * Copyright 2015, 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 + +#include "src/core/client_config/resolvers/sockaddr_resolver.h" + +#include +#include +#ifdef GPR_POSIX_SOCKET +#include +#endif + +#include +#include +#include + +#include "src/core/client_config/lb_policies/pick_first.h" +#include "src/core/iomgr/resolve_address.h" +#include "src/core/support/string.h" + +typedef struct { + /** base class: must be first */ + grpc_resolver base; + /** refcount */ + gpr_refcount refs; + /** subchannel factory */ + grpc_subchannel_factory *subchannel_factory; + /** load balancing policy factory */ + grpc_lb_policy *(*lb_policy_factory)(grpc_subchannel **subchannels, + size_t num_subchannels); + + /** the addresses that we've 'resolved' */ + struct sockaddr_storage *addrs; + /** the corresponding length of the addresses */ + int *addrs_len; + /** how many elements in \a addrs */ + size_t num_addrs; + + /** mutex guarding the rest of the state */ + gpr_mu mu; + /** have we published? */ + int published; + /** pending next completion, or NULL */ + grpc_iomgr_closure *next_completion; + /** target config address for next completion */ + grpc_client_config **target_config; +} sockaddr_resolver; + +static void sockaddr_destroy(grpc_resolver *r); + +static void sockaddr_maybe_finish_next_locked(sockaddr_resolver *r); + +static void sockaddr_shutdown(grpc_resolver *r); +static void sockaddr_channel_saw_error(grpc_resolver *r, + struct sockaddr *failing_address, + int failing_address_len); +static void sockaddr_next(grpc_resolver *r, grpc_client_config **target_config, + grpc_iomgr_closure *on_complete); + +static const grpc_resolver_vtable sockaddr_resolver_vtable = { + sockaddr_destroy, sockaddr_shutdown, sockaddr_channel_saw_error, + sockaddr_next}; + +static void sockaddr_shutdown(grpc_resolver *resolver) { + sockaddr_resolver *r = (sockaddr_resolver *)resolver; + gpr_mu_lock(&r->mu); + if (r->next_completion != NULL) { + *r->target_config = NULL; + /* TODO(ctiller): add delayed callback */ + grpc_iomgr_add_callback(r->next_completion); + r->next_completion = NULL; + } + gpr_mu_unlock(&r->mu); +} + +static void sockaddr_channel_saw_error(grpc_resolver *resolver, + struct sockaddr *sa, int len) {} + +static void sockaddr_next(grpc_resolver *resolver, + grpc_client_config **target_config, + grpc_iomgr_closure *on_complete) { + sockaddr_resolver *r = (sockaddr_resolver *)resolver; + gpr_mu_lock(&r->mu); + GPR_ASSERT(!r->next_completion); + r->next_completion = on_complete; + r->target_config = target_config; + sockaddr_maybe_finish_next_locked(r); + gpr_mu_unlock(&r->mu); +} + +static void sockaddr_maybe_finish_next_locked(sockaddr_resolver *r) { + grpc_client_config *cfg; + grpc_lb_policy *lb_policy; + grpc_subchannel **subchannels; + grpc_subchannel_args args; + + if (r->next_completion != NULL && !r->published) { + size_t i; + cfg = grpc_client_config_create(); + subchannels = gpr_malloc(sizeof(grpc_subchannel *) * r->num_addrs); + for (i = 0; i < r->num_addrs; i++) { + memset(&args, 0, sizeof(args)); + args.addr = (struct sockaddr *)&r->addrs[i]; + args.addr_len = r->addrs_len[i]; + subchannels[i] = grpc_subchannel_factory_create_subchannel( + r->subchannel_factory, &args); + } + lb_policy = r->lb_policy_factory(subchannels, r->num_addrs); + gpr_free(subchannels); + grpc_client_config_set_lb_policy(cfg, lb_policy); + GRPC_LB_POLICY_UNREF(lb_policy, "unix"); + r->published = 1; + *r->target_config = cfg; + grpc_iomgr_add_callback(r->next_completion); + r->next_completion = NULL; + } +} + +static void sockaddr_destroy(grpc_resolver *gr) { + sockaddr_resolver *r = (sockaddr_resolver *)gr; + gpr_mu_destroy(&r->mu); + grpc_subchannel_factory_unref(r->subchannel_factory); + gpr_free(r->addrs); + gpr_free(r->addrs_len); + gpr_free(r); +} + +#ifdef GPR_POSIX_SOCKET +static int parse_unix(grpc_uri *uri, struct sockaddr_storage *addr, int *len) { + struct sockaddr_un *un = (struct sockaddr_un *)addr; + + un->sun_family = AF_UNIX; + strcpy(un->sun_path, uri->path); + *len = strlen(un->sun_path) + sizeof(un->sun_family) + 1; + + return 1; +} + +static char *unix_get_default_authority(grpc_resolver_factory *factory, + grpc_uri *uri) { + return gpr_strdup("localhost"); +} +#endif + +static char *ip_get_default_authority(grpc_uri *uri) { + const char *path = uri->path; + if (path[0] == '/') ++path; + return gpr_strdup(path); +} + +static char *ipv4_get_default_authority(grpc_resolver_factory *factory, + grpc_uri *uri) { + return ip_get_default_authority(uri); +} + +static char *ipv6_get_default_authority(grpc_resolver_factory *factory, + grpc_uri *uri) { + return ip_get_default_authority(uri); +} + +static int parse_ipv4(grpc_uri *uri, struct sockaddr_storage *addr, int *len) { + const char *host_port = uri->path; + char *host; + char *port; + int port_num; + int result = 0; + struct sockaddr_in *in = (struct sockaddr_in *)addr; + + if (*host_port == '/') ++host_port; + if (!gpr_split_host_port(host_port, &host, &port)) { + return 0; + } + + memset(in, 0, sizeof(*in)); + *len = sizeof(*in); + in->sin_family = AF_INET; + if (inet_pton(AF_INET, host, &in->sin_addr) == 0) { + gpr_log(GPR_ERROR, "invalid ipv4 address: '%s'", host); + goto done; + } + + if (port != NULL) { + if (sscanf(port, "%d", &port_num) != 1 || port_num < 0 || + port_num > 65535) { + gpr_log(GPR_ERROR, "invalid ipv4 port: '%s'", port); + goto done; + } + in->sin_port = htons(port_num); + } else { + gpr_log(GPR_ERROR, "no port given for ipv4 scheme"); + goto done; + } + + result = 1; +done: + gpr_free(host); + gpr_free(port); + return result; +} + +static int parse_ipv6(grpc_uri *uri, struct sockaddr_storage *addr, int *len) { + const char *host_port = uri->path; + char *host; + char *port; + int port_num; + int result = 0; + struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)addr; + + if (*host_port == '/') ++host_port; + if (!gpr_split_host_port(host_port, &host, &port)) { + return 0; + } + + memset(in6, 0, sizeof(*in6)); + *len = sizeof(*in6); + in6->sin6_family = AF_INET6; + if (inet_pton(AF_INET6, host, &in6->sin6_addr) == 0) { + gpr_log(GPR_ERROR, "invalid ipv6 address: '%s'", host); + goto done; + } + + if (port != NULL) { + if (sscanf(port, "%d", &port_num) != 1 || port_num < 0 || + port_num > 65535) { + gpr_log(GPR_ERROR, "invalid ipv6 port: '%s'", port); + goto done; + } + in6->sin6_port = htons(port_num); + } else { + gpr_log(GPR_ERROR, "no port given for ipv6 scheme"); + goto done; + } + + result = 1; +done: + gpr_free(host); + gpr_free(port); + return result; +} + +static void do_nothing(void *ignored) {} +static grpc_resolver *sockaddr_create( + grpc_uri *uri, + grpc_lb_policy *(*lb_policy_factory)(grpc_subchannel **subchannels, + size_t num_subchannels), + grpc_subchannel_factory *subchannel_factory, + int parse(grpc_uri *uri, struct sockaddr_storage *dst, int *len)) { + size_t i; + int errors_found = 0; /* GPR_FALSE */ + sockaddr_resolver *r; + gpr_slice path_slice; + gpr_slice_buffer path_parts; + + if (0 != strcmp(uri->authority, "")) { + gpr_log(GPR_ERROR, "authority based uri's not supported"); + return NULL; + } + + r = gpr_malloc(sizeof(sockaddr_resolver)); + memset(r, 0, sizeof(*r)); + + path_slice = gpr_slice_new(uri->path, strlen(uri->path), do_nothing); + gpr_slice_buffer_init(&path_parts); + + gpr_slice_split(path_slice, ",", &path_parts); + r->num_addrs = path_parts.count; + r->addrs = gpr_malloc(sizeof(struct sockaddr_storage) * r->num_addrs); + r->addrs_len = gpr_malloc(sizeof(int) * r->num_addrs); + + for(i = 0; i < r->num_addrs; i++) { + grpc_uri ith_uri = *uri; + char* part_str = gpr_dump_slice(path_parts.slices[i], GPR_DUMP_ASCII); + ith_uri.path = part_str; + if (!parse(&ith_uri, &r->addrs[i], &r->addrs_len[i])) { + errors_found = 1; /* GPR_TRUE */ + } + gpr_free(part_str); + if (errors_found) break; + } + + gpr_slice_buffer_destroy(&path_parts); + gpr_slice_unref(path_slice); + if (errors_found) { + gpr_free(r); + return NULL; + } + + gpr_ref_init(&r->refs, 1); + gpr_mu_init(&r->mu); + grpc_resolver_init(&r->base, &sockaddr_resolver_vtable); + r->subchannel_factory = subchannel_factory; + r->lb_policy_factory = lb_policy_factory; + + grpc_subchannel_factory_ref(subchannel_factory); + return &r->base; +} + +/* + * FACTORY + */ + +static void sockaddr_factory_ref(grpc_resolver_factory *factory) {} + +static void sockaddr_factory_unref(grpc_resolver_factory *factory) {} + +#define DECL_FACTORY(name) \ + static grpc_resolver *name##_factory_create_resolver( \ + grpc_resolver_factory *factory, grpc_uri *uri, \ + grpc_subchannel_factory *subchannel_factory) { \ + return sockaddr_create(uri, grpc_create_pick_first_lb_policy, \ + subchannel_factory, parse_##name); \ + } \ + static const grpc_resolver_factory_vtable name##_factory_vtable = { \ + sockaddr_factory_ref, sockaddr_factory_unref, \ + name##_factory_create_resolver, name##_get_default_authority, #name}; \ + static grpc_resolver_factory name##_resolver_factory = { \ + &name##_factory_vtable}; \ + grpc_resolver_factory *grpc_##name##_resolver_factory_create() { \ + return &name##_resolver_factory; \ + } + +#ifdef GPR_POSIX_SOCKET +DECL_FACTORY(unix) +#endif +DECL_FACTORY(ipv4) +DECL_FACTORY(ipv6) diff --git a/src/core/client_config/resolvers/sockaddr_resolver.h b/src/core/client_config/resolvers/sockaddr_resolver.h new file mode 100644 index 00000000..1b7a18f9 --- /dev/null +++ b/src/core/client_config/resolvers/sockaddr_resolver.h @@ -0,0 +1,50 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_CLIENT_CONFIG_RESOLVERS_UNIX_RESOLVER_H +#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_RESOLVERS_UNIX_RESOLVER_H + +#include + +#include "src/core/client_config/resolver_factory.h" + +grpc_resolver_factory *grpc_ipv4_resolver_factory_create(void); + +grpc_resolver_factory *grpc_ipv6_resolver_factory_create(void); + +#ifdef GPR_POSIX_SOCKET +/** Create a unix resolver factory */ +grpc_resolver_factory *grpc_unix_resolver_factory_create(void); +#endif + +#endif /* GRPC_INTERNAL_CORE_CLIENT_CONFIG_RESOLVERS_UNIX_RESOLVER_H */ diff --git a/src/core/client_config/resolvers/zookeeper_resolver.c b/src/core/client_config/resolvers/zookeeper_resolver.c new file mode 100644 index 00000000..da399f99 --- /dev/null +++ b/src/core/client_config/resolvers/zookeeper_resolver.c @@ -0,0 +1,506 @@ +/* + * + * Copyright 2015, 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 "src/core/client_config/resolvers/zookeeper_resolver.h" + +#include + +#include +#include + +#include +#include + +#include "src/core/client_config/lb_policies/pick_first.h" +#include "src/core/client_config/resolver_registry.h" +#include "src/core/iomgr/resolve_address.h" +#include "src/core/support/string.h" +#include "src/core/json/json.h" + +/** Zookeeper session expiration time in milliseconds */ +#define GRPC_ZOOKEEPER_SESSION_TIMEOUT 15000 + +typedef struct { + /** base class: must be first */ + grpc_resolver base; + /** refcount */ + gpr_refcount refs; + /** name to resolve */ + char *name; + /** subchannel factory */ + grpc_subchannel_factory *subchannel_factory; + /** load balancing policy factory */ + grpc_lb_policy *(*lb_policy_factory)(grpc_subchannel **subchannels, + size_t num_subchannels); + + /** mutex guarding the rest of the state */ + gpr_mu mu; + /** are we currently resolving? */ + int resolving; + /** which version of resolved_config have we published? */ + int published_version; + /** which version of resolved_config is current? */ + int resolved_version; + /** pending next completion, or NULL */ + grpc_iomgr_closure *next_completion; + /** target config address for next completion */ + grpc_client_config **target_config; + /** current (fully resolved) config */ + grpc_client_config *resolved_config; + + /** zookeeper handle */ + zhandle_t *zookeeper_handle; + /** zookeeper resolved addresses */ + grpc_resolved_addresses *resolved_addrs; + /** total number of addresses to be resolved */ + int resolved_total; + /** number of addresses resolved */ + int resolved_num; +} zookeeper_resolver; + +static void zookeeper_destroy(grpc_resolver *r); + +static void zookeeper_start_resolving_locked(zookeeper_resolver *r); +static void zookeeper_maybe_finish_next_locked(zookeeper_resolver *r); + +static void zookeeper_shutdown(grpc_resolver *r); +static void zookeeper_channel_saw_error(grpc_resolver *r, + struct sockaddr *failing_address, + int failing_address_len); +static void zookeeper_next(grpc_resolver *r, grpc_client_config **target_config, + grpc_iomgr_closure *on_complete); + +static const grpc_resolver_vtable zookeeper_resolver_vtable = { + zookeeper_destroy, zookeeper_shutdown, zookeeper_channel_saw_error, + zookeeper_next}; + +static void zookeeper_shutdown(grpc_resolver *resolver) { + zookeeper_resolver *r = (zookeeper_resolver *)resolver; + gpr_mu_lock(&r->mu); + if (r->next_completion != NULL) { + *r->target_config = NULL; + grpc_iomgr_add_callback(r->next_completion); + r->next_completion = NULL; + } + zookeeper_close(r->zookeeper_handle); + gpr_mu_unlock(&r->mu); +} + +static void zookeeper_channel_saw_error(grpc_resolver *resolver, + struct sockaddr *sa, int len) { + zookeeper_resolver *r = (zookeeper_resolver *)resolver; + gpr_mu_lock(&r->mu); + if (r->resolving == 0) { + zookeeper_start_resolving_locked(r); + } + gpr_mu_unlock(&r->mu); +} + +static void zookeeper_next(grpc_resolver *resolver, + grpc_client_config **target_config, + grpc_iomgr_closure *on_complete) { + zookeeper_resolver *r = (zookeeper_resolver *)resolver; + gpr_mu_lock(&r->mu); + GPR_ASSERT(r->next_completion == NULL); + r->next_completion = on_complete; + r->target_config = target_config; + if (r->resolved_version == 0 && r->resolving == 0) { + zookeeper_start_resolving_locked(r); + } else { + zookeeper_maybe_finish_next_locked(r); + } + gpr_mu_unlock(&r->mu); +} + +/** Zookeeper global watcher for connection management + TODO: better connection management besides logs */ +static void zookeeper_global_watcher(zhandle_t *zookeeper_handle, int type, + int state, const char *path, + void *watcher_ctx) { + if (type == ZOO_SESSION_EVENT) { + if (state == ZOO_EXPIRED_SESSION_STATE) { + gpr_log(GPR_ERROR, "Zookeeper session expired"); + } else if (state == ZOO_AUTH_FAILED_STATE) { + gpr_log(GPR_ERROR, "Zookeeper authentication failed"); + } + } +} + +/** Zookeeper watcher triggered by changes to watched nodes + Once triggered, it tries to resolve again to get updated addresses */ +static void zookeeper_watcher(zhandle_t *zookeeper_handle, int type, int state, + const char *path, void *watcher_ctx) { + if (watcher_ctx != NULL) { + zookeeper_resolver *r = (zookeeper_resolver *)watcher_ctx; + if (state == ZOO_CONNECTED_STATE) { + gpr_mu_lock(&r->mu); + if (r->resolving == 0) { + zookeeper_start_resolving_locked(r); + } + gpr_mu_unlock(&r->mu); + } + } +} + +/** Callback function after getting all resolved addresses + Creates a subchannel for each address */ +static void zookeeper_on_resolved(void *arg, + grpc_resolved_addresses *addresses) { + zookeeper_resolver *r = arg; + grpc_client_config *config = NULL; + grpc_subchannel **subchannels; + grpc_subchannel_args args; + grpc_lb_policy *lb_policy; + size_t i; + if (addresses != NULL) { + config = grpc_client_config_create(); + subchannels = gpr_malloc(sizeof(grpc_subchannel *) * addresses->naddrs); + for (i = 0; i < addresses->naddrs; i++) { + memset(&args, 0, sizeof(args)); + args.addr = (struct sockaddr *)(addresses->addrs[i].addr); + args.addr_len = addresses->addrs[i].len; + subchannels[i] = grpc_subchannel_factory_create_subchannel( + r->subchannel_factory, &args); + } + lb_policy = r->lb_policy_factory(subchannels, addresses->naddrs); + grpc_client_config_set_lb_policy(config, lb_policy); + GRPC_LB_POLICY_UNREF(lb_policy, "construction"); + grpc_resolved_addresses_destroy(addresses); + gpr_free(subchannels); + } + gpr_mu_lock(&r->mu); + GPR_ASSERT(r->resolving == 1); + r->resolving = 0; + if (r->resolved_config != NULL) { + grpc_client_config_unref(r->resolved_config); + } + r->resolved_config = config; + r->resolved_version++; + zookeeper_maybe_finish_next_locked(r); + gpr_mu_unlock(&r->mu); + + GRPC_RESOLVER_UNREF(&r->base, "zookeeper-resolving"); +} + +/** Callback function for each DNS resolved address */ +static void zookeeper_dns_resolved(void *arg, + grpc_resolved_addresses *addresses) { + size_t i; + zookeeper_resolver *r = arg; + int resolve_done = 0; + + gpr_mu_lock(&r->mu); + r->resolved_num++; + r->resolved_addrs->addrs = + gpr_realloc(r->resolved_addrs->addrs, + sizeof(grpc_resolved_address) * + (r->resolved_addrs->naddrs + addresses->naddrs)); + for (i = 0; i < addresses->naddrs; i++) { + memcpy(r->resolved_addrs->addrs[i + r->resolved_addrs->naddrs].addr, + addresses->addrs[i].addr, addresses->addrs[i].len); + r->resolved_addrs->addrs[i + r->resolved_addrs->naddrs].len = + addresses->addrs[i].len; + } + + r->resolved_addrs->naddrs += addresses->naddrs; + grpc_resolved_addresses_destroy(addresses); + + /** Wait for all addresses to be resolved */ + resolve_done = (r->resolved_num == r->resolved_total); + gpr_mu_unlock(&r->mu); + if (resolve_done) { + zookeeper_on_resolved(r, r->resolved_addrs); + } +} + +/** Parses JSON format address of a zookeeper node */ +static char *zookeeper_parse_address(const char *value, int value_len) { + grpc_json *json; + grpc_json *cur; + const char *host; + const char *port; + char *buffer; + char *address = NULL; + + buffer = gpr_malloc(value_len); + memcpy(buffer, value, value_len); + json = grpc_json_parse_string_with_len(buffer, value_len); + if (json != NULL) { + host = NULL; + port = NULL; + for (cur = json->child; cur != NULL; cur = cur->next) { + if (!strcmp(cur->key, "host")) { + host = cur->value; + if (port != NULL) { + break; + } + } else if (!strcmp(cur->key, "port")) { + port = cur->value; + if (host != NULL) { + break; + } + } + } + if (host != NULL && port != NULL) { + gpr_asprintf(&address, "%s:%s", host, port); + } + grpc_json_destroy(json); + } + gpr_free(buffer); + + return address; +} + +static void zookeeper_get_children_node_completion(int rc, const char *value, + int value_len, + const struct Stat *stat, + const void *arg) { + char *address = NULL; + zookeeper_resolver *r = (zookeeper_resolver *)arg; + int resolve_done = 0; + + if (rc != 0) { + gpr_log(GPR_ERROR, "Error in getting a child node of %s", r->name); + return; + } + + address = zookeeper_parse_address(value, value_len); + if (address != NULL) { + /** Further resolves address by DNS */ + grpc_resolve_address(address, NULL, zookeeper_dns_resolved, r); + gpr_free(address); + } else { + gpr_log(GPR_ERROR, "Error in resolving a child node of %s", r->name); + gpr_mu_lock(&r->mu); + r->resolved_total--; + resolve_done = (r->resolved_num == r->resolved_total); + gpr_mu_unlock(&r->mu); + if (resolve_done) { + zookeeper_on_resolved(r, r->resolved_addrs); + } + } +} + +static void zookeeper_get_children_completion( + int rc, const struct String_vector *children, const void *arg) { + char *path; + int status; + int i; + zookeeper_resolver *r = (zookeeper_resolver *)arg; + + if (rc != 0) { + gpr_log(GPR_ERROR, "Error in getting zookeeper children of %s", r->name); + return; + } + + if (children->count == 0) { + gpr_log(GPR_ERROR, "Error in resolving zookeeper address %s", r->name); + return; + } + + r->resolved_addrs = gpr_malloc(sizeof(grpc_resolved_addresses)); + r->resolved_addrs->addrs = NULL; + r->resolved_addrs->naddrs = 0; + r->resolved_total = children->count; + + /** TODO: Replace expensive heap allocation with stack + if we can get maximum length of zookeeper path */ + for (i = 0; i < children->count; i++) { + gpr_asprintf(&path, "%s/%s", r->name, children->data[i]); + status = zoo_awget(r->zookeeper_handle, path, zookeeper_watcher, r, + zookeeper_get_children_node_completion, r); + gpr_free(path); + if (status != 0) { + gpr_log(GPR_ERROR, "Error in getting zookeeper node %s", path); + } + } +} + +static void zookeeper_get_node_completion(int rc, const char *value, + int value_len, + const struct Stat *stat, + const void *arg) { + int status; + char *address = NULL; + zookeeper_resolver *r = (zookeeper_resolver *)arg; + r->resolved_addrs = NULL; + r->resolved_total = 0; + r->resolved_num = 0; + + if (rc != 0) { + gpr_log(GPR_ERROR, "Error in getting zookeeper node %s", r->name); + return; + } + + /** If zookeeper node of path r->name does not have address + (i.e. service node), get its children */ + address = zookeeper_parse_address(value, value_len); + if (address != NULL) { + r->resolved_addrs = gpr_malloc(sizeof(grpc_resolved_addresses)); + r->resolved_addrs->addrs = NULL; + r->resolved_addrs->naddrs = 0; + r->resolved_total = 1; + /** Further resolves address by DNS */ + grpc_resolve_address(address, NULL, zookeeper_dns_resolved, r); + gpr_free(address); + return; + } + + status = zoo_awget_children(r->zookeeper_handle, r->name, zookeeper_watcher, + r, zookeeper_get_children_completion, r); + if (status != 0) { + gpr_log(GPR_ERROR, "Error in getting zookeeper children of %s", r->name); + } +} + +static void zookeeper_resolve_address(zookeeper_resolver *r) { + int status; + status = zoo_awget(r->zookeeper_handle, r->name, zookeeper_watcher, r, + zookeeper_get_node_completion, r); + if (status != 0) { + gpr_log(GPR_ERROR, "Error in getting zookeeper node %s", r->name); + } +} + +static void zookeeper_start_resolving_locked(zookeeper_resolver *r) { + GRPC_RESOLVER_REF(&r->base, "zookeeper-resolving"); + GPR_ASSERT(r->resolving == 0); + r->resolving = 1; + zookeeper_resolve_address(r); +} + +static void zookeeper_maybe_finish_next_locked(zookeeper_resolver *r) { + if (r->next_completion != NULL && + r->resolved_version != r->published_version) { + *r->target_config = r->resolved_config; + if (r->resolved_config != NULL) { + grpc_client_config_ref(r->resolved_config); + } + grpc_iomgr_add_callback(r->next_completion); + r->next_completion = NULL; + r->published_version = r->resolved_version; + } +} + +static void zookeeper_destroy(grpc_resolver *gr) { + zookeeper_resolver *r = (zookeeper_resolver *)gr; + gpr_mu_destroy(&r->mu); + if (r->resolved_config != NULL) { + grpc_client_config_unref(r->resolved_config); + } + grpc_subchannel_factory_unref(r->subchannel_factory); + gpr_free(r->name); + gpr_free(r); +} + +static grpc_resolver *zookeeper_create( + grpc_uri *uri, + grpc_lb_policy *(*lb_policy_factory)(grpc_subchannel **subchannels, + size_t num_subchannels), + grpc_subchannel_factory *subchannel_factory) { + zookeeper_resolver *r; + size_t length; + char *path = uri->path; + + if (0 == strcmp(uri->authority, "")) { + gpr_log(GPR_ERROR, "No authority specified in zookeeper uri"); + return NULL; + } + + /** Removes the trailing slash if exists */ + length = strlen(path); + if (length > 1 && path[length - 1] == '/') { + path[length - 1] = 0; + } + + r = gpr_malloc(sizeof(zookeeper_resolver)); + memset(r, 0, sizeof(*r)); + gpr_ref_init(&r->refs, 1); + gpr_mu_init(&r->mu); + grpc_resolver_init(&r->base, &zookeeper_resolver_vtable); + r->name = gpr_strdup(path); + + r->subchannel_factory = subchannel_factory; + r->lb_policy_factory = lb_policy_factory; + grpc_subchannel_factory_ref(subchannel_factory); + + /** Initializes zookeeper client */ + zoo_set_debug_level(ZOO_LOG_LEVEL_WARN); + r->zookeeper_handle = zookeeper_init(uri->authority, zookeeper_global_watcher, + GRPC_ZOOKEEPER_SESSION_TIMEOUT, 0, 0, 0); + if (r->zookeeper_handle == NULL) { + gpr_log(GPR_ERROR, "Unable to connect to zookeeper server"); + return NULL; + } + + return &r->base; +} + +static void zookeeper_plugin_init() { + grpc_register_resolver_type(grpc_zookeeper_resolver_factory_create()); +} + +void grpc_zookeeper_register() { + grpc_register_plugin(zookeeper_plugin_init, NULL); +} + +/* + * FACTORY + */ + +static void zookeeper_factory_ref(grpc_resolver_factory *factory) {} + +static void zookeeper_factory_unref(grpc_resolver_factory *factory) {} + +static char *zookeeper_factory_get_default_hostname( + grpc_resolver_factory *factory, grpc_uri *uri) { + return NULL; +} + +static grpc_resolver *zookeeper_factory_create_resolver( + grpc_resolver_factory *factory, grpc_uri *uri, + grpc_subchannel_factory *subchannel_factory) { + return zookeeper_create(uri, grpc_create_pick_first_lb_policy, + subchannel_factory); +} + +static const grpc_resolver_factory_vtable zookeeper_factory_vtable = { + zookeeper_factory_ref, zookeeper_factory_unref, + zookeeper_factory_create_resolver, zookeeper_factory_get_default_hostname, + "zookeeper"}; +static grpc_resolver_factory zookeeper_resolver_factory = { + &zookeeper_factory_vtable}; + +grpc_resolver_factory *grpc_zookeeper_resolver_factory_create() { + return &zookeeper_resolver_factory; +} diff --git a/src/core/client_config/resolvers/zookeeper_resolver.h b/src/core/client_config/resolvers/zookeeper_resolver.h new file mode 100644 index 00000000..a6f002dd --- /dev/null +++ b/src/core/client_config/resolvers/zookeeper_resolver.h @@ -0,0 +1,42 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_CLIENT_CONFIG_RESOLVERS_ZOOKEEPER_RESOLVER_H +#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_RESOLVERS_ZOOKEEPER_RESOLVER_H + +#include "src/core/client_config/resolver_factory.h" + +/** Create a zookeeper resolver factory */ +grpc_resolver_factory *grpc_zookeeper_resolver_factory_create(void); + +#endif /* GRPC_INTERNAL_CORE_CLIENT_CONFIG_RESOLVERS_ZOOKEEPER_RESOLVER_H */ diff --git a/src/core/client_config/subchannel.c b/src/core/client_config/subchannel.c new file mode 100644 index 00000000..ca52c75b --- /dev/null +++ b/src/core/client_config/subchannel.c @@ -0,0 +1,732 @@ +/* + * + * Copyright 2015, 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 "src/core/client_config/subchannel.h" + +#include + +#include + +#include "src/core/channel/channel_args.h" +#include "src/core/channel/client_channel.h" +#include "src/core/channel/connected_channel.h" +#include "src/core/iomgr/alarm.h" +#include "src/core/transport/connectivity_state.h" +#include "src/core/surface/channel.h" + +#define GRPC_SUBCHANNEL_MIN_CONNECT_TIMEOUT_SECONDS 20 +#define GRPC_SUBCHANNEL_INITIAL_CONNECT_BACKOFF_SECONDS 1 +#define GRPC_SUBCHANNEL_RECONNECT_BACKOFF_MULTIPLIER 1.6 +#define GRPC_SUBCHANNEL_RECONNECT_MAX_BACKOFF_SECONDS 120 +#define GRPC_SUBCHANNEL_RECONNECT_JITTER 0.2 + +typedef struct { + /* all fields protected by subchannel->mu */ + /** refcount */ + int refs; + /** parent subchannel */ + grpc_subchannel *subchannel; +} connection; + +typedef struct { + grpc_iomgr_closure closure; + size_t version; + grpc_subchannel *subchannel; + grpc_connectivity_state connectivity_state; +} state_watcher; + +typedef struct waiting_for_connect { + struct waiting_for_connect *next; + grpc_iomgr_closure *notify; + grpc_pollset *pollset; + grpc_subchannel_call **target; + grpc_subchannel *subchannel; + grpc_iomgr_closure continuation; +} waiting_for_connect; + +struct grpc_subchannel { + grpc_connector *connector; + + /** non-transport related channel filters */ + const grpc_channel_filter **filters; + size_t num_filters; + /** channel arguments */ + grpc_channel_args *args; + /** address to connect to */ + struct sockaddr *addr; + size_t addr_len; + /** metadata context */ + grpc_mdctx *mdctx; + /** master channel - the grpc_channel instance that ultimately owns + this channel_data via its channel stack. + We occasionally use this to bump the refcount on the master channel + to keep ourselves alive through an asynchronous operation. */ + grpc_channel *master; + /** have we seen a disconnection? */ + int disconnected; + + /** set during connection */ + grpc_connect_out_args connecting_result; + + /** callback for connection finishing */ + grpc_iomgr_closure connected; + + /** pollset_set tracking who's interested in a connection + being setup - owned by the master channel (in particular the + client_channel + filter there-in) */ + grpc_pollset_set *pollset_set; + + /** mutex protecting remaining elements */ + gpr_mu mu; + + /** active connection */ + connection *active; + /** version number for the active connection */ + size_t active_version; + /** refcount */ + int refs; + /** are we connecting */ + int connecting; + /** things waiting for a connection */ + waiting_for_connect *waiting; + /** connectivity state tracking */ + grpc_connectivity_state_tracker state_tracker; + + /** next connect attempt time */ + gpr_timespec next_attempt; + /** amount to backoff each failure */ + gpr_timespec backoff_delta; + /** do we have an active alarm? */ + int have_alarm; + /** our alarm */ + grpc_alarm alarm; + /** current random value */ + gpr_uint32 random; +}; + +struct grpc_subchannel_call { + connection *connection; + gpr_refcount refs; +}; + +#define SUBCHANNEL_CALL_TO_CALL_STACK(call) ((grpc_call_stack *)((call) + 1)) +#define CHANNEL_STACK_FROM_CONNECTION(con) ((grpc_channel_stack *)((con) + 1)) + +static grpc_subchannel_call *create_call(connection *con); +static void connectivity_state_changed_locked(grpc_subchannel *c, + const char *reason); +static grpc_connectivity_state compute_connectivity_locked(grpc_subchannel *c); +static gpr_timespec compute_connect_deadline(grpc_subchannel *c); +static void subchannel_connected(void *subchannel, int iomgr_success); + +static void subchannel_ref_locked( + grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS); +static int subchannel_unref_locked( + grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) GRPC_MUST_USE_RESULT; +static void connection_ref_locked(connection *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS); +static grpc_subchannel *connection_unref_locked( + connection *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) GRPC_MUST_USE_RESULT; +static void subchannel_destroy(grpc_subchannel *c); + +#ifdef GRPC_SUBCHANNEL_REFCOUNT_DEBUG +#define SUBCHANNEL_REF_LOCKED(p, r) \ + subchannel_ref_locked((p), __FILE__, __LINE__, (r)) +#define SUBCHANNEL_UNREF_LOCKED(p, r) \ + subchannel_unref_locked((p), __FILE__, __LINE__, (r)) +#define CONNECTION_REF_LOCKED(p, r) \ + connection_ref_locked((p), __FILE__, __LINE__, (r)) +#define CONNECTION_UNREF_LOCKED(p, r) \ + connection_unref_locked((p), __FILE__, __LINE__, (r)) +#define REF_PASS_ARGS , file, line, reason +#define REF_LOG(name, p) \ + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "%s: %p ref %d -> %d %s", \ + (name), (p), (p)->refs, (p)->refs + 1, reason) +#define UNREF_LOG(name, p) \ + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "%s: %p unref %d -> %d %s", \ + (name), (p), (p)->refs, (p)->refs - 1, reason) +#else +#define SUBCHANNEL_REF_LOCKED(p, r) subchannel_ref_locked((p)) +#define SUBCHANNEL_UNREF_LOCKED(p, r) subchannel_unref_locked((p)) +#define CONNECTION_REF_LOCKED(p, r) connection_ref_locked((p)) +#define CONNECTION_UNREF_LOCKED(p, r) connection_unref_locked((p)) +#define REF_PASS_ARGS +#define REF_LOG(name, p) \ + do { \ + } while (0) +#define UNREF_LOG(name, p) \ + do { \ + } while (0) +#endif + +/* + * connection implementation + */ + +static void connection_destroy(connection *c) { + GPR_ASSERT(c->refs == 0); + grpc_channel_stack_destroy(CHANNEL_STACK_FROM_CONNECTION(c)); + gpr_free(c); +} + +static void connection_ref_locked( + connection *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + REF_LOG("CONNECTION", c); + subchannel_ref_locked(c->subchannel REF_PASS_ARGS); + ++c->refs; +} + +static grpc_subchannel *connection_unref_locked( + connection *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + grpc_subchannel *destroy = NULL; + UNREF_LOG("CONNECTION", c); + if (subchannel_unref_locked(c->subchannel REF_PASS_ARGS)) { + destroy = c->subchannel; + } + if (--c->refs == 0 && c->subchannel->active != c) { + connection_destroy(c); + } + return destroy; +} + +/* + * grpc_subchannel implementation + */ + +static void subchannel_ref_locked( + grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + REF_LOG("SUBCHANNEL", c); + ++c->refs; +} + +static int subchannel_unref_locked( + grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + UNREF_LOG("SUBCHANNEL", c); + return --c->refs == 0; +} + +void grpc_subchannel_ref(grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + gpr_mu_lock(&c->mu); + subchannel_ref_locked(c REF_PASS_ARGS); + gpr_mu_unlock(&c->mu); +} + +void grpc_subchannel_unref(grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + int destroy; + gpr_mu_lock(&c->mu); + destroy = subchannel_unref_locked(c REF_PASS_ARGS); + gpr_mu_unlock(&c->mu); + if (destroy) subchannel_destroy(c); +} + +static void subchannel_destroy(grpc_subchannel *c) { + if (c->active != NULL) { + connection_destroy(c->active); + } + gpr_free(c->filters); + grpc_channel_args_destroy(c->args); + gpr_free(c->addr); + grpc_mdctx_unref(c->mdctx); + grpc_connectivity_state_destroy(&c->state_tracker); + grpc_connector_unref(c->connector); + gpr_free(c); +} + +void grpc_subchannel_add_interested_party(grpc_subchannel *c, + grpc_pollset *pollset) { + grpc_pollset_set_add_pollset(c->pollset_set, pollset); +} + +void grpc_subchannel_del_interested_party(grpc_subchannel *c, + grpc_pollset *pollset) { + grpc_pollset_set_del_pollset(c->pollset_set, pollset); +} + +static gpr_uint32 random_seed() { + return (gpr_uint32)(gpr_time_to_millis(gpr_now(GPR_CLOCK_MONOTONIC))); +} + +grpc_subchannel *grpc_subchannel_create(grpc_connector *connector, + grpc_subchannel_args *args) { + grpc_subchannel *c = gpr_malloc(sizeof(*c)); + grpc_channel_element *parent_elem = grpc_channel_stack_last_element( + grpc_channel_get_channel_stack(args->master)); + memset(c, 0, sizeof(*c)); + c->refs = 1; + c->connector = connector; + grpc_connector_ref(c->connector); + c->num_filters = args->filter_count; + c->filters = gpr_malloc(sizeof(grpc_channel_filter *) * c->num_filters); + memcpy(c->filters, args->filters, + sizeof(grpc_channel_filter *) * c->num_filters); + c->addr = gpr_malloc(args->addr_len); + memcpy(c->addr, args->addr, args->addr_len); + c->addr_len = args->addr_len; + c->args = grpc_channel_args_copy(args->args); + c->mdctx = args->mdctx; + c->master = args->master; + c->pollset_set = grpc_client_channel_get_connecting_pollset_set(parent_elem); + c->random = random_seed(); + grpc_mdctx_ref(c->mdctx); + grpc_iomgr_closure_init(&c->connected, subchannel_connected, c); + grpc_connectivity_state_init(&c->state_tracker, GRPC_CHANNEL_IDLE, + "subchannel"); + gpr_mu_init(&c->mu); + return c; +} + +static void continue_connect(grpc_subchannel *c) { + grpc_connect_in_args args; + + args.interested_parties = c->pollset_set; + args.addr = c->addr; + args.addr_len = c->addr_len; + args.deadline = compute_connect_deadline(c); + args.channel_args = c->args; + args.metadata_context = c->mdctx; + + grpc_connector_connect(c->connector, &args, &c->connecting_result, + &c->connected); +} + +static void start_connect(grpc_subchannel *c) { + c->backoff_delta = gpr_time_from_seconds( + GRPC_SUBCHANNEL_INITIAL_CONNECT_BACKOFF_SECONDS, GPR_TIMESPAN); + c->next_attempt = + gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), c->backoff_delta); + continue_connect(c); +} + +static void continue_creating_call(void *arg, int iomgr_success) { + waiting_for_connect *w4c = arg; + grpc_subchannel_del_interested_party(w4c->subchannel, w4c->pollset); + grpc_subchannel_create_call(w4c->subchannel, w4c->pollset, w4c->target, + w4c->notify); + GRPC_SUBCHANNEL_UNREF(w4c->subchannel, "waiting_for_connect"); + gpr_free(w4c); +} + +void grpc_subchannel_create_call(grpc_subchannel *c, grpc_pollset *pollset, + grpc_subchannel_call **target, + grpc_iomgr_closure *notify) { + connection *con; + gpr_mu_lock(&c->mu); + if (c->active != NULL) { + con = c->active; + CONNECTION_REF_LOCKED(con, "call"); + gpr_mu_unlock(&c->mu); + + *target = create_call(con); + notify->cb(notify->cb_arg, 1); + } else { + waiting_for_connect *w4c = gpr_malloc(sizeof(*w4c)); + w4c->next = c->waiting; + w4c->notify = notify; + w4c->pollset = pollset; + w4c->target = target; + w4c->subchannel = c; + /* released when clearing w4c */ + SUBCHANNEL_REF_LOCKED(c, "waiting_for_connect"); + grpc_iomgr_closure_init(&w4c->continuation, continue_creating_call, w4c); + c->waiting = w4c; + grpc_subchannel_add_interested_party(c, pollset); + if (!c->connecting) { + c->connecting = 1; + connectivity_state_changed_locked(c, "create_call"); + /* released by connection */ + SUBCHANNEL_REF_LOCKED(c, "connecting"); + GRPC_CHANNEL_INTERNAL_REF(c->master, "connecting"); + gpr_mu_unlock(&c->mu); + + start_connect(c); + } else { + gpr_mu_unlock(&c->mu); + } + } +} + +grpc_connectivity_state grpc_subchannel_check_connectivity(grpc_subchannel *c) { + grpc_connectivity_state state; + gpr_mu_lock(&c->mu); + state = grpc_connectivity_state_check(&c->state_tracker); + gpr_mu_unlock(&c->mu); + return state; +} + +void grpc_subchannel_notify_on_state_change(grpc_subchannel *c, + grpc_connectivity_state *state, + grpc_iomgr_closure *notify) { + int do_connect = 0; + gpr_mu_lock(&c->mu); + if (grpc_connectivity_state_notify_on_state_change(&c->state_tracker, state, + notify)) { + do_connect = 1; + c->connecting = 1; + /* released by connection */ + SUBCHANNEL_REF_LOCKED(c, "connecting"); + GRPC_CHANNEL_INTERNAL_REF(c->master, "connecting"); + connectivity_state_changed_locked(c, "state_change"); + } + gpr_mu_unlock(&c->mu); + if (do_connect) { + start_connect(c); + } +} + +void grpc_subchannel_process_transport_op(grpc_subchannel *c, + grpc_transport_op *op) { + connection *con = NULL; + grpc_subchannel *destroy; + int cancel_alarm = 0; + gpr_mu_lock(&c->mu); + if (op->disconnect) { + c->disconnected = 1; + connectivity_state_changed_locked(c, "disconnect"); + if (c->have_alarm) { + cancel_alarm = 1; + } + } + if (c->active != NULL) { + con = c->active; + CONNECTION_REF_LOCKED(con, "transport-op"); + } + gpr_mu_unlock(&c->mu); + + if (con != NULL) { + grpc_channel_stack *channel_stack = CHANNEL_STACK_FROM_CONNECTION(con); + grpc_channel_element *top_elem = + grpc_channel_stack_element(channel_stack, 0); + top_elem->filter->start_transport_op(top_elem, op); + + gpr_mu_lock(&c->mu); + destroy = CONNECTION_UNREF_LOCKED(con, "transport-op"); + gpr_mu_unlock(&c->mu); + if (destroy) { + subchannel_destroy(destroy); + } + } + + if (cancel_alarm) { + grpc_alarm_cancel(&c->alarm); + } +} + +static void on_state_changed(void *p, int iomgr_success) { + state_watcher *sw = p; + grpc_subchannel *c = sw->subchannel; + gpr_mu *mu = &c->mu; + int destroy; + grpc_transport_op op; + grpc_channel_element *elem; + connection *destroy_connection = NULL; + + gpr_mu_lock(mu); + + /* if we failed or there is a version number mismatch, just leave + this closure */ + if (!iomgr_success || sw->subchannel->active_version != sw->version) { + goto done; + } + + switch (sw->connectivity_state) { + case GRPC_CHANNEL_CONNECTING: + case GRPC_CHANNEL_READY: + case GRPC_CHANNEL_IDLE: + /* all is still good: keep watching */ + memset(&op, 0, sizeof(op)); + op.connectivity_state = &sw->connectivity_state; + op.on_connectivity_state_change = &sw->closure; + elem = grpc_channel_stack_element( + CHANNEL_STACK_FROM_CONNECTION(c->active), 0); + elem->filter->start_transport_op(elem, &op); + /* early out */ + gpr_mu_unlock(mu); + return; + case GRPC_CHANNEL_FATAL_FAILURE: + case GRPC_CHANNEL_TRANSIENT_FAILURE: + /* things have gone wrong, deactivate and enter idle */ + if (sw->subchannel->active->refs == 0) { + destroy_connection = sw->subchannel->active; + } + sw->subchannel->active = NULL; + grpc_connectivity_state_set( + &c->state_tracker, c->disconnected ? GRPC_CHANNEL_FATAL_FAILURE + : GRPC_CHANNEL_TRANSIENT_FAILURE, + "connection_failed"); + break; + } + +done: + connectivity_state_changed_locked(c, "transport_state_changed"); + destroy = SUBCHANNEL_UNREF_LOCKED(c, "state_watcher"); + gpr_free(sw); + gpr_mu_unlock(mu); + if (destroy) { + subchannel_destroy(c); + } + if (destroy_connection != NULL) { + connection_destroy(destroy_connection); + } +} + +static void publish_transport(grpc_subchannel *c) { + size_t channel_stack_size; + connection *con; + grpc_channel_stack *stk; + size_t num_filters; + const grpc_channel_filter **filters; + waiting_for_connect *w4c; + grpc_transport_op op; + state_watcher *sw; + connection *destroy_connection = NULL; + grpc_channel_element *elem; + + /* build final filter list */ + num_filters = c->num_filters + c->connecting_result.num_filters + 1; + filters = gpr_malloc(sizeof(*filters) * num_filters); + memcpy(filters, c->filters, sizeof(*filters) * c->num_filters); + memcpy(filters + c->num_filters, c->connecting_result.filters, + sizeof(*filters) * c->connecting_result.num_filters); + filters[num_filters - 1] = &grpc_connected_channel_filter; + + /* construct channel stack */ + channel_stack_size = grpc_channel_stack_size(filters, num_filters); + con = gpr_malloc(sizeof(connection) + channel_stack_size); + stk = (grpc_channel_stack *)(con + 1); + con->refs = 0; + con->subchannel = c; + grpc_channel_stack_init(filters, num_filters, c->master, c->args, c->mdctx, + stk); + grpc_connected_channel_bind_transport(stk, c->connecting_result.transport); + gpr_free(c->connecting_result.filters); + memset(&c->connecting_result, 0, sizeof(c->connecting_result)); + + /* initialize state watcher */ + sw = gpr_malloc(sizeof(*sw)); + grpc_iomgr_closure_init(&sw->closure, on_state_changed, sw); + sw->subchannel = c; + sw->connectivity_state = GRPC_CHANNEL_READY; + + gpr_mu_lock(&c->mu); + + if (c->disconnected) { + gpr_mu_unlock(&c->mu); + gpr_free(sw); + gpr_free(filters); + grpc_channel_stack_destroy(stk); + GRPC_CHANNEL_INTERNAL_UNREF(c->master, "connecting"); + GRPC_SUBCHANNEL_UNREF(c, "connecting"); + return; + } + + /* publish */ + if (c->active != NULL && c->active->refs == 0) { + destroy_connection = c->active; + } + c->active = con; + c->active_version++; + sw->version = c->active_version; + c->connecting = 0; + + /* watch for changes; subchannel ref for connecting is donated + to the state watcher */ + memset(&op, 0, sizeof(op)); + op.connectivity_state = &sw->connectivity_state; + op.on_connectivity_state_change = &sw->closure; + op.bind_pollset_set = c->pollset_set; + SUBCHANNEL_REF_LOCKED(c, "state_watcher"); + GRPC_CHANNEL_INTERNAL_UNREF(c->master, "connecting"); + GPR_ASSERT(!SUBCHANNEL_UNREF_LOCKED(c, "connecting")); + elem = + grpc_channel_stack_element(CHANNEL_STACK_FROM_CONNECTION(c->active), 0); + elem->filter->start_transport_op(elem, &op); + + /* signal completion */ + connectivity_state_changed_locked(c, "connected"); + while ((w4c = c->waiting)) { + c->waiting = w4c->next; + grpc_iomgr_add_callback(&w4c->continuation); + } + + gpr_mu_unlock(&c->mu); + + gpr_free(filters); + + if (destroy_connection != NULL) { + connection_destroy(destroy_connection); + } +} + +/* Generate a random number between 0 and 1. */ +static double generate_uniform_random_number(grpc_subchannel *c) { + c->random = (1103515245 * c->random + 12345) % ((gpr_uint32)1 << 31); + return c->random / (double)((gpr_uint32)1 << 31); +} + +/* Update backoff_delta and next_attempt in subchannel */ +static void update_reconnect_parameters(grpc_subchannel *c) { + gpr_int32 backoff_delta_millis, jitter; + gpr_int32 max_backoff_millis = + GRPC_SUBCHANNEL_RECONNECT_MAX_BACKOFF_SECONDS * 1000; + double jitter_range; + backoff_delta_millis = + (gpr_int32)(gpr_time_to_millis(c->backoff_delta) * + GRPC_SUBCHANNEL_RECONNECT_BACKOFF_MULTIPLIER); + if (backoff_delta_millis > max_backoff_millis) { + backoff_delta_millis = max_backoff_millis; + } + c->backoff_delta = gpr_time_from_millis(backoff_delta_millis, GPR_TIMESPAN); + c->next_attempt = + gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), c->backoff_delta); + + jitter_range = GRPC_SUBCHANNEL_RECONNECT_JITTER * backoff_delta_millis; + jitter = + (gpr_int32)((2 * generate_uniform_random_number(c) - 1) * jitter_range); + c->next_attempt = + gpr_time_add(c->next_attempt, gpr_time_from_millis(jitter, GPR_TIMESPAN)); +} + +static void on_alarm(void *arg, int iomgr_success) { + grpc_subchannel *c = arg; + gpr_mu_lock(&c->mu); + c->have_alarm = 0; + if (c->disconnected) { + iomgr_success = 0; + } + connectivity_state_changed_locked(c, "alarm"); + gpr_mu_unlock(&c->mu); + if (iomgr_success) { + update_reconnect_parameters(c); + continue_connect(c); + } else { + GRPC_CHANNEL_INTERNAL_UNREF(c->master, "connecting"); + GRPC_SUBCHANNEL_UNREF(c, "connecting"); + } +} + +static void subchannel_connected(void *arg, int iomgr_success) { + grpc_subchannel *c = arg; + if (c->connecting_result.transport != NULL) { + publish_transport(c); + } else { + gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); + gpr_mu_lock(&c->mu); + GPR_ASSERT(!c->have_alarm); + c->have_alarm = 1; + connectivity_state_changed_locked(c, "connect_failed"); + grpc_alarm_init(&c->alarm, c->next_attempt, on_alarm, c, now); + gpr_mu_unlock(&c->mu); + } +} + +static gpr_timespec compute_connect_deadline(grpc_subchannel *c) { + gpr_timespec current_deadline = + gpr_time_add(c->next_attempt, c->backoff_delta); + gpr_timespec min_deadline = gpr_time_add( + gpr_now(GPR_CLOCK_MONOTONIC), + gpr_time_from_seconds(GRPC_SUBCHANNEL_MIN_CONNECT_TIMEOUT_SECONDS, + GPR_TIMESPAN)); + return gpr_time_cmp(current_deadline, min_deadline) > 0 ? current_deadline + : min_deadline; +} + +static grpc_connectivity_state compute_connectivity_locked(grpc_subchannel *c) { + if (c->disconnected) { + return GRPC_CHANNEL_FATAL_FAILURE; + } + if (c->connecting) { + if (c->have_alarm) { + return GRPC_CHANNEL_TRANSIENT_FAILURE; + } + return GRPC_CHANNEL_CONNECTING; + } + if (c->active) { + return GRPC_CHANNEL_READY; + } + return GRPC_CHANNEL_IDLE; +} + +static void connectivity_state_changed_locked(grpc_subchannel *c, + const char *reason) { + grpc_connectivity_state current = compute_connectivity_locked(c); + grpc_connectivity_state_set(&c->state_tracker, current, reason); +} + +/* + * grpc_subchannel_call implementation + */ + +void grpc_subchannel_call_ref( + grpc_subchannel_call *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + gpr_ref(&c->refs); +} + +void grpc_subchannel_call_unref( + grpc_subchannel_call *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + if (gpr_unref(&c->refs)) { + gpr_mu *mu = &c->connection->subchannel->mu; + grpc_subchannel *destroy; + grpc_call_stack_destroy(SUBCHANNEL_CALL_TO_CALL_STACK(c)); + gpr_mu_lock(mu); + destroy = CONNECTION_UNREF_LOCKED(c->connection, "call"); + gpr_mu_unlock(mu); + gpr_free(c); + if (destroy != NULL) { + subchannel_destroy(destroy); + } + } +} + +char *grpc_subchannel_call_get_peer(grpc_subchannel_call *call) { + grpc_call_stack *call_stack = SUBCHANNEL_CALL_TO_CALL_STACK(call); + grpc_call_element *top_elem = grpc_call_stack_element(call_stack, 0); + return top_elem->filter->get_peer(top_elem); +} + +void grpc_subchannel_call_process_op(grpc_subchannel_call *call, + grpc_transport_stream_op *op) { + grpc_call_stack *call_stack = SUBCHANNEL_CALL_TO_CALL_STACK(call); + grpc_call_element *top_elem = grpc_call_stack_element(call_stack, 0); + top_elem->filter->start_transport_stream_op(top_elem, op); +} + +grpc_subchannel_call *create_call(connection *con) { + grpc_channel_stack *chanstk = CHANNEL_STACK_FROM_CONNECTION(con); + grpc_subchannel_call *call = + gpr_malloc(sizeof(grpc_subchannel_call) + chanstk->call_stack_size); + grpc_call_stack *callstk = SUBCHANNEL_CALL_TO_CALL_STACK(call); + call->connection = con; + gpr_ref_init(&call->refs, 1); + grpc_call_stack_init(chanstk, NULL, NULL, callstk); + return call; +} diff --git a/src/core/client_config/subchannel.h b/src/core/client_config/subchannel.h new file mode 100644 index 00000000..2e36c691 --- /dev/null +++ b/src/core/client_config/subchannel.h @@ -0,0 +1,129 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_CLIENT_CONFIG_SUBCHANNEL_H +#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_SUBCHANNEL_H + +#include "src/core/channel/channel_stack.h" +#include "src/core/client_config/connector.h" + +/** A (sub-)channel that knows how to connect to exactly one target + address. Provides a target for load balancing. */ +typedef struct grpc_subchannel grpc_subchannel; +typedef struct grpc_subchannel_call grpc_subchannel_call; +typedef struct grpc_subchannel_args grpc_subchannel_args; + +#ifdef GRPC_SUBCHANNEL_REFCOUNT_DEBUG +#define GRPC_SUBCHANNEL_REF(p, r) \ + grpc_subchannel_ref((p), __FILE__, __LINE__, (r)) +#define GRPC_SUBCHANNEL_UNREF(p, r) \ + grpc_subchannel_unref((p), __FILE__, __LINE__, (r)) +#define GRPC_SUBCHANNEL_CALL_REF(p, r) \ + grpc_subchannel_call_ref((p), __FILE__, __LINE__, (r)) +#define GRPC_SUBCHANNEL_CALL_UNREF(p, r) \ + grpc_subchannel_call_unref((p), __FILE__, __LINE__, (r)) +#define GRPC_SUBCHANNEL_REF_EXTRA_ARGS \ + , const char *file, int line, const char *reason +#else +#define GRPC_SUBCHANNEL_REF(p, r) grpc_subchannel_ref((p)) +#define GRPC_SUBCHANNEL_UNREF(p, r) grpc_subchannel_unref((p)) +#define GRPC_SUBCHANNEL_CALL_REF(p, r) grpc_subchannel_call_ref((p)) +#define GRPC_SUBCHANNEL_CALL_UNREF(p, r) grpc_subchannel_call_unref((p)) +#define GRPC_SUBCHANNEL_REF_EXTRA_ARGS +#endif + +void grpc_subchannel_ref( + grpc_subchannel *channel GRPC_SUBCHANNEL_REF_EXTRA_ARGS); +void grpc_subchannel_unref( + grpc_subchannel *channel GRPC_SUBCHANNEL_REF_EXTRA_ARGS); +void grpc_subchannel_call_ref( + grpc_subchannel_call *call GRPC_SUBCHANNEL_REF_EXTRA_ARGS); +void grpc_subchannel_call_unref( + grpc_subchannel_call *call GRPC_SUBCHANNEL_REF_EXTRA_ARGS); + +/** construct a call (possibly asynchronously) */ +void grpc_subchannel_create_call(grpc_subchannel *subchannel, + grpc_pollset *pollset, + grpc_subchannel_call **target, + grpc_iomgr_closure *notify); + +/** process a transport level op */ +void grpc_subchannel_process_transport_op(grpc_subchannel *subchannel, + grpc_transport_op *op); + +/** poll the current connectivity state of a channel */ +grpc_connectivity_state grpc_subchannel_check_connectivity( + grpc_subchannel *channel); + +/** call notify when the connectivity state of a channel changes from *state. + Updates *state with the new state of the channel */ +void grpc_subchannel_notify_on_state_change(grpc_subchannel *channel, + grpc_connectivity_state *state, + grpc_iomgr_closure *notify); + +/** express interest in \a channel's activities through \a pollset. */ +void grpc_subchannel_add_interested_party(grpc_subchannel *channel, + grpc_pollset *pollset); +/** stop following \a channel's activity through \a pollset. */ +void grpc_subchannel_del_interested_party(grpc_subchannel *channel, + grpc_pollset *pollset); + +/** continue processing a transport op */ +void grpc_subchannel_call_process_op(grpc_subchannel_call *subchannel_call, + grpc_transport_stream_op *op); + +/** continue querying for peer */ +char *grpc_subchannel_call_get_peer(grpc_subchannel_call *subchannel_call); + +struct grpc_subchannel_args { + /** Channel filters for this channel - wrapped factories will likely + want to mutate this */ + const grpc_channel_filter **filters; + /** The number of filters in the above array */ + size_t filter_count; + /** Channel arguments to be supplied to the newly created channel */ + const grpc_channel_args *args; + /** Address to connect to */ + struct sockaddr *addr; + size_t addr_len; + /** metadata context to use */ + grpc_mdctx *mdctx; + /** master channel */ + grpc_channel *master; +}; + +/** create a subchannel given a connector */ +grpc_subchannel *grpc_subchannel_create(grpc_connector *connector, + grpc_subchannel_args *args); + +#endif /* GRPC_INTERNAL_CORE_CLIENT_CONFIG_SUBCHANNEL_H */ diff --git a/src/core/client_config/subchannel_factory.c b/src/core/client_config/subchannel_factory.c new file mode 100644 index 00000000..f7138659 --- /dev/null +++ b/src/core/client_config/subchannel_factory.c @@ -0,0 +1,46 @@ +/* + * + * Copyright 2015, 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 "src/core/client_config/subchannel_factory.h" + +void grpc_subchannel_factory_ref(grpc_subchannel_factory *factory) { + factory->vtable->ref(factory); +} +void grpc_subchannel_factory_unref(grpc_subchannel_factory *factory) { + factory->vtable->unref(factory); +} + +grpc_subchannel *grpc_subchannel_factory_create_subchannel( + grpc_subchannel_factory *factory, grpc_subchannel_args *args) { + return factory->vtable->create_subchannel(factory, args); +} diff --git a/src/core/client_config/subchannel_factory.h b/src/core/client_config/subchannel_factory.h new file mode 100644 index 00000000..d7eae1c9 --- /dev/null +++ b/src/core/client_config/subchannel_factory.h @@ -0,0 +1,63 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_CLIENT_CONFIG_SUBCHANNEL_FACTORY_H +#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_SUBCHANNEL_FACTORY_H + +#include "src/core/channel/channel_stack.h" +#include "src/core/client_config/subchannel.h" + +typedef struct grpc_subchannel_factory grpc_subchannel_factory; +typedef struct grpc_subchannel_factory_vtable grpc_subchannel_factory_vtable; + +/** Constructor for new configured channels. + Creating decorators around this type is encouraged to adapt behavior. */ +struct grpc_subchannel_factory { + const grpc_subchannel_factory_vtable *vtable; +}; + +struct grpc_subchannel_factory_vtable { + void (*ref)(grpc_subchannel_factory *factory); + void (*unref)(grpc_subchannel_factory *factory); + grpc_subchannel *(*create_subchannel)(grpc_subchannel_factory *factory, + grpc_subchannel_args *args); +}; + +void grpc_subchannel_factory_ref(grpc_subchannel_factory *factory); +void grpc_subchannel_factory_unref(grpc_subchannel_factory *factory); + +/** Create a new grpc_subchannel */ +grpc_subchannel *grpc_subchannel_factory_create_subchannel( + grpc_subchannel_factory *factory, grpc_subchannel_args *args); + +#endif /* GRPC_INTERNAL_CORE_CLIENT_CONFIG_SUBCHANNEL_FACTORY_H */ diff --git a/src/core/client_config/subchannel_factory_decorators/add_channel_arg.c b/src/core/client_config/subchannel_factory_decorators/add_channel_arg.c new file mode 100644 index 00000000..585e465f --- /dev/null +++ b/src/core/client_config/subchannel_factory_decorators/add_channel_arg.c @@ -0,0 +1,43 @@ +/* + * + * Copyright 2015, 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 "src/core/client_config/subchannel_factory_decorators/add_channel_arg.h" +#include "src/core/client_config/subchannel_factory_decorators/merge_channel_args.h" + +grpc_subchannel_factory *grpc_subchannel_factory_add_channel_arg( + grpc_subchannel_factory *input, const grpc_arg *arg) { + grpc_channel_args args; + args.num_args = 1; + args.args = (grpc_arg *)arg; + return grpc_subchannel_factory_merge_channel_args(input, &args); +} diff --git a/src/core/client_config/subchannel_factory_decorators/add_channel_arg.h b/src/core/client_config/subchannel_factory_decorators/add_channel_arg.h new file mode 100644 index 00000000..84572940 --- /dev/null +++ b/src/core/client_config/subchannel_factory_decorators/add_channel_arg.h @@ -0,0 +1,46 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_CLIENT_CONFIG_SUBCHANNEL_FACTORY_DECORATORS_ADD_CHANNEL_ARG_H +#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_SUBCHANNEL_FACTORY_DECORATORS_ADD_CHANNEL_ARG_H + +#include "src/core/client_config/subchannel_factory.h" + +/** Takes a subchannel factory, returns a new one that mutates incoming + channel_args by adding a new argument; ownership of input, arg is retained + by the caller. */ +grpc_subchannel_factory *grpc_subchannel_factory_add_channel_arg( + grpc_subchannel_factory *input, const grpc_arg *arg); + +#endif /* GRPC_INTERNAL_CORE_CLIENT_CONFIG_SUBCHANNEL_FACTORY_DECORATORS_ADD_CHANNEL_ARG_H \ + */ diff --git a/src/core/client_config/subchannel_factory_decorators/merge_channel_args.c b/src/core/client_config/subchannel_factory_decorators/merge_channel_args.c new file mode 100644 index 00000000..c1b5507f --- /dev/null +++ b/src/core/client_config/subchannel_factory_decorators/merge_channel_args.c @@ -0,0 +1,84 @@ +/* + * + * Copyright 2015, 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 "src/core/client_config/subchannel_factory_decorators/merge_channel_args.h" +#include +#include "src/core/channel/channel_args.h" + +typedef struct { + grpc_subchannel_factory base; + gpr_refcount refs; + grpc_subchannel_factory *wrapped; + grpc_channel_args *merge_args; +} merge_args_factory; + +static void merge_args_factory_ref(grpc_subchannel_factory *scf) { + merge_args_factory *f = (merge_args_factory *)scf; + gpr_ref(&f->refs); +} + +static void merge_args_factory_unref(grpc_subchannel_factory *scf) { + merge_args_factory *f = (merge_args_factory *)scf; + if (gpr_unref(&f->refs)) { + grpc_subchannel_factory_unref(f->wrapped); + grpc_channel_args_destroy(f->merge_args); + gpr_free(f); + } +} + +static grpc_subchannel *merge_args_factory_create_subchannel( + grpc_subchannel_factory *scf, grpc_subchannel_args *args) { + merge_args_factory *f = (merge_args_factory *)scf; + grpc_channel_args *final_args = + grpc_channel_args_merge(args->args, f->merge_args); + grpc_subchannel *s; + args->args = final_args; + s = grpc_subchannel_factory_create_subchannel(f->wrapped, args); + grpc_channel_args_destroy(final_args); + return s; +} + +static const grpc_subchannel_factory_vtable merge_args_factory_vtable = { + merge_args_factory_ref, merge_args_factory_unref, + merge_args_factory_create_subchannel}; + +grpc_subchannel_factory *grpc_subchannel_factory_merge_channel_args( + grpc_subchannel_factory *input, const grpc_channel_args *args) { + merge_args_factory *f = gpr_malloc(sizeof(*f)); + f->base.vtable = &merge_args_factory_vtable; + gpr_ref_init(&f->refs, 1); + grpc_subchannel_factory_ref(input); + f->wrapped = input; + f->merge_args = grpc_channel_args_copy(args); + return &f->base; +} diff --git a/src/core/client_config/subchannel_factory_decorators/merge_channel_args.h b/src/core/client_config/subchannel_factory_decorators/merge_channel_args.h new file mode 100644 index 00000000..f4757f06 --- /dev/null +++ b/src/core/client_config/subchannel_factory_decorators/merge_channel_args.h @@ -0,0 +1,46 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_CLIENT_CONFIG_SUBCHANNEL_FACTORY_DECORATORS_MERGE_CHANNEL_ARGS_H +#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_SUBCHANNEL_FACTORY_DECORATORS_MERGE_CHANNEL_ARGS_H + +#include "src/core/client_config/subchannel_factory.h" + +/** Takes a subchannel factory, returns a new one that mutates incoming + channel_args by adding a new argument; ownership of input, args is retained + by the caller. */ +grpc_subchannel_factory *grpc_subchannel_factory_merge_channel_args( + grpc_subchannel_factory *input, const grpc_channel_args *args); + +#endif /* GRPC_INTERNAL_CORE_CLIENT_CONFIG_SUBCHANNEL_FACTORY_DECORATORS_MERGE_CHANNEL_ARGS_H \ + */ diff --git a/src/core/client_config/uri_parser.c b/src/core/client_config/uri_parser.c new file mode 100644 index 00000000..410a61c8 --- /dev/null +++ b/src/core/client_config/uri_parser.c @@ -0,0 +1,149 @@ +/* + * + * Copyright 2015, 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 "src/core/client_config/uri_parser.h" + +#include + +#include +#include +#include + +static grpc_uri *bad_uri(const char *uri_text, int pos, const char *section, + int suppress_errors) { + char *line_prefix; + int pfx_len; + + if (!suppress_errors) { + gpr_asprintf(&line_prefix, "bad uri.%s: '", section); + pfx_len = strlen(line_prefix) + pos; + gpr_log(GPR_ERROR, "%s%s'", line_prefix, uri_text); + gpr_free(line_prefix); + + line_prefix = gpr_malloc(pfx_len + 1); + memset(line_prefix, ' ', pfx_len); + line_prefix[pfx_len] = 0; + gpr_log(GPR_ERROR, "%s^ here", line_prefix); + gpr_free(line_prefix); + } + + return NULL; +} + +static char *copy_fragment(const char *src, int begin, int end) { + char *out = gpr_malloc(end - begin + 1); + memcpy(out, src + begin, end - begin); + out[end - begin] = 0; + return out; +} + +grpc_uri *grpc_uri_parse(const char *uri_text, int suppress_errors) { + grpc_uri *uri; + int scheme_begin = 0; + int scheme_end = -1; + int authority_begin = -1; + int authority_end = -1; + int path_begin = -1; + int path_end = -1; + int i; + + for (i = scheme_begin; uri_text[i] != 0; i++) { + if (uri_text[i] == ':') { + scheme_end = i; + break; + } + if (uri_text[i] >= 'a' && uri_text[i] <= 'z') continue; + if (uri_text[i] >= 'A' && uri_text[i] <= 'Z') continue; + if (i != scheme_begin) { + if (uri_text[i] >= '0' && uri_text[i] <= '9') continue; + if (uri_text[i] == '+') continue; + if (uri_text[i] == '-') continue; + if (uri_text[i] == '.') continue; + } + break; + } + if (scheme_end == -1) { + return bad_uri(uri_text, i, "scheme", suppress_errors); + } + + if (uri_text[scheme_end + 1] == '/' && uri_text[scheme_end + 2] == '/') { + authority_begin = scheme_end + 3; + for (i = authority_begin; uri_text[i] != 0 && authority_end == -1; i++) { + if (uri_text[i] == '/') { + authority_end = i; + } + if (uri_text[i] == '?') { + return bad_uri(uri_text, i, "query_not_supported", suppress_errors); + } + if (uri_text[i] == '#') { + return bad_uri(uri_text, i, "fragment_not_supported", suppress_errors); + } + } + if (authority_end == -1 && uri_text[i] == 0) { + authority_end = i; + } + if (authority_end == -1) { + return bad_uri(uri_text, i, "authority", suppress_errors); + } + /* TODO(ctiller): parse the authority correctly */ + path_begin = authority_end; + } else { + path_begin = scheme_end + 1; + } + + for (i = path_begin; uri_text[i] != 0; i++) { + if (uri_text[i] == '?') { + return bad_uri(uri_text, i, "query_not_supported", suppress_errors); + } + if (uri_text[i] == '#') { + return bad_uri(uri_text, i, "fragment_not_supported", suppress_errors); + } + } + path_end = i; + + uri = gpr_malloc(sizeof(*uri)); + memset(uri, 0, sizeof(*uri)); + uri->scheme = copy_fragment(uri_text, scheme_begin, scheme_end); + uri->authority = copy_fragment(uri_text, authority_begin, authority_end); + uri->path = copy_fragment(uri_text, path_begin, path_end); + + return uri; +} + +void grpc_uri_destroy(grpc_uri *uri) { + if (!uri) return; + gpr_free(uri->scheme); + gpr_free(uri->authority); + gpr_free(uri->path); + gpr_free(uri); +} diff --git a/src/core/client_config/uri_parser.h b/src/core/client_config/uri_parser.h new file mode 100644 index 00000000..ce4e6aec --- /dev/null +++ b/src/core/client_config/uri_parser.h @@ -0,0 +1,49 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_CLIENT_CONFIG_URI_PARSER_H +#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_URI_PARSER_H + +typedef struct { + char *scheme; + char *authority; + char *path; +} grpc_uri; + +/** parse a uri, return NULL on failure */ +grpc_uri *grpc_uri_parse(const char *uri_text, int suppress_errors); + +/** destroy a uri */ +void grpc_uri_destroy(grpc_uri *uri); + +#endif diff --git a/src/core/compression/algorithm.c b/src/core/compression/algorithm.c new file mode 100644 index 00000000..6ed6dbe9 --- /dev/null +++ b/src/core/compression/algorithm.c @@ -0,0 +1,104 @@ +/* + * + * Copyright 2015, 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 +#include +#include + +int grpc_compression_algorithm_parse(const char *name, size_t name_length, + grpc_compression_algorithm *algorithm) { + /* we use strncmp not only because it's safer (even though in this case it + * doesn't matter, given that we are comparing against string literals, but + * because this way we needn't have "name" nil-terminated (useful for slice + * data, for example) */ + if (name_length == 0) { + return 0; + } + if (strncmp(name, "identity", name_length) == 0) { + *algorithm = GRPC_COMPRESS_NONE; + } else if (strncmp(name, "gzip", name_length) == 0) { + *algorithm = GRPC_COMPRESS_GZIP; + } else if (strncmp(name, "deflate", name_length) == 0) { + *algorithm = GRPC_COMPRESS_DEFLATE; + } else { + return 0; + } + return 1; +} + +int grpc_compression_algorithm_name(grpc_compression_algorithm algorithm, + char **name) { + switch (algorithm) { + case GRPC_COMPRESS_NONE: + *name = "identity"; + break; + case GRPC_COMPRESS_DEFLATE: + *name = "deflate"; + break; + case GRPC_COMPRESS_GZIP: + *name = "gzip"; + break; + default: + return 0; + } + return 1; +} + +/* TODO(dgq): Add the ability to specify parameters to the individual + * compression algorithms */ +grpc_compression_algorithm grpc_compression_algorithm_for_level( + grpc_compression_level level) { + switch (level) { + case GRPC_COMPRESS_LEVEL_NONE: + return GRPC_COMPRESS_NONE; + case GRPC_COMPRESS_LEVEL_LOW: + case GRPC_COMPRESS_LEVEL_MED: + case GRPC_COMPRESS_LEVEL_HIGH: + return GRPC_COMPRESS_DEFLATE; + default: + /* we shouldn't be making it here */ + abort(); + } +} + +grpc_compression_level grpc_compression_level_for_algorithm( + grpc_compression_algorithm algorithm) { + grpc_compression_level clevel; + for (clevel = GRPC_COMPRESS_LEVEL_NONE; clevel < GRPC_COMPRESS_LEVEL_COUNT; + ++clevel) { + if (grpc_compression_algorithm_for_level(clevel) == algorithm) { + return clevel; + } + } + abort(); +} diff --git a/src/core/compression/message_compress.c b/src/core/compression/message_compress.c new file mode 100644 index 00000000..7856f40d --- /dev/null +++ b/src/core/compression/message_compress.c @@ -0,0 +1,190 @@ +/* + * + * Copyright 2015, 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 "src/core/compression/message_compress.h" + +#include + +#include +#include + +#include + +#define OUTPUT_BLOCK_SIZE 1024 + +static int zlib_body(z_stream *zs, gpr_slice_buffer *input, + gpr_slice_buffer *output, + int (*flate)(z_stream *zs, int flush)) { + int r; + int flush; + size_t i; + gpr_slice outbuf = gpr_slice_malloc(OUTPUT_BLOCK_SIZE); + + zs->avail_out = GPR_SLICE_LENGTH(outbuf); + zs->next_out = GPR_SLICE_START_PTR(outbuf); + flush = Z_NO_FLUSH; + for (i = 0; i < input->count; i++) { + if (i == input->count - 1) flush = Z_FINISH; + zs->avail_in = GPR_SLICE_LENGTH(input->slices[i]); + zs->next_in = GPR_SLICE_START_PTR(input->slices[i]); + do { + if (zs->avail_out == 0) { + gpr_slice_buffer_add_indexed(output, outbuf); + outbuf = gpr_slice_malloc(OUTPUT_BLOCK_SIZE); + zs->avail_out = GPR_SLICE_LENGTH(outbuf); + zs->next_out = GPR_SLICE_START_PTR(outbuf); + } + r = flate(zs, flush); + if (r == Z_STREAM_ERROR) { + gpr_log(GPR_INFO, "zlib: stream error"); + goto error; + } + } while (zs->avail_out == 0); + if (zs->avail_in) { + gpr_log(GPR_INFO, "zlib: not all input consumed"); + goto error; + } + } + + GPR_ASSERT(outbuf.refcount); + outbuf.data.refcounted.length -= zs->avail_out; + gpr_slice_buffer_add_indexed(output, outbuf); + + return 1; + +error: + gpr_slice_unref(outbuf); + return 0; +} + +static int zlib_compress(gpr_slice_buffer *input, gpr_slice_buffer *output, + int gzip) { + z_stream zs; + int r; + size_t i; + size_t count_before = output->count; + size_t length_before = output->length; + memset(&zs, 0, sizeof(zs)); + r = deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 | (gzip ? 16 : 0), + 8, Z_DEFAULT_STRATEGY); + if (r != Z_OK) { + gpr_log(GPR_ERROR, "deflateInit2 returns %d", r); + return 0; + } + r = zlib_body(&zs, input, output, deflate) && output->length < input->length; + if (!r) { + for (i = count_before; i < output->count; i++) { + gpr_slice_unref(output->slices[i]); + } + output->count = count_before; + output->length = length_before; + } + deflateEnd(&zs); + return r; +} + +static int zlib_decompress(gpr_slice_buffer *input, gpr_slice_buffer *output, + int gzip) { + z_stream zs; + int r; + size_t i; + size_t count_before = output->count; + size_t length_before = output->length; + memset(&zs, 0, sizeof(zs)); + r = inflateInit2(&zs, 15 | (gzip ? 16 : 0)); + if (r != Z_OK) { + gpr_log(GPR_ERROR, "inflateInit2 returns %d", r); + return 0; + } + r = zlib_body(&zs, input, output, inflate); + if (!r) { + for (i = count_before; i < output->count; i++) { + gpr_slice_unref(output->slices[i]); + } + output->count = count_before; + output->length = length_before; + } + inflateEnd(&zs); + return r; +} + +static int copy(gpr_slice_buffer *input, gpr_slice_buffer *output) { + size_t i; + for (i = 0; i < input->count; i++) { + gpr_slice_buffer_add(output, gpr_slice_ref(input->slices[i])); + } + return 1; +} + +int compress_inner(grpc_compression_algorithm algorithm, + gpr_slice_buffer *input, gpr_slice_buffer *output) { + switch (algorithm) { + case GRPC_COMPRESS_NONE: + /* the fallback path always needs to be send uncompressed: we simply + rely on that here */ + return 0; + case GRPC_COMPRESS_DEFLATE: + return zlib_compress(input, output, 0); + case GRPC_COMPRESS_GZIP: + return zlib_compress(input, output, 1); + case GRPC_COMPRESS_ALGORITHMS_COUNT: + break; + } + gpr_log(GPR_ERROR, "invalid compression algorithm %d", algorithm); + return 0; +} + +int grpc_msg_compress(grpc_compression_algorithm algorithm, + gpr_slice_buffer *input, gpr_slice_buffer *output) { + if (!compress_inner(algorithm, input, output)) { + copy(input, output); + return 0; + } + return 1; +} + +int grpc_msg_decompress(grpc_compression_algorithm algorithm, + gpr_slice_buffer *input, gpr_slice_buffer *output) { + switch (algorithm) { + case GRPC_COMPRESS_NONE: + return copy(input, output); + case GRPC_COMPRESS_DEFLATE: + return zlib_decompress(input, output, 0); + case GRPC_COMPRESS_GZIP: + return zlib_decompress(input, output, 1); + case GRPC_COMPRESS_ALGORITHMS_COUNT: + break; + } + gpr_log(GPR_ERROR, "invalid compression algorithm %d", algorithm); + return 0; +} diff --git a/src/core/compression/message_compress.h b/src/core/compression/message_compress.h new file mode 100644 index 00000000..b3eb8f57 --- /dev/null +++ b/src/core/compression/message_compress.h @@ -0,0 +1,52 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_COMPRESSION_MESSAGE_COMPRESS_H +#define GRPC_INTERNAL_CORE_COMPRESSION_MESSAGE_COMPRESS_H + +#include +#include + +/* compress 'input' to 'output' using 'algorithm'. + On success, appends compressed slices to output and returns 1. + On failure, appends uncompressed slices to output and returns 0. */ +int grpc_msg_compress(grpc_compression_algorithm algorithm, + gpr_slice_buffer *input, gpr_slice_buffer *output); + +/* decompress 'input' to 'output' using 'algorithm'. + On success, appends slices to output and returns 1. + On failure, output is unchanged, and returns 0. */ +int grpc_msg_decompress(grpc_compression_algorithm algorithm, + gpr_slice_buffer *input, gpr_slice_buffer *output); + +#endif /* GRPC_INTERNAL_CORE_COMPRESSION_MESSAGE_COMPRESS_H */ diff --git a/src/core/debug/trace.c b/src/core/debug/trace.c new file mode 100644 index 00000000..1014b1f4 --- /dev/null +++ b/src/core/debug/trace.c @@ -0,0 +1,132 @@ +/* + * + * Copyright 2015, 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 "src/core/debug/trace.h" + +#include + +#include +#include +#include +#include "src/core/support/env.h" + +typedef struct tracer { + const char *name; + int *flag; + struct tracer *next; +} tracer; +static tracer *tracers; + +void grpc_register_tracer(const char *name, int *flag) { + tracer *t = gpr_malloc(sizeof(*t)); + t->name = name; + t->flag = flag; + t->next = tracers; + *flag = 0; + tracers = t; +} + +static void add(const char *beg, const char *end, char ***ss, size_t *ns) { + size_t n = *ns; + size_t np = n + 1; + char *s = gpr_malloc(end - beg + 1); + memcpy(s, beg, end - beg); + s[end - beg] = 0; + *ss = gpr_realloc(*ss, sizeof(char **) * np); + (*ss)[n] = s; + *ns = np; +} + +static void split(const char *s, char ***ss, size_t *ns) { + const char *c = strchr(s, ','); + if (c == NULL) { + add(s, s + strlen(s), ss, ns); + } else { + add(s, c, ss, ns); + split(c + 1, ss, ns); + } +} + +static void parse(const char *s) { + char **strings = NULL; + size_t nstrings = 0; + size_t i; + split(s, &strings, &nstrings); + + for (i = 0; i < nstrings; i++) { + grpc_tracer_set_enabled(strings[i], 1); + } + + for (i = 0; i < nstrings; i++) { + gpr_free(strings[i]); + } + gpr_free(strings); +} + +void grpc_tracer_init(const char *env_var) { + char *e = gpr_getenv(env_var); + if (e != NULL) { + parse(e); + gpr_free(e); + } +} + +void grpc_tracer_shutdown(void) { + while (tracers) { + tracer *t = tracers; + tracers = t->next; + gpr_free(t); + } +} + +int grpc_tracer_set_enabled(const char *name, int enabled) { + tracer *t; + if (0 == strcmp(name, "all")) { + for (t = tracers; t; t = t->next) { + *t->flag = 1; + } + } else { + int found = 0; + for (t = tracers; t; t = t->next) { + if (0 == strcmp(name, t->name)) { + *t->flag = enabled; + found = 1; + } + } + if (!found) { + gpr_log(GPR_ERROR, "Unknown trace var: '%s'", name); + return 0; /* early return */ + } + } + return 1; +} diff --git a/src/core/debug/trace.h b/src/core/debug/trace.h new file mode 100644 index 00000000..dc587597 --- /dev/null +++ b/src/core/debug/trace.h @@ -0,0 +1,43 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_DEBUG_TRACE_H +#define GRPC_INTERNAL_CORE_DEBUG_TRACE_H + +#include + +void grpc_register_tracer(const char *name, int *flag); +void grpc_tracer_init(const char *env_var_name); +void grpc_tracer_shutdown(void); + +#endif /* GRPC_INTERNAL_CORE_DEBUG_TRACE_H */ diff --git a/src/core/httpcli/format_request.c b/src/core/httpcli/format_request.c new file mode 100644 index 00000000..6189fce8 --- /dev/null +++ b/src/core/httpcli/format_request.c @@ -0,0 +1,120 @@ +/* + * + * Copyright 2015, 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 "src/core/httpcli/format_request.h" + +#include +#include +#include + +#include "src/core/support/string.h" +#include +#include +#include +#include + +static void fill_common_header(const grpc_httpcli_request *request, + gpr_strvec *buf) { + size_t i; + gpr_strvec_add(buf, gpr_strdup(request->path)); + gpr_strvec_add(buf, gpr_strdup(" HTTP/1.0\r\n")); + /* just in case some crazy server really expects HTTP/1.1 */ + gpr_strvec_add(buf, gpr_strdup("Host: ")); + gpr_strvec_add(buf, gpr_strdup(request->host)); + gpr_strvec_add(buf, gpr_strdup("\r\n")); + gpr_strvec_add(buf, gpr_strdup("Connection: close\r\n")); + gpr_strvec_add(buf, + gpr_strdup("User-Agent: " GRPC_HTTPCLI_USER_AGENT "\r\n")); + /* user supplied headers */ + for (i = 0; i < request->hdr_count; i++) { + gpr_strvec_add(buf, gpr_strdup(request->hdrs[i].key)); + gpr_strvec_add(buf, gpr_strdup(": ")); + gpr_strvec_add(buf, gpr_strdup(request->hdrs[i].value)); + gpr_strvec_add(buf, gpr_strdup("\r\n")); + } +} + +gpr_slice grpc_httpcli_format_get_request(const grpc_httpcli_request *request) { + gpr_strvec out; + char *flat; + size_t flat_len; + + gpr_strvec_init(&out); + gpr_strvec_add(&out, gpr_strdup("GET ")); + fill_common_header(request, &out); + gpr_strvec_add(&out, gpr_strdup("\r\n")); + + flat = gpr_strvec_flatten(&out, &flat_len); + gpr_strvec_destroy(&out); + + return gpr_slice_new(flat, flat_len, gpr_free); +} + +gpr_slice grpc_httpcli_format_post_request(const grpc_httpcli_request *request, + const char *body_bytes, + size_t body_size) { + gpr_strvec out; + char *tmp; + size_t out_len; + size_t i; + + gpr_strvec_init(&out); + + gpr_strvec_add(&out, gpr_strdup("POST ")); + fill_common_header(request, &out); + if (body_bytes) { + gpr_uint8 has_content_type = 0; + for (i = 0; i < request->hdr_count; i++) { + if (strcmp(request->hdrs[i].key, "Content-Type") == 0) { + has_content_type = 1; + break; + } + } + if (!has_content_type) { + gpr_strvec_add(&out, gpr_strdup("Content-Type: text/plain\r\n")); + } + gpr_asprintf(&tmp, "Content-Length: %lu\r\n", (unsigned long)body_size); + gpr_strvec_add(&out, tmp); + } + gpr_strvec_add(&out, gpr_strdup("\r\n")); + tmp = gpr_strvec_flatten(&out, &out_len); + gpr_strvec_destroy(&out); + + if (body_bytes) { + tmp = gpr_realloc(tmp, out_len + body_size); + memcpy(tmp + out_len, body_bytes, body_size); + out_len += body_size; + } + + return gpr_slice_new(tmp, out_len, gpr_free); +} diff --git a/src/core/httpcli/format_request.h b/src/core/httpcli/format_request.h new file mode 100644 index 00000000..c8dc8f7d --- /dev/null +++ b/src/core/httpcli/format_request.h @@ -0,0 +1,45 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_HTTPCLI_FORMAT_REQUEST_H +#define GRPC_INTERNAL_CORE_HTTPCLI_FORMAT_REQUEST_H + +#include "src/core/httpcli/httpcli.h" +#include + +gpr_slice grpc_httpcli_format_get_request(const grpc_httpcli_request *request); +gpr_slice grpc_httpcli_format_post_request(const grpc_httpcli_request *request, + const char *body_bytes, + size_t body_size); + +#endif /* GRPC_INTERNAL_CORE_HTTPCLI_FORMAT_REQUEST_H */ diff --git a/src/core/httpcli/httpcli.c b/src/core/httpcli/httpcli.c new file mode 100644 index 00000000..1e38479e --- /dev/null +++ b/src/core/httpcli/httpcli.c @@ -0,0 +1,296 @@ +/* + * + * Copyright 2015, 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 "src/core/iomgr/sockaddr.h" +#include "src/core/httpcli/httpcli.h" + +#include + +#include "src/core/iomgr/endpoint.h" +#include "src/core/iomgr/resolve_address.h" +#include "src/core/iomgr/tcp_client.h" +#include "src/core/httpcli/format_request.h" +#include "src/core/httpcli/parser.h" +#include "src/core/support/string.h" +#include +#include +#include + +typedef struct { + gpr_slice request_text; + grpc_httpcli_parser parser; + grpc_resolved_addresses *addresses; + size_t next_address; + grpc_endpoint *ep; + char *host; + gpr_timespec deadline; + int have_read_byte; + const grpc_httpcli_handshaker *handshaker; + grpc_httpcli_response_cb on_response; + void *user_data; + grpc_httpcli_context *context; + grpc_pollset *pollset; + grpc_iomgr_object iomgr_obj; + gpr_slice_buffer incoming; + gpr_slice_buffer outgoing; + grpc_iomgr_closure on_read; + grpc_iomgr_closure done_write; +} internal_request; + +static grpc_httpcli_get_override g_get_override = NULL; +static grpc_httpcli_post_override g_post_override = NULL; + +static void plaintext_handshake(void *arg, grpc_endpoint *endpoint, + const char *host, + void (*on_done)(void *arg, + grpc_endpoint *endpoint)) { + on_done(arg, endpoint); +} + +const grpc_httpcli_handshaker grpc_httpcli_plaintext = {"http", + plaintext_handshake}; + +void grpc_httpcli_context_init(grpc_httpcli_context *context) { + grpc_pollset_set_init(&context->pollset_set); +} + +void grpc_httpcli_context_destroy(grpc_httpcli_context *context) { + grpc_pollset_set_destroy(&context->pollset_set); +} + +static void next_address(internal_request *req); + +static void finish(internal_request *req, int success) { + grpc_pollset_set_del_pollset(&req->context->pollset_set, req->pollset); + req->on_response(req->user_data, success ? &req->parser.r : NULL); + grpc_httpcli_parser_destroy(&req->parser); + if (req->addresses != NULL) { + grpc_resolved_addresses_destroy(req->addresses); + } + if (req->ep != NULL) { + grpc_endpoint_destroy(req->ep); + } + gpr_slice_unref(req->request_text); + gpr_free(req->host); + grpc_iomgr_unregister_object(&req->iomgr_obj); + gpr_slice_buffer_destroy(&req->incoming); + gpr_slice_buffer_destroy(&req->outgoing); + gpr_free(req); +} + +static void on_read(void *user_data, int success); + +static void do_read(internal_request *req) { + switch (grpc_endpoint_read(req->ep, &req->incoming, &req->on_read)) { + case GRPC_ENDPOINT_DONE: + on_read(req, 1); + break; + case GRPC_ENDPOINT_PENDING: + break; + case GRPC_ENDPOINT_ERROR: + on_read(req, 0); + break; + } +} + +static void on_read(void *user_data, int success) { + internal_request *req = user_data; + size_t i; + + for (i = 0; i < req->incoming.count; i++) { + if (GPR_SLICE_LENGTH(req->incoming.slices[i])) { + req->have_read_byte = 1; + if (!grpc_httpcli_parser_parse(&req->parser, req->incoming.slices[i])) { + finish(req, 0); + return; + } + } + } + + if (success) { + do_read(req); + } else if (!req->have_read_byte) { + next_address(req); + } else { + finish(req, grpc_httpcli_parser_eof(&req->parser)); + } +} + +static void on_written(internal_request *req) { do_read(req); } + +static void done_write(void *arg, int success) { + internal_request *req = arg; + if (success) { + on_written(req); + } else { + next_address(req); + } +} + +static void start_write(internal_request *req) { + gpr_slice_ref(req->request_text); + gpr_slice_buffer_add(&req->outgoing, req->request_text); + switch (grpc_endpoint_write(req->ep, &req->outgoing, &req->done_write)) { + case GRPC_ENDPOINT_DONE: + on_written(req); + break; + case GRPC_ENDPOINT_PENDING: + break; + case GRPC_ENDPOINT_ERROR: + finish(req, 0); + break; + } +} + +static void on_handshake_done(void *arg, grpc_endpoint *ep) { + internal_request *req = arg; + + if (!ep) { + next_address(req); + return; + } + + req->ep = ep; + start_write(req); +} + +static void on_connected(void *arg, grpc_endpoint *tcp) { + internal_request *req = arg; + + if (!tcp) { + next_address(req); + return; + } + req->handshaker->handshake(req, tcp, req->host, on_handshake_done); +} + +static void next_address(internal_request *req) { + grpc_resolved_address *addr; + if (req->next_address == req->addresses->naddrs) { + finish(req, 0); + return; + } + addr = &req->addresses->addrs[req->next_address++]; + grpc_tcp_client_connect(on_connected, req, &req->context->pollset_set, + (struct sockaddr *)&addr->addr, addr->len, + req->deadline); +} + +static void on_resolved(void *arg, grpc_resolved_addresses *addresses) { + internal_request *req = arg; + if (!addresses) { + finish(req, 0); + return; + } + req->addresses = addresses; + req->next_address = 0; + next_address(req); +} + +void grpc_httpcli_get(grpc_httpcli_context *context, grpc_pollset *pollset, + const grpc_httpcli_request *request, + gpr_timespec deadline, + grpc_httpcli_response_cb on_response, void *user_data) { + internal_request *req; + char *name; + if (g_get_override && + g_get_override(request, deadline, on_response, user_data)) { + return; + } + req = gpr_malloc(sizeof(internal_request)); + memset(req, 0, sizeof(*req)); + req->request_text = grpc_httpcli_format_get_request(request); + grpc_httpcli_parser_init(&req->parser); + req->on_response = on_response; + req->user_data = user_data; + req->deadline = deadline; + req->handshaker = + request->handshaker ? request->handshaker : &grpc_httpcli_plaintext; + req->context = context; + req->pollset = pollset; + grpc_iomgr_closure_init(&req->on_read, on_read, req); + grpc_iomgr_closure_init(&req->done_write, done_write, req); + gpr_slice_buffer_init(&req->incoming); + gpr_slice_buffer_init(&req->outgoing); + gpr_asprintf(&name, "HTTP:GET:%s:%s", request->host, request->path); + grpc_iomgr_register_object(&req->iomgr_obj, name); + gpr_free(name); + req->host = gpr_strdup(request->host); + + grpc_pollset_set_add_pollset(&req->context->pollset_set, req->pollset); + grpc_resolve_address(request->host, req->handshaker->default_port, + on_resolved, req); +} + +void grpc_httpcli_post(grpc_httpcli_context *context, grpc_pollset *pollset, + const grpc_httpcli_request *request, + const char *body_bytes, size_t body_size, + gpr_timespec deadline, + grpc_httpcli_response_cb on_response, void *user_data) { + internal_request *req; + char *name; + if (g_post_override && g_post_override(request, body_bytes, body_size, + deadline, on_response, user_data)) { + return; + } + req = gpr_malloc(sizeof(internal_request)); + memset(req, 0, sizeof(*req)); + req->request_text = + grpc_httpcli_format_post_request(request, body_bytes, body_size); + grpc_httpcli_parser_init(&req->parser); + req->on_response = on_response; + req->user_data = user_data; + req->deadline = deadline; + req->handshaker = + request->handshaker ? request->handshaker : &grpc_httpcli_plaintext; + req->context = context; + req->pollset = pollset; + grpc_iomgr_closure_init(&req->on_read, on_read, req); + grpc_iomgr_closure_init(&req->done_write, done_write, req); + gpr_slice_buffer_init(&req->incoming); + gpr_slice_buffer_init(&req->outgoing); + gpr_asprintf(&name, "HTTP:POST:%s:%s", request->host, request->path); + grpc_iomgr_register_object(&req->iomgr_obj, name); + gpr_free(name); + req->host = gpr_strdup(request->host); + + grpc_pollset_set_add_pollset(&req->context->pollset_set, req->pollset); + grpc_resolve_address(request->host, req->handshaker->default_port, + on_resolved, req); +} + +void grpc_httpcli_set_override(grpc_httpcli_get_override get, + grpc_httpcli_post_override post) { + g_get_override = get; + g_post_override = post; +} diff --git a/src/core/httpcli/httpcli.h b/src/core/httpcli/httpcli.h new file mode 100644 index 00000000..c4596671 --- /dev/null +++ b/src/core/httpcli/httpcli.h @@ -0,0 +1,156 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_HTTPCLI_HTTPCLI_H +#define GRPC_INTERNAL_CORE_HTTPCLI_HTTPCLI_H + +#include + +#include + +#include "src/core/iomgr/endpoint.h" +#include "src/core/iomgr/pollset_set.h" + +/* User agent this library reports */ +#define GRPC_HTTPCLI_USER_AGENT "grpc-httpcli/0.0" +/* Maximum length of a header string of the form 'Key: Value\r\n' */ +#define GRPC_HTTPCLI_MAX_HEADER_LENGTH 4096 + +/* A single header to be passed in a request */ +typedef struct grpc_httpcli_header { + char *key; + char *value; +} grpc_httpcli_header; + +/* Tracks in-progress http requests + TODO(ctiller): allow caching and capturing multiple requests for the + same content and combining them */ +typedef struct grpc_httpcli_context { + grpc_pollset_set pollset_set; +} grpc_httpcli_context; + +typedef struct { + const char *default_port; + void (*handshake)(void *arg, grpc_endpoint *endpoint, const char *host, + void (*on_done)(void *arg, grpc_endpoint *endpoint)); +} grpc_httpcli_handshaker; + +extern const grpc_httpcli_handshaker grpc_httpcli_plaintext; +extern const grpc_httpcli_handshaker grpc_httpcli_ssl; + +/* A request */ +typedef struct grpc_httpcli_request { + /* The host name to connect to */ + char *host; + /* The path of the resource to fetch */ + char *path; + /* Additional headers: count and key/values; the following are supplied + automatically and MUST NOT be set here: + Host, Connection, User-Agent */ + size_t hdr_count; + grpc_httpcli_header *hdrs; + /* handshaker to use ssl for the request */ + const grpc_httpcli_handshaker *handshaker; +} grpc_httpcli_request; + +/* A response */ +typedef struct grpc_httpcli_response { + /* HTTP status code */ + int status; + /* Headers: count and key/values */ + size_t hdr_count; + grpc_httpcli_header *hdrs; + /* Body: length and contents; contents are NOT null-terminated */ + size_t body_length; + char *body; +} grpc_httpcli_response; + +/* Callback for grpc_httpcli_get and grpc_httpcli_post. */ +typedef void (*grpc_httpcli_response_cb)(void *user_data, + const grpc_httpcli_response *response); + +void grpc_httpcli_context_init(grpc_httpcli_context *context); +void grpc_httpcli_context_destroy(grpc_httpcli_context *context); + +/* Asynchronously perform a HTTP GET. + 'context' specifies the http context under which to do the get + 'pollset' indicates a grpc_pollset that is interested in the result + of the get - work on this pollset may be used to progress the get + operation + 'request' contains request parameters - these are caller owned and can be + destroyed once the call returns + 'deadline' contains a deadline for the request (or gpr_inf_future) + 'on_response' is a callback to report results to (and 'user_data' is a user + supplied pointer to pass to said call) */ +void grpc_httpcli_get(grpc_httpcli_context *context, grpc_pollset *pollset, + const grpc_httpcli_request *request, + gpr_timespec deadline, + grpc_httpcli_response_cb on_response, void *user_data); + +/* Asynchronously perform a HTTP POST. + 'context' specifies the http context under which to do the post + 'pollset' indicates a grpc_pollset that is interested in the result + of the post - work on this pollset may be used to progress the post + operation + 'request' contains request parameters - these are caller owned and can be + destroyed once the call returns + 'body_bytes' and 'body_size' specify the payload for the post. + When there is no body, pass in NULL as body_bytes. + 'deadline' contains a deadline for the request (or gpr_inf_future) + 'em' points to a caller owned event manager that must be alive for the + lifetime of the request + 'on_response' is a callback to report results to (and 'user_data' is a user + supplied pointer to pass to said call) + Does not support ?var1=val1&var2=val2 in the path. */ +void grpc_httpcli_post(grpc_httpcli_context *context, grpc_pollset *pollset, + const grpc_httpcli_request *request, + const char *body_bytes, size_t body_size, + gpr_timespec deadline, + grpc_httpcli_response_cb on_response, void *user_data); + +/* override functions return 1 if they handled the request, 0 otherwise */ +typedef int (*grpc_httpcli_get_override)(const grpc_httpcli_request *request, + gpr_timespec deadline, + grpc_httpcli_response_cb on_response, + void *user_data); +typedef int (*grpc_httpcli_post_override)(const grpc_httpcli_request *request, + const char *body_bytes, + size_t body_size, + gpr_timespec deadline, + grpc_httpcli_response_cb on_response, + void *user_data); + +void grpc_httpcli_set_override(grpc_httpcli_get_override get, + grpc_httpcli_post_override post); + +#endif /* GRPC_INTERNAL_CORE_HTTPCLI_HTTPCLI_H */ diff --git a/src/core/httpcli/httpcli_security_connector.c b/src/core/httpcli/httpcli_security_connector.c new file mode 100644 index 00000000..7887f9d5 --- /dev/null +++ b/src/core/httpcli/httpcli_security_connector.c @@ -0,0 +1,177 @@ +/* + * + * Copyright 2015, 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 "src/core/httpcli/httpcli.h" + +#include + +#include "src/core/security/secure_transport_setup.h" +#include "src/core/support/string.h" +#include +#include +#include +#include "src/core/tsi/ssl_transport_security.h" + +typedef struct { + grpc_channel_security_connector base; + tsi_ssl_handshaker_factory *handshaker_factory; + char *secure_peer_name; +} grpc_httpcli_ssl_channel_security_connector; + +static void httpcli_ssl_destroy(grpc_security_connector *sc) { + grpc_httpcli_ssl_channel_security_connector *c = + (grpc_httpcli_ssl_channel_security_connector *)sc; + if (c->handshaker_factory != NULL) { + tsi_ssl_handshaker_factory_destroy(c->handshaker_factory); + } + if (c->secure_peer_name != NULL) gpr_free(c->secure_peer_name); + gpr_free(sc); +} + +static grpc_security_status httpcli_ssl_create_handshaker( + grpc_security_connector *sc, tsi_handshaker **handshaker) { + grpc_httpcli_ssl_channel_security_connector *c = + (grpc_httpcli_ssl_channel_security_connector *)sc; + tsi_result result = TSI_OK; + if (c->handshaker_factory == NULL) return GRPC_SECURITY_ERROR; + result = tsi_ssl_handshaker_factory_create_handshaker( + c->handshaker_factory, c->secure_peer_name, handshaker); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Handshaker creation failed with error %s.", + tsi_result_to_string(result)); + return GRPC_SECURITY_ERROR; + } + return GRPC_SECURITY_OK; +} + +static grpc_security_status httpcli_ssl_check_peer(grpc_security_connector *sc, + tsi_peer peer, + grpc_security_check_cb cb, + void *user_data) { + grpc_httpcli_ssl_channel_security_connector *c = + (grpc_httpcli_ssl_channel_security_connector *)sc; + grpc_security_status status = GRPC_SECURITY_OK; + + /* Check the peer name. */ + if (c->secure_peer_name != NULL && + !tsi_ssl_peer_matches_name(&peer, c->secure_peer_name)) { + gpr_log(GPR_ERROR, "Peer name %s is not in peer certificate", + c->secure_peer_name); + status = GRPC_SECURITY_ERROR; + } + tsi_peer_destruct(&peer); + return status; +} + +static grpc_security_connector_vtable httpcli_ssl_vtable = { + httpcli_ssl_destroy, httpcli_ssl_create_handshaker, httpcli_ssl_check_peer}; + +static grpc_security_status httpcli_ssl_channel_security_connector_create( + const unsigned char *pem_root_certs, size_t pem_root_certs_size, + const char *secure_peer_name, grpc_channel_security_connector **sc) { + tsi_result result = TSI_OK; + grpc_httpcli_ssl_channel_security_connector *c; + + if (secure_peer_name != NULL && pem_root_certs == NULL) { + gpr_log(GPR_ERROR, + "Cannot assert a secure peer name without a trust root."); + return GRPC_SECURITY_ERROR; + } + + c = gpr_malloc(sizeof(grpc_httpcli_ssl_channel_security_connector)); + memset(c, 0, sizeof(grpc_httpcli_ssl_channel_security_connector)); + + gpr_ref_init(&c->base.base.refcount, 1); + c->base.base.is_client_side = 1; + c->base.base.vtable = &httpcli_ssl_vtable; + if (secure_peer_name != NULL) { + c->secure_peer_name = gpr_strdup(secure_peer_name); + } + result = tsi_create_ssl_client_handshaker_factory( + NULL, 0, NULL, 0, pem_root_certs, pem_root_certs_size, NULL, NULL, NULL, + 0, &c->handshaker_factory); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.", + tsi_result_to_string(result)); + httpcli_ssl_destroy(&c->base.base); + *sc = NULL; + return GRPC_SECURITY_ERROR; + } + *sc = &c->base; + return GRPC_SECURITY_OK; +} + +/* handshaker */ + +typedef struct { + void (*func)(void *arg, grpc_endpoint *endpoint); + void *arg; +} on_done_closure; + +static void on_secure_transport_setup_done(void *rp, + grpc_security_status status, + grpc_endpoint *wrapped_endpoint, + grpc_endpoint *secure_endpoint) { + on_done_closure *c = rp; + if (status != GRPC_SECURITY_OK) { + gpr_log(GPR_ERROR, "Secure transport setup failed with error %d.", status); + c->func(c->arg, NULL); + } else { + c->func(c->arg, secure_endpoint); + } + gpr_free(c); +} + +static void ssl_handshake(void *arg, grpc_endpoint *tcp, const char *host, + void (*on_done)(void *arg, grpc_endpoint *endpoint)) { + grpc_channel_security_connector *sc = NULL; + const unsigned char *pem_root_certs = NULL; + on_done_closure *c = gpr_malloc(sizeof(*c)); + size_t pem_root_certs_size = grpc_get_default_ssl_roots(&pem_root_certs); + if (pem_root_certs == NULL || pem_root_certs_size == 0) { + gpr_log(GPR_ERROR, "Could not get default pem root certs."); + on_done(arg, NULL); + gpr_free(c); + return; + } + c->func = on_done; + c->arg = arg; + GPR_ASSERT(httpcli_ssl_channel_security_connector_create( + pem_root_certs, pem_root_certs_size, host, &sc) == + GRPC_SECURITY_OK); + grpc_setup_secure_transport(&sc->base, tcp, on_secure_transport_setup_done, + c); + GRPC_SECURITY_CONNECTOR_UNREF(&sc->base, "httpcli"); +} + +const grpc_httpcli_handshaker grpc_httpcli_ssl = {"https", ssl_handshake}; diff --git a/src/core/httpcli/parser.c b/src/core/httpcli/parser.c new file mode 100644 index 00000000..7b2a6206 --- /dev/null +++ b/src/core/httpcli/parser.c @@ -0,0 +1,214 @@ +/* + * + * Copyright 2015, 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 "src/core/httpcli/parser.h" + +#include + +#include +#include +#include + +static int handle_response_line(grpc_httpcli_parser *parser) { + gpr_uint8 *beg = parser->cur_line; + gpr_uint8 *cur = beg; + gpr_uint8 *end = beg + parser->cur_line_length; + + if (cur == end || *cur++ != 'H') goto error; + if (cur == end || *cur++ != 'T') goto error; + if (cur == end || *cur++ != 'T') goto error; + if (cur == end || *cur++ != 'P') goto error; + if (cur == end || *cur++ != '/') goto error; + if (cur == end || *cur++ != '1') goto error; + if (cur == end || *cur++ != '.') goto error; + if (cur == end || *cur < '0' || *cur++ > '1') goto error; + if (cur == end || *cur++ != ' ') goto error; + if (cur == end || *cur < '1' || *cur++ > '9') goto error; + if (cur == end || *cur < '0' || *cur++ > '9') goto error; + if (cur == end || *cur < '0' || *cur++ > '9') goto error; + parser->r.status = + (cur[-3] - '0') * 100 + (cur[-2] - '0') * 10 + (cur[-1] - '0'); + if (cur == end || *cur++ != ' ') goto error; + + /* we don't really care about the status code message */ + + return 1; + +error: + gpr_log(GPR_ERROR, "Failed parsing response line"); + return 0; +} + +static char *buf2str(void *buffer, size_t length) { + char *out = gpr_malloc(length + 1); + memcpy(out, buffer, length); + out[length] = 0; + return out; +} + +static int add_header(grpc_httpcli_parser *parser) { + gpr_uint8 *beg = parser->cur_line; + gpr_uint8 *cur = beg; + gpr_uint8 *end = beg + parser->cur_line_length; + grpc_httpcli_header hdr = {NULL, NULL}; + + GPR_ASSERT(cur != end); + + if (*cur == ' ' || *cur == '\t') { + gpr_log(GPR_ERROR, "Continued header lines not supported yet"); + goto error; + } + + while (cur != end && *cur != ':') { + cur++; + } + if (cur == end) { + gpr_log(GPR_ERROR, "Didn't find ':' in header string"); + goto error; + } + hdr.key = buf2str(beg, cur - beg); + cur++; /* skip : */ + + while (cur != end && (*cur == ' ' || *cur == '\t')) { + cur++; + } + hdr.value = buf2str(cur, end - cur - 2); + + if (parser->r.hdr_count == parser->hdr_capacity) { + parser->hdr_capacity = + GPR_MAX(parser->hdr_capacity + 1, parser->hdr_capacity * 3 / 2); + parser->r.hdrs = gpr_realloc( + parser->r.hdrs, parser->hdr_capacity * sizeof(*parser->r.hdrs)); + } + parser->r.hdrs[parser->r.hdr_count++] = hdr; + return 1; + +error: + gpr_free(hdr.key); + gpr_free(hdr.value); + return 0; +} + +static int finish_line(grpc_httpcli_parser *parser) { + switch (parser->state) { + case GRPC_HTTPCLI_INITIAL_RESPONSE: + if (!handle_response_line(parser)) { + return 0; + } + parser->state = GRPC_HTTPCLI_HEADERS; + break; + case GRPC_HTTPCLI_HEADERS: + if (parser->cur_line_length == 2) { + parser->state = GRPC_HTTPCLI_BODY; + break; + } + if (!add_header(parser)) { + return 0; + } + break; + case GRPC_HTTPCLI_BODY: + gpr_log(GPR_ERROR, "should never reach here"); + abort(); + } + + parser->cur_line_length = 0; + return 1; +} + +static int addbyte(grpc_httpcli_parser *parser, gpr_uint8 byte) { + switch (parser->state) { + case GRPC_HTTPCLI_INITIAL_RESPONSE: + case GRPC_HTTPCLI_HEADERS: + if (parser->cur_line_length >= GRPC_HTTPCLI_MAX_HEADER_LENGTH) { + gpr_log(GPR_ERROR, "HTTP client max line length (%d) exceeded", + GRPC_HTTPCLI_MAX_HEADER_LENGTH); + return 0; + } + parser->cur_line[parser->cur_line_length] = byte; + parser->cur_line_length++; + if (parser->cur_line_length >= 2 && + parser->cur_line[parser->cur_line_length - 2] == '\r' && + parser->cur_line[parser->cur_line_length - 1] == '\n') { + return finish_line(parser); + } else { + return 1; + } + gpr_log(GPR_ERROR, "should never reach here"); + abort(); + case GRPC_HTTPCLI_BODY: + if (parser->r.body_length == parser->body_capacity) { + parser->body_capacity = GPR_MAX(8, parser->body_capacity * 3 / 2); + parser->r.body = + gpr_realloc((void *)parser->r.body, parser->body_capacity); + } + ((char *)parser->r.body)[parser->r.body_length] = byte; + parser->r.body_length++; + return 1; + } + gpr_log(GPR_ERROR, "should never reach here"); + abort(); + + return 0; +} + +void grpc_httpcli_parser_init(grpc_httpcli_parser *parser) { + memset(parser, 0, sizeof(*parser)); + parser->state = GRPC_HTTPCLI_INITIAL_RESPONSE; + parser->r.status = 500; +} + +void grpc_httpcli_parser_destroy(grpc_httpcli_parser *parser) { + size_t i; + gpr_free(parser->r.body); + for (i = 0; i < parser->r.hdr_count; i++) { + gpr_free(parser->r.hdrs[i].key); + gpr_free(parser->r.hdrs[i].value); + } + gpr_free(parser->r.hdrs); +} + +int grpc_httpcli_parser_parse(grpc_httpcli_parser *parser, gpr_slice slice) { + size_t i; + + for (i = 0; i < GPR_SLICE_LENGTH(slice); i++) { + if (!addbyte(parser, GPR_SLICE_START_PTR(slice)[i])) { + return 0; + } + } + + return 1; +} + +int grpc_httpcli_parser_eof(grpc_httpcli_parser *parser) { + return parser->state == GRPC_HTTPCLI_BODY; +} diff --git a/src/core/httpcli/parser.h b/src/core/httpcli/parser.h new file mode 100644 index 00000000..3fbb4c74 --- /dev/null +++ b/src/core/httpcli/parser.h @@ -0,0 +1,64 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_HTTPCLI_PARSER_H +#define GRPC_INTERNAL_CORE_HTTPCLI_PARSER_H + +#include "src/core/httpcli/httpcli.h" +#include +#include + +typedef enum { + GRPC_HTTPCLI_INITIAL_RESPONSE, + GRPC_HTTPCLI_HEADERS, + GRPC_HTTPCLI_BODY +} grpc_httpcli_parser_state; + +typedef struct { + grpc_httpcli_parser_state state; + + grpc_httpcli_response r; + size_t body_capacity; + size_t hdr_capacity; + + gpr_uint8 cur_line[GRPC_HTTPCLI_MAX_HEADER_LENGTH]; + size_t cur_line_length; +} grpc_httpcli_parser; + +void grpc_httpcli_parser_init(grpc_httpcli_parser *parser); +void grpc_httpcli_parser_destroy(grpc_httpcli_parser *parser); + +int grpc_httpcli_parser_parse(grpc_httpcli_parser *parser, gpr_slice slice); +int grpc_httpcli_parser_eof(grpc_httpcli_parser *parser); + +#endif /* GRPC_INTERNAL_CORE_HTTPCLI_PARSER_H */ diff --git a/src/core/iomgr/alarm.c b/src/core/iomgr/alarm.c new file mode 100644 index 00000000..ddb30dc4 --- /dev/null +++ b/src/core/iomgr/alarm.c @@ -0,0 +1,374 @@ +/* + * + * Copyright 2015, 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 "src/core/iomgr/alarm.h" + +#include "src/core/iomgr/alarm_heap.h" +#include "src/core/iomgr/alarm_internal.h" +#include "src/core/iomgr/time_averaged_stats.h" +#include +#include +#include + +#define INVALID_HEAP_INDEX 0xffffffffu + +#define LOG2_NUM_SHARDS 5 +#define NUM_SHARDS (1 << LOG2_NUM_SHARDS) +#define MAX_ALARMS_PER_CHECK 128 +#define ADD_DEADLINE_SCALE 0.33 +#define MIN_QUEUE_WINDOW_DURATION 0.01 +#define MAX_QUEUE_WINDOW_DURATION 1 + +typedef struct { + gpr_mu mu; + grpc_time_averaged_stats stats; + /* All and only alarms with deadlines <= this will be in the heap. */ + gpr_timespec queue_deadline_cap; + gpr_timespec min_deadline; + /* Index in the g_shard_queue */ + gpr_uint32 shard_queue_index; + /* This holds all alarms with deadlines < queue_deadline_cap. Alarms in this + list have the top bit of their deadline set to 0. */ + grpc_alarm_heap heap; + /* This holds alarms whose deadline is >= queue_deadline_cap. */ + grpc_alarm list; +} shard_type; + +/* Protects g_shard_queue */ +static gpr_mu g_mu; +/* Allow only one run_some_expired_alarms at once */ +static gpr_mu g_checker_mu; +static gpr_clock_type g_clock_type; +static shard_type g_shards[NUM_SHARDS]; +/* Protected by g_mu */ +static shard_type *g_shard_queue[NUM_SHARDS]; + +static int run_some_expired_alarms(gpr_mu *drop_mu, gpr_timespec now, + gpr_timespec *next, int success); + +static gpr_timespec compute_min_deadline(shard_type *shard) { + return grpc_alarm_heap_is_empty(&shard->heap) + ? shard->queue_deadline_cap + : grpc_alarm_heap_top(&shard->heap)->deadline; +} + +void grpc_alarm_list_init(gpr_timespec now) { + int i; + + gpr_mu_init(&g_mu); + gpr_mu_init(&g_checker_mu); + g_clock_type = now.clock_type; + + for (i = 0; i < NUM_SHARDS; i++) { + shard_type *shard = &g_shards[i]; + gpr_mu_init(&shard->mu); + grpc_time_averaged_stats_init(&shard->stats, 1.0 / ADD_DEADLINE_SCALE, 0.1, + 0.5); + shard->queue_deadline_cap = now; + shard->shard_queue_index = i; + grpc_alarm_heap_init(&shard->heap); + shard->list.next = shard->list.prev = &shard->list; + shard->min_deadline = compute_min_deadline(shard); + g_shard_queue[i] = shard; + } +} + +void grpc_alarm_list_shutdown(void) { + int i; + while (run_some_expired_alarms(NULL, gpr_inf_future(g_clock_type), NULL, 0)) + ; + for (i = 0; i < NUM_SHARDS; i++) { + shard_type *shard = &g_shards[i]; + gpr_mu_destroy(&shard->mu); + grpc_alarm_heap_destroy(&shard->heap); + } + gpr_mu_destroy(&g_mu); + gpr_mu_destroy(&g_checker_mu); +} + +/* This is a cheap, but good enough, pointer hash for sharding the tasks: */ +static size_t shard_idx(const grpc_alarm *info) { + size_t x = (size_t)info; + return ((x >> 4) ^ (x >> 9) ^ (x >> 14)) & (NUM_SHARDS - 1); +} + +static double ts_to_dbl(gpr_timespec ts) { + return ts.tv_sec + 1e-9 * ts.tv_nsec; +} + +static gpr_timespec dbl_to_ts(double d) { + gpr_timespec ts; + ts.tv_sec = d; + ts.tv_nsec = 1e9 * (d - ts.tv_sec); + ts.clock_type = GPR_TIMESPAN; + return ts; +} + +static void list_join(grpc_alarm *head, grpc_alarm *alarm) { + alarm->next = head; + alarm->prev = head->prev; + alarm->next->prev = alarm->prev->next = alarm; +} + +static void list_remove(grpc_alarm *alarm) { + alarm->next->prev = alarm->prev; + alarm->prev->next = alarm->next; +} + +static void swap_adjacent_shards_in_queue(size_t first_shard_queue_index) { + shard_type *temp; + temp = g_shard_queue[first_shard_queue_index]; + g_shard_queue[first_shard_queue_index] = + g_shard_queue[first_shard_queue_index + 1]; + g_shard_queue[first_shard_queue_index + 1] = temp; + g_shard_queue[first_shard_queue_index]->shard_queue_index = + first_shard_queue_index; + g_shard_queue[first_shard_queue_index + 1]->shard_queue_index = + first_shard_queue_index + 1; +} + +static void note_deadline_change(shard_type *shard) { + while (shard->shard_queue_index > 0 && + gpr_time_cmp( + shard->min_deadline, + g_shard_queue[shard->shard_queue_index - 1]->min_deadline) < 0) { + swap_adjacent_shards_in_queue(shard->shard_queue_index - 1); + } + while (shard->shard_queue_index < NUM_SHARDS - 1 && + gpr_time_cmp( + shard->min_deadline, + g_shard_queue[shard->shard_queue_index + 1]->min_deadline) > 0) { + swap_adjacent_shards_in_queue(shard->shard_queue_index); + } +} + +void grpc_alarm_init(grpc_alarm *alarm, gpr_timespec deadline, + grpc_iomgr_cb_func alarm_cb, void *alarm_cb_arg, + gpr_timespec now) { + int is_first_alarm = 0; + shard_type *shard = &g_shards[shard_idx(alarm)]; + GPR_ASSERT(deadline.clock_type == g_clock_type); + GPR_ASSERT(now.clock_type == g_clock_type); + alarm->cb = alarm_cb; + alarm->cb_arg = alarm_cb_arg; + alarm->deadline = deadline; + alarm->triggered = 0; + + /* TODO(ctiller): check deadline expired */ + + gpr_mu_lock(&shard->mu); + grpc_time_averaged_stats_add_sample(&shard->stats, + ts_to_dbl(gpr_time_sub(deadline, now))); + if (gpr_time_cmp(deadline, shard->queue_deadline_cap) < 0) { + is_first_alarm = grpc_alarm_heap_add(&shard->heap, alarm); + } else { + alarm->heap_index = INVALID_HEAP_INDEX; + list_join(&shard->list, alarm); + } + gpr_mu_unlock(&shard->mu); + + /* Deadline may have decreased, we need to adjust the master queue. Note + that there is a potential racy unlocked region here. There could be a + reordering of multiple grpc_alarm_init calls, at this point, but the < test + below should ensure that we err on the side of caution. There could + also be a race with grpc_alarm_check, which might beat us to the lock. In + that case, it is possible that the alarm that we added will have already + run by the time we hold the lock, but that too is a safe error. + Finally, it's possible that the grpc_alarm_check that intervened failed to + trigger the new alarm because the min_deadline hadn't yet been reduced. + In that case, the alarm will simply have to wait for the next + grpc_alarm_check. */ + if (is_first_alarm) { + gpr_mu_lock(&g_mu); + if (gpr_time_cmp(deadline, shard->min_deadline) < 0) { + gpr_timespec old_min_deadline = g_shard_queue[0]->min_deadline; + shard->min_deadline = deadline; + note_deadline_change(shard); + if (shard->shard_queue_index == 0 && + gpr_time_cmp(deadline, old_min_deadline) < 0) { + grpc_kick_poller(); + } + } + gpr_mu_unlock(&g_mu); + } +} + +void grpc_alarm_cancel(grpc_alarm *alarm) { + shard_type *shard = &g_shards[shard_idx(alarm)]; + int triggered = 0; + gpr_mu_lock(&shard->mu); + if (!alarm->triggered) { + triggered = 1; + alarm->triggered = 1; + if (alarm->heap_index == INVALID_HEAP_INDEX) { + list_remove(alarm); + } else { + grpc_alarm_heap_remove(&shard->heap, alarm); + } + } + gpr_mu_unlock(&shard->mu); + + if (triggered) { + alarm->cb(alarm->cb_arg, 0); + } +} + +/* This is called when the queue is empty and "now" has reached the + queue_deadline_cap. We compute a new queue deadline and then scan the map + for alarms that fall at or under it. Returns true if the queue is no + longer empty. + REQUIRES: shard->mu locked */ +static int refill_queue(shard_type *shard, gpr_timespec now) { + /* Compute the new queue window width and bound by the limits: */ + double computed_deadline_delta = + grpc_time_averaged_stats_update_average(&shard->stats) * + ADD_DEADLINE_SCALE; + double deadline_delta = + GPR_CLAMP(computed_deadline_delta, MIN_QUEUE_WINDOW_DURATION, + MAX_QUEUE_WINDOW_DURATION); + grpc_alarm *alarm, *next; + + /* Compute the new cap and put all alarms under it into the queue: */ + shard->queue_deadline_cap = gpr_time_add( + gpr_time_max(now, shard->queue_deadline_cap), dbl_to_ts(deadline_delta)); + for (alarm = shard->list.next; alarm != &shard->list; alarm = next) { + next = alarm->next; + + if (gpr_time_cmp(alarm->deadline, shard->queue_deadline_cap) < 0) { + list_remove(alarm); + grpc_alarm_heap_add(&shard->heap, alarm); + } + } + return !grpc_alarm_heap_is_empty(&shard->heap); +} + +/* This pops the next non-cancelled alarm with deadline <= now from the queue, + or returns NULL if there isn't one. + REQUIRES: shard->mu locked */ +static grpc_alarm *pop_one(shard_type *shard, gpr_timespec now) { + grpc_alarm *alarm; + for (;;) { + if (grpc_alarm_heap_is_empty(&shard->heap)) { + if (gpr_time_cmp(now, shard->queue_deadline_cap) < 0) return NULL; + if (!refill_queue(shard, now)) return NULL; + } + alarm = grpc_alarm_heap_top(&shard->heap); + if (gpr_time_cmp(alarm->deadline, now) > 0) return NULL; + alarm->triggered = 1; + grpc_alarm_heap_pop(&shard->heap); + return alarm; + } +} + +/* REQUIRES: shard->mu unlocked */ +static size_t pop_alarms(shard_type *shard, gpr_timespec now, + grpc_alarm **alarms, size_t max_alarms, + gpr_timespec *new_min_deadline) { + size_t n = 0; + grpc_alarm *alarm; + gpr_mu_lock(&shard->mu); + while (n < max_alarms && (alarm = pop_one(shard, now))) { + alarms[n++] = alarm; + } + *new_min_deadline = compute_min_deadline(shard); + gpr_mu_unlock(&shard->mu); + return n; +} + +static int run_some_expired_alarms(gpr_mu *drop_mu, gpr_timespec now, + gpr_timespec *next, int success) { + size_t n = 0; + size_t i; + grpc_alarm *alarms[MAX_ALARMS_PER_CHECK]; + + /* TODO(ctiller): verify that there are any alarms (atomically) here */ + + if (gpr_mu_trylock(&g_checker_mu)) { + gpr_mu_lock(&g_mu); + + while (n < MAX_ALARMS_PER_CHECK && + gpr_time_cmp(g_shard_queue[0]->min_deadline, now) < 0) { + gpr_timespec new_min_deadline; + + /* For efficiency, we pop as many available alarms as we can from the + shard. This may violate perfect alarm deadline ordering, but that + shouldn't be a big deal because we don't make ordering guarantees. */ + n += pop_alarms(g_shard_queue[0], now, alarms + n, + MAX_ALARMS_PER_CHECK - n, &new_min_deadline); + + /* An grpc_alarm_init() on the shard could intervene here, adding a new + alarm that is earlier than new_min_deadline. However, + grpc_alarm_init() will block on the master_lock before it can call + set_min_deadline, so this one will complete first and then the AddAlarm + will reduce the min_deadline (perhaps unnecessarily). */ + g_shard_queue[0]->min_deadline = new_min_deadline; + note_deadline_change(g_shard_queue[0]); + } + + if (next) { + *next = gpr_time_min(*next, g_shard_queue[0]->min_deadline); + } + + gpr_mu_unlock(&g_mu); + gpr_mu_unlock(&g_checker_mu); + } + + if (n && drop_mu) { + gpr_mu_unlock(drop_mu); + } + + for (i = 0; i < n; i++) { + alarms[i]->cb(alarms[i]->cb_arg, success); + } + + if (n && drop_mu) { + gpr_mu_lock(drop_mu); + } + + return n; +} + +int grpc_alarm_check(gpr_mu *drop_mu, gpr_timespec now, gpr_timespec *next) { + GPR_ASSERT(now.clock_type == g_clock_type); + return run_some_expired_alarms( + drop_mu, now, next, + gpr_time_cmp(now, gpr_inf_future(now.clock_type)) != 0); +} + +gpr_timespec grpc_alarm_list_next_timeout(void) { + gpr_timespec out; + gpr_mu_lock(&g_mu); + out = g_shard_queue[0]->min_deadline; + gpr_mu_unlock(&g_mu); + return out; +} diff --git a/src/core/iomgr/alarm.h b/src/core/iomgr/alarm.h new file mode 100644 index 00000000..4a13527e --- /dev/null +++ b/src/core/iomgr/alarm.h @@ -0,0 +1,89 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_IOMGR_ALARM_H +#define GRPC_INTERNAL_CORE_IOMGR_ALARM_H + +#include "src/core/iomgr/iomgr.h" +#include +#include + +typedef struct grpc_alarm { + gpr_timespec deadline; + gpr_uint32 heap_index; /* INVALID_HEAP_INDEX if not in heap */ + int triggered; + struct grpc_alarm *next; + struct grpc_alarm *prev; + grpc_iomgr_cb_func cb; + void *cb_arg; +} grpc_alarm; + +/* Initialize *alarm. When expired or canceled, alarm_cb will be called with + *alarm_cb_arg and status to indicate if it expired (SUCCESS) or was + canceled (CANCELLED). alarm_cb is guaranteed to be called exactly once, + and application code should check the status to determine how it was + invoked. The application callback is also responsible for maintaining + information about when to free up any user-level state. */ +void grpc_alarm_init(grpc_alarm *alarm, gpr_timespec deadline, + grpc_iomgr_cb_func alarm_cb, void *alarm_cb_arg, + gpr_timespec now); + +/* Note that there is no alarm destroy function. This is because the + alarm is a one-time occurrence with a guarantee that the callback will + be called exactly once, either at expiration or cancellation. Thus, all + the internal alarm event management state is destroyed just before + that callback is invoked. If the user has additional state associated with + the alarm, the user is responsible for determining when it is safe to + destroy that state. */ + +/* Cancel an *alarm. + There are three cases: + 1. We normally cancel the alarm + 2. The alarm has already run + 3. We can't cancel the alarm because it is "in flight". + + In all of these cases, the cancellation is still considered successful. + They are essentially distinguished in that the alarm_cb will be run + exactly once from either the cancellation (with status CANCELLED) + or from the activation (with status SUCCESS) + + Note carefully that the callback function MAY occur in the same callstack + as grpc_alarm_cancel. It's expected that most alarms will be cancelled (their + primary use is to implement deadlines), and so this code is optimized such + that cancellation costs as little as possible. Making callbacks run inline + matches this aim. + + Requires: cancel() must happen after add() on a given alarm */ +void grpc_alarm_cancel(grpc_alarm *alarm); + +#endif /* GRPC_INTERNAL_CORE_IOMGR_ALARM_H */ diff --git a/src/core/iomgr/alarm_heap.c b/src/core/iomgr/alarm_heap.c new file mode 100644 index 00000000..daed2519 --- /dev/null +++ b/src/core/iomgr/alarm_heap.c @@ -0,0 +1,148 @@ +/* + * + * Copyright 2015, 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 "src/core/iomgr/alarm_heap.h" + +#include + +#include +#include + +/* Adjusts a heap so as to move a hole at position i closer to the root, + until a suitable position is found for element t. Then, copies t into that + position. This functor is called each time immediately after modifying a + value in the underlying container, with the offset of the modified element as + its argument. */ +static void adjust_upwards(grpc_alarm **first, int i, grpc_alarm *t) { + while (i > 0) { + int parent = (i - 1) / 2; + if (gpr_time_cmp(first[parent]->deadline, t->deadline) >= 0) break; + first[i] = first[parent]; + first[i]->heap_index = i; + i = parent; + } + first[i] = t; + t->heap_index = i; +} + +/* Adjusts a heap so as to move a hole at position i farther away from the root, + until a suitable position is found for element t. Then, copies t into that + position. */ +static void adjust_downwards(grpc_alarm **first, int i, int length, + grpc_alarm *t) { + for (;;) { + int left_child = 1 + 2 * i; + int right_child; + int next_i; + if (left_child >= length) break; + right_child = left_child + 1; + next_i = right_child < length && + gpr_time_cmp(first[left_child]->deadline, + first[right_child]->deadline) < 0 + ? right_child + : left_child; + if (gpr_time_cmp(t->deadline, first[next_i]->deadline) >= 0) break; + first[i] = first[next_i]; + first[i]->heap_index = i; + i = next_i; + } + first[i] = t; + t->heap_index = i; +} + +#define SHRINK_MIN_ELEMS 8 +#define SHRINK_FULLNESS_FACTOR 2 + +static void maybe_shrink(grpc_alarm_heap *heap) { + if (heap->alarm_count >= 8 && + heap->alarm_count <= heap->alarm_capacity / SHRINK_FULLNESS_FACTOR / 2) { + heap->alarm_capacity = heap->alarm_count * SHRINK_FULLNESS_FACTOR; + heap->alarms = + gpr_realloc(heap->alarms, heap->alarm_capacity * sizeof(grpc_alarm *)); + } +} + +static void note_changed_priority(grpc_alarm_heap *heap, grpc_alarm *alarm) { + int i = alarm->heap_index; + int parent = (i - 1) / 2; + if (gpr_time_cmp(heap->alarms[parent]->deadline, alarm->deadline) < 0) { + adjust_upwards(heap->alarms, i, alarm); + } else { + adjust_downwards(heap->alarms, i, heap->alarm_count, alarm); + } +} + +void grpc_alarm_heap_init(grpc_alarm_heap *heap) { + memset(heap, 0, sizeof(*heap)); +} + +void grpc_alarm_heap_destroy(grpc_alarm_heap *heap) { gpr_free(heap->alarms); } + +int grpc_alarm_heap_add(grpc_alarm_heap *heap, grpc_alarm *alarm) { + if (heap->alarm_count == heap->alarm_capacity) { + heap->alarm_capacity = + GPR_MAX(heap->alarm_capacity + 1, heap->alarm_capacity * 3 / 2); + heap->alarms = + gpr_realloc(heap->alarms, heap->alarm_capacity * sizeof(grpc_alarm *)); + } + alarm->heap_index = heap->alarm_count; + adjust_upwards(heap->alarms, heap->alarm_count, alarm); + heap->alarm_count++; + return alarm->heap_index == 0; +} + +void grpc_alarm_heap_remove(grpc_alarm_heap *heap, grpc_alarm *alarm) { + int i = alarm->heap_index; + if (i == heap->alarm_count - 1) { + heap->alarm_count--; + maybe_shrink(heap); + return; + } + heap->alarms[i] = heap->alarms[heap->alarm_count - 1]; + heap->alarms[i]->heap_index = i; + heap->alarm_count--; + maybe_shrink(heap); + note_changed_priority(heap, heap->alarms[i]); +} + +int grpc_alarm_heap_is_empty(grpc_alarm_heap *heap) { + return heap->alarm_count == 0; +} + +grpc_alarm *grpc_alarm_heap_top(grpc_alarm_heap *heap) { + return heap->alarms[0]; +} + +void grpc_alarm_heap_pop(grpc_alarm_heap *heap) { + grpc_alarm_heap_remove(heap, grpc_alarm_heap_top(heap)); +} diff --git a/src/core/iomgr/alarm_heap.h b/src/core/iomgr/alarm_heap.h new file mode 100644 index 00000000..60db6c99 --- /dev/null +++ b/src/core/iomgr/alarm_heap.h @@ -0,0 +1,57 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_IOMGR_ALARM_HEAP_H +#define GRPC_INTERNAL_CORE_IOMGR_ALARM_HEAP_H + +#include "src/core/iomgr/alarm.h" + +typedef struct { + grpc_alarm **alarms; + int alarm_count; + int alarm_capacity; +} grpc_alarm_heap; + +/* return 1 if the new alarm is the first alarm in the heap */ +int grpc_alarm_heap_add(grpc_alarm_heap *heap, grpc_alarm *alarm); + +void grpc_alarm_heap_init(grpc_alarm_heap *heap); +void grpc_alarm_heap_destroy(grpc_alarm_heap *heap); + +void grpc_alarm_heap_remove(grpc_alarm_heap *heap, grpc_alarm *alarm); +grpc_alarm *grpc_alarm_heap_top(grpc_alarm_heap *heap); +void grpc_alarm_heap_pop(grpc_alarm_heap *heap); + +int grpc_alarm_heap_is_empty(grpc_alarm_heap *heap); + +#endif /* GRPC_INTERNAL_CORE_IOMGR_ALARM_HEAP_H */ diff --git a/src/core/iomgr/alarm_internal.h b/src/core/iomgr/alarm_internal.h new file mode 100644 index 00000000..e9f98a34 --- /dev/null +++ b/src/core/iomgr/alarm_internal.h @@ -0,0 +1,62 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_IOMGR_ALARM_INTERNAL_H +#define GRPC_INTERNAL_CORE_IOMGR_ALARM_INTERNAL_H + +#include +#include + +/* iomgr internal api for dealing with alarms */ + +/* Check for alarms to be run, and run them. + Return non zero if alarm callbacks were executed. + Drops drop_mu if it is non-null before executing callbacks. + If next is non-null, TRY to update *next with the next running alarm + IF that alarm occurs before *next current value. + *next is never guaranteed to be updated on any given execution; however, + with high probability at least one thread in the system will see an update + at any time slice. */ + +int grpc_alarm_check(gpr_mu *drop_mu, gpr_timespec now, gpr_timespec *next); + +void grpc_alarm_list_init(gpr_timespec now); +void grpc_alarm_list_shutdown(void); + +gpr_timespec grpc_alarm_list_next_timeout(void); + +/* the following must be implemented by each iomgr implementation */ + +void grpc_kick_poller(void); + +#endif /* GRPC_INTERNAL_CORE_IOMGR_ALARM_INTERNAL_H */ diff --git a/src/core/iomgr/endpoint.c b/src/core/iomgr/endpoint.c new file mode 100644 index 00000000..a7878e31 --- /dev/null +++ b/src/core/iomgr/endpoint.c @@ -0,0 +1,63 @@ +/* + * + * Copyright 2015, 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 "src/core/iomgr/endpoint.h" + +grpc_endpoint_op_status grpc_endpoint_read(grpc_endpoint *ep, + gpr_slice_buffer *slices, + grpc_iomgr_closure *cb) { + return ep->vtable->read(ep, slices, cb); +} + +grpc_endpoint_op_status grpc_endpoint_write(grpc_endpoint *ep, + gpr_slice_buffer *slices, + grpc_iomgr_closure *cb) { + return ep->vtable->write(ep, slices, cb); +} + +void grpc_endpoint_add_to_pollset(grpc_endpoint *ep, grpc_pollset *pollset) { + ep->vtable->add_to_pollset(ep, pollset); +} + +void grpc_endpoint_add_to_pollset_set(grpc_endpoint *ep, + grpc_pollset_set *pollset_set) { + ep->vtable->add_to_pollset_set(ep, pollset_set); +} + +void grpc_endpoint_shutdown(grpc_endpoint *ep) { ep->vtable->shutdown(ep); } + +void grpc_endpoint_destroy(grpc_endpoint *ep) { ep->vtable->destroy(ep); } + +char *grpc_endpoint_get_peer(grpc_endpoint *ep) { + return ep->vtable->get_peer(ep); +} diff --git a/src/core/iomgr/endpoint.h b/src/core/iomgr/endpoint.h new file mode 100644 index 00000000..d14d52d5 --- /dev/null +++ b/src/core/iomgr/endpoint.h @@ -0,0 +1,106 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_IOMGR_ENDPOINT_H +#define GRPC_INTERNAL_CORE_IOMGR_ENDPOINT_H + +#include "src/core/iomgr/pollset.h" +#include "src/core/iomgr/pollset_set.h" +#include +#include +#include + +/* An endpoint caps a streaming channel between two communicating processes. + Examples may be: a tcp socket, , or some shared memory. */ + +typedef struct grpc_endpoint grpc_endpoint; +typedef struct grpc_endpoint_vtable grpc_endpoint_vtable; + +typedef enum grpc_endpoint_op_status { + GRPC_ENDPOINT_DONE, /* completed immediately, cb won't be called */ + GRPC_ENDPOINT_PENDING, /* cb will be called when completed */ + GRPC_ENDPOINT_ERROR /* write errored out, cb won't be called */ +} grpc_endpoint_op_status; + +struct grpc_endpoint_vtable { + grpc_endpoint_op_status (*read)(grpc_endpoint *ep, gpr_slice_buffer *slices, + grpc_iomgr_closure *cb); + grpc_endpoint_op_status (*write)(grpc_endpoint *ep, gpr_slice_buffer *slices, + grpc_iomgr_closure *cb); + void (*add_to_pollset)(grpc_endpoint *ep, grpc_pollset *pollset); + void (*add_to_pollset_set)(grpc_endpoint *ep, grpc_pollset_set *pollset); + void (*shutdown)(grpc_endpoint *ep); + void (*destroy)(grpc_endpoint *ep); + char *(*get_peer)(grpc_endpoint *ep); +}; + +/* When data is available on the connection, calls the callback with slices. + Callback success indicates that the endpoint can accept more reads, failure + indicates the endpoint is closed. + Valid slices may be placed into \a slices even on callback success == 0. */ +grpc_endpoint_op_status grpc_endpoint_read( + grpc_endpoint *ep, gpr_slice_buffer *slices, + grpc_iomgr_closure *cb) GRPC_MUST_USE_RESULT; + +char *grpc_endpoint_get_peer(grpc_endpoint *ep); + +/* Write slices out to the socket. + + If the connection is ready for more data after the end of the call, it + returns GRPC_ENDPOINT_DONE. + Otherwise it returns GRPC_ENDPOINT_PENDING and calls cb when the + connection is ready for more data. + \a slices may be mutated at will by the endpoint until cb is called. + No guarantee is made to the content of slices after a write EXCEPT that + it is a valid slice buffer. + */ +grpc_endpoint_op_status grpc_endpoint_write( + grpc_endpoint *ep, gpr_slice_buffer *slices, + grpc_iomgr_closure *cb) GRPC_MUST_USE_RESULT; + +/* Causes any pending read/write callbacks to run immediately with + success==0 */ +void grpc_endpoint_shutdown(grpc_endpoint *ep); +void grpc_endpoint_destroy(grpc_endpoint *ep); + +/* Add an endpoint to a pollset, so that when the pollset is polled, events from + this endpoint are considered */ +void grpc_endpoint_add_to_pollset(grpc_endpoint *ep, grpc_pollset *pollset); +void grpc_endpoint_add_to_pollset_set(grpc_endpoint *ep, + grpc_pollset_set *pollset_set); + +struct grpc_endpoint { + const grpc_endpoint_vtable *vtable; +}; + +#endif /* GRPC_INTERNAL_CORE_IOMGR_ENDPOINT_H */ diff --git a/src/core/iomgr/endpoint_pair.h b/src/core/iomgr/endpoint_pair.h new file mode 100644 index 00000000..095ec5fc --- /dev/null +++ b/src/core/iomgr/endpoint_pair.h @@ -0,0 +1,47 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_IOMGR_ENDPOINT_PAIR_H +#define GRPC_INTERNAL_CORE_IOMGR_ENDPOINT_PAIR_H + +#include "src/core/iomgr/endpoint.h" + +typedef struct { + grpc_endpoint *client; + grpc_endpoint *server; +} grpc_endpoint_pair; + +grpc_endpoint_pair grpc_iomgr_create_endpoint_pair(const char *name, + size_t read_slice_size); + +#endif /* GRPC_INTERNAL_CORE_IOMGR_ENDPOINT_PAIR_H */ diff --git a/src/core/iomgr/endpoint_pair_posix.c b/src/core/iomgr/endpoint_pair_posix.c new file mode 100644 index 00000000..deae9c68 --- /dev/null +++ b/src/core/iomgr/endpoint_pair_posix.c @@ -0,0 +1,79 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_POSIX_SOCKET + +#include "src/core/iomgr/endpoint_pair.h" + +#include +#include +#include +#include +#include + +#include "src/core/iomgr/tcp_posix.h" +#include "src/core/support/string.h" +#include +#include +#include + +static void create_sockets(int sv[2]) { + int flags; + GPR_ASSERT(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == 0); + flags = fcntl(sv[0], F_GETFL, 0); + GPR_ASSERT(fcntl(sv[0], F_SETFL, flags | O_NONBLOCK) == 0); + flags = fcntl(sv[1], F_GETFL, 0); + GPR_ASSERT(fcntl(sv[1], F_SETFL, flags | O_NONBLOCK) == 0); +} + +grpc_endpoint_pair grpc_iomgr_create_endpoint_pair(const char *name, + size_t read_slice_size) { + int sv[2]; + grpc_endpoint_pair p; + char *final_name; + create_sockets(sv); + + gpr_asprintf(&final_name, "%s:client", name); + p.client = grpc_tcp_create(grpc_fd_create(sv[1], final_name), read_slice_size, + "socketpair-server"); + gpr_free(final_name); + gpr_asprintf(&final_name, "%s:server", name); + p.server = grpc_tcp_create(grpc_fd_create(sv[0], final_name), read_slice_size, + "socketpair-client"); + gpr_free(final_name); + return p; +} + +#endif diff --git a/src/core/iomgr/endpoint_pair_windows.c b/src/core/iomgr/endpoint_pair_windows.c new file mode 100644 index 00000000..db9d092d --- /dev/null +++ b/src/core/iomgr/endpoint_pair_windows.c @@ -0,0 +1,97 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_WINSOCK_SOCKET +#include "src/core/iomgr/sockaddr_utils.h" +#include "src/core/iomgr/endpoint_pair.h" + +#include +#include +#include + +#include "src/core/iomgr/tcp_windows.h" +#include "src/core/iomgr/socket_windows.h" +#include + +static void create_sockets(SOCKET sv[2]) { + SOCKET svr_sock = INVALID_SOCKET; + SOCKET lst_sock = INVALID_SOCKET; + SOCKET cli_sock = INVALID_SOCKET; + SOCKADDR_IN addr; + int addr_len = sizeof(addr); + + lst_sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, + WSA_FLAG_OVERLAPPED); + GPR_ASSERT(lst_sock != INVALID_SOCKET); + + memset(&addr, 0, sizeof(addr)); + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr.sin_family = AF_INET; + GPR_ASSERT(bind(lst_sock, (struct sockaddr *)&addr, sizeof(addr)) != + SOCKET_ERROR); + GPR_ASSERT(listen(lst_sock, SOMAXCONN) != SOCKET_ERROR); + GPR_ASSERT(getsockname(lst_sock, (struct sockaddr *)&addr, &addr_len) != + SOCKET_ERROR); + + cli_sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, + WSA_FLAG_OVERLAPPED); + GPR_ASSERT(cli_sock != INVALID_SOCKET); + + GPR_ASSERT(WSAConnect(cli_sock, (struct sockaddr *)&addr, addr_len, NULL, + NULL, NULL, NULL) == 0); + svr_sock = accept(lst_sock, (struct sockaddr *)&addr, &addr_len); + GPR_ASSERT(svr_sock != INVALID_SOCKET); + + closesocket(lst_sock); + grpc_tcp_prepare_socket(cli_sock); + grpc_tcp_prepare_socket(svr_sock); + + sv[1] = cli_sock; + sv[0] = svr_sock; +} + +grpc_endpoint_pair grpc_iomgr_create_endpoint_pair(const char *name, + size_t read_slice_size) { + SOCKET sv[2]; + grpc_endpoint_pair p; + create_sockets(sv); + p.client = grpc_tcp_create(grpc_winsocket_create(sv[1], "endpoint:client"), + "endpoint:server"); + p.server = grpc_tcp_create(grpc_winsocket_create(sv[0], "endpoint:server"), + "endpoint:client"); + return p; +} + +#endif diff --git a/src/core/iomgr/fd_posix.c b/src/core/iomgr/fd_posix.c new file mode 100644 index 00000000..2d08a77a --- /dev/null +++ b/src/core/iomgr/fd_posix.c @@ -0,0 +1,460 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_POSIX_SOCKET + +#include "src/core/iomgr/fd_posix.h" + +#include +#include +#include + +#include +#include +#include + +enum descriptor_state { + NOT_READY = 0, + READY = 1 +}; /* or a pointer to a closure to call */ + +/* We need to keep a freelist not because of any concerns of malloc performance + * but instead so that implementations with multiple threads in (for example) + * epoll_wait deal with the race between pollset removal and incoming poll + * notifications. + * + * The problem is that the poller ultimately holds a reference to this + * object, so it is very difficult to know when is safe to free it, at least + * without some expensive synchronization. + * + * If we keep the object freelisted, in the worst case losing this race just + * becomes a spurious read notification on a reused fd. + */ +/* TODO(klempner): We could use some form of polling generation count to know + * when these are safe to free. */ +/* TODO(klempner): Consider disabling freelisting if we don't have multiple + * threads in poll on the same fd */ +/* TODO(klempner): Batch these allocations to reduce fragmentation */ +static grpc_fd *fd_freelist = NULL; +static gpr_mu fd_freelist_mu; + +static void freelist_fd(grpc_fd *fd) { + gpr_mu_lock(&fd_freelist_mu); + fd->freelist_next = fd_freelist; + fd_freelist = fd; + grpc_iomgr_unregister_object(&fd->iomgr_object); + gpr_mu_unlock(&fd_freelist_mu); +} + +static grpc_fd *alloc_fd(int fd) { + grpc_fd *r = NULL; + gpr_mu_lock(&fd_freelist_mu); + if (fd_freelist != NULL) { + r = fd_freelist; + fd_freelist = fd_freelist->freelist_next; + } + gpr_mu_unlock(&fd_freelist_mu); + if (r == NULL) { + r = gpr_malloc(sizeof(grpc_fd)); + gpr_mu_init(&r->set_state_mu); + gpr_mu_init(&r->watcher_mu); + } + + gpr_atm_rel_store(&r->refst, 1); + gpr_atm_rel_store(&r->readst, NOT_READY); + gpr_atm_rel_store(&r->writest, NOT_READY); + gpr_atm_rel_store(&r->shutdown, 0); + r->fd = fd; + r->inactive_watcher_root.next = r->inactive_watcher_root.prev = + &r->inactive_watcher_root; + r->freelist_next = NULL; + r->read_watcher = r->write_watcher = NULL; + r->on_done_closure = NULL; + r->closed = 0; + return r; +} + +static void destroy(grpc_fd *fd) { + gpr_mu_destroy(&fd->set_state_mu); + gpr_mu_destroy(&fd->watcher_mu); + gpr_free(fd); +} + +#ifdef GRPC_FD_REF_COUNT_DEBUG +#define REF_BY(fd, n, reason) ref_by(fd, n, reason, __FILE__, __LINE__) +#define UNREF_BY(fd, n, reason) unref_by(fd, n, reason, __FILE__, __LINE__) +static void ref_by(grpc_fd *fd, int n, const char *reason, const char *file, + int line) { + gpr_log(GPR_DEBUG, "FD %d %p ref %d %d -> %d [%s; %s:%d]", fd->fd, fd, n, + gpr_atm_no_barrier_load(&fd->refst), + gpr_atm_no_barrier_load(&fd->refst) + n, reason, file, line); +#else +#define REF_BY(fd, n, reason) ref_by(fd, n) +#define UNREF_BY(fd, n, reason) unref_by(fd, n) +static void ref_by(grpc_fd *fd, int n) { +#endif + GPR_ASSERT(gpr_atm_no_barrier_fetch_add(&fd->refst, n) > 0); +} + +#ifdef GRPC_FD_REF_COUNT_DEBUG +static void unref_by(grpc_fd *fd, int n, const char *reason, const char *file, + int line) { + gpr_atm old; + gpr_log(GPR_DEBUG, "FD %d %p unref %d %d -> %d [%s; %s:%d]", fd->fd, fd, n, + gpr_atm_no_barrier_load(&fd->refst), + gpr_atm_no_barrier_load(&fd->refst) - n, reason, file, line); +#else +static void unref_by(grpc_fd *fd, int n) { + gpr_atm old; +#endif + old = gpr_atm_full_fetch_add(&fd->refst, -n); + if (old == n) { + freelist_fd(fd); + } else { + GPR_ASSERT(old > n); + } +} + +void grpc_fd_global_init(void) { gpr_mu_init(&fd_freelist_mu); } + +void grpc_fd_global_shutdown(void) { + while (fd_freelist != NULL) { + grpc_fd *fd = fd_freelist; + fd_freelist = fd_freelist->freelist_next; + destroy(fd); + } + gpr_mu_destroy(&fd_freelist_mu); +} + +grpc_fd *grpc_fd_create(int fd, const char *name) { + grpc_fd *r = alloc_fd(fd); + grpc_iomgr_register_object(&r->iomgr_object, name); + return r; +} + +int grpc_fd_is_orphaned(grpc_fd *fd) { + return (gpr_atm_acq_load(&fd->refst) & 1) == 0; +} + +static void pollset_kick_locked(grpc_pollset *pollset) { + gpr_mu_lock(GRPC_POLLSET_MU(pollset)); + grpc_pollset_kick(pollset, NULL); + gpr_mu_unlock(GRPC_POLLSET_MU(pollset)); +} + +static void maybe_wake_one_watcher_locked(grpc_fd *fd) { + if (fd->inactive_watcher_root.next != &fd->inactive_watcher_root) { + pollset_kick_locked(fd->inactive_watcher_root.next->pollset); + } else if (fd->read_watcher) { + pollset_kick_locked(fd->read_watcher->pollset); + } else if (fd->write_watcher) { + pollset_kick_locked(fd->write_watcher->pollset); + } +} + +static void maybe_wake_one_watcher(grpc_fd *fd) { + gpr_mu_lock(&fd->watcher_mu); + maybe_wake_one_watcher_locked(fd); + gpr_mu_unlock(&fd->watcher_mu); +} + +static void wake_all_watchers_locked(grpc_fd *fd) { + grpc_fd_watcher *watcher; + for (watcher = fd->inactive_watcher_root.next; + watcher != &fd->inactive_watcher_root; watcher = watcher->next) { + pollset_kick_locked(watcher->pollset); + } + if (fd->read_watcher) { + pollset_kick_locked(fd->read_watcher->pollset); + } + if (fd->write_watcher && fd->write_watcher != fd->read_watcher) { + pollset_kick_locked(fd->write_watcher->pollset); + } +} + +static int has_watchers(grpc_fd *fd) { + return fd->read_watcher != NULL || fd->write_watcher != NULL || + fd->inactive_watcher_root.next != &fd->inactive_watcher_root; +} + +void grpc_fd_orphan(grpc_fd *fd, grpc_iomgr_closure *on_done, + const char *reason) { + fd->on_done_closure = on_done; + shutdown(fd->fd, SHUT_RDWR); + REF_BY(fd, 1, reason); /* remove active status, but keep referenced */ + gpr_mu_lock(&fd->watcher_mu); + if (!has_watchers(fd)) { + GPR_ASSERT(!fd->closed); + fd->closed = 1; + close(fd->fd); + if (fd->on_done_closure) { + grpc_iomgr_add_callback(fd->on_done_closure); + } + } else { + wake_all_watchers_locked(fd); + } + gpr_mu_unlock(&fd->watcher_mu); + UNREF_BY(fd, 2, reason); /* drop the reference */ +} + +/* increment refcount by two to avoid changing the orphan bit */ +#ifdef GRPC_FD_REF_COUNT_DEBUG +void grpc_fd_ref(grpc_fd *fd, const char *reason, const char *file, int line) { + ref_by(fd, 2, reason, file, line); +} + +void grpc_fd_unref(grpc_fd *fd, const char *reason, const char *file, + int line) { + unref_by(fd, 2, reason, file, line); +} +#else +void grpc_fd_ref(grpc_fd *fd) { ref_by(fd, 2); } + +void grpc_fd_unref(grpc_fd *fd) { unref_by(fd, 2); } +#endif + +static void process_callback(grpc_iomgr_closure *closure, int success, + int allow_synchronous_callback) { + if (allow_synchronous_callback) { + closure->cb(closure->cb_arg, success); + } else { + grpc_iomgr_add_delayed_callback(closure, success); + } +} + +static void process_callbacks(grpc_iomgr_closure *callbacks, size_t n, + int success, int allow_synchronous_callback) { + size_t i; + for (i = 0; i < n; i++) { + process_callback(callbacks + i, success, allow_synchronous_callback); + } +} + +static void notify_on(grpc_fd *fd, gpr_atm *st, grpc_iomgr_closure *closure, + int allow_synchronous_callback) { + switch (gpr_atm_acq_load(st)) { + case NOT_READY: + /* There is no race if the descriptor is already ready, so we skip + the interlocked op in that case. As long as the app doesn't + try to set the same upcall twice (which it shouldn't) then + oldval should never be anything other than READY or NOT_READY. We + don't + check for user error on the fast path. */ + if (gpr_atm_rel_cas(st, NOT_READY, (gpr_intptr)closure)) { + /* swap was successful -- the closure will run after the next + set_ready call. NOTE: we don't have an ABA problem here, + since we should never have concurrent calls to the same + notify_on function. */ + maybe_wake_one_watcher(fd); + return; + } + /* swap was unsuccessful due to an intervening set_ready call. + Fall through to the READY code below */ + case READY: + GPR_ASSERT(gpr_atm_no_barrier_load(st) == READY); + gpr_atm_rel_store(st, NOT_READY); + process_callback(closure, !gpr_atm_acq_load(&fd->shutdown), + allow_synchronous_callback); + return; + default: /* WAITING */ + /* upcallptr was set to a different closure. This is an error! */ + gpr_log(GPR_ERROR, + "User called a notify_on function with a previous callback still " + "pending"); + abort(); + } + gpr_log(GPR_ERROR, "Corrupt memory in &st->state"); + abort(); +} + +static void set_ready_locked(gpr_atm *st, grpc_iomgr_closure **callbacks, + size_t *ncallbacks) { + gpr_intptr state = gpr_atm_acq_load(st); + + switch (state) { + case READY: + /* duplicate ready, ignore */ + return; + case NOT_READY: + if (gpr_atm_rel_cas(st, NOT_READY, READY)) { + /* swap was successful -- the closure will run after the next + notify_on call. */ + return; + } + /* swap was unsuccessful due to an intervening set_ready call. + Fall through to the WAITING code below */ + state = gpr_atm_acq_load(st); + default: /* waiting */ + GPR_ASSERT(gpr_atm_no_barrier_load(st) != READY && + gpr_atm_no_barrier_load(st) != NOT_READY); + callbacks[(*ncallbacks)++] = (grpc_iomgr_closure *)state; + gpr_atm_rel_store(st, NOT_READY); + return; + } +} + +static void set_ready(grpc_fd *fd, gpr_atm *st, + int allow_synchronous_callback) { + /* only one set_ready can be active at once (but there may be a racing + notify_on) */ + int success; + grpc_iomgr_closure *closure; + size_t ncb = 0; + + gpr_mu_lock(&fd->set_state_mu); + set_ready_locked(st, &closure, &ncb); + gpr_mu_unlock(&fd->set_state_mu); + success = !gpr_atm_acq_load(&fd->shutdown); + GPR_ASSERT(ncb <= 1); + if (ncb > 0) { + process_callbacks(closure, ncb, success, allow_synchronous_callback); + } +} + +void grpc_fd_shutdown(grpc_fd *fd) { + size_t ncb = 0; + gpr_mu_lock(&fd->set_state_mu); + GPR_ASSERT(!gpr_atm_no_barrier_load(&fd->shutdown)); + gpr_atm_rel_store(&fd->shutdown, 1); + set_ready_locked(&fd->readst, &fd->shutdown_closures[0], &ncb); + set_ready_locked(&fd->writest, &fd->shutdown_closures[0], &ncb); + gpr_mu_unlock(&fd->set_state_mu); + GPR_ASSERT(ncb <= 2); + process_callbacks(fd->shutdown_closures[0], ncb, 0 /* GPR_FALSE */, + 0 /* GPR_FALSE */); +} + +void grpc_fd_notify_on_read(grpc_fd *fd, grpc_iomgr_closure *closure) { + notify_on(fd, &fd->readst, closure, 0); +} + +void grpc_fd_notify_on_write(grpc_fd *fd, grpc_iomgr_closure *closure) { + notify_on(fd, &fd->writest, closure, 0); +} + +gpr_uint32 grpc_fd_begin_poll(grpc_fd *fd, grpc_pollset *pollset, + gpr_uint32 read_mask, gpr_uint32 write_mask, + grpc_fd_watcher *watcher) { + gpr_uint32 mask = 0; + /* keep track of pollers that have requested our events, in case they change + */ + GRPC_FD_REF(fd, "poll"); + + gpr_mu_lock(&fd->watcher_mu); + /* if we are shutdown, then don't add to the watcher set */ + if (gpr_atm_no_barrier_load(&fd->shutdown)) { + watcher->fd = NULL; + watcher->pollset = NULL; + gpr_mu_unlock(&fd->watcher_mu); + GRPC_FD_UNREF(fd, "poll"); + return 0; + } + /* if there is nobody polling for read, but we need to, then start doing so */ + if (read_mask && !fd->read_watcher && + (gpr_uintptr)gpr_atm_acq_load(&fd->readst) > READY) { + fd->read_watcher = watcher; + mask |= read_mask; + } + /* if there is nobody polling for write, but we need to, then start doing so + */ + if (write_mask && !fd->write_watcher && + (gpr_uintptr)gpr_atm_acq_load(&fd->writest) > READY) { + fd->write_watcher = watcher; + mask |= write_mask; + } + /* if not polling, remember this watcher in case we need someone to later */ + if (mask == 0) { + watcher->next = &fd->inactive_watcher_root; + watcher->prev = watcher->next->prev; + watcher->next->prev = watcher->prev->next = watcher; + } + watcher->pollset = pollset; + watcher->fd = fd; + gpr_mu_unlock(&fd->watcher_mu); + + return mask; +} + +void grpc_fd_end_poll(grpc_fd_watcher *watcher, int got_read, int got_write) { + int was_polling = 0; + int kick = 0; + grpc_fd *fd = watcher->fd; + + if (fd == NULL) { + return; + } + + gpr_mu_lock(&fd->watcher_mu); + if (watcher == fd->read_watcher) { + /* remove read watcher, kick if we still need a read */ + was_polling = 1; + kick = kick || !got_read; + fd->read_watcher = NULL; + } + if (watcher == fd->write_watcher) { + /* remove write watcher, kick if we still need a write */ + was_polling = 1; + kick = kick || !got_write; + fd->write_watcher = NULL; + } + if (!was_polling) { + /* remove from inactive list */ + watcher->next->prev = watcher->prev; + watcher->prev->next = watcher->next; + } + if (kick) { + maybe_wake_one_watcher_locked(fd); + } + if (grpc_fd_is_orphaned(fd) && !has_watchers(fd) && !fd->closed) { + fd->closed = 1; + close(fd->fd); + if (fd->on_done_closure != NULL) { + grpc_iomgr_add_callback(fd->on_done_closure); + } + } + gpr_mu_unlock(&fd->watcher_mu); + + GRPC_FD_UNREF(fd, "poll"); +} + +void grpc_fd_become_readable(grpc_fd *fd, int allow_synchronous_callback) { + set_ready(fd, &fd->readst, allow_synchronous_callback); +} + +void grpc_fd_become_writable(grpc_fd *fd, int allow_synchronous_callback) { + set_ready(fd, &fd->writest, allow_synchronous_callback); +} + +#endif diff --git a/src/core/iomgr/fd_posix.h b/src/core/iomgr/fd_posix.h new file mode 100644 index 00000000..835e9b33 --- /dev/null +++ b/src/core/iomgr/fd_posix.h @@ -0,0 +1,182 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_IOMGR_FD_POSIX_H +#define GRPC_INTERNAL_CORE_IOMGR_FD_POSIX_H + +#include "src/core/iomgr/iomgr_internal.h" +#include "src/core/iomgr/pollset.h" +#include +#include +#include + +typedef struct grpc_fd grpc_fd; + +typedef struct grpc_fd_watcher { + struct grpc_fd_watcher *next; + struct grpc_fd_watcher *prev; + grpc_pollset *pollset; + grpc_fd *fd; +} grpc_fd_watcher; + +struct grpc_fd { + int fd; + /* refst format: + bit0: 1=active/0=orphaned + bit1-n: refcount + meaning that mostly we ref by two to avoid altering the orphaned bit, + and just unref by 1 when we're ready to flag the object as orphaned */ + gpr_atm refst; + + gpr_mu set_state_mu; + gpr_atm shutdown; + int closed; + + /* The watcher list. + + The following watcher related fields are protected by watcher_mu. + + An fd_watcher is an ephemeral object created when an fd wants to + begin polling, and destroyed after the poll. + + It denotes the fd's interest in whether to read poll or write poll + or both or neither on this fd. + + If a watcher is asked to poll for reads or writes, the read_watcher + or write_watcher fields are set respectively. A watcher may be asked + to poll for both, in which case both fields will be set. + + read_watcher and write_watcher may be NULL if no watcher has been + asked to poll for reads or writes. + + If an fd_watcher is not asked to poll for reads or writes, it's added + to a linked list of inactive watchers, rooted at inactive_watcher_root. + If at a later time there becomes need of a poller to poll, one of + the inactive pollers may be kicked out of their poll loops to take + that responsibility. */ + gpr_mu watcher_mu; + grpc_fd_watcher inactive_watcher_root; + grpc_fd_watcher *read_watcher; + grpc_fd_watcher *write_watcher; + + gpr_atm readst; + gpr_atm writest; + + struct grpc_fd *freelist_next; + + grpc_iomgr_closure *on_done_closure; + grpc_iomgr_closure *shutdown_closures[2]; + + grpc_iomgr_object iomgr_object; +}; + +/* Create a wrapped file descriptor. + Requires fd is a non-blocking file descriptor. + This takes ownership of closing fd. */ +grpc_fd *grpc_fd_create(int fd, const char *name); + +/* Releases fd to be asynchronously destroyed. + on_done is called when the underlying file descriptor is definitely close()d. + If on_done is NULL, no callback will be made. + Requires: *fd initialized; no outstanding notify_on_read or + notify_on_write. + MUST NOT be called with a pollset lock taken */ +void grpc_fd_orphan(grpc_fd *fd, grpc_iomgr_closure *on_done, + const char *reason); + +/* Begin polling on an fd. + Registers that the given pollset is interested in this fd - so that if read + or writability interest changes, the pollset can be kicked to pick up that + new interest. + Return value is: + (fd_needs_read? read_mask : 0) | (fd_needs_write? write_mask : 0) + i.e. a combination of read_mask and write_mask determined by the fd's current + interest in said events. + Polling strategies that do not need to alter their behavior depending on the + fd's current interest (such as epoll) do not need to call this function. + MUST NOT be called with a pollset lock taken */ +gpr_uint32 grpc_fd_begin_poll(grpc_fd *fd, grpc_pollset *pollset, + gpr_uint32 read_mask, gpr_uint32 write_mask, + grpc_fd_watcher *rec); +/* Complete polling previously started with grpc_fd_begin_poll + MUST NOT be called with a pollset lock taken */ +void grpc_fd_end_poll(grpc_fd_watcher *rec, int got_read, int got_write); + +/* Return 1 if this fd is orphaned, 0 otherwise */ +int grpc_fd_is_orphaned(grpc_fd *fd); + +/* Cause any current callbacks to error out with GRPC_CALLBACK_CANCELLED. */ +void grpc_fd_shutdown(grpc_fd *fd); + +/* Register read interest, causing read_cb to be called once when fd becomes + readable, on deadline specified by deadline, or on shutdown triggered by + grpc_fd_shutdown. + read_cb will be called with read_cb_arg when *fd becomes readable. + read_cb is Called with status of GRPC_CALLBACK_SUCCESS if readable, + GRPC_CALLBACK_TIMED_OUT if the call timed out, + and CANCELLED if the call was cancelled. + + Requires:This method must not be called before the read_cb for any previous + call runs. Edge triggered events are used whenever they are supported by the + underlying platform. This means that users must drain fd in read_cb before + calling notify_on_read again. Users are also expected to handle spurious + events, i.e read_cb is called while nothing can be readable from fd */ +void grpc_fd_notify_on_read(grpc_fd *fd, grpc_iomgr_closure *closure); + +/* Exactly the same semantics as above, except based on writable events. */ +void grpc_fd_notify_on_write(grpc_fd *fd, grpc_iomgr_closure *closure); + +/* Notification from the poller to an fd that it has become readable or + writable. + If allow_synchronous_callback is 1, allow running the fd callback inline + in this callstack, otherwise register an asynchronous callback and return */ +void grpc_fd_become_readable(grpc_fd *fd, int allow_synchronous_callback); +void grpc_fd_become_writable(grpc_fd *fd, int allow_synchronous_callback); + +/* Reference counting for fds */ +#ifdef GRPC_FD_REF_COUNT_DEBUG +void grpc_fd_ref(grpc_fd *fd, const char *reason, const char *file, int line); +void grpc_fd_unref(grpc_fd *fd, const char *reason, const char *file, int line); +#define GRPC_FD_REF(fd, reason) grpc_fd_ref(fd, reason, __FILE__, __LINE__) +#define GRPC_FD_UNREF(fd, reason) grpc_fd_unref(fd, reason, __FILE__, __LINE__) +#else +void grpc_fd_ref(grpc_fd *fd); +void grpc_fd_unref(grpc_fd *fd); +#define GRPC_FD_REF(fd, reason) grpc_fd_ref(fd) +#define GRPC_FD_UNREF(fd, reason) grpc_fd_unref(fd) +#endif + +void grpc_fd_global_init(void); +void grpc_fd_global_shutdown(void); + +#endif /* GRPC_INTERNAL_CORE_IOMGR_FD_POSIX_H */ diff --git a/src/core/iomgr/iocp_windows.c b/src/core/iomgr/iocp_windows.c new file mode 100644 index 00000000..006f8b2a --- /dev/null +++ b/src/core/iomgr/iocp_windows.c @@ -0,0 +1,198 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_WINSOCK_SOCKET + +#include + +#include +#include +#include +#include + +#include "src/core/iomgr/alarm_internal.h" +#include "src/core/iomgr/iocp_windows.h" +#include "src/core/iomgr/iomgr_internal.h" +#include "src/core/iomgr/socket_windows.h" + +static ULONG g_iocp_kick_token; +static OVERLAPPED g_iocp_custom_overlap; + +static gpr_event g_shutdown_iocp; +static gpr_event g_iocp_done; +static gpr_atm g_custom_events = 0; + +static HANDLE g_iocp; + +static void do_iocp_work() { + BOOL success; + DWORD bytes = 0; + DWORD flags = 0; + ULONG_PTR completion_key; + LPOVERLAPPED overlapped; + grpc_winsocket *socket; + grpc_winsocket_callback_info *info; + void (*f)(void *, int) = NULL; + void *opaque = NULL; + success = GetQueuedCompletionStatus(g_iocp, &bytes, &completion_key, + &overlapped, INFINITE); + /* success = 0 and overlapped = NULL means the deadline got attained. + Which is impossible. since our wait time is +inf */ + GPR_ASSERT(success || overlapped); + GPR_ASSERT(completion_key && overlapped); + if (overlapped == &g_iocp_custom_overlap) { + gpr_atm_full_fetch_add(&g_custom_events, -1); + if (completion_key == (ULONG_PTR)&g_iocp_kick_token) { + /* We were awoken from a kick. */ + return; + } + gpr_log(GPR_ERROR, "Unknown custom completion key."); + abort(); + } + + socket = (grpc_winsocket *)completion_key; + if (overlapped == &socket->write_info.overlapped) { + info = &socket->write_info; + } else if (overlapped == &socket->read_info.overlapped) { + info = &socket->read_info; + } else { + gpr_log(GPR_ERROR, "Unknown IOCP operation"); + abort(); + } + success = WSAGetOverlappedResult(socket->socket, &info->overlapped, &bytes, + FALSE, &flags); + info->bytes_transfered = bytes; + info->wsa_error = success ? 0 : WSAGetLastError(); + GPR_ASSERT(overlapped == &info->overlapped); + GPR_ASSERT(!info->has_pending_iocp); + gpr_mu_lock(&socket->state_mu); + if (info->cb) { + f = info->cb; + opaque = info->opaque; + info->cb = NULL; + } else { + info->has_pending_iocp = 1; + } + gpr_mu_unlock(&socket->state_mu); + if (f) f(opaque, 1); +} + +static void iocp_loop(void *p) { + while (gpr_atm_acq_load(&g_custom_events) || + !gpr_event_get(&g_shutdown_iocp)) { + do_iocp_work(); + } + + gpr_event_set(&g_iocp_done, (void *)1); +} + +void grpc_iocp_init(void) { + gpr_thd_id id; + + g_iocp = + CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, (ULONG_PTR)NULL, 0); + GPR_ASSERT(g_iocp); + + gpr_event_init(&g_iocp_done); + gpr_event_init(&g_shutdown_iocp); + gpr_thd_new(&id, iocp_loop, NULL, NULL); +} + +void grpc_iocp_kick(void) { + BOOL success; + + gpr_atm_full_fetch_add(&g_custom_events, 1); + success = PostQueuedCompletionStatus(g_iocp, 0, (ULONG_PTR)&g_iocp_kick_token, + &g_iocp_custom_overlap); + GPR_ASSERT(success); +} + +void grpc_iocp_shutdown(void) { + BOOL success; + gpr_event_set(&g_shutdown_iocp, (void *)1); + grpc_iocp_kick(); + gpr_event_wait(&g_iocp_done, gpr_inf_future(GPR_CLOCK_REALTIME)); + success = CloseHandle(g_iocp); + GPR_ASSERT(success); +} + +void grpc_iocp_add_socket(grpc_winsocket *socket) { + HANDLE ret; + if (socket->added_to_iocp) return; + ret = CreateIoCompletionPort((HANDLE)socket->socket, g_iocp, + (gpr_uintptr)socket, 0); + if (!ret) { + char *utf8_message = gpr_format_message(WSAGetLastError()); + gpr_log(GPR_ERROR, "Unable to add socket to iocp: %s", utf8_message); + gpr_free(utf8_message); + __debugbreak(); + abort(); + } + socket->added_to_iocp = 1; + GPR_ASSERT(ret == g_iocp); +} + +/* Calling notify_on_read or write means either of two things: + -) The IOCP already completed in the background, and we need to call + the callback now. + -) The IOCP hasn't completed yet, and we're queuing it for later. */ +static void socket_notify_on_iocp(grpc_winsocket *socket, + void (*cb)(void *, int), void *opaque, + grpc_winsocket_callback_info *info) { + int run_now = 0; + GPR_ASSERT(!info->cb); + gpr_mu_lock(&socket->state_mu); + if (info->has_pending_iocp) { + run_now = 1; + info->has_pending_iocp = 0; + } else { + info->cb = cb; + info->opaque = opaque; + } + gpr_mu_unlock(&socket->state_mu); + if (run_now) cb(opaque, 1); +} + +void grpc_socket_notify_on_write(grpc_winsocket *socket, + void (*cb)(void *, int), void *opaque) { + socket_notify_on_iocp(socket, cb, opaque, &socket->write_info); +} + +void grpc_socket_notify_on_read(grpc_winsocket *socket, void (*cb)(void *, int), + void *opaque) { + socket_notify_on_iocp(socket, cb, opaque, &socket->read_info); +} + +#endif /* GPR_WINSOCK_SOCKET */ diff --git a/src/core/iomgr/iocp_windows.h b/src/core/iomgr/iocp_windows.h new file mode 100644 index 00000000..7d2dc451 --- /dev/null +++ b/src/core/iomgr/iocp_windows.h @@ -0,0 +1,52 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_IOMGR_IOCP_WINDOWS_H +#define GRPC_INTERNAL_CORE_IOMGR_IOCP_WINDOWS_H + +#include + +#include "src/core/iomgr/socket_windows.h" + +void grpc_iocp_init(void); +void grpc_iocp_kick(void); +void grpc_iocp_shutdown(void); +void grpc_iocp_add_socket(grpc_winsocket *); + +void grpc_socket_notify_on_write(grpc_winsocket *, + void (*cb)(void *, int success), void *opaque); + +void grpc_socket_notify_on_read(grpc_winsocket *, + void (*cb)(void *, int success), void *opaque); + +#endif /* GRPC_INTERNAL_CORE_IOMGR_IOCP_WINDOWS_H */ diff --git a/src/core/iomgr/iomgr.c b/src/core/iomgr/iomgr.c new file mode 100644 index 00000000..1dd03992 --- /dev/null +++ b/src/core/iomgr/iomgr.c @@ -0,0 +1,280 @@ +/* + * + * Copyright 2015, 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 "src/core/iomgr/iomgr.h" + +#include + +#include "src/core/iomgr/iomgr_internal.h" +#include "src/core/iomgr/alarm_internal.h" +#include "src/core/support/string.h" +#include +#include +#include +#include +#include + +static gpr_mu g_mu; +static gpr_cv g_rcv; +static grpc_iomgr_closure *g_cbs_head = NULL; +static grpc_iomgr_closure *g_cbs_tail = NULL; +static int g_shutdown; +static gpr_event g_background_callback_executor_done; +static grpc_iomgr_object g_root_object; + +/* Execute followup callbacks continuously. + Other threads may check in and help during pollset_work() */ +static void background_callback_executor(void *ignored) { + gpr_mu_lock(&g_mu); + while (!g_shutdown) { + gpr_timespec deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC); + gpr_timespec short_deadline = gpr_time_add( + gpr_now(GPR_CLOCK_MONOTONIC), gpr_time_from_millis(100, GPR_TIMESPAN)); + if (g_cbs_head) { + grpc_iomgr_closure *closure = g_cbs_head; + g_cbs_head = closure->next; + if (!g_cbs_head) g_cbs_tail = NULL; + gpr_mu_unlock(&g_mu); + closure->cb(closure->cb_arg, closure->success); + gpr_mu_lock(&g_mu); + } else if (grpc_alarm_check(&g_mu, gpr_now(GPR_CLOCK_MONOTONIC), + &deadline)) { + } else { + gpr_mu_unlock(&g_mu); + gpr_sleep_until(gpr_time_min(short_deadline, deadline)); + gpr_mu_lock(&g_mu); + } + } + gpr_mu_unlock(&g_mu); + gpr_event_set(&g_background_callback_executor_done, (void *)1); +} + +void grpc_kick_poller(void) { + /* Empty. The background callback executor polls periodically. The activity + * the kicker is trying to draw the executor's attention to will be picked up + * either by one of the periodic wakeups or by one of the polling application + * threads. */ +} + +void grpc_iomgr_init(void) { + gpr_thd_id id; + g_shutdown = 0; + gpr_mu_init(&g_mu); + gpr_cv_init(&g_rcv); + grpc_alarm_list_init(gpr_now(GPR_CLOCK_MONOTONIC)); + g_root_object.next = g_root_object.prev = &g_root_object; + g_root_object.name = "root"; + grpc_iomgr_platform_init(); + gpr_event_init(&g_background_callback_executor_done); + gpr_thd_new(&id, background_callback_executor, NULL, NULL); +} + +static size_t count_objects(void) { + grpc_iomgr_object *obj; + size_t n = 0; + for (obj = g_root_object.next; obj != &g_root_object; obj = obj->next) { + n++; + } + return n; +} + +static void dump_objects(const char *kind) { + grpc_iomgr_object *obj; + for (obj = g_root_object.next; obj != &g_root_object; obj = obj->next) { + gpr_log(GPR_DEBUG, "%s OBJECT: %s %p", kind, obj->name, obj); + } +} + +void grpc_iomgr_shutdown(void) { + grpc_iomgr_closure *closure; + gpr_timespec shutdown_deadline = gpr_time_add( + gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_seconds(10, GPR_TIMESPAN)); + gpr_timespec last_warning_time = gpr_now(GPR_CLOCK_REALTIME); + + gpr_mu_lock(&g_mu); + g_shutdown = 1; + while (g_cbs_head != NULL || g_root_object.next != &g_root_object) { + if (gpr_time_cmp( + gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME), last_warning_time), + gpr_time_from_seconds(1, GPR_TIMESPAN)) >= 0) { + if (g_cbs_head != NULL && g_root_object.next != &g_root_object) { + gpr_log(GPR_DEBUG, + "Waiting for %d iomgr objects to be destroyed and executing " + "final callbacks", + count_objects()); + } else if (g_cbs_head != NULL) { + gpr_log(GPR_DEBUG, "Executing final iomgr callbacks"); + } else { + gpr_log(GPR_DEBUG, "Waiting for %d iomgr objects to be destroyed", + count_objects()); + } + last_warning_time = gpr_now(GPR_CLOCK_REALTIME); + } + if (g_cbs_head) { + do { + closure = g_cbs_head; + g_cbs_head = closure->next; + if (!g_cbs_head) g_cbs_tail = NULL; + gpr_mu_unlock(&g_mu); + + closure->cb(closure->cb_arg, 0); + gpr_mu_lock(&g_mu); + } while (g_cbs_head); + continue; + } + if (grpc_alarm_check(&g_mu, gpr_inf_future(GPR_CLOCK_MONOTONIC), NULL)) { + continue; + } + if (g_root_object.next != &g_root_object) { + int timeout = 0; + while (g_cbs_head == NULL) { + gpr_timespec short_deadline = gpr_time_add( + gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_millis(100, GPR_TIMESPAN)); + if (gpr_cv_wait(&g_rcv, &g_mu, short_deadline) && g_cbs_head == NULL) { + if (gpr_time_cmp(gpr_now(GPR_CLOCK_REALTIME), shutdown_deadline) > 0) { + timeout = 1; + break; + } + } + } + if (timeout) { + gpr_log(GPR_DEBUG, + "Failed to free %d iomgr objects before shutdown deadline: " + "memory leaks are likely", + count_objects()); + dump_objects("LEAKED"); + break; + } + } + } + gpr_mu_unlock(&g_mu); + + grpc_kick_poller(); + gpr_event_wait(&g_background_callback_executor_done, + gpr_inf_future(GPR_CLOCK_REALTIME)); + + grpc_alarm_list_shutdown(); + + grpc_iomgr_platform_shutdown(); + gpr_mu_destroy(&g_mu); + gpr_cv_destroy(&g_rcv); +} + +void grpc_iomgr_register_object(grpc_iomgr_object *obj, const char *name) { + obj->name = gpr_strdup(name); + gpr_mu_lock(&g_mu); + obj->next = &g_root_object; + obj->prev = g_root_object.prev; + obj->next->prev = obj->prev->next = obj; + gpr_mu_unlock(&g_mu); +} + +void grpc_iomgr_unregister_object(grpc_iomgr_object *obj) { + gpr_mu_lock(&g_mu); + obj->next->prev = obj->prev; + obj->prev->next = obj->next; + gpr_cv_signal(&g_rcv); + gpr_mu_unlock(&g_mu); + gpr_free(obj->name); +} + +void grpc_iomgr_closure_init(grpc_iomgr_closure *closure, grpc_iomgr_cb_func cb, + void *cb_arg) { + closure->cb = cb; + closure->cb_arg = cb_arg; + closure->next = NULL; +} + +static void assert_not_scheduled_locked(grpc_iomgr_closure *closure) { +#ifndef NDEBUG + grpc_iomgr_closure *c; + + for (c = g_cbs_head; c; c = c->next) { + GPR_ASSERT(c != closure); + } +#endif +} + +void grpc_iomgr_add_delayed_callback(grpc_iomgr_closure *closure, int success) { + closure->success = success; + GPR_ASSERT(closure->cb); + gpr_mu_lock(&g_mu); + assert_not_scheduled_locked(closure); + closure->next = NULL; + if (!g_cbs_tail) { + g_cbs_head = g_cbs_tail = closure; + } else { + g_cbs_tail->next = closure; + g_cbs_tail = closure; + } + if (g_shutdown) { + gpr_cv_signal(&g_rcv); + } + gpr_mu_unlock(&g_mu); +} + +void grpc_iomgr_add_callback(grpc_iomgr_closure *closure) { + grpc_iomgr_add_delayed_callback(closure, 1 /* GPR_TRUE */); +} + +int grpc_maybe_call_delayed_callbacks(gpr_mu *drop_mu, int success) { + int n = 0; + gpr_mu *retake_mu = NULL; + grpc_iomgr_closure *closure; + for (;;) { + /* check for new work */ + if (!gpr_mu_trylock(&g_mu)) { + break; + } + closure = g_cbs_head; + if (!closure) { + gpr_mu_unlock(&g_mu); + break; + } + g_cbs_head = closure->next; + if (!g_cbs_head) g_cbs_tail = NULL; + gpr_mu_unlock(&g_mu); + /* if we have a mutex to drop, do so before executing work */ + if (drop_mu) { + gpr_mu_unlock(drop_mu); + retake_mu = drop_mu; + drop_mu = NULL; + } + closure->cb(closure->cb_arg, success && closure->success); + n++; + } + if (retake_mu) { + gpr_mu_lock(retake_mu); + } + return n; +} diff --git a/src/core/iomgr/iomgr.h b/src/core/iomgr/iomgr.h new file mode 100644 index 00000000..261c1736 --- /dev/null +++ b/src/core/iomgr/iomgr.h @@ -0,0 +1,80 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_IOMGR_IOMGR_H +#define GRPC_INTERNAL_CORE_IOMGR_IOMGR_H + +/** gRPC Callback definition. + * + * \param arg Arbitrary input. + * \param success An indication on the state of the iomgr. On false, cleanup + * actions should be taken (eg, shutdown). */ +typedef void (*grpc_iomgr_cb_func)(void *arg, int success); + +/** A closure over a grpc_iomgr_cb_func. */ +typedef struct grpc_iomgr_closure { + /** Bound callback. */ + grpc_iomgr_cb_func cb; + + /** Arguments to be passed to "cb". */ + void *cb_arg; + + /** Internal. A boolean indication to "cb" on the state of the iomgr. + * For instance, closures created during a shutdown would have this field set + * to false. */ + int success; + + /**< Internal. Do not touch */ + struct grpc_iomgr_closure *next; +} grpc_iomgr_closure; + +/** Initializes \a closure with \a cb and \a cb_arg. */ +void grpc_iomgr_closure_init(grpc_iomgr_closure *closure, grpc_iomgr_cb_func cb, + void *cb_arg); + +/** Initializes the iomgr. */ +void grpc_iomgr_init(void); + +/** Signals the intention to shutdown the iomgr. */ +void grpc_iomgr_shutdown(void); + +/** Registers a closure to be invoked at some point in the future. + * + * Can be called from within a callback or from anywhere else */ +void grpc_iomgr_add_callback(grpc_iomgr_closure *closure); + +/** As per grpc_iomgr_add_callback, with the ability to set the success + argument. */ +void grpc_iomgr_add_delayed_callback(grpc_iomgr_closure *iocb, int success); + +#endif /* GRPC_INTERNAL_CORE_IOMGR_IOMGR_H */ diff --git a/src/core/iomgr/iomgr_internal.h b/src/core/iomgr/iomgr_internal.h new file mode 100644 index 00000000..4cec973b --- /dev/null +++ b/src/core/iomgr/iomgr_internal.h @@ -0,0 +1,55 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_IOMGR_IOMGR_INTERNAL_H +#define GRPC_INTERNAL_CORE_IOMGR_IOMGR_INTERNAL_H + +#include "src/core/iomgr/iomgr.h" +#include + +typedef struct grpc_iomgr_object { + char *name; + struct grpc_iomgr_object *next; + struct grpc_iomgr_object *prev; +} grpc_iomgr_object; + +int grpc_maybe_call_delayed_callbacks(gpr_mu *drop_mu, int success); +void grpc_iomgr_add_delayed_callback(grpc_iomgr_closure *iocb, int success); + +void grpc_iomgr_register_object(grpc_iomgr_object *obj, const char *name); +void grpc_iomgr_unregister_object(grpc_iomgr_object *obj); + +void grpc_iomgr_platform_init(void); +void grpc_iomgr_platform_shutdown(void); + +#endif /* GRPC_INTERNAL_CORE_IOMGR_IOMGR_INTERNAL_H */ diff --git a/src/core/iomgr/iomgr_posix.c b/src/core/iomgr/iomgr_posix.c new file mode 100644 index 00000000..2425e599 --- /dev/null +++ b/src/core/iomgr/iomgr_posix.c @@ -0,0 +1,54 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_POSIX_SOCKET + +#include "src/core/iomgr/iomgr_posix.h" +#include "src/core/debug/trace.h" +#include "src/core/iomgr/fd_posix.h" +#include "src/core/iomgr/tcp_posix.h" + +void grpc_iomgr_platform_init(void) { + grpc_fd_global_init(); + grpc_pollset_global_init(); + grpc_register_tracer("tcp", &grpc_tcp_trace); +} + +void grpc_iomgr_platform_shutdown(void) { + grpc_pollset_global_shutdown(); + grpc_fd_global_shutdown(); +} + +#endif /* GRPC_POSIX_SOCKET */ diff --git a/src/core/iomgr/iomgr_posix.h b/src/core/iomgr/iomgr_posix.h new file mode 100644 index 00000000..716fedb6 --- /dev/null +++ b/src/core/iomgr/iomgr_posix.h @@ -0,0 +1,42 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_IOMGR_IOMGR_POSIX_H +#define GRPC_INTERNAL_CORE_IOMGR_IOMGR_POSIX_H + +#include "src/core/iomgr/iomgr_internal.h" + +void grpc_pollset_global_init(void); +void grpc_pollset_global_shutdown(void); + +#endif /* GRPC_INTERNAL_CORE_IOMGR_IOMGR_POSIX_H */ diff --git a/src/core/iomgr/iomgr_windows.c b/src/core/iomgr/iomgr_windows.c new file mode 100644 index 00000000..b49cb87e --- /dev/null +++ b/src/core/iomgr/iomgr_windows.c @@ -0,0 +1,71 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_WINSOCK_SOCKET + +#include "src/core/iomgr/sockaddr_win32.h" + +#include + +#include "src/core/iomgr/socket_windows.h" +#include "src/core/iomgr/iocp_windows.h" +#include "src/core/iomgr/iomgr.h" + +/* Windows' io manager is going to be fully designed using IO completion + ports. All of what we're doing here is basically make sure that + Windows sockets are initialized in and out. */ + +static void winsock_init(void) { + WSADATA wsaData; + int status = WSAStartup(MAKEWORD(2, 0), &wsaData); + GPR_ASSERT(status == 0); +} + +static void winsock_shutdown(void) { + int status = WSACleanup(); + GPR_ASSERT(status == 0); +} + +void grpc_iomgr_platform_init(void) { + winsock_init(); + grpc_iocp_init(); +} + +void grpc_iomgr_platform_shutdown(void) { + grpc_iocp_shutdown(); + winsock_shutdown(); +} + +#endif /* GRPC_WINSOCK_SOCKET */ diff --git a/src/core/iomgr/pollset.h b/src/core/iomgr/pollset.h new file mode 100644 index 00000000..337596cb --- /dev/null +++ b/src/core/iomgr/pollset.h @@ -0,0 +1,87 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_IOMGR_POLLSET_H +#define GRPC_INTERNAL_CORE_IOMGR_POLLSET_H + +#include +#include + +#define GRPC_POLLSET_KICK_BROADCAST ((grpc_pollset_worker *)1) + +/* A grpc_pollset is a set of file descriptors that a higher level item is + interested in. For example: + - a server will typically keep a pollset containing all connected channels, + so that it can find new calls to service + - a completion queue might keep a pollset with an entry for each transport + that is servicing a call that it's tracking */ + +#ifdef GPR_POSIX_SOCKET +#include "src/core/iomgr/pollset_posix.h" +#endif + +#ifdef GPR_WIN32 +#include "src/core/iomgr/pollset_windows.h" +#endif + +void grpc_pollset_init(grpc_pollset *pollset); +void grpc_pollset_shutdown(grpc_pollset *pollset, + void (*shutdown_done)(void *arg), + void *shutdown_done_arg); +void grpc_pollset_destroy(grpc_pollset *pollset); + +/* Do some work on a pollset. + May involve invoking asynchronous callbacks, or actually polling file + descriptors. + Requires GRPC_POLLSET_MU(pollset) locked. + May unlock GRPC_POLLSET_MU(pollset) during its execution. + + worker is a (platform-specific) handle that can be used to wake up + from grpc_pollset_work before any events are received and before the timeout + has expired. It is both initialized and destroyed by grpc_pollset_work. + Initialization of worker is guaranteed to occur BEFORE the + GRPC_POLLSET_MU(pollset) is released for the first time by + grpc_pollset_work, and it is guaranteed that GRPC_POLLSET_MU(pollset) will + not be released by grpc_pollset_work AFTER worker has been destroyed. + + Tries not to block past deadline. */ +void grpc_pollset_work(grpc_pollset *pollset, grpc_pollset_worker *worker, + gpr_timespec now, gpr_timespec deadline); + +/* Break one polling thread out of polling work for this pollset. + If specific_worker is GRPC_POLLSET_KICK_BROADCAST, kick ALL the workers. + Otherwise, if specific_worker is non-NULL, then kick that worker. */ +void grpc_pollset_kick(grpc_pollset *pollset, + grpc_pollset_worker *specific_worker); + +#endif /* GRPC_INTERNAL_CORE_IOMGR_POLLSET_H */ diff --git a/src/core/iomgr/pollset_multipoller_with_epoll.c b/src/core/iomgr/pollset_multipoller_with_epoll.c new file mode 100644 index 00000000..8f62ce29 --- /dev/null +++ b/src/core/iomgr/pollset_multipoller_with_epoll.c @@ -0,0 +1,262 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_LINUX_MULTIPOLL_WITH_EPOLL + +#include +#include +#include +#include +#include + +#include "src/core/iomgr/fd_posix.h" +#include +#include + +typedef struct wakeup_fd_hdl { + grpc_wakeup_fd wakeup_fd; + struct wakeup_fd_hdl *next; +} wakeup_fd_hdl; + +typedef struct { + grpc_pollset *pollset; + grpc_fd *fd; + grpc_iomgr_closure closure; +} delayed_add; + +typedef struct { + int epoll_fd; + wakeup_fd_hdl *free_wakeup_fds; +} pollset_hdr; + +static void finally_add_fd(grpc_pollset *pollset, grpc_fd *fd) { + pollset_hdr *h = pollset->data.ptr; + struct epoll_event ev; + int err; + grpc_fd_watcher watcher; + + /* We pretend to be polling whilst adding an fd to keep the fd from being + closed during the add. This may result in a spurious wakeup being assigned + to this pollset whilst adding, but that should be benign. */ + GPR_ASSERT(grpc_fd_begin_poll(fd, pollset, 0, 0, &watcher) == 0); + if (watcher.fd != NULL) { + ev.events = EPOLLIN | EPOLLOUT | EPOLLET; + ev.data.ptr = fd; + err = epoll_ctl(h->epoll_fd, EPOLL_CTL_ADD, fd->fd, &ev); + if (err < 0) { + /* FDs may be added to a pollset multiple times, so EEXIST is normal. */ + if (errno != EEXIST) { + gpr_log(GPR_ERROR, "epoll_ctl add for %d failed: %s", fd->fd, + strerror(errno)); + } + } + } + grpc_fd_end_poll(&watcher, 0, 0); +} + +static void perform_delayed_add(void *arg, int iomgr_status) { + delayed_add *da = arg; + int do_shutdown_cb = 0; + + if (!grpc_fd_is_orphaned(da->fd)) { + finally_add_fd(da->pollset, da->fd); + } + + gpr_mu_lock(&da->pollset->mu); + da->pollset->in_flight_cbs--; + if (da->pollset->shutting_down) { + /* We don't care about this pollset anymore. */ + if (da->pollset->in_flight_cbs == 0 && !da->pollset->called_shutdown) { + da->pollset->called_shutdown = 1; + do_shutdown_cb = 1; + } + } + gpr_mu_unlock(&da->pollset->mu); + + GRPC_FD_UNREF(da->fd, "delayed_add"); + + if (do_shutdown_cb) { + da->pollset->shutdown_done_cb(da->pollset->shutdown_done_arg); + } + + gpr_free(da); +} + +static void multipoll_with_epoll_pollset_add_fd(grpc_pollset *pollset, + grpc_fd *fd, + int and_unlock_pollset) { + if (and_unlock_pollset) { + gpr_mu_unlock(&pollset->mu); + finally_add_fd(pollset, fd); + } else { + delayed_add *da = gpr_malloc(sizeof(*da)); + da->pollset = pollset; + da->fd = fd; + GRPC_FD_REF(fd, "delayed_add"); + grpc_iomgr_closure_init(&da->closure, perform_delayed_add, da); + pollset->in_flight_cbs++; + grpc_iomgr_add_callback(&da->closure); + } +} + +static void multipoll_with_epoll_pollset_del_fd(grpc_pollset *pollset, + grpc_fd *fd, + int and_unlock_pollset) { + pollset_hdr *h = pollset->data.ptr; + int err; + + if (and_unlock_pollset) { + gpr_mu_unlock(&pollset->mu); + } + + /* Note that this can race with concurrent poll, but that should be fine since + * at worst it creates a spurious read event on a reused grpc_fd object. */ + err = epoll_ctl(h->epoll_fd, EPOLL_CTL_DEL, fd->fd, NULL); + if (err < 0) { + gpr_log(GPR_ERROR, "epoll_ctl del for %d failed: %s", fd->fd, + strerror(errno)); + } +} + +/* TODO(klempner): We probably want to turn this down a bit */ +#define GRPC_EPOLL_MAX_EVENTS 1000 + +static void multipoll_with_epoll_pollset_maybe_work( + grpc_pollset *pollset, grpc_pollset_worker *worker, gpr_timespec deadline, + gpr_timespec now, int allow_synchronous_callback) { + struct epoll_event ep_ev[GRPC_EPOLL_MAX_EVENTS]; + int ep_rv; + int poll_rv; + pollset_hdr *h = pollset->data.ptr; + int timeout_ms; + struct pollfd pfds[2]; + + /* If you want to ignore epoll's ability to sanely handle parallel pollers, + * for a more apples-to-apples performance comparison with poll, add a + * if (pollset->counter != 0) { return 0; } + * here. + */ + + gpr_mu_unlock(&pollset->mu); + + timeout_ms = grpc_poll_deadline_to_millis_timeout(deadline, now); + + pfds[0].fd = GRPC_WAKEUP_FD_GET_READ_FD(&worker->wakeup_fd); + pfds[0].events = POLLIN; + pfds[0].revents = 0; + pfds[1].fd = h->epoll_fd; + pfds[1].events = POLLIN; + pfds[1].revents = 0; + + poll_rv = grpc_poll_function(pfds, 2, timeout_ms); + + if (poll_rv < 0) { + if (errno != EINTR) { + gpr_log(GPR_ERROR, "poll() failed: %s", strerror(errno)); + } + } else if (poll_rv == 0) { + /* do nothing */ + } else { + if (pfds[0].revents) { + grpc_wakeup_fd_consume_wakeup(&worker->wakeup_fd); + } + if (pfds[1].revents) { + do { + ep_rv = epoll_wait(h->epoll_fd, ep_ev, GRPC_EPOLL_MAX_EVENTS, 0); + if (ep_rv < 0) { + if (errno != EINTR) { + gpr_log(GPR_ERROR, "epoll_wait() failed: %s", strerror(errno)); + } + } else { + int i; + for (i = 0; i < ep_rv; ++i) { + grpc_fd *fd = ep_ev[i].data.ptr; + /* TODO(klempner): We might want to consider making err and pri + * separate events */ + int cancel = ep_ev[i].events & (EPOLLERR | EPOLLHUP); + int read = ep_ev[i].events & (EPOLLIN | EPOLLPRI); + int write = ep_ev[i].events & EPOLLOUT; + if (read || cancel) { + grpc_fd_become_readable(fd, allow_synchronous_callback); + } + if (write || cancel) { + grpc_fd_become_writable(fd, allow_synchronous_callback); + } + } + } + } while (ep_rv == GRPC_EPOLL_MAX_EVENTS); + } + } + + gpr_mu_lock(&pollset->mu); +} + +static void multipoll_with_epoll_pollset_finish_shutdown( + grpc_pollset *pollset) {} + +static void multipoll_with_epoll_pollset_destroy(grpc_pollset *pollset) { + pollset_hdr *h = pollset->data.ptr; + close(h->epoll_fd); + gpr_free(h); +} + +static const grpc_pollset_vtable multipoll_with_epoll_pollset = { + multipoll_with_epoll_pollset_add_fd, multipoll_with_epoll_pollset_del_fd, + multipoll_with_epoll_pollset_maybe_work, + multipoll_with_epoll_pollset_finish_shutdown, + multipoll_with_epoll_pollset_destroy}; + +static void epoll_become_multipoller(grpc_pollset *pollset, grpc_fd **fds, + size_t nfds) { + size_t i; + pollset_hdr *h = gpr_malloc(sizeof(pollset_hdr)); + + pollset->vtable = &multipoll_with_epoll_pollset; + pollset->data.ptr = h; + h->epoll_fd = epoll_create1(EPOLL_CLOEXEC); + if (h->epoll_fd < 0) { + /* TODO(klempner): Fall back to poll here, especially on ENOSYS */ + gpr_log(GPR_ERROR, "epoll_create1 failed: %s", strerror(errno)); + abort(); + } + for (i = 0; i < nfds; i++) { + multipoll_with_epoll_pollset_add_fd(pollset, fds[i], 0); + } +} + +grpc_platform_become_multipoller_type grpc_platform_become_multipoller = + epoll_become_multipoller; + +#endif /* GPR_LINUX_MULTIPOLL_WITH_EPOLL */ diff --git a/src/core/iomgr/pollset_multipoller_with_poll_posix.c b/src/core/iomgr/pollset_multipoller_with_poll_posix.c new file mode 100644 index 00000000..30ee6e24 --- /dev/null +++ b/src/core/iomgr/pollset_multipoller_with_poll_posix.c @@ -0,0 +1,233 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_POSIX_SOCKET + +#include "src/core/iomgr/pollset_posix.h" + +#include +#include +#include +#include + +#include "src/core/iomgr/fd_posix.h" +#include "src/core/iomgr/iomgr_internal.h" +#include +#include +#include + +typedef struct { + /* all polled fds */ + size_t fd_count; + size_t fd_capacity; + grpc_fd **fds; + /* fds that have been removed from the pollset explicitly */ + size_t del_count; + size_t del_capacity; + grpc_fd **dels; +} pollset_hdr; + +static void multipoll_with_poll_pollset_add_fd(grpc_pollset *pollset, + grpc_fd *fd, + int and_unlock_pollset) { + size_t i; + pollset_hdr *h = pollset->data.ptr; + /* TODO(ctiller): this is O(num_fds^2); maybe switch to a hash set here */ + for (i = 0; i < h->fd_count; i++) { + if (h->fds[i] == fd) goto exit; + } + if (h->fd_count == h->fd_capacity) { + h->fd_capacity = GPR_MAX(h->fd_capacity + 8, h->fd_count * 3 / 2); + h->fds = gpr_realloc(h->fds, sizeof(grpc_fd *) * h->fd_capacity); + } + h->fds[h->fd_count++] = fd; + GRPC_FD_REF(fd, "multipoller"); +exit: + if (and_unlock_pollset) { + gpr_mu_unlock(&pollset->mu); + } +} + +static void multipoll_with_poll_pollset_del_fd(grpc_pollset *pollset, + grpc_fd *fd, + int and_unlock_pollset) { + /* will get removed next poll cycle */ + pollset_hdr *h = pollset->data.ptr; + if (h->del_count == h->del_capacity) { + h->del_capacity = GPR_MAX(h->del_capacity + 8, h->del_count * 3 / 2); + h->dels = gpr_realloc(h->dels, sizeof(grpc_fd *) * h->del_capacity); + } + h->dels[h->del_count++] = fd; + GRPC_FD_REF(fd, "multipoller_del"); + if (and_unlock_pollset) { + gpr_mu_unlock(&pollset->mu); + } +} + +static void multipoll_with_poll_pollset_maybe_work( + grpc_pollset *pollset, grpc_pollset_worker *worker, gpr_timespec deadline, + gpr_timespec now, int allow_synchronous_callback) { + int timeout; + int r; + size_t i, j, pfd_count, fd_count; + pollset_hdr *h; + /* TODO(ctiller): inline some elements to avoid an allocation */ + grpc_fd_watcher *watchers; + struct pollfd *pfds; + + h = pollset->data.ptr; + timeout = grpc_poll_deadline_to_millis_timeout(deadline, now); + /* TODO(ctiller): perform just one malloc here if we exceed the inline case */ + pfds = gpr_malloc(sizeof(*pfds) * (h->fd_count + 1)); + watchers = gpr_malloc(sizeof(*watchers) * (h->fd_count + 1)); + fd_count = 0; + pfd_count = 1; + pfds[0].fd = GRPC_WAKEUP_FD_GET_READ_FD(&worker->wakeup_fd); + pfds[0].events = POLLIN; + pfds[0].revents = POLLOUT; + for (i = 0; i < h->fd_count; i++) { + int remove = grpc_fd_is_orphaned(h->fds[i]); + for (j = 0; !remove && j < h->del_count; j++) { + if (h->fds[i] == h->dels[j]) remove = 1; + } + if (remove) { + GRPC_FD_UNREF(h->fds[i], "multipoller"); + } else { + h->fds[fd_count++] = h->fds[i]; + watchers[pfd_count].fd = h->fds[i]; + pfds[pfd_count].fd = h->fds[i]->fd; + pfds[pfd_count].revents = 0; + pfd_count++; + } + } + for (j = 0; j < h->del_count; j++) { + GRPC_FD_UNREF(h->dels[j], "multipoller_del"); + } + h->del_count = 0; + h->fd_count = fd_count; + gpr_mu_unlock(&pollset->mu); + + for (i = 1; i < pfd_count; i++) { + pfds[i].events = grpc_fd_begin_poll(watchers[i].fd, pollset, POLLIN, + POLLOUT, &watchers[i]); + } + + r = grpc_poll_function(pfds, pfd_count, timeout); + + for (i = 1; i < pfd_count; i++) { + grpc_fd_end_poll(&watchers[i], pfds[i].revents & POLLIN, + pfds[i].revents & POLLOUT); + } + + if (r < 0) { + if (errno != EINTR) { + gpr_log(GPR_ERROR, "poll() failed: %s", strerror(errno)); + } + } else if (r == 0) { + /* do nothing */ + } else { + if (pfds[0].revents & POLLIN) { + grpc_wakeup_fd_consume_wakeup(&worker->wakeup_fd); + } + for (i = 1; i < pfd_count; i++) { + if (watchers[i].fd == NULL) { + continue; + } + if (pfds[i].revents & (POLLIN | POLLHUP | POLLERR)) { + grpc_fd_become_readable(watchers[i].fd, allow_synchronous_callback); + } + if (pfds[i].revents & (POLLOUT | POLLHUP | POLLERR)) { + grpc_fd_become_writable(watchers[i].fd, allow_synchronous_callback); + } + } + } + + gpr_free(pfds); + gpr_free(watchers); + + gpr_mu_lock(&pollset->mu); +} + +static void multipoll_with_poll_pollset_finish_shutdown(grpc_pollset *pollset) { + size_t i; + pollset_hdr *h = pollset->data.ptr; + for (i = 0; i < h->fd_count; i++) { + GRPC_FD_UNREF(h->fds[i], "multipoller"); + } + for (i = 0; i < h->del_count; i++) { + GRPC_FD_UNREF(h->dels[i], "multipoller_del"); + } + h->fd_count = 0; + h->del_count = 0; +} + +static void multipoll_with_poll_pollset_destroy(grpc_pollset *pollset) { + pollset_hdr *h = pollset->data.ptr; + multipoll_with_poll_pollset_finish_shutdown(pollset); + gpr_free(h->fds); + gpr_free(h->dels); + gpr_free(h); +} + +static const grpc_pollset_vtable multipoll_with_poll_pollset = { + multipoll_with_poll_pollset_add_fd, multipoll_with_poll_pollset_del_fd, + multipoll_with_poll_pollset_maybe_work, + multipoll_with_poll_pollset_finish_shutdown, + multipoll_with_poll_pollset_destroy}; + +void grpc_poll_become_multipoller(grpc_pollset *pollset, grpc_fd **fds, + size_t nfds) { + size_t i; + pollset_hdr *h = gpr_malloc(sizeof(pollset_hdr)); + pollset->vtable = &multipoll_with_poll_pollset; + pollset->data.ptr = h; + h->fd_count = nfds; + h->fd_capacity = nfds; + h->fds = gpr_malloc(nfds * sizeof(grpc_fd *)); + h->del_count = 0; + h->del_capacity = 0; + h->dels = NULL; + for (i = 0; i < nfds; i++) { + h->fds[i] = fds[i]; + GRPC_FD_REF(fds[i], "multipoller"); + } +} + +#endif /* GPR_POSIX_SOCKET */ + +#ifdef GPR_POSIX_MULTIPOLL_WITH_POLL +grpc_platform_become_multipoller_type grpc_platform_become_multipoller = + grpc_poll_become_multipoller; +#endif diff --git a/src/core/iomgr/pollset_posix.c b/src/core/iomgr/pollset_posix.c new file mode 100644 index 00000000..2a3ae4bb --- /dev/null +++ b/src/core/iomgr/pollset_posix.c @@ -0,0 +1,506 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_POSIX_SOCKET + +#include "src/core/iomgr/pollset_posix.h" + +#include +#include +#include +#include + +#include "src/core/iomgr/alarm_internal.h" +#include "src/core/iomgr/fd_posix.h" +#include "src/core/iomgr/iomgr_internal.h" +#include "src/core/iomgr/socket_utils_posix.h" +#include "src/core/profiling/timers.h" +#include +#include +#include +#include +#include + +GPR_TLS_DECL(g_current_thread_poller); +GPR_TLS_DECL(g_current_thread_worker); + +grpc_poll_function_type grpc_poll_function = poll; + +static void remove_worker(grpc_pollset *p, grpc_pollset_worker *worker) { + worker->prev->next = worker->next; + worker->next->prev = worker->prev; +} + +int grpc_pollset_has_workers(grpc_pollset *p) { + return p->root_worker.next != &p->root_worker; +} + +static grpc_pollset_worker *pop_front_worker(grpc_pollset *p) { + if (grpc_pollset_has_workers(p)) { + grpc_pollset_worker *w = p->root_worker.next; + remove_worker(p, w); + return w; + } else { + return NULL; + } +} + +static void push_back_worker(grpc_pollset *p, grpc_pollset_worker *worker) { + worker->next = &p->root_worker; + worker->prev = worker->next->prev; + worker->prev->next = worker->next->prev = worker; +} + +static void push_front_worker(grpc_pollset *p, grpc_pollset_worker *worker) { + worker->prev = &p->root_worker; + worker->next = worker->prev->next; + worker->prev->next = worker->next->prev = worker; +} + +void grpc_pollset_kick(grpc_pollset *p, grpc_pollset_worker *specific_worker) { + /* pollset->mu already held */ + if (specific_worker != NULL) { + if (specific_worker == GRPC_POLLSET_KICK_BROADCAST) { + for (specific_worker = p->root_worker.next; + specific_worker != &p->root_worker; + specific_worker = specific_worker->next) { + grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd); + } + p->kicked_without_pollers = 1; + } else if (gpr_tls_get(&g_current_thread_worker) != + (gpr_intptr)specific_worker) { + grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd); + } + } else if (gpr_tls_get(&g_current_thread_poller) != (gpr_intptr)p) { + specific_worker = pop_front_worker(p); + if (specific_worker != NULL) { + push_back_worker(p, specific_worker); + grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd); + } else { + p->kicked_without_pollers = 1; + } + } +} + +/* global state management */ + +void grpc_pollset_global_init(void) { + gpr_tls_init(&g_current_thread_poller); + grpc_wakeup_fd_global_init(); +} + +void grpc_pollset_global_shutdown(void) { + gpr_tls_destroy(&g_current_thread_poller); + grpc_wakeup_fd_global_destroy(); +} + +/* main interface */ + +static void become_basic_pollset(grpc_pollset *pollset, grpc_fd *fd_or_null); + +void grpc_pollset_init(grpc_pollset *pollset) { + gpr_mu_init(&pollset->mu); + pollset->root_worker.next = pollset->root_worker.prev = &pollset->root_worker; + pollset->in_flight_cbs = 0; + pollset->shutting_down = 0; + pollset->called_shutdown = 0; + become_basic_pollset(pollset, NULL); +} + +void grpc_pollset_add_fd(grpc_pollset *pollset, grpc_fd *fd) { + gpr_mu_lock(&pollset->mu); + pollset->vtable->add_fd(pollset, fd, 1); +/* the following (enabled only in debug) will reacquire and then release + our lock - meaning that if the unlocking flag passed to del_fd above is + not respected, the code will deadlock (in a way that we have a chance of + debugging) */ +#ifndef NDEBUG + gpr_mu_lock(&pollset->mu); + gpr_mu_unlock(&pollset->mu); +#endif +} + +void grpc_pollset_del_fd(grpc_pollset *pollset, grpc_fd *fd) { + gpr_mu_lock(&pollset->mu); + pollset->vtable->del_fd(pollset, fd, 1); +/* the following (enabled only in debug) will reacquire and then release + our lock - meaning that if the unlocking flag passed to del_fd above is + not respected, the code will deadlock (in a way that we have a chance of + debugging) */ +#ifndef NDEBUG + gpr_mu_lock(&pollset->mu); + gpr_mu_unlock(&pollset->mu); +#endif +} + +static void finish_shutdown(grpc_pollset *pollset) { + pollset->vtable->finish_shutdown(pollset); + pollset->shutdown_done_cb(pollset->shutdown_done_arg); +} + +void grpc_pollset_work(grpc_pollset *pollset, grpc_pollset_worker *worker, + gpr_timespec now, gpr_timespec deadline) { + /* pollset->mu already held */ + int added_worker = 0; + /* this must happen before we (potentially) drop pollset->mu */ + worker->next = worker->prev = NULL; + /* TODO(ctiller): pool these */ + grpc_wakeup_fd_init(&worker->wakeup_fd); + if (grpc_maybe_call_delayed_callbacks(&pollset->mu, 1)) { + goto done; + } + if (grpc_alarm_check(&pollset->mu, now, &deadline)) { + goto done; + } + if (pollset->shutting_down) { + goto done; + } + if (pollset->in_flight_cbs) { + /* Give do_promote priority so we don't starve it out */ + gpr_mu_unlock(&pollset->mu); + gpr_mu_lock(&pollset->mu); + goto done; + } + if (!pollset->kicked_without_pollers) { + push_front_worker(pollset, worker); + added_worker = 1; + gpr_tls_set(&g_current_thread_poller, (gpr_intptr)pollset); + pollset->vtable->maybe_work(pollset, worker, deadline, now, 1); + gpr_tls_set(&g_current_thread_poller, 0); + } else { + pollset->kicked_without_pollers = 0; + } +done: + grpc_wakeup_fd_destroy(&worker->wakeup_fd); + if (added_worker) { + remove_worker(pollset, worker); + } + if (pollset->shutting_down) { + if (grpc_pollset_has_workers(pollset)) { + grpc_pollset_kick(pollset, NULL); + } else if (!pollset->called_shutdown && pollset->in_flight_cbs == 0) { + pollset->called_shutdown = 1; + gpr_mu_unlock(&pollset->mu); + finish_shutdown(pollset); + /* Continuing to access pollset here is safe -- it is the caller's + * responsibility to not destroy when it has outstanding calls to + * grpc_pollset_work. + * TODO(dklempner): Can we refactor the shutdown logic to avoid this? */ + gpr_mu_lock(&pollset->mu); + } + } +} + +void grpc_pollset_shutdown(grpc_pollset *pollset, + void (*shutdown_done)(void *arg), + void *shutdown_done_arg) { + int call_shutdown = 0; + gpr_mu_lock(&pollset->mu); + GPR_ASSERT(!pollset->shutting_down); + pollset->shutting_down = 1; + if (!pollset->called_shutdown && pollset->in_flight_cbs == 0 && + !grpc_pollset_has_workers(pollset)) { + pollset->called_shutdown = 1; + call_shutdown = 1; + } + pollset->shutdown_done_cb = shutdown_done; + pollset->shutdown_done_arg = shutdown_done_arg; + grpc_pollset_kick(pollset, GRPC_POLLSET_KICK_BROADCAST); + gpr_mu_unlock(&pollset->mu); + + if (call_shutdown) { + finish_shutdown(pollset); + } +} + +void grpc_pollset_destroy(grpc_pollset *pollset) { + GPR_ASSERT(pollset->shutting_down); + GPR_ASSERT(pollset->in_flight_cbs == 0); + GPR_ASSERT(!grpc_pollset_has_workers(pollset)); + pollset->vtable->destroy(pollset); + gpr_mu_destroy(&pollset->mu); +} + +int grpc_poll_deadline_to_millis_timeout(gpr_timespec deadline, + gpr_timespec now) { + gpr_timespec timeout; + static const int max_spin_polling_us = 10; + if (gpr_time_cmp(deadline, gpr_inf_future(deadline.clock_type)) == 0) { + return -1; + } + if (gpr_time_cmp(deadline, gpr_time_add(now, gpr_time_from_micros( + max_spin_polling_us, + GPR_TIMESPAN))) <= 0) { + return 0; + } + timeout = gpr_time_sub(deadline, now); + return gpr_time_to_millis(gpr_time_add( + timeout, gpr_time_from_nanos(GPR_NS_PER_SEC - 1, GPR_TIMESPAN))); +} + +/* + * basic_pollset - a vtable that provides polling for zero or one file + * descriptor via poll() + */ + +typedef struct grpc_unary_promote_args { + const grpc_pollset_vtable *original_vtable; + grpc_pollset *pollset; + grpc_fd *fd; + grpc_iomgr_closure promotion_closure; +} grpc_unary_promote_args; + +static void basic_do_promote(void *args, int success) { + grpc_unary_promote_args *up_args = args; + const grpc_pollset_vtable *original_vtable = up_args->original_vtable; + grpc_pollset *pollset = up_args->pollset; + grpc_fd *fd = up_args->fd; + int do_shutdown_cb = 0; + + /* + * This is quite tricky. There are a number of cases to keep in mind here: + * 1. fd may have been orphaned + * 2. The pollset may no longer be a unary poller (and we can't let case #1 + * leak to other pollset types!) + * 3. pollset's fd (which may have changed) may have been orphaned + * 4. The pollset may be shutting down. + */ + + gpr_mu_lock(&pollset->mu); + /* First we need to ensure that nobody is polling concurrently */ + if (grpc_pollset_has_workers(pollset)) { + grpc_pollset_kick(pollset, GRPC_POLLSET_KICK_BROADCAST); + grpc_iomgr_add_callback(&up_args->promotion_closure); + gpr_mu_unlock(&pollset->mu); + return; + } + + gpr_free(up_args); + /* At this point the pollset may no longer be a unary poller. In that case + * we should just call the right add function and be done. */ + /* TODO(klempner): If we're not careful this could cause infinite recursion. + * That's not a problem for now because empty_pollset has a trivial poller + * and we don't have any mechanism to unbecome multipoller. */ + pollset->in_flight_cbs--; + if (pollset->shutting_down) { + /* We don't care about this pollset anymore. */ + if (pollset->in_flight_cbs == 0 && !pollset->called_shutdown) { + GPR_ASSERT(!grpc_pollset_has_workers(pollset)); + pollset->called_shutdown = 1; + do_shutdown_cb = 1; + } + } else if (grpc_fd_is_orphaned(fd)) { + /* Don't try to add it to anything, we'll drop our ref on it below */ + } else if (pollset->vtable != original_vtable) { + pollset->vtable->add_fd(pollset, fd, 0); + } else if (fd != pollset->data.ptr) { + grpc_fd *fds[2]; + fds[0] = pollset->data.ptr; + fds[1] = fd; + + if (fds[0] && !grpc_fd_is_orphaned(fds[0])) { + grpc_platform_become_multipoller(pollset, fds, GPR_ARRAY_SIZE(fds)); + GRPC_FD_UNREF(fds[0], "basicpoll"); + } else { + /* old fd is orphaned and we haven't cleaned it up until now, so remain a + * unary poller */ + /* Note that it is possible that fds[1] is also orphaned at this point. + * That's okay, we'll correct it at the next add or poll. */ + if (fds[0]) GRPC_FD_UNREF(fds[0], "basicpoll"); + pollset->data.ptr = fd; + GRPC_FD_REF(fd, "basicpoll"); + } + } + + gpr_mu_unlock(&pollset->mu); + + if (do_shutdown_cb) { + pollset->shutdown_done_cb(pollset->shutdown_done_arg); + } + + /* Matching ref in basic_pollset_add_fd */ + GRPC_FD_UNREF(fd, "basicpoll_add"); +} + +static void basic_pollset_add_fd(grpc_pollset *pollset, grpc_fd *fd, + int and_unlock_pollset) { + grpc_unary_promote_args *up_args; + GPR_ASSERT(fd); + if (fd == pollset->data.ptr) goto exit; + + if (!grpc_pollset_has_workers(pollset)) { + /* Fast path -- no in flight cbs */ + /* TODO(klempner): Comment this out and fix any test failures or establish + * they are due to timing issues */ + grpc_fd *fds[2]; + fds[0] = pollset->data.ptr; + fds[1] = fd; + + if (fds[0] == NULL) { + pollset->data.ptr = fd; + GRPC_FD_REF(fd, "basicpoll"); + } else if (!grpc_fd_is_orphaned(fds[0])) { + grpc_platform_become_multipoller(pollset, fds, GPR_ARRAY_SIZE(fds)); + GRPC_FD_UNREF(fds[0], "basicpoll"); + } else { + /* old fd is orphaned and we haven't cleaned it up until now, so remain a + * unary poller */ + GRPC_FD_UNREF(fds[0], "basicpoll"); + pollset->data.ptr = fd; + GRPC_FD_REF(fd, "basicpoll"); + } + goto exit; + } + + /* Now we need to promote. This needs to happen when we're not polling. Since + * this may be called from poll, the wait needs to happen asynchronously. */ + GRPC_FD_REF(fd, "basicpoll_add"); + pollset->in_flight_cbs++; + up_args = gpr_malloc(sizeof(*up_args)); + up_args->pollset = pollset; + up_args->fd = fd; + up_args->original_vtable = pollset->vtable; + up_args->promotion_closure.cb = basic_do_promote; + up_args->promotion_closure.cb_arg = up_args; + grpc_iomgr_add_callback(&up_args->promotion_closure); + + grpc_pollset_kick(pollset, GRPC_POLLSET_KICK_BROADCAST); + +exit: + if (and_unlock_pollset) { + gpr_mu_unlock(&pollset->mu); + } +} + +static void basic_pollset_del_fd(grpc_pollset *pollset, grpc_fd *fd, + int and_unlock_pollset) { + GPR_ASSERT(fd); + if (fd == pollset->data.ptr) { + GRPC_FD_UNREF(pollset->data.ptr, "basicpoll"); + pollset->data.ptr = NULL; + } + + if (and_unlock_pollset) { + gpr_mu_unlock(&pollset->mu); + } +} + +static void basic_pollset_maybe_work(grpc_pollset *pollset, + grpc_pollset_worker *worker, + gpr_timespec deadline, gpr_timespec now, + int allow_synchronous_callback) { + struct pollfd pfd[2]; + grpc_fd *fd; + grpc_fd_watcher fd_watcher; + int timeout; + int r; + int nfds; + + fd = pollset->data.ptr; + if (fd && grpc_fd_is_orphaned(fd)) { + GRPC_FD_UNREF(fd, "basicpoll"); + fd = pollset->data.ptr = NULL; + } + timeout = grpc_poll_deadline_to_millis_timeout(deadline, now); + pfd[0].fd = GRPC_WAKEUP_FD_GET_READ_FD(&worker->wakeup_fd); + pfd[0].events = POLLIN; + pfd[0].revents = 0; + nfds = 1; + if (fd) { + pfd[1].fd = fd->fd; + pfd[1].revents = 0; + gpr_mu_unlock(&pollset->mu); + pfd[1].events = + grpc_fd_begin_poll(fd, pollset, POLLIN, POLLOUT, &fd_watcher); + if (pfd[1].events != 0) { + nfds++; + } + } else { + gpr_mu_unlock(&pollset->mu); + } + + /* poll fd count (argument 2) is shortened by one if we have no events + to poll on - such that it only includes the kicker */ + r = grpc_poll_function(pfd, nfds, timeout); + GRPC_TIMER_MARK(GRPC_PTAG_POLL_FINISHED, r); + + if (fd) { + grpc_fd_end_poll(&fd_watcher, pfd[1].revents & POLLIN, + pfd[1].revents & POLLOUT); + } + + if (r < 0) { + if (errno != EINTR) { + gpr_log(GPR_ERROR, "poll() failed: %s", strerror(errno)); + } + } else if (r == 0) { + /* do nothing */ + } else { + if (pfd[0].revents & POLLIN) { + grpc_wakeup_fd_consume_wakeup(&worker->wakeup_fd); + } + if (nfds > 1) { + if (pfd[1].revents & (POLLIN | POLLHUP | POLLERR)) { + grpc_fd_become_readable(fd, allow_synchronous_callback); + } + if (pfd[1].revents & (POLLOUT | POLLHUP | POLLERR)) { + grpc_fd_become_writable(fd, allow_synchronous_callback); + } + } + } + + gpr_mu_lock(&pollset->mu); +} + +static void basic_pollset_destroy(grpc_pollset *pollset) { + if (pollset->data.ptr != NULL) { + GRPC_FD_UNREF(pollset->data.ptr, "basicpoll"); + pollset->data.ptr = NULL; + } +} + +static const grpc_pollset_vtable basic_pollset = { + basic_pollset_add_fd, basic_pollset_del_fd, basic_pollset_maybe_work, + basic_pollset_destroy, basic_pollset_destroy}; + +static void become_basic_pollset(grpc_pollset *pollset, grpc_fd *fd_or_null) { + pollset->vtable = &basic_pollset; + pollset->data.ptr = fd_or_null; + if (fd_or_null != NULL) { + GRPC_FD_REF(fd_or_null, "basicpoll"); + } +} + +#endif /* GPR_POSIX_POLLSET */ diff --git a/src/core/iomgr/pollset_posix.h b/src/core/iomgr/pollset_posix.h new file mode 100644 index 00000000..69bd9cca --- /dev/null +++ b/src/core/iomgr/pollset_posix.h @@ -0,0 +1,127 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_IOMGR_POLLSET_POSIX_H +#define GRPC_INTERNAL_CORE_IOMGR_POLLSET_POSIX_H + +#include + +#include +#include "src/core/iomgr/wakeup_fd_posix.h" + +typedef struct grpc_pollset_vtable grpc_pollset_vtable; + +/* forward declare only in this file to avoid leaking impl details via + pollset.h; real users of grpc_fd should always include 'fd_posix.h' and not + use the struct tag */ +struct grpc_fd; + +typedef struct grpc_pollset_worker { + grpc_wakeup_fd wakeup_fd; + struct grpc_pollset_worker *next; + struct grpc_pollset_worker *prev; +} grpc_pollset_worker; + +typedef struct grpc_pollset { + /* pollsets under posix can mutate representation as fds are added and + removed. + For example, we may choose a poll() based implementation on linux for + few fds, and an epoll() based implementation for many fds */ + const grpc_pollset_vtable *vtable; + gpr_mu mu; + grpc_pollset_worker root_worker; + int in_flight_cbs; + int shutting_down; + int called_shutdown; + int kicked_without_pollers; + void (*shutdown_done_cb)(void *arg); + void *shutdown_done_arg; + union { + int fd; + void *ptr; + } data; +} grpc_pollset; + +struct grpc_pollset_vtable { + void (*add_fd)(grpc_pollset *pollset, struct grpc_fd *fd, + int and_unlock_pollset); + void (*del_fd)(grpc_pollset *pollset, struct grpc_fd *fd, + int and_unlock_pollset); + void (*maybe_work)(grpc_pollset *pollset, grpc_pollset_worker *worker, + gpr_timespec deadline, gpr_timespec now, + int allow_synchronous_callback); + void (*finish_shutdown)(grpc_pollset *pollset); + void (*destroy)(grpc_pollset *pollset); +}; + +#define GRPC_POLLSET_MU(pollset) (&(pollset)->mu) + +/* Add an fd to a pollset */ +void grpc_pollset_add_fd(grpc_pollset *pollset, struct grpc_fd *fd); +/* Force remove an fd from a pollset (normally they are removed on the next + poll after an fd is orphaned) */ +void grpc_pollset_del_fd(grpc_pollset *pollset, struct grpc_fd *fd); + +/* Returns the fd to listen on for kicks */ +int grpc_kick_read_fd(grpc_pollset *p); +/* Call after polling has been kicked to leave the kicked state */ +void grpc_kick_drain(grpc_pollset *p); + +/* Convert a timespec to milliseconds: + - very small or negative poll times are clamped to zero to do a + non-blocking poll (which becomes spin polling) + - other small values are rounded up to one millisecond + - longer than a millisecond polls are rounded up to the next nearest + millisecond to avoid spinning + - infinite timeouts are converted to -1 */ +int grpc_poll_deadline_to_millis_timeout(gpr_timespec deadline, + gpr_timespec now); + +/* turn a pollset into a multipoller: platform specific */ +typedef void (*grpc_platform_become_multipoller_type)(grpc_pollset *pollset, + struct grpc_fd **fds, + size_t fd_count); +extern grpc_platform_become_multipoller_type grpc_platform_become_multipoller; + +void grpc_poll_become_multipoller(grpc_pollset *pollset, struct grpc_fd **fds, + size_t fd_count); + +/* Return 1 if the pollset has active threads in grpc_pollset_work (pollset must + * be locked) */ +int grpc_pollset_has_workers(grpc_pollset *pollset); + +/* override to allow tests to hook poll() usage */ +typedef int (*grpc_poll_function_type)(struct pollfd *, nfds_t, int); +extern grpc_poll_function_type grpc_poll_function; + +#endif /* GRPC_INTERNAL_CORE_IOMGR_POLLSET_POSIX_H */ diff --git a/src/core/iomgr/pollset_set.h b/src/core/iomgr/pollset_set.h new file mode 100644 index 00000000..6d73951c --- /dev/null +++ b/src/core/iomgr/pollset_set.h @@ -0,0 +1,59 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_IOMGR_POLLSET_SET_H +#define GRPC_INTERNAL_CORE_IOMGR_POLLSET_SET_H + +#include "src/core/iomgr/pollset.h" + +/* A grpc_pollset_set is a set of pollsets that are interested in an + action. Adding a pollset to a pollset_set automatically adds any + fd's (etc) that have been registered with the set_set to that pollset. + Registering fd's automatically adds them to all current pollsets. */ + +#ifdef GPR_POSIX_SOCKET +#include "src/core/iomgr/pollset_set_posix.h" +#endif + +#ifdef GPR_WIN32 +#include "src/core/iomgr/pollset_set_windows.h" +#endif + +void grpc_pollset_set_init(grpc_pollset_set *pollset_set); +void grpc_pollset_set_destroy(grpc_pollset_set *pollset_set); +void grpc_pollset_set_add_pollset(grpc_pollset_set *pollset_set, + grpc_pollset *pollset); +void grpc_pollset_set_del_pollset(grpc_pollset_set *pollset_set, + grpc_pollset *pollset); + +#endif /* GRPC_INTERNAL_CORE_IOMGR_POLLSET_H */ diff --git a/src/core/iomgr/pollset_set_posix.c b/src/core/iomgr/pollset_set_posix.c new file mode 100644 index 00000000..2076ac70 --- /dev/null +++ b/src/core/iomgr/pollset_set_posix.c @@ -0,0 +1,131 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_POSIX_SOCKET + +#include +#include + +#include +#include + +#include "src/core/iomgr/pollset_set.h" + +void grpc_pollset_set_init(grpc_pollset_set *pollset_set) { + memset(pollset_set, 0, sizeof(*pollset_set)); + gpr_mu_init(&pollset_set->mu); +} + +void grpc_pollset_set_destroy(grpc_pollset_set *pollset_set) { + size_t i; + gpr_mu_destroy(&pollset_set->mu); + for (i = 0; i < pollset_set->fd_count; i++) { + GRPC_FD_UNREF(pollset_set->fds[i], "pollset"); + } + gpr_free(pollset_set->pollsets); + gpr_free(pollset_set->fds); +} + +void grpc_pollset_set_add_pollset(grpc_pollset_set *pollset_set, + grpc_pollset *pollset) { + size_t i, j; + gpr_mu_lock(&pollset_set->mu); + if (pollset_set->pollset_count == pollset_set->pollset_capacity) { + pollset_set->pollset_capacity = + GPR_MAX(8, 2 * pollset_set->pollset_capacity); + pollset_set->pollsets = + gpr_realloc(pollset_set->pollsets, pollset_set->pollset_capacity * + sizeof(*pollset_set->pollsets)); + } + pollset_set->pollsets[pollset_set->pollset_count++] = pollset; + for (i = 0, j = 0; i < pollset_set->fd_count; i++) { + if (grpc_fd_is_orphaned(pollset_set->fds[i])) { + GRPC_FD_UNREF(pollset_set->fds[i], "pollset"); + } else { + grpc_pollset_add_fd(pollset, pollset_set->fds[i]); + pollset_set->fds[j++] = pollset_set->fds[i]; + } + } + pollset_set->fd_count = j; + gpr_mu_unlock(&pollset_set->mu); +} + +void grpc_pollset_set_del_pollset(grpc_pollset_set *pollset_set, + grpc_pollset *pollset) { + size_t i; + gpr_mu_lock(&pollset_set->mu); + for (i = 0; i < pollset_set->pollset_count; i++) { + if (pollset_set->pollsets[i] == pollset) { + pollset_set->pollset_count--; + GPR_SWAP(grpc_pollset *, pollset_set->pollsets[i], + pollset_set->pollsets[pollset_set->pollset_count]); + break; + } + } + gpr_mu_unlock(&pollset_set->mu); +} + +void grpc_pollset_set_add_fd(grpc_pollset_set *pollset_set, grpc_fd *fd) { + size_t i; + gpr_mu_lock(&pollset_set->mu); + if (pollset_set->fd_count == pollset_set->fd_capacity) { + pollset_set->fd_capacity = GPR_MAX(8, 2 * pollset_set->fd_capacity); + pollset_set->fds = gpr_realloc( + pollset_set->fds, pollset_set->fd_capacity * sizeof(*pollset_set->fds)); + } + GRPC_FD_REF(fd, "pollset_set"); + pollset_set->fds[pollset_set->fd_count++] = fd; + for (i = 0; i < pollset_set->pollset_count; i++) { + grpc_pollset_add_fd(pollset_set->pollsets[i], fd); + } + gpr_mu_unlock(&pollset_set->mu); +} + +void grpc_pollset_set_del_fd(grpc_pollset_set *pollset_set, grpc_fd *fd) { + size_t i; + gpr_mu_lock(&pollset_set->mu); + for (i = 0; i < pollset_set->fd_count; i++) { + if (pollset_set->fds[i] == fd) { + pollset_set->fd_count--; + GPR_SWAP(grpc_fd *, pollset_set->fds[i], + pollset_set->fds[pollset_set->fd_count]); + GRPC_FD_UNREF(fd, "pollset_set"); + break; + } + } + gpr_mu_unlock(&pollset_set->mu); +} + +#endif /* GPR_POSIX_SOCKET */ diff --git a/src/core/iomgr/pollset_set_posix.h b/src/core/iomgr/pollset_set_posix.h new file mode 100644 index 00000000..e88740bd --- /dev/null +++ b/src/core/iomgr/pollset_set_posix.h @@ -0,0 +1,55 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_IOMGR_POLLSET_SET_POSIX_H +#define GRPC_INTERNAL_CORE_IOMGR_POLLSET_SET_POSIX_H + +#include "src/core/iomgr/fd_posix.h" +#include "src/core/iomgr/pollset_posix.h" + +typedef struct grpc_pollset_set { + gpr_mu mu; + + size_t pollset_count; + size_t pollset_capacity; + grpc_pollset **pollsets; + + size_t fd_count; + size_t fd_capacity; + grpc_fd **fds; +} grpc_pollset_set; + +void grpc_pollset_set_add_fd(grpc_pollset_set *pollset_set, grpc_fd *fd); +void grpc_pollset_set_del_fd(grpc_pollset_set *pollset_set, grpc_fd *fd); + +#endif /* GRPC_INTERNAL_CORE_IOMGR_POLLSET_WINDOWS_H */ diff --git a/src/core/iomgr/pollset_set_windows.c b/src/core/iomgr/pollset_set_windows.c new file mode 100644 index 00000000..b9c209cd --- /dev/null +++ b/src/core/iomgr/pollset_set_windows.c @@ -0,0 +1,50 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_WINSOCK_SOCKET + +#include "src/core/iomgr/pollset_set.h" + +void grpc_pollset_set_init(grpc_pollset_set *pollset_set) {} + +void grpc_pollset_set_destroy(grpc_pollset_set *pollset_set) {} + +void grpc_pollset_set_add_pollset(grpc_pollset_set *pollset_set, + grpc_pollset *pollset) {} + +void grpc_pollset_set_del_pollset(grpc_pollset_set *pollset_set, + grpc_pollset *pollset) {} + +#endif /* GPR_WINSOCK_SOCKET */ diff --git a/src/core/iomgr/pollset_set_windows.h b/src/core/iomgr/pollset_set_windows.h new file mode 100644 index 00000000..cada0d2b --- /dev/null +++ b/src/core/iomgr/pollset_set_windows.h @@ -0,0 +1,39 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_IOMGR_POLLSET_SET_WINDOWS_H +#define GRPC_INTERNAL_CORE_IOMGR_POLLSET_SET_WINDOWS_H + +typedef struct grpc_pollset_set { void *unused; } grpc_pollset_set; + +#endif /* GRPC_INTERNAL_CORE_IOMGR_POLLSET_WINDOWS_H */ diff --git a/src/core/iomgr/pollset_windows.c b/src/core/iomgr/pollset_windows.c new file mode 100644 index 00000000..07522c8a --- /dev/null +++ b/src/core/iomgr/pollset_windows.c @@ -0,0 +1,150 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_WINSOCK_SOCKET + +#include + +#include "src/core/iomgr/alarm_internal.h" +#include "src/core/iomgr/iomgr_internal.h" +#include "src/core/iomgr/pollset.h" +#include "src/core/iomgr/pollset_windows.h" + +static void remove_worker(grpc_pollset *p, grpc_pollset_worker *worker) { + worker->prev->next = worker->next; + worker->next->prev = worker->prev; +} + +static int has_workers(grpc_pollset *p) { + return p->root_worker.next != &p->root_worker; +} + +static grpc_pollset_worker *pop_front_worker(grpc_pollset *p) { + if (has_workers(p)) { + grpc_pollset_worker *w = p->root_worker.next; + remove_worker(p, w); + return w; + } else { + return NULL; + } +} + +static void push_back_worker(grpc_pollset *p, grpc_pollset_worker *worker) { + worker->next = &p->root_worker; + worker->prev = worker->next->prev; + worker->prev->next = worker->next->prev = worker; +} + +static void push_front_worker(grpc_pollset *p, grpc_pollset_worker *worker) { + worker->prev = &p->root_worker; + worker->next = worker->prev->next; + worker->prev->next = worker->next->prev = worker; +} + +/* There isn't really any such thing as a pollset under Windows, due to the + nature of the IO completion ports. We're still going to provide a minimal + set of features for the sake of the rest of grpc. But grpc_pollset_work + won't actually do any polling, and return as quickly as possible. */ + +void grpc_pollset_init(grpc_pollset *pollset) { + memset(pollset, 0, sizeof(*pollset)); + gpr_mu_init(&pollset->mu); + pollset->root_worker.next = pollset->root_worker.prev = &pollset->root_worker; + pollset->kicked_without_pollers = 0; +} + +void grpc_pollset_shutdown(grpc_pollset *pollset, + void (*shutdown_done)(void *arg), + void *shutdown_done_arg) { + gpr_mu_lock(&pollset->mu); + pollset->shutting_down = 1; + grpc_pollset_kick(pollset, GRPC_POLLSET_KICK_BROADCAST); + gpr_mu_unlock(&pollset->mu); + shutdown_done(shutdown_done_arg); +} + +void grpc_pollset_destroy(grpc_pollset *pollset) { + gpr_mu_destroy(&pollset->mu); +} + +void grpc_pollset_work(grpc_pollset *pollset, grpc_pollset_worker *worker, + gpr_timespec now, gpr_timespec deadline) { + int added_worker = 0; + worker->next = worker->prev = NULL; + gpr_cv_init(&worker->cv); + if (grpc_maybe_call_delayed_callbacks(&pollset->mu, 1 /* GPR_TRUE */)) { + goto done; + } + if (grpc_alarm_check(&pollset->mu, now, &deadline)) { + goto done; + } + if (!pollset->kicked_without_pollers && !pollset->shutting_down) { + push_front_worker(pollset, worker); + added_worker = 1; + gpr_cv_wait(&worker->cv, &pollset->mu, deadline); + } else { + pollset->kicked_without_pollers = 0; + } +done: + gpr_cv_destroy(&worker->cv); + if (added_worker) { + remove_worker(pollset, worker); + } +} + +void grpc_pollset_kick(grpc_pollset *p, grpc_pollset_worker *specific_worker) { + if (specific_worker != NULL) { + if (specific_worker == GRPC_POLLSET_KICK_BROADCAST) { + for (specific_worker = p->root_worker.next; + specific_worker != &p->root_worker; + specific_worker = specific_worker->next) { + gpr_cv_signal(&specific_worker->cv); + } + p->kicked_without_pollers = 1; + } else { + gpr_cv_signal(&specific_worker->cv); + } + } else { + specific_worker = pop_front_worker(p); + if (specific_worker != NULL) { + push_back_worker(p, specific_worker); + gpr_cv_signal(&specific_worker->cv); + } else { + p->kicked_without_pollers = 1; + } + } +} + +#endif /* GPR_WINSOCK_SOCKET */ diff --git a/src/core/iomgr/pollset_windows.h b/src/core/iomgr/pollset_windows.h new file mode 100644 index 00000000..4efa5a17 --- /dev/null +++ b/src/core/iomgr/pollset_windows.h @@ -0,0 +1,61 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_IOMGR_POLLSET_WINDOWS_H +#define GRPC_INTERNAL_CORE_IOMGR_POLLSET_WINDOWS_H + +#include + +#include "src/core/iomgr/socket_windows.h" + +/* There isn't really any such thing as a pollset under Windows, due to the + nature of the IO completion ports. A Windows "pollset" is merely a mutex + used to synchronize with the IOCP, and workers are condition variables + used to block threads until work is ready. */ + +typedef struct grpc_pollset_worker { + gpr_cv cv; + struct grpc_pollset_worker *next; + struct grpc_pollset_worker *prev; +} grpc_pollset_worker; + +typedef struct grpc_pollset { + gpr_mu mu; + int shutting_down; + int kicked_without_pollers; + grpc_pollset_worker root_worker; +} grpc_pollset; + +#define GRPC_POLLSET_MU(pollset) (&(pollset)->mu) + +#endif /* GRPC_INTERNAL_CORE_IOMGR_POLLSET_WINDOWS_H */ diff --git a/src/core/iomgr/resolve_address.h b/src/core/iomgr/resolve_address.h new file mode 100644 index 00000000..cc1bd428 --- /dev/null +++ b/src/core/iomgr/resolve_address.h @@ -0,0 +1,69 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_IOMGR_RESOLVE_ADDRESS_H +#define GRPC_INTERNAL_CORE_IOMGR_RESOLVE_ADDRESS_H + +#include + +#define GRPC_MAX_SOCKADDR_SIZE 128 + +typedef struct { + char addr[GRPC_MAX_SOCKADDR_SIZE]; + int len; +} grpc_resolved_address; + +typedef struct { + size_t naddrs; + grpc_resolved_address *addrs; +} grpc_resolved_addresses; + +/* Async result callback: + On success: addresses is the result, and the callee must call + grpc_resolved_addresses_destroy when it's done with them + On failure: addresses is NULL */ +typedef void (*grpc_resolve_cb)(void *arg, grpc_resolved_addresses *addresses); +/* Asynchronously resolve addr. Use default_port if a port isn't designated + in addr, otherwise use the port in addr. */ +/* TODO(ctiller): add a timeout here */ +void grpc_resolve_address(const char *addr, const char *default_port, + grpc_resolve_cb cb, void *arg); +/* Destroy resolved addresses */ +void grpc_resolved_addresses_destroy(grpc_resolved_addresses *addresses); + +/* Resolve addr in a blocking fashion. Returns NULL on failure. On success, + result must be freed with grpc_resolved_addresses_destroy. */ +grpc_resolved_addresses *grpc_blocking_resolve_address( + const char *addr, const char *default_port); + +#endif /* GRPC_INTERNAL_CORE_IOMGR_RESOLVE_ADDRESS_H */ diff --git a/src/core/iomgr/resolve_address_posix.c b/src/core/iomgr/resolve_address_posix.c new file mode 100644 index 00000000..ce6972b7 --- /dev/null +++ b/src/core/iomgr/resolve_address_posix.c @@ -0,0 +1,181 @@ +/* + * + * Copyright 2015, 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 +#ifdef GPR_POSIX_SOCKET + +#include "src/core/iomgr/sockaddr.h" +#include "src/core/iomgr/resolve_address.h" + +#include +#include +#include + +#include "src/core/iomgr/iomgr_internal.h" +#include "src/core/iomgr/sockaddr_utils.h" +#include "src/core/support/string.h" +#include +#include +#include +#include +#include +#include + +typedef struct { + char *name; + char *default_port; + grpc_resolve_cb cb; + void *arg; + grpc_iomgr_object iomgr_object; +} request; + +grpc_resolved_addresses *grpc_blocking_resolve_address( + const char *name, const char *default_port) { + struct addrinfo hints; + struct addrinfo *result = NULL, *resp; + char *host; + char *port; + int s; + size_t i; + grpc_resolved_addresses *addrs = NULL; + struct sockaddr_un *un; + + if (name[0] == 'u' && name[1] == 'n' && name[2] == 'i' && name[3] == 'x' && + name[4] == ':' && name[5] != 0) { + addrs = gpr_malloc(sizeof(grpc_resolved_addresses)); + addrs->naddrs = 1; + addrs->addrs = gpr_malloc(sizeof(grpc_resolved_address)); + un = (struct sockaddr_un *)addrs->addrs->addr; + un->sun_family = AF_UNIX; + strcpy(un->sun_path, name + 5); + addrs->addrs->len = strlen(un->sun_path) + sizeof(un->sun_family) + 1; + return addrs; + } + + /* parse name, splitting it into host and port parts */ + gpr_split_host_port(name, &host, &port); + if (host == NULL) { + gpr_log(GPR_ERROR, "unparseable host:port: '%s'", name); + goto done; + } + if (port == NULL) { + if (default_port == NULL) { + gpr_log(GPR_ERROR, "no port in name '%s'", name); + goto done; + } + port = gpr_strdup(default_port); + } + + /* Call getaddrinfo */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; /* ipv4 or ipv6 */ + hints.ai_socktype = SOCK_STREAM; /* stream socket */ + hints.ai_flags = AI_PASSIVE; /* for wildcard IP address */ + + s = getaddrinfo(host, port, &hints, &result); + if (s != 0) { + /* Retry if well-known service name is recognized */ + char *svc[][2] = {{"http", "80"}, {"https", "443"}}; + int i; + for (i = 0; i < (int)(sizeof(svc) / sizeof(svc[0])); i++) { + if (strcmp(port, svc[i][0]) == 0) { + s = getaddrinfo(host, svc[i][1], &hints, &result); + break; + } + } + } + + if (s != 0) { + gpr_log(GPR_ERROR, "getaddrinfo: %s", gai_strerror(s)); + goto done; + } + + /* Success path: set addrs non-NULL, fill it in */ + addrs = gpr_malloc(sizeof(grpc_resolved_addresses)); + addrs->naddrs = 0; + for (resp = result; resp != NULL; resp = resp->ai_next) { + addrs->naddrs++; + } + addrs->addrs = gpr_malloc(sizeof(grpc_resolved_address) * addrs->naddrs); + i = 0; + for (resp = result; resp != NULL; resp = resp->ai_next) { + memcpy(&addrs->addrs[i].addr, resp->ai_addr, resp->ai_addrlen); + addrs->addrs[i].len = resp->ai_addrlen; + i++; + } + +done: + gpr_free(host); + gpr_free(port); + if (result) { + freeaddrinfo(result); + } + return addrs; +} + +/* Thread function to asynch-ify grpc_blocking_resolve_address */ +static void do_request(void *rp) { + request *r = rp; + grpc_resolved_addresses *resolved = + grpc_blocking_resolve_address(r->name, r->default_port); + void *arg = r->arg; + grpc_resolve_cb cb = r->cb; + gpr_free(r->name); + gpr_free(r->default_port); + cb(arg, resolved); + grpc_iomgr_unregister_object(&r->iomgr_object); + gpr_free(r); +} + +void grpc_resolved_addresses_destroy(grpc_resolved_addresses *addrs) { + gpr_free(addrs->addrs); + gpr_free(addrs); +} + +void grpc_resolve_address(const char *name, const char *default_port, + grpc_resolve_cb cb, void *arg) { + request *r = gpr_malloc(sizeof(request)); + gpr_thd_id id; + char *tmp; + gpr_asprintf(&tmp, "resolve_address:name='%s':default_port='%s'", name, + default_port); + grpc_iomgr_register_object(&r->iomgr_object, tmp); + gpr_free(tmp); + r->name = gpr_strdup(name); + r->default_port = gpr_strdup(default_port); + r->cb = cb; + r->arg = arg; + gpr_thd_new(&id, do_request, r, NULL); +} + +#endif diff --git a/src/core/iomgr/resolve_address_windows.c b/src/core/iomgr/resolve_address_windows.c new file mode 100644 index 00000000..fb5fd0d4 --- /dev/null +++ b/src/core/iomgr/resolve_address_windows.c @@ -0,0 +1,163 @@ +/* + * + * Copyright 2015, 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 +#ifdef GPR_WINSOCK_SOCKET + +#include "src/core/iomgr/sockaddr.h" +#include "src/core/iomgr/resolve_address.h" + +#include +#include + +#include "src/core/iomgr/iomgr_internal.h" +#include "src/core/iomgr/sockaddr_utils.h" +#include "src/core/support/string.h" +#include +#include +#include +#include +#include +#include + +typedef struct { + char *name; + char *default_port; + grpc_resolve_cb cb; + void *arg; + grpc_iomgr_object iomgr_object; +} request; + +grpc_resolved_addresses *grpc_blocking_resolve_address( + const char *name, const char *default_port) { + struct addrinfo hints; + struct addrinfo *result = NULL, *resp; + char *host; + char *port; + int s; + size_t i; + grpc_resolved_addresses *addrs = NULL; + + /* parse name, splitting it into host and port parts */ + gpr_split_host_port(name, &host, &port); + if (host == NULL) { + gpr_log(GPR_ERROR, "unparseable host:port: '%s'", name); + goto done; + } + if (port == NULL) { + if (default_port == NULL) { + gpr_log(GPR_ERROR, "no port in name '%s'", name); + goto done; + } + port = gpr_strdup(default_port); + } + + /* Call getaddrinfo */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; /* ipv4 or ipv6 */ + hints.ai_socktype = SOCK_STREAM; /* stream socket */ + hints.ai_flags = AI_PASSIVE; /* for wildcard IP address */ + + s = getaddrinfo(host, port, &hints, &result); + if (s != 0) { + gpr_log(GPR_ERROR, "getaddrinfo: %s", gai_strerror(s)); + goto done; + } + + /* Success path: set addrs non-NULL, fill it in */ + addrs = gpr_malloc(sizeof(grpc_resolved_addresses)); + addrs->naddrs = 0; + for (resp = result; resp != NULL; resp = resp->ai_next) { + addrs->naddrs++; + } + addrs->addrs = gpr_malloc(sizeof(grpc_resolved_address) * addrs->naddrs); + i = 0; + for (resp = result; resp != NULL; resp = resp->ai_next) { + memcpy(&addrs->addrs[i].addr, resp->ai_addr, resp->ai_addrlen); + addrs->addrs[i].len = resp->ai_addrlen; + i++; + } + + { + for (i = 0; i < addrs->naddrs; i++) { + char *buf; + grpc_sockaddr_to_string(&buf, (struct sockaddr *)&addrs->addrs[i].addr, + 0); + gpr_free(buf); + } + } + +done: + gpr_free(host); + gpr_free(port); + if (result) { + freeaddrinfo(result); + } + return addrs; +} + +/* Thread function to asynch-ify grpc_blocking_resolve_address */ +static void do_request(void *rp) { + request *r = rp; + grpc_resolved_addresses *resolved = + grpc_blocking_resolve_address(r->name, r->default_port); + void *arg = r->arg; + grpc_resolve_cb cb = r->cb; + gpr_free(r->name); + gpr_free(r->default_port); + grpc_iomgr_unregister_object(&r->iomgr_object); + gpr_free(r); + cb(arg, resolved); +} + +void grpc_resolved_addresses_destroy(grpc_resolved_addresses *addrs) { + gpr_free(addrs->addrs); + gpr_free(addrs); +} + +void grpc_resolve_address(const char *name, const char *default_port, + grpc_resolve_cb cb, void *arg) { + request *r = gpr_malloc(sizeof(request)); + gpr_thd_id id; + const char *label; + gpr_asprintf(&label, "resolve:%s", name); + grpc_iomgr_register_object(&r->iomgr_object, label); + gpr_free(label); + r->name = gpr_strdup(name); + r->default_port = gpr_strdup(default_port); + r->cb = cb; + r->arg = arg; + gpr_thd_new(&id, do_request, r, NULL); +} + +#endif diff --git a/src/core/iomgr/sockaddr.h b/src/core/iomgr/sockaddr.h new file mode 100644 index 00000000..e41e1ec6 --- /dev/null +++ b/src/core/iomgr/sockaddr.h @@ -0,0 +1,47 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_IOMGR_SOCKADDR_H +#define GRPC_INTERNAL_CORE_IOMGR_SOCKADDR_H + +#include + +#ifdef GPR_WIN32 +#include "src/core/iomgr/sockaddr_win32.h" +#endif + +#ifdef GPR_POSIX_SOCKETADDR +#include "src/core/iomgr/sockaddr_posix.h" +#endif + +#endif /* GRPC_INTERNAL_CORE_IOMGR_SOCKADDR_H */ diff --git a/src/core/iomgr/sockaddr_posix.h b/src/core/iomgr/sockaddr_posix.h new file mode 100644 index 00000000..388abb33 --- /dev/null +++ b/src/core/iomgr/sockaddr_posix.h @@ -0,0 +1,44 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_IOMGR_SOCKADDR_POSIX_H +#define GRPC_INTERNAL_CORE_IOMGR_SOCKADDR_POSIX_H + +#include +#include +#include +#include +#include +#include + +#endif /* GRPC_INTERNAL_CORE_IOMGR_SOCKADDR_POSIX_H */ diff --git a/src/core/iomgr/sockaddr_utils.c b/src/core/iomgr/sockaddr_utils.c new file mode 100644 index 00000000..efdc4803 --- /dev/null +++ b/src/core/iomgr/sockaddr_utils.c @@ -0,0 +1,228 @@ +/* + * + * Copyright 2015, 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 "src/core/iomgr/sockaddr_utils.h" + +#include +#include + +#ifdef GPR_POSIX_SOCKET +#include +#endif + +#include +#include +#include +#include +#include + +#include "src/core/support/string.h" + +static const gpr_uint8 kV4MappedPrefix[] = {0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0xff, 0xff}; + +int grpc_sockaddr_is_v4mapped(const struct sockaddr *addr, + struct sockaddr_in *addr4_out) { + GPR_ASSERT(addr != (struct sockaddr *)addr4_out); + if (addr->sa_family == AF_INET6) { + const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr; + if (memcmp(addr6->sin6_addr.s6_addr, kV4MappedPrefix, + sizeof(kV4MappedPrefix)) == 0) { + if (addr4_out != NULL) { + /* Normalize ::ffff:0.0.0.0/96 to IPv4. */ + memset(addr4_out, 0, sizeof(*addr4_out)); + addr4_out->sin_family = AF_INET; + /* s6_addr32 would be nice, but it's non-standard. */ + memcpy(&addr4_out->sin_addr, &addr6->sin6_addr.s6_addr[12], 4); + addr4_out->sin_port = addr6->sin6_port; + } + return 1; + } + } + return 0; +} + +int grpc_sockaddr_to_v4mapped(const struct sockaddr *addr, + struct sockaddr_in6 *addr6_out) { + GPR_ASSERT(addr != (struct sockaddr *)addr6_out); + if (addr->sa_family == AF_INET) { + const struct sockaddr_in *addr4 = (const struct sockaddr_in *)addr; + memset(addr6_out, 0, sizeof(*addr6_out)); + addr6_out->sin6_family = AF_INET6; + memcpy(&addr6_out->sin6_addr.s6_addr[0], kV4MappedPrefix, 12); + memcpy(&addr6_out->sin6_addr.s6_addr[12], &addr4->sin_addr, 4); + addr6_out->sin6_port = addr4->sin_port; + return 1; + } + return 0; +} + +int grpc_sockaddr_is_wildcard(const struct sockaddr *addr, int *port_out) { + struct sockaddr_in addr4_normalized; + if (grpc_sockaddr_is_v4mapped(addr, &addr4_normalized)) { + addr = (struct sockaddr *)&addr4_normalized; + } + if (addr->sa_family == AF_INET) { + /* Check for 0.0.0.0 */ + const struct sockaddr_in *addr4 = (const struct sockaddr_in *)addr; + if (addr4->sin_addr.s_addr != 0) { + return 0; + } + *port_out = ntohs(addr4->sin_port); + return 1; + } else if (addr->sa_family == AF_INET6) { + /* Check for :: */ + const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr; + int i; + for (i = 0; i < 16; i++) { + if (addr6->sin6_addr.s6_addr[i] != 0) { + return 0; + } + } + *port_out = ntohs(addr6->sin6_port); + return 1; + } else { + return 0; + } +} + +void grpc_sockaddr_make_wildcards(int port, struct sockaddr_in *wild4_out, + struct sockaddr_in6 *wild6_out) { + grpc_sockaddr_make_wildcard4(port, wild4_out); + grpc_sockaddr_make_wildcard6(port, wild6_out); +} + +void grpc_sockaddr_make_wildcard4(int port, struct sockaddr_in *wild_out) { + memset(wild_out, 0, sizeof(*wild_out)); + wild_out->sin_family = AF_INET; + wild_out->sin_port = htons(port); +} + +void grpc_sockaddr_make_wildcard6(int port, struct sockaddr_in6 *wild_out) { + memset(wild_out, 0, sizeof(*wild_out)); + wild_out->sin6_family = AF_INET6; + wild_out->sin6_port = htons(port); +} + +int grpc_sockaddr_to_string(char **out, const struct sockaddr *addr, + int normalize) { + const int save_errno = errno; + struct sockaddr_in addr_normalized; + char ntop_buf[INET6_ADDRSTRLEN]; + const void *ip = NULL; + int port; + int ret; + + *out = NULL; + if (normalize && grpc_sockaddr_is_v4mapped(addr, &addr_normalized)) { + addr = (const struct sockaddr *)&addr_normalized; + } + if (addr->sa_family == AF_INET) { + const struct sockaddr_in *addr4 = (const struct sockaddr_in *)addr; + ip = &addr4->sin_addr; + port = ntohs(addr4->sin_port); + } else if (addr->sa_family == AF_INET6) { + const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr; + ip = &addr6->sin6_addr; + port = ntohs(addr6->sin6_port); + } + if (ip != NULL && + inet_ntop(addr->sa_family, ip, ntop_buf, sizeof(ntop_buf)) != NULL) { + ret = gpr_join_host_port(out, ntop_buf, port); + } else { + ret = gpr_asprintf(out, "(sockaddr family=%d)", addr->sa_family); + } + /* This is probably redundant, but we wouldn't want to log the wrong error. */ + errno = save_errno; + return ret; +} + +char *grpc_sockaddr_to_uri(const struct sockaddr *addr) { + char *temp; + char *result; + struct sockaddr_in addr_normalized; + + if (grpc_sockaddr_is_v4mapped(addr, &addr_normalized)) { + addr = (const struct sockaddr *)&addr_normalized; + } + + switch (addr->sa_family) { + case AF_INET: + grpc_sockaddr_to_string(&temp, addr, 0); + gpr_asprintf(&result, "ipv4:%s", temp); + gpr_free(temp); + return result; + case AF_INET6: + grpc_sockaddr_to_string(&temp, addr, 0); + gpr_asprintf(&result, "ipv6:%s", temp); + gpr_free(temp); + return result; +#ifdef GPR_POSIX_SOCKET + case AF_UNIX: + gpr_asprintf(&result, "unix:%s", ((struct sockaddr_un *)addr)->sun_path); + return result; +#endif + } + + return NULL; +} + +int grpc_sockaddr_get_port(const struct sockaddr *addr) { + switch (addr->sa_family) { + case AF_INET: + return ntohs(((struct sockaddr_in *)addr)->sin_port); + case AF_INET6: + return ntohs(((struct sockaddr_in6 *)addr)->sin6_port); + case AF_UNIX: + return 1; + default: + gpr_log(GPR_ERROR, "Unknown socket family %d in grpc_sockaddr_get_port", + addr->sa_family); + return 0; + } +} + +int grpc_sockaddr_set_port(const struct sockaddr *addr, int port) { + switch (addr->sa_family) { + case AF_INET: + ((struct sockaddr_in *)addr)->sin_port = htons(port); + return 1; + case AF_INET6: + ((struct sockaddr_in6 *)addr)->sin6_port = htons(port); + return 1; + default: + gpr_log(GPR_ERROR, "Unknown socket family %d in grpc_sockaddr_set_port", + addr->sa_family); + return 0; + } +} diff --git a/src/core/iomgr/sockaddr_utils.h b/src/core/iomgr/sockaddr_utils.h new file mode 100644 index 00000000..6f7a2799 --- /dev/null +++ b/src/core/iomgr/sockaddr_utils.h @@ -0,0 +1,89 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_IOMGR_SOCKADDR_UTILS_H +#define GRPC_INTERNAL_CORE_IOMGR_SOCKADDR_UTILS_H + +#include "src/core/iomgr/sockaddr.h" + +/* Returns true if addr is an IPv4-mapped IPv6 address within the + ::ffff:0.0.0.0/96 range, or false otherwise. + + If addr4_out is non-NULL, the inner IPv4 address will be copied here when + returning true. */ +int grpc_sockaddr_is_v4mapped(const struct sockaddr *addr, + struct sockaddr_in *addr4_out); + +/* If addr is an AF_INET address, writes the corresponding ::ffff:0.0.0.0/96 + address to addr6_out and returns true. Otherwise returns false. */ +int grpc_sockaddr_to_v4mapped(const struct sockaddr *addr, + struct sockaddr_in6 *addr6_out); + +/* If addr is ::, 0.0.0.0, or ::ffff:0.0.0.0, writes the port number to + *port_out (if not NULL) and returns true, otherwise returns false. */ +int grpc_sockaddr_is_wildcard(const struct sockaddr *addr, int *port_out); + +/* Writes 0.0.0.0:port and [::]:port to separate sockaddrs. */ +void grpc_sockaddr_make_wildcards(int port, struct sockaddr_in *wild4_out, + struct sockaddr_in6 *wild6_out); + +/* Writes 0.0.0.0:port. */ +void grpc_sockaddr_make_wildcard4(int port, struct sockaddr_in *wild_out); + +/* Writes [::]:port. */ +void grpc_sockaddr_make_wildcard6(int port, struct sockaddr_in6 *wild_out); + +/* Return the IP port number of a sockaddr */ +int grpc_sockaddr_get_port(const struct sockaddr *addr); + +/* Set IP port number of a sockaddr */ +int grpc_sockaddr_set_port(const struct sockaddr *addr, int port); + +/* Converts a sockaddr into a newly-allocated human-readable string. + + Currently, only the AF_INET and AF_INET6 families are recognized. + If the normalize flag is enabled, ::ffff:0.0.0.0/96 IPv6 addresses are + displayed as plain IPv4. + + Usage is similar to gpr_asprintf: returns the number of bytes written + (excluding the final '\0'), and *out points to a string which must later be + destroyed using gpr_free(). + + In the unlikely event of an error, returns -1 and sets *out to NULL. + The existing value of errno is always preserved. */ +int grpc_sockaddr_to_string(char **out, const struct sockaddr *addr, + int normalize); + +char *grpc_sockaddr_to_uri(const struct sockaddr *addr); + +#endif /* GRPC_INTERNAL_CORE_IOMGR_SOCKADDR_UTILS_H */ diff --git a/src/core/iomgr/sockaddr_win32.h b/src/core/iomgr/sockaddr_win32.h new file mode 100644 index 00000000..fe2be991 --- /dev/null +++ b/src/core/iomgr/sockaddr_win32.h @@ -0,0 +1,46 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_IOMGR_SOCKADDR_WIN32_H +#define GRPC_INTERNAL_CORE_IOMGR_SOCKADDR_WIN32_H + +#include +#include +#include + +#ifdef __MINGW32__ +/* mingw seems to be missing that definition. */ +const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); +#endif + +#endif /* GRPC_INTERNAL_CORE_IOMGR_SOCKADDR_WIN32_H */ diff --git a/src/core/iomgr/socket_utils_common_posix.c b/src/core/iomgr/socket_utils_common_posix.c new file mode 100644 index 00000000..a9af5947 --- /dev/null +++ b/src/core/iomgr/socket_utils_common_posix.c @@ -0,0 +1,208 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_POSIX_SOCKET + +#include "src/core/iomgr/socket_utils_posix.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "src/core/iomgr/sockaddr_utils.h" +#include "src/core/support/string.h" +#include +#include +#include +#include + +/* set a socket to non blocking mode */ +int grpc_set_socket_nonblocking(int fd, int non_blocking) { + int oldflags = fcntl(fd, F_GETFL, 0); + if (oldflags < 0) { + return 0; + } + + if (non_blocking) { + oldflags |= O_NONBLOCK; + } else { + oldflags &= ~O_NONBLOCK; + } + + if (fcntl(fd, F_SETFL, oldflags) != 0) { + return 0; + } + + return 1; +} + +int grpc_set_socket_no_sigpipe_if_possible(int fd) { +#ifdef GPR_HAVE_SO_NOSIGPIPE + int val = 1; + int newval; + socklen_t intlen = sizeof(newval); + return 0 == setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &val, sizeof(val)) && + 0 == getsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &newval, &intlen) && + (newval != 0) == val; +#else + return 1; +#endif +} + +/* set a socket to close on exec */ +int grpc_set_socket_cloexec(int fd, int close_on_exec) { + int oldflags = fcntl(fd, F_GETFD, 0); + if (oldflags < 0) { + return 0; + } + + if (close_on_exec) { + oldflags |= FD_CLOEXEC; + } else { + oldflags &= ~FD_CLOEXEC; + } + + if (fcntl(fd, F_SETFD, oldflags) != 0) { + return 0; + } + + return 1; +} + +/* set a socket to reuse old addresses */ +int grpc_set_socket_reuse_addr(int fd, int reuse) { + int val = (reuse != 0); + int newval; + socklen_t intlen = sizeof(newval); + return 0 == setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) && + 0 == getsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &newval, &intlen) && + (newval != 0) == val; +} + +/* disable nagle */ +int grpc_set_socket_low_latency(int fd, int low_latency) { + int val = (low_latency != 0); + int newval; + socklen_t intlen = sizeof(newval); + return 0 == setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)) && + 0 == getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &newval, &intlen) && + (newval != 0) == val; +} + +static gpr_once g_probe_ipv6_once = GPR_ONCE_INIT; +static int g_ipv6_loopback_available; + +static void probe_ipv6_once(void) { + int fd = socket(AF_INET6, SOCK_STREAM, 0); + g_ipv6_loopback_available = 0; + if (fd < 0) { + gpr_log(GPR_INFO, "Disabling AF_INET6 sockets because socket() failed."); + } else { + struct sockaddr_in6 addr; + memset(&addr, 0, sizeof(addr)); + addr.sin6_family = AF_INET6; + addr.sin6_addr.s6_addr[15] = 1; /* [::1]:0 */ + if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == 0) { + g_ipv6_loopback_available = 1; + } else { + gpr_log(GPR_INFO, + "Disabling AF_INET6 sockets because ::1 is not available."); + } + close(fd); + } +} + +int grpc_ipv6_loopback_available(void) { + gpr_once_init(&g_probe_ipv6_once, probe_ipv6_once); + return g_ipv6_loopback_available; +} + +/* This should be 0 in production, but it may be enabled for testing or + debugging purposes, to simulate an environment where IPv6 sockets can't + also speak IPv4. */ +int grpc_forbid_dualstack_sockets_for_testing = 0; + +static int set_socket_dualstack(int fd) { + if (!grpc_forbid_dualstack_sockets_for_testing) { + const int off = 0; + return 0 == setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off)); + } else { + /* Force an IPv6-only socket, for testing purposes. */ + const int on = 1; + setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); + return 0; + } +} + +int grpc_create_dualstack_socket(const struct sockaddr *addr, int type, + int protocol, grpc_dualstack_mode *dsmode) { + int family = addr->sa_family; + if (family == AF_INET6) { + int fd; + if (grpc_ipv6_loopback_available()) { + fd = socket(family, type, protocol); + } else { + fd = -1; + errno = EAFNOSUPPORT; + } + /* Check if we've got a valid dualstack socket. */ + if (fd >= 0 && set_socket_dualstack(fd)) { + *dsmode = GRPC_DSMODE_DUALSTACK; + return fd; + } + /* If this isn't an IPv4 address, then return whatever we've got. */ + if (!grpc_sockaddr_is_v4mapped(addr, NULL)) { + *dsmode = GRPC_DSMODE_IPV6; + return fd; + } + /* Fall back to AF_INET. */ + if (fd >= 0) { + close(fd); + } + family = AF_INET; + } + *dsmode = family == AF_INET ? GRPC_DSMODE_IPV4 : GRPC_DSMODE_NONE; + return socket(family, type, protocol); +} + +#endif diff --git a/src/core/iomgr/socket_utils_linux.c b/src/core/iomgr/socket_utils_linux.c new file mode 100644 index 00000000..a8762526 --- /dev/null +++ b/src/core/iomgr/socket_utils_linux.c @@ -0,0 +1,51 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_LINUX_SOCKETUTILS + +#include "src/core/iomgr/socket_utils_posix.h" + +#include +#include + +int grpc_accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, + int nonblock, int cloexec) { + int flags = 0; + flags |= nonblock ? SOCK_NONBLOCK : 0; + flags |= cloexec ? SOCK_CLOEXEC : 0; + return accept4(sockfd, addr, addrlen, flags); +} + +#endif diff --git a/src/core/iomgr/socket_utils_posix.c b/src/core/iomgr/socket_utils_posix.c new file mode 100644 index 00000000..3c56b467 --- /dev/null +++ b/src/core/iomgr/socket_utils_posix.c @@ -0,0 +1,70 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_POSIX_SOCKETUTILS + +#include "src/core/iomgr/socket_utils_posix.h" + +#include +#include +#include + +#include + +int grpc_accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, + int nonblock, int cloexec) { + int fd, flags; + + fd = accept(sockfd, addr, addrlen); + if (fd >= 0) { + if (nonblock) { + flags = fcntl(fd, F_GETFL, 0); + if (flags < 0) goto close_and_error; + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) != 0) goto close_and_error; + } + if (cloexec) { + flags = fcntl(fd, F_GETFD, 0); + if (flags < 0) goto close_and_error; + if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) != 0) goto close_and_error; + } + } + return fd; + +close_and_error: + close(fd); + return -1; +} + +#endif /* GPR_POSIX_SOCKETUTILS */ diff --git a/src/core/iomgr/socket_utils_posix.h b/src/core/iomgr/socket_utils_posix.h new file mode 100644 index 00000000..d330d198 --- /dev/null +++ b/src/core/iomgr/socket_utils_posix.h @@ -0,0 +1,113 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_IOMGR_SOCKET_UTILS_POSIX_H +#define GRPC_INTERNAL_CORE_IOMGR_SOCKET_UTILS_POSIX_H + +#include +#include + +/* a wrapper for accept or accept4 */ +int grpc_accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, + int nonblock, int cloexec); + +/* set a socket to non blocking mode */ +int grpc_set_socket_nonblocking(int fd, int non_blocking); + +/* set a socket to close on exec */ +int grpc_set_socket_cloexec(int fd, int close_on_exec); + +/* set a socket to reuse old addresses */ +int grpc_set_socket_reuse_addr(int fd, int reuse); + +/* disable nagle */ +int grpc_set_socket_low_latency(int fd, int low_latency); + +/* Returns true if this system can create AF_INET6 sockets bound to ::1. + The value is probed once, and cached for the life of the process. + + This is more restrictive than checking for socket(AF_INET6) to succeed, + because Linux with "net.ipv6.conf.all.disable_ipv6 = 1" is able to create + and bind IPv6 sockets, but cannot connect to a getsockname() of [::]:port + without a valid loopback interface. Rather than expose this half-broken + state to library users, we turn off IPv6 sockets. */ +int grpc_ipv6_loopback_available(void); + +/* Tries to set SO_NOSIGPIPE if available on this platform. + Returns 1 on success, 0 on failure. + If SO_NO_SIGPIPE is not available, returns 1. */ +int grpc_set_socket_no_sigpipe_if_possible(int fd); + +/* An enum to keep track of IPv4/IPv6 socket modes. + + Currently, this information is only used when a socket is first created, but + in the future we may wish to store it alongside the fd. This would let calls + like sendto() know which family to use without asking the kernel first. */ +typedef enum grpc_dualstack_mode { + /* Uninitialized, or a non-IP socket. */ + GRPC_DSMODE_NONE, + /* AF_INET only. */ + GRPC_DSMODE_IPV4, + /* AF_INET6 only, because IPV6_V6ONLY could not be cleared. */ + GRPC_DSMODE_IPV6, + /* AF_INET6, which also supports ::ffff-mapped IPv4 addresses. */ + GRPC_DSMODE_DUALSTACK +} grpc_dualstack_mode; + +/* Only tests should use this flag. */ +extern int grpc_forbid_dualstack_sockets_for_testing; + +/* Creates a new socket for connecting to (or listening on) an address. + + If addr is AF_INET6, this creates an IPv6 socket first. If that fails, + and addr is within ::ffff:0.0.0.0/96, then it automatically falls back to + an IPv4 socket. + + If addr is AF_INET, AF_UNIX, or anything else, then this is similar to + calling socket() directly. + + Returns an fd on success, otherwise returns -1 with errno set to the result + of a failed socket() call. + + The *dsmode output indicates which address family was actually created. + The recommended way to use this is: + - First convert to IPv6 using grpc_sockaddr_to_v4mapped(). + - Create the socket. + - If *dsmode is IPV4, use grpc_sockaddr_is_v4mapped() to convert back to + IPv4, so that bind() or connect() see the correct family. + Also, it's important to distinguish between DUALSTACK and IPV6 when + listening on the [::] wildcard address. */ +int grpc_create_dualstack_socket(const struct sockaddr *addr, int type, + int protocol, grpc_dualstack_mode *dsmode); + +#endif /* GRPC_INTERNAL_CORE_IOMGR_SOCKET_UTILS_POSIX_H */ diff --git a/src/core/iomgr/socket_windows.c b/src/core/iomgr/socket_windows.c new file mode 100644 index 00000000..557ca822 --- /dev/null +++ b/src/core/iomgr/socket_windows.c @@ -0,0 +1,97 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_WINSOCK_SOCKET + +#include +#include + +#include +#include +#include +#include + +#include "src/core/iomgr/iocp_windows.h" +#include "src/core/iomgr/iomgr_internal.h" +#include "src/core/iomgr/pollset.h" +#include "src/core/iomgr/pollset_windows.h" +#include "src/core/iomgr/socket_windows.h" + +grpc_winsocket *grpc_winsocket_create(SOCKET socket, const char *name) { + char *final_name; + grpc_winsocket *r = gpr_malloc(sizeof(grpc_winsocket)); + memset(r, 0, sizeof(grpc_winsocket)); + r->socket = socket; + gpr_mu_init(&r->state_mu); + gpr_asprintf(&final_name, "%s:socket=0x%p", name, r); + grpc_iomgr_register_object(&r->iomgr_object, final_name); + gpr_free(final_name); + grpc_iocp_add_socket(r); + return r; +} + +/* Schedule a shutdown of the socket operations. Will call the pending + operations to abort them. We need to do that this way because of the + various callsites of that function, which happens to be in various + mutex hold states, and that'd be unsafe to call them directly. */ +void grpc_winsocket_shutdown(grpc_winsocket *winsocket) { + /* Grab the function pointer for DisconnectEx for that specific socket. + It may change depending on the interface. */ + int status; + GUID guid = WSAID_DISCONNECTEX; + LPFN_DISCONNECTEX DisconnectEx; + DWORD ioctl_num_bytes; + + status = WSAIoctl(winsocket->socket, SIO_GET_EXTENSION_FUNCTION_POINTER, + &guid, sizeof(guid), &DisconnectEx, sizeof(DisconnectEx), + &ioctl_num_bytes, NULL, NULL); + + if (status == 0) { + DisconnectEx(winsocket->socket, NULL, 0, 0); + } else { + char *utf8_message = gpr_format_message(WSAGetLastError()); + gpr_log(GPR_ERROR, "Unable to retrieve DisconnectEx pointer : %s", utf8_message); + gpr_free(utf8_message); + } + closesocket(winsocket->socket); +} + +void grpc_winsocket_destroy(grpc_winsocket *winsocket) { + grpc_iomgr_unregister_object(&winsocket->iomgr_object); + gpr_mu_destroy(&winsocket->state_mu); + gpr_free(winsocket); +} + +#endif /* GPR_WINSOCK_SOCKET */ diff --git a/src/core/iomgr/socket_windows.h b/src/core/iomgr/socket_windows.h new file mode 100644 index 00000000..498921e0 --- /dev/null +++ b/src/core/iomgr/socket_windows.h @@ -0,0 +1,111 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_IOMGR_SOCKET_WINDOWS_H +#define GRPC_INTERNAL_CORE_IOMGR_SOCKET_WINDOWS_H + +#include +#include + +#include +#include + +#include "src/core/iomgr/iomgr_internal.h" + +/* This holds the data for an outstanding read or write on a socket. + The mutex to protect the concurrent access to that data is the one + inside the winsocket wrapper. */ +typedef struct grpc_winsocket_callback_info { + /* This is supposed to be a WSAOVERLAPPED, but in order to get that + definition, we need to include ws2tcpip.h, which needs to be included + from the top, otherwise it'll clash with a previous inclusion of + windows.h that in turns includes winsock.h. If anyone knows a way + to do it properly, feel free to send a patch. */ + OVERLAPPED overlapped; + /* The callback information for the pending operation. May be empty if the + caller hasn't registered a callback yet. */ + void (*cb)(void *opaque, int success); + void *opaque; + /* A boolean to describe if the IO Completion Port got a notification for + that operation. This will happen if the operation completed before the + called had time to register a callback. We could avoid that behavior + altogether by forcing the caller to always register its callback before + proceeding queue an operation, but it is frequent for an IO Completion + Port to trigger quickly. This way we avoid a context switch for calling + the callback. We also simplify the read / write operations to avoid having + to hold a mutex for a long amount of time. */ + int has_pending_iocp; + /* The results of the overlapped operation. */ + DWORD bytes_transfered; + int wsa_error; +} grpc_winsocket_callback_info; + +/* This is a wrapper to a Windows socket. A socket can have one outstanding + read, and one outstanding write. Doing an asynchronous accept means waiting + for a read operation. Doing an asynchronous connect means waiting for a + write operation. These are completely arbitrary ties between the operation + and the kind of event, because we can have one overlapped per pending + operation, whichever its nature is. So we could have more dedicated pending + operation callbacks for connect and listen. But given the scope of listen + and accept, we don't need to go to that extent and waste memory. Also, this + is closer to what happens in posix world. */ +typedef struct grpc_winsocket { + SOCKET socket; + + grpc_winsocket_callback_info write_info; + grpc_winsocket_callback_info read_info; + + gpr_mu state_mu; + + /* You can't add the same socket twice to the same IO Completion Port. + This prevents that. */ + int added_to_iocp; + + grpc_iomgr_closure shutdown_closure; + + /* A label for iomgr to track outstanding objects */ + grpc_iomgr_object iomgr_object; +} grpc_winsocket; + +/* Create a wrapped windows handle. This takes ownership of it, meaning that + it will be responsible for closing it. */ +grpc_winsocket *grpc_winsocket_create(SOCKET socket, const char *name); + +/* Initiate an asynchronous shutdown of the socket. Will call off any pending + operation to cancel them. */ +void grpc_winsocket_shutdown(grpc_winsocket *socket); + +/* Destroy a socket. Should only be called if there's no pending operation. */ +void grpc_winsocket_destroy(grpc_winsocket *socket); + +#endif /* GRPC_INTERNAL_CORE_IOMGR_SOCKET_WINDOWS_H */ diff --git a/src/core/iomgr/tcp_client.h b/src/core/iomgr/tcp_client.h new file mode 100644 index 00000000..8ad9b818 --- /dev/null +++ b/src/core/iomgr/tcp_client.h @@ -0,0 +1,52 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_IOMGR_TCP_CLIENT_H +#define GRPC_INTERNAL_CORE_IOMGR_TCP_CLIENT_H + +#include "src/core/iomgr/endpoint.h" +#include "src/core/iomgr/pollset_set.h" +#include "src/core/iomgr/sockaddr.h" +#include + +/* Asynchronously connect to an address (specified as (addr, len)), and call + cb with arg and the completed connection when done (or call cb with arg and + NULL on failure). + interested_parties points to a set of pollsets that would be interested + in this connection being established (in order to continue their work) */ +void grpc_tcp_client_connect(void (*cb)(void *arg, grpc_endpoint *tcp), + void *arg, grpc_pollset_set *interested_parties, + const struct sockaddr *addr, int addr_len, + gpr_timespec deadline); + +#endif /* GRPC_INTERNAL_CORE_IOMGR_TCP_CLIENT_H */ diff --git a/src/core/iomgr/tcp_client_posix.c b/src/core/iomgr/tcp_client_posix.c new file mode 100644 index 00000000..66027f87 --- /dev/null +++ b/src/core/iomgr/tcp_client_posix.c @@ -0,0 +1,278 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_POSIX_SOCKET + +#include "src/core/iomgr/tcp_client.h" + +#include +#include +#include +#include + +#include "src/core/iomgr/alarm.h" +#include "src/core/iomgr/iomgr_posix.h" +#include "src/core/iomgr/pollset_posix.h" +#include "src/core/iomgr/sockaddr_utils.h" +#include "src/core/iomgr/socket_utils_posix.h" +#include "src/core/iomgr/tcp_posix.h" +#include "src/core/support/string.h" +#include +#include +#include +#include + +typedef struct { + void (*cb)(void *arg, grpc_endpoint *tcp); + void *cb_arg; + gpr_mu mu; + grpc_fd *fd; + gpr_timespec deadline; + grpc_alarm alarm; + int refs; + grpc_iomgr_closure write_closure; + grpc_pollset_set *interested_parties; + char *addr_str; +} async_connect; + +static int prepare_socket(const struct sockaddr *addr, int fd) { + if (fd < 0) { + goto error; + } + + if (!grpc_set_socket_nonblocking(fd, 1) || !grpc_set_socket_cloexec(fd, 1) || + (addr->sa_family != AF_UNIX && !grpc_set_socket_low_latency(fd, 1)) || + !grpc_set_socket_no_sigpipe_if_possible(fd)) { + gpr_log(GPR_ERROR, "Unable to configure socket %d: %s", fd, + strerror(errno)); + goto error; + } + + return 1; + +error: + if (fd >= 0) { + close(fd); + } + return 0; +} + +static void tc_on_alarm(void *acp, int success) { + int done; + async_connect *ac = acp; + gpr_mu_lock(&ac->mu); + if (ac->fd != NULL) { + grpc_fd_shutdown(ac->fd); + } + done = (--ac->refs == 0); + gpr_mu_unlock(&ac->mu); + if (done) { + gpr_mu_destroy(&ac->mu); + gpr_free(ac->addr_str); + gpr_free(ac); + } +} + +static void on_writable(void *acp, int success) { + async_connect *ac = acp; + int so_error = 0; + socklen_t so_error_size; + int err; + int done; + grpc_endpoint *ep = NULL; + void (*cb)(void *arg, grpc_endpoint *tcp) = ac->cb; + void *cb_arg = ac->cb_arg; + grpc_fd *fd; + + gpr_mu_lock(&ac->mu); + GPR_ASSERT(ac->fd); + fd = ac->fd; + ac->fd = NULL; + gpr_mu_unlock(&ac->mu); + + grpc_alarm_cancel(&ac->alarm); + + gpr_mu_lock(&ac->mu); + if (success) { + do { + so_error_size = sizeof(so_error); + err = getsockopt(fd->fd, SOL_SOCKET, SO_ERROR, &so_error, &so_error_size); + } while (err < 0 && errno == EINTR); + if (err < 0) { + gpr_log(GPR_ERROR, "getsockopt(ERROR): %s", strerror(errno)); + goto finish; + } else if (so_error != 0) { + if (so_error == ENOBUFS) { + /* We will get one of these errors if we have run out of + memory in the kernel for the data structures allocated + when you connect a socket. If this happens it is very + likely that if we wait a little bit then try again the + connection will work (since other programs or this + program will close their network connections and free up + memory). This does _not_ indicate that there is anything + wrong with the server we are connecting to, this is a + local problem. + + If you are looking at this code, then chances are that + your program or another program on the same computer + opened too many network connections. The "easy" fix: + don't do that! */ + gpr_log(GPR_ERROR, "kernel out of buffers"); + gpr_mu_unlock(&ac->mu); + grpc_fd_notify_on_write(fd, &ac->write_closure); + return; + } else { + switch (so_error) { + case ECONNREFUSED: + gpr_log(GPR_ERROR, "socket error: connection refused"); + break; + default: + gpr_log(GPR_ERROR, "socket error: %d", so_error); + break; + } + goto finish; + } + } else { + grpc_pollset_set_del_fd(ac->interested_parties, fd); + ep = grpc_tcp_create(fd, GRPC_TCP_DEFAULT_READ_SLICE_SIZE, ac->addr_str); + fd = NULL; + goto finish; + } + } else { + gpr_log(GPR_ERROR, "on_writable failed during connect"); + goto finish; + } + + abort(); + +finish: + if (fd != NULL) { + grpc_pollset_set_del_fd(ac->interested_parties, fd); + grpc_fd_orphan(fd, NULL, "tcp_client_orphan"); + fd = NULL; + } + done = (--ac->refs == 0); + gpr_mu_unlock(&ac->mu); + if (done) { + gpr_mu_destroy(&ac->mu); + gpr_free(ac->addr_str); + gpr_free(ac); + } + cb(cb_arg, ep); +} + +void grpc_tcp_client_connect(void (*cb)(void *arg, grpc_endpoint *ep), + void *arg, grpc_pollset_set *interested_parties, + const struct sockaddr *addr, int addr_len, + gpr_timespec deadline) { + int fd; + grpc_dualstack_mode dsmode; + int err; + async_connect *ac; + struct sockaddr_in6 addr6_v4mapped; + struct sockaddr_in addr4_copy; + grpc_fd *fdobj; + char *name; + char *addr_str; + + /* Use dualstack sockets where available. */ + if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { + addr = (const struct sockaddr *)&addr6_v4mapped; + addr_len = sizeof(addr6_v4mapped); + } + + fd = grpc_create_dualstack_socket(addr, SOCK_STREAM, 0, &dsmode); + if (fd < 0) { + gpr_log(GPR_ERROR, "Unable to create socket: %s", strerror(errno)); + } + if (dsmode == GRPC_DSMODE_IPV4) { + /* If we got an AF_INET socket, map the address back to IPv4. */ + GPR_ASSERT(grpc_sockaddr_is_v4mapped(addr, &addr4_copy)); + addr = (struct sockaddr *)&addr4_copy; + addr_len = sizeof(addr4_copy); + } + if (!prepare_socket(addr, fd)) { + cb(arg, NULL); + return; + } + + do { + err = connect(fd, addr, addr_len); + } while (err < 0 && errno == EINTR); + + addr_str = grpc_sockaddr_to_uri(addr); + gpr_asprintf(&name, "tcp-client:%s", addr_str); + + fdobj = grpc_fd_create(fd, name); + + if (err >= 0) { + cb(arg, grpc_tcp_create(fdobj, GRPC_TCP_DEFAULT_READ_SLICE_SIZE, addr_str)); + goto done; + } + + if (errno != EWOULDBLOCK && errno != EINPROGRESS) { + gpr_log(GPR_ERROR, "connect error to '%s': %s", addr_str, strerror(errno)); + grpc_fd_orphan(fdobj, NULL, "tcp_client_connect_error"); + cb(arg, NULL); + goto done; + } + + grpc_pollset_set_add_fd(interested_parties, fdobj); + + ac = gpr_malloc(sizeof(async_connect)); + ac->cb = cb; + ac->cb_arg = arg; + ac->fd = fdobj; + ac->interested_parties = interested_parties; + ac->addr_str = addr_str; + addr_str = NULL; + gpr_mu_init(&ac->mu); + ac->refs = 2; + ac->write_closure.cb = on_writable; + ac->write_closure.cb_arg = ac; + + gpr_mu_lock(&ac->mu); + grpc_alarm_init(&ac->alarm, + gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC), + tc_on_alarm, ac, gpr_now(GPR_CLOCK_MONOTONIC)); + grpc_fd_notify_on_write(ac->fd, &ac->write_closure); + gpr_mu_unlock(&ac->mu); + +done: + gpr_free(name); + gpr_free(addr_str); +} + +#endif diff --git a/src/core/iomgr/tcp_client_windows.c b/src/core/iomgr/tcp_client_windows.c new file mode 100644 index 00000000..a42ec7cf --- /dev/null +++ b/src/core/iomgr/tcp_client_windows.c @@ -0,0 +1,216 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_WINSOCK_SOCKET + +#include "src/core/iomgr/sockaddr_win32.h" + +#include +#include +#include +#include +#include + +#include "src/core/iomgr/alarm.h" +#include "src/core/iomgr/iocp_windows.h" +#include "src/core/iomgr/tcp_client.h" +#include "src/core/iomgr/tcp_windows.h" +#include "src/core/iomgr/sockaddr.h" +#include "src/core/iomgr/sockaddr_utils.h" +#include "src/core/iomgr/socket_windows.h" + +typedef struct { + void (*cb)(void *arg, grpc_endpoint *tcp); + void *cb_arg; + gpr_mu mu; + grpc_winsocket *socket; + gpr_timespec deadline; + grpc_alarm alarm; + char *addr_name; + int refs; +} async_connect; + +static void async_connect_unlock_and_cleanup(async_connect *ac) { + int done = (--ac->refs == 0); + gpr_mu_unlock(&ac->mu); + if (done) { + if (ac->socket != NULL) grpc_winsocket_destroy(ac->socket); + gpr_mu_destroy(&ac->mu); + gpr_free(ac->addr_name); + gpr_free(ac); + } +} + +static void on_alarm(void *acp, int occured) { + async_connect *ac = acp; + gpr_mu_lock(&ac->mu); + /* If the alarm didn't occur, it got cancelled. */ + if (ac->socket != NULL && occured) { + grpc_winsocket_shutdown(ac->socket); + } + async_connect_unlock_and_cleanup(ac); +} + +static void on_connect(void *acp, int from_iocp) { + async_connect *ac = acp; + SOCKET sock = ac->socket->socket; + grpc_endpoint *ep = NULL; + grpc_winsocket_callback_info *info = &ac->socket->write_info; + void (*cb)(void *arg, grpc_endpoint *tcp) = ac->cb; + void *cb_arg = ac->cb_arg; + + grpc_alarm_cancel(&ac->alarm); + + gpr_mu_lock(&ac->mu); + + if (from_iocp) { + DWORD transfered_bytes = 0; + DWORD flags; + BOOL wsa_success = WSAGetOverlappedResult(sock, &info->overlapped, + &transfered_bytes, FALSE, &flags); + GPR_ASSERT(transfered_bytes == 0); + if (!wsa_success) { + char *utf8_message = gpr_format_message(WSAGetLastError()); + gpr_log(GPR_ERROR, "on_connect error: %s", utf8_message); + gpr_free(utf8_message); + } else { + ep = grpc_tcp_create(ac->socket, ac->addr_name); + ac->socket = NULL; + } + } + + async_connect_unlock_and_cleanup(ac); + /* If the connection was aborted, the callback was already called when + the deadline was met. */ + cb(cb_arg, ep); +} + +/* Tries to issue one async connection, then schedules both an IOCP + notification request for the connection, and one timeout alert. */ +void grpc_tcp_client_connect(void (*cb)(void *arg, grpc_endpoint *tcp), + void *arg, grpc_pollset_set *interested_parties, + const struct sockaddr *addr, int addr_len, + gpr_timespec deadline) { + SOCKET sock = INVALID_SOCKET; + BOOL success; + int status; + struct sockaddr_in6 addr6_v4mapped; + struct sockaddr_in6 local_address; + async_connect *ac; + grpc_winsocket *socket = NULL; + LPFN_CONNECTEX ConnectEx; + GUID guid = WSAID_CONNECTEX; + DWORD ioctl_num_bytes; + const char *message = NULL; + char *utf8_message; + grpc_winsocket_callback_info *info; + + /* Use dualstack sockets where available. */ + if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { + addr = (const struct sockaddr *)&addr6_v4mapped; + addr_len = sizeof(addr6_v4mapped); + } + + sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0, + WSA_FLAG_OVERLAPPED); + if (sock == INVALID_SOCKET) { + message = "Unable to create socket: %s"; + goto failure; + } + + if (!grpc_tcp_prepare_socket(sock)) { + message = "Unable to set socket options: %s"; + goto failure; + } + + /* Grab the function pointer for ConnectEx for that specific socket. + It may change depending on the interface. */ + status = + WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid), + &ConnectEx, sizeof(ConnectEx), &ioctl_num_bytes, NULL, NULL); + + if (status != 0) { + message = "Unable to retrieve ConnectEx pointer: %s"; + goto failure; + } + + grpc_sockaddr_make_wildcard6(0, &local_address); + + status = bind(sock, (struct sockaddr *)&local_address, sizeof(local_address)); + if (status != 0) { + message = "Unable to bind socket: %s"; + goto failure; + } + + socket = grpc_winsocket_create(sock, "client"); + info = &socket->write_info; + success = ConnectEx(sock, addr, addr_len, NULL, 0, NULL, &info->overlapped); + + /* It wouldn't be unusual to get a success immediately. But we'll still get + an IOCP notification, so let's ignore it. */ + if (!success) { + int error = WSAGetLastError(); + if (error != ERROR_IO_PENDING) { + message = "ConnectEx failed: %s"; + goto failure; + } + } + + ac = gpr_malloc(sizeof(async_connect)); + ac->cb = cb; + ac->cb_arg = arg; + ac->socket = socket; + gpr_mu_init(&ac->mu); + ac->refs = 2; + ac->addr_name = grpc_sockaddr_to_uri(addr); + + grpc_alarm_init(&ac->alarm, deadline, on_alarm, ac, + gpr_now(GPR_CLOCK_MONOTONIC)); + grpc_socket_notify_on_write(socket, on_connect, ac); + return; + +failure: + utf8_message = gpr_format_message(WSAGetLastError()); + gpr_log(GPR_ERROR, message, utf8_message); + gpr_free(utf8_message); + if (socket != NULL) { + grpc_winsocket_destroy(socket); + } else if (sock != INVALID_SOCKET) { + closesocket(sock); + } + cb(arg, NULL); +} + +#endif /* GPR_WINSOCK_SOCKET */ diff --git a/src/core/iomgr/tcp_posix.c b/src/core/iomgr/tcp_posix.c new file mode 100644 index 00000000..0db7cd9f --- /dev/null +++ b/src/core/iomgr/tcp_posix.c @@ -0,0 +1,451 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_POSIX_SOCKET + +#include "src/core/iomgr/tcp_posix.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "src/core/support/string.h" +#include "src/core/debug/trace.h" +#include "src/core/profiling/timers.h" + +#ifdef GPR_HAVE_MSG_NOSIGNAL +#define SENDMSG_FLAGS MSG_NOSIGNAL +#else +#define SENDMSG_FLAGS 0 +#endif + +int grpc_tcp_trace = 0; + +typedef struct { + grpc_endpoint base; + grpc_fd *em_fd; + int fd; + int iov_size; /* Number of slices to allocate per read attempt */ + int finished_edge; + size_t slice_size; + gpr_refcount refcount; + + gpr_slice_buffer *incoming_buffer; + gpr_slice_buffer *outgoing_buffer; + /** slice within outgoing_buffer to write next */ + size_t outgoing_slice_idx; + /** byte within outgoing_buffer->slices[outgoing_slice_idx] to write next */ + size_t outgoing_byte_idx; + + grpc_iomgr_closure *read_cb; + grpc_iomgr_closure *write_cb; + + grpc_iomgr_closure read_closure; + grpc_iomgr_closure write_closure; + + char *peer_string; +} grpc_tcp; + +static void tcp_handle_read(void *arg /* grpc_tcp */, int success); +static void tcp_handle_write(void *arg /* grpc_tcp */, int success); + +static void tcp_shutdown(grpc_endpoint *ep) { + grpc_tcp *tcp = (grpc_tcp *)ep; + grpc_fd_shutdown(tcp->em_fd); +} + +static void tcp_free(grpc_tcp *tcp) { + grpc_fd_orphan(tcp->em_fd, NULL, "tcp_unref_orphan"); + gpr_free(tcp->peer_string); + gpr_free(tcp); +} + +/*#define GRPC_TCP_REFCOUNT_DEBUG*/ +#ifdef GRPC_TCP_REFCOUNT_DEBUG +#define TCP_UNREF(tcp, reason) tcp_unref((tcp), (reason), __FILE__, __LINE__) +#define TCP_REF(tcp, reason) tcp_ref((tcp), (reason), __FILE__, __LINE__) +static void tcp_unref(grpc_tcp *tcp, const char *reason, const char *file, + int line) { + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "TCP unref %p : %s %d -> %d", tcp, + reason, tcp->refcount.count, tcp->refcount.count - 1); + if (gpr_unref(&tcp->refcount)) { + tcp_free(tcp); + } +} + +static void tcp_ref(grpc_tcp *tcp, const char *reason, const char *file, + int line) { + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "TCP ref %p : %s %d -> %d", tcp, + reason, tcp->refcount.count, tcp->refcount.count + 1); + gpr_ref(&tcp->refcount); +} +#else +#define TCP_UNREF(tcp, reason) tcp_unref((tcp)) +#define TCP_REF(tcp, reason) tcp_ref((tcp)) +static void tcp_unref(grpc_tcp *tcp) { + if (gpr_unref(&tcp->refcount)) { + tcp_free(tcp); + } +} + +static void tcp_ref(grpc_tcp *tcp) { gpr_ref(&tcp->refcount); } +#endif + +static void tcp_destroy(grpc_endpoint *ep) { + grpc_tcp *tcp = (grpc_tcp *)ep; + TCP_UNREF(tcp, "destroy"); +} + +static void call_read_cb(grpc_tcp *tcp, int success) { + grpc_iomgr_closure *cb = tcp->read_cb; + + if (grpc_tcp_trace) { + size_t i; + gpr_log(GPR_DEBUG, "read: success=%d", success); + for (i = 0; i < tcp->incoming_buffer->count; i++) { + char *dump = gpr_dump_slice(tcp->incoming_buffer->slices[i], + GPR_DUMP_HEX | GPR_DUMP_ASCII); + gpr_log(GPR_DEBUG, "READ %p: %s", tcp, dump); + gpr_free(dump); + } + } + + tcp->read_cb = NULL; + tcp->incoming_buffer = NULL; + cb->cb(cb->cb_arg, success); +} + +#define MAX_READ_IOVEC 4 +static void tcp_continue_read(grpc_tcp *tcp) { + struct msghdr msg; + struct iovec iov[MAX_READ_IOVEC]; + ssize_t read_bytes; + size_t i; + + GPR_ASSERT(!tcp->finished_edge); + GPR_ASSERT(tcp->iov_size <= MAX_READ_IOVEC); + GPR_ASSERT(tcp->incoming_buffer->count <= MAX_READ_IOVEC); + GRPC_TIMER_BEGIN(GRPC_PTAG_HANDLE_READ, 0); + + while (tcp->incoming_buffer->count < (size_t)tcp->iov_size) { + gpr_slice_buffer_add_indexed(tcp->incoming_buffer, + gpr_slice_malloc(tcp->slice_size)); + } + for (i = 0; i < tcp->incoming_buffer->count; i++) { + iov[i].iov_base = GPR_SLICE_START_PTR(tcp->incoming_buffer->slices[i]); + iov[i].iov_len = GPR_SLICE_LENGTH(tcp->incoming_buffer->slices[i]); + } + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = iov; + msg.msg_iovlen = tcp->iov_size; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + GRPC_TIMER_BEGIN(GRPC_PTAG_RECVMSG, 0); + do { + read_bytes = recvmsg(tcp->fd, &msg, 0); + } while (read_bytes < 0 && errno == EINTR); + GRPC_TIMER_END(GRPC_PTAG_RECVMSG, 0); + + if (read_bytes < 0) { + /* NB: After calling call_read_cb a parallel call of the read handler may + * be running. */ + if (errno == EAGAIN) { + if (tcp->iov_size > 1) { + tcp->iov_size /= 2; + } + /* We've consumed the edge, request a new one */ + grpc_fd_notify_on_read(tcp->em_fd, &tcp->read_closure); + } else { + /* TODO(klempner): Log interesting errors */ + gpr_slice_buffer_reset_and_unref(tcp->incoming_buffer); + call_read_cb(tcp, 0); + TCP_UNREF(tcp, "read"); + } + } else if (read_bytes == 0) { + /* 0 read size ==> end of stream */ + gpr_slice_buffer_reset_and_unref(tcp->incoming_buffer); + call_read_cb(tcp, 0); + TCP_UNREF(tcp, "read"); + } else { + GPR_ASSERT((size_t)read_bytes <= tcp->incoming_buffer->length); + if ((size_t)read_bytes < tcp->incoming_buffer->length) { + gpr_slice_buffer_trim_end(tcp->incoming_buffer, + tcp->incoming_buffer->length - read_bytes); + } else if (tcp->iov_size < MAX_READ_IOVEC) { + ++tcp->iov_size; + } + GPR_ASSERT((size_t)read_bytes == tcp->incoming_buffer->length); + call_read_cb(tcp, 1); + TCP_UNREF(tcp, "read"); + } + + GRPC_TIMER_END(GRPC_PTAG_HANDLE_READ, 0); +} + +static void tcp_handle_read(void *arg /* grpc_tcp */, int success) { + grpc_tcp *tcp = (grpc_tcp *)arg; + GPR_ASSERT(!tcp->finished_edge); + + if (!success) { + gpr_slice_buffer_reset_and_unref(tcp->incoming_buffer); + call_read_cb(tcp, 0); + TCP_UNREF(tcp, "read"); + } else { + tcp_continue_read(tcp); + } +} + +static grpc_endpoint_op_status tcp_read(grpc_endpoint *ep, + gpr_slice_buffer *incoming_buffer, + grpc_iomgr_closure *cb) { + grpc_tcp *tcp = (grpc_tcp *)ep; + GPR_ASSERT(tcp->read_cb == NULL); + tcp->read_cb = cb; + tcp->incoming_buffer = incoming_buffer; + gpr_slice_buffer_reset_and_unref(incoming_buffer); + TCP_REF(tcp, "read"); + if (tcp->finished_edge) { + tcp->finished_edge = 0; + grpc_fd_notify_on_read(tcp->em_fd, &tcp->read_closure); + } else { + grpc_iomgr_add_delayed_callback(&tcp->read_closure, 1); + } + /* TODO(ctiller): immediate return */ + return GRPC_ENDPOINT_PENDING; +} + +#define MAX_WRITE_IOVEC 16 +static grpc_endpoint_op_status tcp_flush(grpc_tcp *tcp) { + struct msghdr msg; + struct iovec iov[MAX_WRITE_IOVEC]; + int iov_size; + ssize_t sent_length; + ssize_t sending_length; + ssize_t trailing; + ssize_t unwind_slice_idx; + ssize_t unwind_byte_idx; + + for (;;) { + sending_length = 0; + unwind_slice_idx = tcp->outgoing_slice_idx; + unwind_byte_idx = tcp->outgoing_byte_idx; + for (iov_size = 0; tcp->outgoing_slice_idx != tcp->outgoing_buffer->count && + iov_size != MAX_WRITE_IOVEC; + iov_size++) { + iov[iov_size].iov_base = + GPR_SLICE_START_PTR( + tcp->outgoing_buffer->slices[tcp->outgoing_slice_idx]) + + tcp->outgoing_byte_idx; + iov[iov_size].iov_len = + GPR_SLICE_LENGTH( + tcp->outgoing_buffer->slices[tcp->outgoing_slice_idx]) - + tcp->outgoing_byte_idx; + sending_length += iov[iov_size].iov_len; + tcp->outgoing_slice_idx++; + tcp->outgoing_byte_idx = 0; + } + GPR_ASSERT(iov_size > 0); + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = iov; + msg.msg_iovlen = iov_size; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + GRPC_TIMER_BEGIN(GRPC_PTAG_SENDMSG, 0); + do { + /* TODO(klempner): Cork if this is a partial write */ + sent_length = sendmsg(tcp->fd, &msg, SENDMSG_FLAGS); + } while (sent_length < 0 && errno == EINTR); + GRPC_TIMER_END(GRPC_PTAG_SENDMSG, 0); + + if (sent_length < 0) { + if (errno == EAGAIN) { + tcp->outgoing_slice_idx = unwind_slice_idx; + tcp->outgoing_byte_idx = unwind_byte_idx; + return GRPC_ENDPOINT_PENDING; + } else { + /* TODO(klempner): Log some of these */ + return GRPC_ENDPOINT_ERROR; + } + } + + GPR_ASSERT(tcp->outgoing_byte_idx == 0); + trailing = sending_length - sent_length; + while (trailing > 0) { + ssize_t slice_length; + + tcp->outgoing_slice_idx--; + slice_length = GPR_SLICE_LENGTH( + tcp->outgoing_buffer->slices[tcp->outgoing_slice_idx]); + if (slice_length > trailing) { + tcp->outgoing_byte_idx = slice_length - trailing; + break; + } else { + trailing -= slice_length; + } + } + + if (tcp->outgoing_slice_idx == tcp->outgoing_buffer->count) { + return GRPC_ENDPOINT_DONE; + } + }; +} + +static void tcp_handle_write(void *arg /* grpc_tcp */, int success) { + grpc_tcp *tcp = (grpc_tcp *)arg; + grpc_endpoint_op_status status; + grpc_iomgr_closure *cb; + + if (!success) { + cb = tcp->write_cb; + tcp->write_cb = NULL; + cb->cb(cb->cb_arg, 0); + TCP_UNREF(tcp, "write"); + return; + } + + GRPC_TIMER_BEGIN(GRPC_PTAG_TCP_CB_WRITE, 0); + status = tcp_flush(tcp); + if (status == GRPC_ENDPOINT_PENDING) { + grpc_fd_notify_on_write(tcp->em_fd, &tcp->write_closure); + } else { + cb = tcp->write_cb; + tcp->write_cb = NULL; + cb->cb(cb->cb_arg, status == GRPC_ENDPOINT_DONE); + TCP_UNREF(tcp, "write"); + } + GRPC_TIMER_END(GRPC_PTAG_TCP_CB_WRITE, 0); +} + +static grpc_endpoint_op_status tcp_write(grpc_endpoint *ep, + gpr_slice_buffer *buf, + grpc_iomgr_closure *cb) { + grpc_tcp *tcp = (grpc_tcp *)ep; + grpc_endpoint_op_status status; + + if (grpc_tcp_trace) { + size_t i; + + for (i = 0; i < buf->count; i++) { + char *data = + gpr_dump_slice(buf->slices[i], GPR_DUMP_HEX | GPR_DUMP_ASCII); + gpr_log(GPR_DEBUG, "WRITE %p: %s", tcp, data); + gpr_free(data); + } + } + + GRPC_TIMER_BEGIN(GRPC_PTAG_TCP_WRITE, 0); + GPR_ASSERT(tcp->write_cb == NULL); + + if (buf->length == 0) { + GRPC_TIMER_END(GRPC_PTAG_TCP_WRITE, 0); + return GRPC_ENDPOINT_DONE; + } + tcp->outgoing_buffer = buf; + tcp->outgoing_slice_idx = 0; + tcp->outgoing_byte_idx = 0; + + status = tcp_flush(tcp); + if (status == GRPC_ENDPOINT_PENDING) { + TCP_REF(tcp, "write"); + tcp->write_cb = cb; + grpc_fd_notify_on_write(tcp->em_fd, &tcp->write_closure); + } + + GRPC_TIMER_END(GRPC_PTAG_TCP_WRITE, 0); + return status; +} + +static void tcp_add_to_pollset(grpc_endpoint *ep, grpc_pollset *pollset) { + grpc_tcp *tcp = (grpc_tcp *)ep; + grpc_pollset_add_fd(pollset, tcp->em_fd); +} + +static void tcp_add_to_pollset_set(grpc_endpoint *ep, + grpc_pollset_set *pollset_set) { + grpc_tcp *tcp = (grpc_tcp *)ep; + grpc_pollset_set_add_fd(pollset_set, tcp->em_fd); +} + +static char *tcp_get_peer(grpc_endpoint *ep) { + grpc_tcp *tcp = (grpc_tcp *)ep; + return gpr_strdup(tcp->peer_string); +} + +static const grpc_endpoint_vtable vtable = { + tcp_read, tcp_write, tcp_add_to_pollset, tcp_add_to_pollset_set, + tcp_shutdown, tcp_destroy, tcp_get_peer}; + +grpc_endpoint *grpc_tcp_create(grpc_fd *em_fd, size_t slice_size, + const char *peer_string) { + grpc_tcp *tcp = (grpc_tcp *)gpr_malloc(sizeof(grpc_tcp)); + tcp->base.vtable = &vtable; + tcp->peer_string = gpr_strdup(peer_string); + tcp->fd = em_fd->fd; + tcp->read_cb = NULL; + tcp->write_cb = NULL; + tcp->incoming_buffer = NULL; + tcp->slice_size = slice_size; + tcp->iov_size = 1; + tcp->finished_edge = 1; + /* paired with unref in grpc_tcp_destroy */ + gpr_ref_init(&tcp->refcount, 1); + tcp->em_fd = em_fd; + tcp->read_closure.cb = tcp_handle_read; + tcp->read_closure.cb_arg = tcp; + tcp->write_closure.cb = tcp_handle_write; + tcp->write_closure.cb_arg = tcp; + + return &tcp->base; +} + +#endif diff --git a/src/core/iomgr/tcp_posix.h b/src/core/iomgr/tcp_posix.h new file mode 100644 index 00000000..40b3ae26 --- /dev/null +++ b/src/core/iomgr/tcp_posix.h @@ -0,0 +1,59 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_IOMGR_TCP_POSIX_H +#define GRPC_INTERNAL_CORE_IOMGR_TCP_POSIX_H +/* + Low level TCP "bottom half" implementation, for use by transports built on + top of a TCP connection. + + Note that this file does not (yet) include APIs for creating the socket in + the first place. + + All calls passing slice transfer ownership of a slice refcount unless + otherwise specified. +*/ + +#include "src/core/iomgr/endpoint.h" +#include "src/core/iomgr/fd_posix.h" + +#define GRPC_TCP_DEFAULT_READ_SLICE_SIZE 8192 + +extern int grpc_tcp_trace; + +/* Create a tcp endpoint given a file desciptor and a read slice size. + Takes ownership of fd. */ +grpc_endpoint *grpc_tcp_create(grpc_fd *fd, size_t read_slice_size, + const char *peer_string); + +#endif /* GRPC_INTERNAL_CORE_IOMGR_TCP_POSIX_H */ diff --git a/src/core/iomgr/tcp_server.h b/src/core/iomgr/tcp_server.h new file mode 100644 index 00000000..66bb3ef7 --- /dev/null +++ b/src/core/iomgr/tcp_server.h @@ -0,0 +1,78 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_IOMGR_TCP_SERVER_H +#define GRPC_INTERNAL_CORE_IOMGR_TCP_SERVER_H + +#include "src/core/iomgr/endpoint.h" + +/* Forward decl of grpc_tcp_server */ +typedef struct grpc_tcp_server grpc_tcp_server; + +/* New server callback: tcp is the newly connected tcp connection */ +typedef void (*grpc_tcp_server_cb)(void *arg, grpc_endpoint *ep); + +/* Create a server, initially not bound to any ports */ +grpc_tcp_server *grpc_tcp_server_create(void); + +/* Start listening to bound ports */ +void grpc_tcp_server_start(grpc_tcp_server *server, grpc_pollset **pollsets, + size_t pollset_count, grpc_tcp_server_cb cb, + void *cb_arg); + +/* Add a port to the server, returning port number on success, or negative + on failure. + + The :: and 0.0.0.0 wildcard addresses are treated identically, accepting + both IPv4 and IPv6 connections, but :: is the preferred style. This usually + creates one socket, but possibly two on systems which support IPv6, + but not dualstack sockets. + + For raw access to the underlying sockets, see grpc_tcp_server_get_fd(). */ +/* TODO(ctiller): deprecate this, and make grpc_tcp_server_add_ports to handle + all of the multiple socket port matching logic in one place */ +int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr, + int addr_len); + +/* Returns the file descriptor of the Nth listening socket on this server, + or -1 if the index is out of bounds. + + The file descriptor remains owned by the server, and will be cleaned + up when grpc_tcp_server_destroy is called. */ +int grpc_tcp_server_get_fd(grpc_tcp_server *s, unsigned index); + +void grpc_tcp_server_destroy(grpc_tcp_server *server, + void (*shutdown_done)(void *shutdown_done_arg), + void *shutdown_done_arg); + +#endif /* GRPC_INTERNAL_CORE_IOMGR_TCP_SERVER_H */ diff --git a/src/core/iomgr/tcp_server_posix.c b/src/core/iomgr/tcp_server_posix.c new file mode 100644 index 00000000..6399aaad --- /dev/null +++ b/src/core/iomgr/tcp_server_posix.c @@ -0,0 +1,509 @@ +/* + * + * Copyright 2015, 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. + * + */ + +/* FIXME: "posix" files shouldn't be depending on _GNU_SOURCE */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include + +#ifdef GPR_POSIX_SOCKET + +#include "src/core/iomgr/tcp_server.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "src/core/iomgr/pollset_posix.h" +#include "src/core/iomgr/resolve_address.h" +#include "src/core/iomgr/sockaddr_utils.h" +#include "src/core/iomgr/socket_utils_posix.h" +#include "src/core/iomgr/tcp_posix.h" +#include "src/core/support/string.h" +#include +#include +#include +#include +#include + +#define INIT_PORT_CAP 2 +#define MIN_SAFE_ACCEPT_QUEUE_SIZE 100 + +static gpr_once s_init_max_accept_queue_size; +static int s_max_accept_queue_size; + +/* one listening port */ +typedef struct { + int fd; + grpc_fd *emfd; + grpc_tcp_server *server; + union { + gpr_uint8 untyped[GRPC_MAX_SOCKADDR_SIZE]; + struct sockaddr sockaddr; + struct sockaddr_un un; + } addr; + int addr_len; + grpc_iomgr_closure read_closure; + grpc_iomgr_closure destroyed_closure; +} server_port; + +static void unlink_if_unix_domain_socket(const struct sockaddr_un *un) { + struct stat st; + + if (stat(un->sun_path, &st) == 0 && (st.st_mode & S_IFMT) == S_IFSOCK) { + unlink(un->sun_path); + } +} + +/* the overall server */ +struct grpc_tcp_server { + grpc_tcp_server_cb cb; + void *cb_arg; + + gpr_mu mu; + + /* active port count: how many ports are actually still listening */ + size_t active_ports; + /* destroyed port count: how many ports are completely destroyed */ + size_t destroyed_ports; + + /* is this server shutting down? (boolean) */ + int shutdown; + + /* all listening ports */ + server_port *ports; + size_t nports; + size_t port_capacity; + + /* shutdown callback */ + void (*shutdown_complete)(void *); + void *shutdown_complete_arg; + + /* all pollsets interested in new connections */ + grpc_pollset **pollsets; + /* number of pollsets in the pollsets array */ + size_t pollset_count; +}; + +grpc_tcp_server *grpc_tcp_server_create(void) { + grpc_tcp_server *s = gpr_malloc(sizeof(grpc_tcp_server)); + gpr_mu_init(&s->mu); + s->active_ports = 0; + s->destroyed_ports = 0; + s->shutdown = 0; + s->cb = NULL; + s->cb_arg = NULL; + s->ports = gpr_malloc(sizeof(server_port) * INIT_PORT_CAP); + s->nports = 0; + s->port_capacity = INIT_PORT_CAP; + return s; +} + +static void finish_shutdown(grpc_tcp_server *s) { + s->shutdown_complete(s->shutdown_complete_arg); + s->shutdown_complete = NULL; + + gpr_mu_destroy(&s->mu); + + gpr_free(s->ports); + gpr_free(s); +} + +static void destroyed_port(void *server, int success) { + grpc_tcp_server *s = server; + gpr_mu_lock(&s->mu); + s->destroyed_ports++; + if (s->destroyed_ports == s->nports) { + gpr_mu_unlock(&s->mu); + finish_shutdown(s); + } else { + GPR_ASSERT(s->destroyed_ports < s->nports); + gpr_mu_unlock(&s->mu); + } +} + +static void dont_care_about_shutdown_completion(void *ignored) {} + +/* called when all listening endpoints have been shutdown, so no further + events will be received on them - at this point it's safe to destroy + things */ +static void deactivated_all_ports(grpc_tcp_server *s) { + size_t i; + + /* delete ALL the things */ + gpr_mu_lock(&s->mu); + + if (!s->shutdown) { + gpr_mu_unlock(&s->mu); + return; + } + + if (s->nports) { + for (i = 0; i < s->nports; i++) { + server_port *sp = &s->ports[i]; + if (sp->addr.sockaddr.sa_family == AF_UNIX) { + unlink_if_unix_domain_socket(&sp->addr.un); + } + sp->destroyed_closure.cb = destroyed_port; + sp->destroyed_closure.cb_arg = s; + grpc_fd_orphan(sp->emfd, &sp->destroyed_closure, "tcp_listener_shutdown"); + } + gpr_mu_unlock(&s->mu); + } else { + gpr_mu_unlock(&s->mu); + finish_shutdown(s); + } +} + +void grpc_tcp_server_destroy( + grpc_tcp_server *s, void (*shutdown_complete)(void *shutdown_complete_arg), + void *shutdown_complete_arg) { + size_t i; + gpr_mu_lock(&s->mu); + + GPR_ASSERT(!s->shutdown); + s->shutdown = 1; + + s->shutdown_complete = shutdown_complete + ? shutdown_complete + : dont_care_about_shutdown_completion; + s->shutdown_complete_arg = shutdown_complete_arg; + + /* shutdown all fd's */ + if (s->active_ports) { + for (i = 0; i < s->nports; i++) { + grpc_fd_shutdown(s->ports[i].emfd); + } + gpr_mu_unlock(&s->mu); + } else { + gpr_mu_unlock(&s->mu); + deactivated_all_ports(s); + } +} + +/* get max listen queue size on linux */ +static void init_max_accept_queue_size(void) { + int n = SOMAXCONN; + char buf[64]; + FILE *fp = fopen("/proc/sys/net/core/somaxconn", "r"); + if (fp == NULL) { + /* 2.4 kernel. */ + s_max_accept_queue_size = SOMAXCONN; + return; + } + if (fgets(buf, sizeof buf, fp)) { + char *end; + long i = strtol(buf, &end, 10); + if (i > 0 && i <= INT_MAX && end && *end == 0) { + n = i; + } + } + fclose(fp); + s_max_accept_queue_size = n; + + if (s_max_accept_queue_size < MIN_SAFE_ACCEPT_QUEUE_SIZE) { + gpr_log(GPR_INFO, + "Suspiciously small accept queue (%d) will probably lead to " + "connection drops", + s_max_accept_queue_size); + } +} + +static int get_max_accept_queue_size(void) { + gpr_once_init(&s_init_max_accept_queue_size, init_max_accept_queue_size); + return s_max_accept_queue_size; +} + +/* Prepare a recently-created socket for listening. */ +static int prepare_socket(int fd, const struct sockaddr *addr, int addr_len) { + struct sockaddr_storage sockname_temp; + socklen_t sockname_len; + + if (fd < 0) { + goto error; + } + + if (!grpc_set_socket_nonblocking(fd, 1) || !grpc_set_socket_cloexec(fd, 1) || + (addr->sa_family != AF_UNIX && (!grpc_set_socket_low_latency(fd, 1) || + !grpc_set_socket_reuse_addr(fd, 1))) || + !grpc_set_socket_no_sigpipe_if_possible(fd)) { + gpr_log(GPR_ERROR, "Unable to configure socket %d: %s", fd, + strerror(errno)); + goto error; + } + + if (bind(fd, addr, addr_len) < 0) { + char *addr_str; + grpc_sockaddr_to_string(&addr_str, addr, 0); + gpr_log(GPR_ERROR, "bind addr=%s: %s", addr_str, strerror(errno)); + gpr_free(addr_str); + goto error; + } + + if (listen(fd, get_max_accept_queue_size()) < 0) { + gpr_log(GPR_ERROR, "listen: %s", strerror(errno)); + goto error; + } + + sockname_len = sizeof(sockname_temp); + if (getsockname(fd, (struct sockaddr *)&sockname_temp, &sockname_len) < 0) { + goto error; + } + + return grpc_sockaddr_get_port((struct sockaddr *)&sockname_temp); + +error: + if (fd >= 0) { + close(fd); + } + return -1; +} + +/* event manager callback when reads are ready */ +static void on_read(void *arg, int success) { + server_port *sp = arg; + grpc_fd *fdobj; + size_t i; + + if (!success) { + goto error; + } + + /* loop until accept4 returns EAGAIN, and then re-arm notification */ + for (;;) { + struct sockaddr_storage addr; + socklen_t addrlen = sizeof(addr); + char *addr_str; + char *name; + /* Note: If we ever decide to return this address to the user, remember to + strip off the ::ffff:0.0.0.0/96 prefix first. */ + int fd = grpc_accept4(sp->fd, (struct sockaddr *)&addr, &addrlen, 1, 1); + if (fd < 0) { + switch (errno) { + case EINTR: + continue; + case EAGAIN: + grpc_fd_notify_on_read(sp->emfd, &sp->read_closure); + return; + default: + gpr_log(GPR_ERROR, "Failed accept4: %s", strerror(errno)); + goto error; + } + } + + grpc_set_socket_no_sigpipe_if_possible(fd); + + addr_str = grpc_sockaddr_to_uri((struct sockaddr *)&addr); + gpr_asprintf(&name, "tcp-server-connection:%s", addr_str); + + fdobj = grpc_fd_create(fd, name); + /* TODO(ctiller): revise this when we have server-side sharding + of channels -- we certainly should not be automatically adding every + incoming channel to every pollset owned by the server */ + for (i = 0; i < sp->server->pollset_count; i++) { + grpc_pollset_add_fd(sp->server->pollsets[i], fdobj); + } + sp->server->cb( + sp->server->cb_arg, + grpc_tcp_create(fdobj, GRPC_TCP_DEFAULT_READ_SLICE_SIZE, addr_str)); + + gpr_free(name); + gpr_free(addr_str); + } + + abort(); + +error: + gpr_mu_lock(&sp->server->mu); + if (0 == --sp->server->active_ports) { + gpr_mu_unlock(&sp->server->mu); + deactivated_all_ports(sp->server); + } else { + gpr_mu_unlock(&sp->server->mu); + } +} + +static int add_socket_to_server(grpc_tcp_server *s, int fd, + const struct sockaddr *addr, int addr_len) { + server_port *sp; + int port; + char *addr_str; + char *name; + + port = prepare_socket(fd, addr, addr_len); + if (port >= 0) { + grpc_sockaddr_to_string(&addr_str, (struct sockaddr *)&addr, 1); + gpr_asprintf(&name, "tcp-server-listener:%s", addr_str); + gpr_mu_lock(&s->mu); + GPR_ASSERT(!s->cb && "must add ports before starting server"); + /* append it to the list under a lock */ + if (s->nports == s->port_capacity) { + s->port_capacity *= 2; + s->ports = gpr_realloc(s->ports, sizeof(server_port) * s->port_capacity); + } + sp = &s->ports[s->nports++]; + sp->server = s; + sp->fd = fd; + sp->emfd = grpc_fd_create(fd, name); + memcpy(sp->addr.untyped, addr, addr_len); + sp->addr_len = addr_len; + GPR_ASSERT(sp->emfd); + gpr_mu_unlock(&s->mu); + gpr_free(addr_str); + gpr_free(name); + } + + return port; +} + +int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr, + int addr_len) { + int allocated_port1 = -1; + int allocated_port2 = -1; + unsigned i; + int fd; + grpc_dualstack_mode dsmode; + struct sockaddr_in6 addr6_v4mapped; + struct sockaddr_in wild4; + struct sockaddr_in6 wild6; + struct sockaddr_in addr4_copy; + struct sockaddr *allocated_addr = NULL; + struct sockaddr_storage sockname_temp; + socklen_t sockname_len; + int port; + + if (((struct sockaddr *)addr)->sa_family == AF_UNIX) { + unlink_if_unix_domain_socket(addr); + } + + /* Check if this is a wildcard port, and if so, try to keep the port the same + as some previously created listener. */ + if (grpc_sockaddr_get_port(addr) == 0) { + for (i = 0; i < s->nports; i++) { + sockname_len = sizeof(sockname_temp); + if (0 == getsockname(s->ports[i].fd, (struct sockaddr *)&sockname_temp, + &sockname_len)) { + port = grpc_sockaddr_get_port((struct sockaddr *)&sockname_temp); + if (port > 0) { + allocated_addr = malloc(addr_len); + memcpy(allocated_addr, addr, addr_len); + grpc_sockaddr_set_port(allocated_addr, port); + addr = allocated_addr; + break; + } + } + } + } + + if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { + addr = (const struct sockaddr *)&addr6_v4mapped; + addr_len = sizeof(addr6_v4mapped); + } + + /* Treat :: or 0.0.0.0 as a family-agnostic wildcard. */ + if (grpc_sockaddr_is_wildcard(addr, &port)) { + grpc_sockaddr_make_wildcards(port, &wild4, &wild6); + + /* Try listening on IPv6 first. */ + addr = (struct sockaddr *)&wild6; + addr_len = sizeof(wild6); + fd = grpc_create_dualstack_socket(addr, SOCK_STREAM, 0, &dsmode); + allocated_port1 = add_socket_to_server(s, fd, addr, addr_len); + if (fd >= 0 && dsmode == GRPC_DSMODE_DUALSTACK) { + goto done; + } + + /* If we didn't get a dualstack socket, also listen on 0.0.0.0. */ + if (port == 0 && allocated_port1 > 0) { + grpc_sockaddr_set_port((struct sockaddr *)&wild4, allocated_port1); + } + addr = (struct sockaddr *)&wild4; + addr_len = sizeof(wild4); + } + + fd = grpc_create_dualstack_socket(addr, SOCK_STREAM, 0, &dsmode); + if (fd < 0) { + gpr_log(GPR_ERROR, "Unable to create socket: %s", strerror(errno)); + } + if (dsmode == GRPC_DSMODE_IPV4 && + grpc_sockaddr_is_v4mapped(addr, &addr4_copy)) { + addr = (struct sockaddr *)&addr4_copy; + addr_len = sizeof(addr4_copy); + } + allocated_port2 = add_socket_to_server(s, fd, addr, addr_len); + +done: + gpr_free(allocated_addr); + return allocated_port1 >= 0 ? allocated_port1 : allocated_port2; +} + +int grpc_tcp_server_get_fd(grpc_tcp_server *s, unsigned index) { + return (index < s->nports) ? s->ports[index].fd : -1; +} + +void grpc_tcp_server_start(grpc_tcp_server *s, grpc_pollset **pollsets, + size_t pollset_count, grpc_tcp_server_cb cb, + void *cb_arg) { + size_t i, j; + GPR_ASSERT(cb); + gpr_mu_lock(&s->mu); + GPR_ASSERT(!s->cb); + GPR_ASSERT(s->active_ports == 0); + s->cb = cb; + s->cb_arg = cb_arg; + s->pollsets = pollsets; + s->pollset_count = pollset_count; + for (i = 0; i < s->nports; i++) { + for (j = 0; j < pollset_count; j++) { + grpc_pollset_add_fd(pollsets[j], s->ports[i].emfd); + } + s->ports[i].read_closure.cb = on_read; + s->ports[i].read_closure.cb_arg = &s->ports[i]; + grpc_fd_notify_on_read(s->ports[i].emfd, &s->ports[i].read_closure); + s->active_ports++; + } + gpr_mu_unlock(&s->mu); +} + +#endif diff --git a/src/core/iomgr/tcp_server_windows.c b/src/core/iomgr/tcp_server_windows.c new file mode 100644 index 00000000..b513d854 --- /dev/null +++ b/src/core/iomgr/tcp_server_windows.c @@ -0,0 +1,481 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_WINSOCK_SOCKET + +#define _GNU_SOURCE +#include "src/core/iomgr/sockaddr_utils.h" + +#include +#include +#include +#include +#include +#include + +#include "src/core/iomgr/iocp_windows.h" +#include "src/core/iomgr/pollset_windows.h" +#include "src/core/iomgr/socket_windows.h" +#include "src/core/iomgr/tcp_server.h" +#include "src/core/iomgr/tcp_windows.h" + +#define INIT_PORT_CAP 2 +#define MIN_SAFE_ACCEPT_QUEUE_SIZE 100 + +/* one listening port */ +typedef struct server_port { + /* This seemingly magic number comes from AcceptEx's documentation. each + address buffer needs to have at least 16 more bytes at their end. */ + gpr_uint8 addresses[(sizeof(struct sockaddr_in6) + 16) * 2]; + /* This will hold the socket for the next accept. */ + SOCKET new_socket; + /* The listener winsocked. */ + grpc_winsocket *socket; + grpc_tcp_server *server; + /* The cached AcceptEx for that port. */ + LPFN_ACCEPTEX AcceptEx; + int shutting_down; +} server_port; + +/* the overall server */ +struct grpc_tcp_server { + grpc_tcp_server_cb cb; + void *cb_arg; + + gpr_mu mu; + + /* active port count: how many ports are actually still listening */ + int active_ports; + + /* all listening ports */ + server_port *ports; + size_t nports; + size_t port_capacity; + + /* shutdown callback */ + void(*shutdown_complete)(void *); + void *shutdown_complete_arg; +}; + +/* Public function. Allocates the proper data structures to hold a + grpc_tcp_server. */ +grpc_tcp_server *grpc_tcp_server_create(void) { + grpc_tcp_server *s = gpr_malloc(sizeof(grpc_tcp_server)); + gpr_mu_init(&s->mu); + s->active_ports = 0; + s->cb = NULL; + s->cb_arg = NULL; + s->ports = gpr_malloc(sizeof(server_port) * INIT_PORT_CAP); + s->nports = 0; + s->port_capacity = INIT_PORT_CAP; + s->shutdown_complete = NULL; + return s; +} + +static void dont_care_about_shutdown_completion(void *arg) {} + +static void finish_shutdown(grpc_tcp_server *s) { + size_t i; + + s->shutdown_complete(s->shutdown_complete_arg); + + /* Now that the accepts have been aborted, we can destroy the sockets. + The IOCP won't get notified on these, so we can flag them as already + closed by the system. */ + for (i = 0; i < s->nports; i++) { + server_port *sp = &s->ports[i]; + grpc_winsocket_destroy(sp->socket); + } + gpr_free(s->ports); + gpr_free(s); +} + +/* Public function. Stops and destroys a grpc_tcp_server. */ +void grpc_tcp_server_destroy(grpc_tcp_server *s, + void (*shutdown_complete)(void *shutdown_done_arg), + void *shutdown_complete_arg) { + size_t i; + int immediately_done = 0; + gpr_mu_lock(&s->mu); + + s->shutdown_complete = shutdown_complete + ? shutdown_complete + : dont_care_about_shutdown_completion; + s->shutdown_complete_arg = shutdown_complete_arg; + + /* First, shutdown all fd's. This will queue abortion calls for all + of the pending accepts due to the normal operation mechanism. */ + if (s->active_ports == 0) { + immediately_done = 1; + } + for (i = 0; i < s->nports; i++) { + server_port *sp = &s->ports[i]; + sp->shutting_down = 1; + grpc_winsocket_shutdown(sp->socket); + } + gpr_mu_unlock(&s->mu); + + if (immediately_done) { + finish_shutdown(s); + } +} + +/* Prepare (bind) a recently-created socket for listening. */ +static int prepare_socket(SOCKET sock, const struct sockaddr *addr, + int addr_len) { + struct sockaddr_storage sockname_temp; + socklen_t sockname_len; + + if (sock == INVALID_SOCKET) goto error; + + if (!grpc_tcp_prepare_socket(sock)) { + char *utf8_message = gpr_format_message(WSAGetLastError()); + gpr_log(GPR_ERROR, "Unable to prepare socket: %s", utf8_message); + gpr_free(utf8_message); + goto error; + } + + if (bind(sock, addr, addr_len) == SOCKET_ERROR) { + char *addr_str; + char *utf8_message = gpr_format_message(WSAGetLastError()); + grpc_sockaddr_to_string(&addr_str, addr, 0); + gpr_log(GPR_ERROR, "bind addr=%s: %s", addr_str, utf8_message); + gpr_free(utf8_message); + gpr_free(addr_str); + goto error; + } + + if (listen(sock, SOMAXCONN) == SOCKET_ERROR) { + char *utf8_message = gpr_format_message(WSAGetLastError()); + gpr_log(GPR_ERROR, "listen: %s", utf8_message); + gpr_free(utf8_message); + goto error; + } + + sockname_len = sizeof(sockname_temp); + if (getsockname(sock, (struct sockaddr *)&sockname_temp, &sockname_len) == + SOCKET_ERROR) { + char *utf8_message = gpr_format_message(WSAGetLastError()); + gpr_log(GPR_ERROR, "getsockname: %s", utf8_message); + gpr_free(utf8_message); + goto error; + } + + return grpc_sockaddr_get_port((struct sockaddr *)&sockname_temp); + +error: + if (sock != INVALID_SOCKET) closesocket(sock); + return -1; +} + +static void decrement_active_ports_and_notify(server_port *sp) { + int notify = 0; + sp->shutting_down = 0; + gpr_mu_lock(&sp->server->mu); + GPR_ASSERT(sp->server->active_ports > 0); + if (0 == --sp->server->active_ports && sp->server->shutdown_complete != NULL) { + notify = 1; + } + gpr_mu_unlock(&sp->server->mu); + if (notify) { + finish_shutdown(sp->server); + } +} + +/* start_accept will reference that for the IOCP notification request. */ +static void on_accept(void *arg, int from_iocp); + +/* In order to do an async accept, we need to create a socket first which + will be the one assigned to the new incoming connection. */ +static void start_accept(server_port *port) { + SOCKET sock = INVALID_SOCKET; + char *message; + char *utf8_message; + BOOL success; + DWORD addrlen = sizeof(struct sockaddr_in6) + 16; + DWORD bytes_received = 0; + + sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0, + WSA_FLAG_OVERLAPPED); + + if (sock == INVALID_SOCKET) { + message = "Unable to create socket: %s"; + goto failure; + } + + if (!grpc_tcp_prepare_socket(sock)) { + message = "Unable to prepare socket: %s"; + goto failure; + } + + /* Start the "accept" asynchronously. */ + success = port->AcceptEx(port->socket->socket, sock, port->addresses, 0, + addrlen, addrlen, &bytes_received, + &port->socket->read_info.overlapped); + + /* It is possible to get an accept immediately without delay. However, we + will still get an IOCP notification for it. So let's just ignore it. */ + if (!success) { + int error = WSAGetLastError(); + if (error != ERROR_IO_PENDING) { + message = "AcceptEx failed: %s"; + goto failure; + } + } + + /* We're ready to do the accept. Calling grpc_socket_notify_on_read may + immediately process an accept that happened in the meantime. */ + port->new_socket = sock; + grpc_socket_notify_on_read(port->socket, on_accept, port); + return; + +failure: + if (port->shutting_down) { + /* We are abandoning the listener port, take that into account to prevent + occasional hangs on shutdown. The hang happens when sp->shutting_down + change is not seen by on_accept and we proceed to trying new accept, + but we fail there because the listening port has been closed in the + meantime. */ + decrement_active_ports_and_notify(port); + return; + } + utf8_message = gpr_format_message(WSAGetLastError()); + gpr_log(GPR_ERROR, message, utf8_message); + gpr_free(utf8_message); + if (sock != INVALID_SOCKET) closesocket(sock); +} + +/* Event manager callback when reads are ready. */ +static void on_accept(void *arg, int from_iocp) { + server_port *sp = arg; + SOCKET sock = sp->new_socket; + grpc_winsocket_callback_info *info = &sp->socket->read_info; + grpc_endpoint *ep = NULL; + struct sockaddr_storage peer_name; + char *peer_name_string; + char *fd_name; + int peer_name_len = sizeof(peer_name); + DWORD transfered_bytes; + DWORD flags; + BOOL wsa_success; + int err; + + /* The general mechanism for shutting down is to queue abortion calls. While + this is necessary in the read/write case, it's useless for the accept + case. We only need to adjust the pending callback count */ + if (!from_iocp) { + return; + } + + /* The IOCP notified us of a completed operation. Let's grab the results, + and act accordingly. */ + transfered_bytes = 0; + wsa_success = WSAGetOverlappedResult(sock, &info->overlapped, + &transfered_bytes, FALSE, &flags); + if (!wsa_success) { + if (sp->shutting_down) { + /* During the shutdown case, we ARE expecting an error. So that's well, + and we can wake up the shutdown thread. */ + decrement_active_ports_and_notify(sp); + return; + } else { + char *utf8_message = gpr_format_message(WSAGetLastError()); + gpr_log(GPR_ERROR, "on_accept error: %s", utf8_message); + gpr_free(utf8_message); + closesocket(sock); + } + } else { + if (!sp->shutting_down) { + peer_name_string = NULL; + err = setsockopt(sock, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, + (char *)&sp->socket->socket, sizeof(sp->socket->socket)); + if (err) { + char *utf8_message = gpr_format_message(WSAGetLastError()); + gpr_log(GPR_ERROR, "setsockopt error: %s", utf8_message); + gpr_free(utf8_message); + } + err = getpeername(sock, (struct sockaddr *)&peer_name, &peer_name_len); + if (!err) { + peer_name_string = grpc_sockaddr_to_uri((struct sockaddr *)&peer_name); + } else { + char *utf8_message = gpr_format_message(WSAGetLastError()); + gpr_log(GPR_ERROR, "getpeername error: %s", utf8_message); + gpr_free(utf8_message); + } + gpr_asprintf(&fd_name, "tcp_server:%s", peer_name_string); + ep = grpc_tcp_create(grpc_winsocket_create(sock, fd_name), + peer_name_string); + gpr_free(fd_name); + gpr_free(peer_name_string); + } + } + + /* The only time we should call our callback, is where we successfully + managed to accept a connection, and created an endpoint. */ + if (ep) sp->server->cb(sp->server->cb_arg, ep); + /* As we were notified from the IOCP of one and exactly one accept, + the former socked we created has now either been destroy or assigned + to the new connection. We need to create a new one for the next + connection. */ + start_accept(sp); +} + +static int add_socket_to_server(grpc_tcp_server *s, SOCKET sock, + const struct sockaddr *addr, int addr_len) { + server_port *sp; + int port; + int status; + GUID guid = WSAID_ACCEPTEX; + DWORD ioctl_num_bytes; + LPFN_ACCEPTEX AcceptEx; + + if (sock == INVALID_SOCKET) return -1; + + /* We need to grab the AcceptEx pointer for that port, as it may be + interface-dependent. We'll cache it to avoid doing that again. */ + status = + WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid), + &AcceptEx, sizeof(AcceptEx), &ioctl_num_bytes, NULL, NULL); + + if (status != 0) { + char *utf8_message = gpr_format_message(WSAGetLastError()); + gpr_log(GPR_ERROR, "on_connect error: %s", utf8_message); + gpr_free(utf8_message); + closesocket(sock); + return -1; + } + + port = prepare_socket(sock, addr, addr_len); + if (port >= 0) { + gpr_mu_lock(&s->mu); + GPR_ASSERT(!s->cb && "must add ports before starting server"); + /* append it to the list under a lock */ + if (s->nports == s->port_capacity) { + s->port_capacity *= 2; + s->ports = gpr_realloc(s->ports, sizeof(server_port) * s->port_capacity); + } + sp = &s->ports[s->nports++]; + sp->server = s; + sp->socket = grpc_winsocket_create(sock, "listener"); + sp->shutting_down = 0; + sp->AcceptEx = AcceptEx; + sp->new_socket = INVALID_SOCKET; + GPR_ASSERT(sp->socket); + gpr_mu_unlock(&s->mu); + } + + return port; +} + +int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr, + int addr_len) { + int allocated_port = -1; + unsigned i; + SOCKET sock; + struct sockaddr_in6 addr6_v4mapped; + struct sockaddr_in6 wildcard; + struct sockaddr *allocated_addr = NULL; + struct sockaddr_storage sockname_temp; + socklen_t sockname_len; + int port; + + /* Check if this is a wildcard port, and if so, try to keep the port the same + as some previously created listener. */ + if (grpc_sockaddr_get_port(addr) == 0) { + for (i = 0; i < s->nports; i++) { + sockname_len = sizeof(sockname_temp); + if (0 == getsockname(s->ports[i].socket->socket, + (struct sockaddr *)&sockname_temp, &sockname_len)) { + port = grpc_sockaddr_get_port((struct sockaddr *)&sockname_temp); + if (port > 0) { + allocated_addr = malloc(addr_len); + memcpy(allocated_addr, addr, addr_len); + grpc_sockaddr_set_port(allocated_addr, port); + addr = allocated_addr; + break; + } + } + } + } + + if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { + addr = (const struct sockaddr *)&addr6_v4mapped; + addr_len = sizeof(addr6_v4mapped); + } + + /* Treat :: or 0.0.0.0 as a family-agnostic wildcard. */ + if (grpc_sockaddr_is_wildcard(addr, &port)) { + grpc_sockaddr_make_wildcard6(port, &wildcard); + + addr = (struct sockaddr *)&wildcard; + addr_len = sizeof(wildcard); + } + + sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0, + WSA_FLAG_OVERLAPPED); + if (sock == INVALID_SOCKET) { + char *utf8_message = gpr_format_message(WSAGetLastError()); + gpr_log(GPR_ERROR, "unable to create socket: %s", utf8_message); + gpr_free(utf8_message); + } + + allocated_port = add_socket_to_server(s, sock, addr, addr_len); + gpr_free(allocated_addr); + + return allocated_port; +} + +SOCKET grpc_tcp_server_get_socket(grpc_tcp_server *s, unsigned index) { + return (index < s->nports) ? s->ports[index].socket->socket : INVALID_SOCKET; +} + +void grpc_tcp_server_start(grpc_tcp_server *s, grpc_pollset **pollset, + size_t pollset_count, grpc_tcp_server_cb cb, + void *cb_arg) { + size_t i; + GPR_ASSERT(cb); + gpr_mu_lock(&s->mu); + GPR_ASSERT(!s->cb); + GPR_ASSERT(s->active_ports == 0); + s->cb = cb; + s->cb_arg = cb_arg; + for (i = 0; i < s->nports; i++) { + start_accept(s->ports + i); + s->active_ports++; + } + gpr_mu_unlock(&s->mu); +} + +#endif /* GPR_WINSOCK_SOCKET */ diff --git a/src/core/iomgr/tcp_windows.c b/src/core/iomgr/tcp_windows.c new file mode 100644 index 00000000..725c18e6 --- /dev/null +++ b/src/core/iomgr/tcp_windows.c @@ -0,0 +1,402 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_WINSOCK_SOCKET + +#include "src/core/iomgr/sockaddr_win32.h" + +#include +#include +#include +#include +#include +#include + +#include "src/core/iomgr/alarm.h" +#include "src/core/iomgr/iocp_windows.h" +#include "src/core/iomgr/sockaddr.h" +#include "src/core/iomgr/sockaddr_utils.h" +#include "src/core/iomgr/socket_windows.h" +#include "src/core/iomgr/tcp_client.h" + +static int set_non_block(SOCKET sock) { + int status; + unsigned long param = 1; + DWORD ret; + status = + WSAIoctl(sock, FIONBIO, ¶m, sizeof(param), NULL, 0, &ret, NULL, NULL); + return status == 0; +} + +static int set_dualstack(SOCKET sock) { + int status; + unsigned long param = 0; + status = setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)¶m, + sizeof(param)); + return status == 0; +} + +int grpc_tcp_prepare_socket(SOCKET sock) { + if (!set_non_block(sock)) return 0; + if (!set_dualstack(sock)) return 0; + return 1; +} + +typedef struct grpc_tcp { + /* This is our C++ class derivation emulation. */ + grpc_endpoint base; + /* The one socket this endpoint is using. */ + grpc_winsocket *socket; + /* Refcounting how many operations are in progress. */ + gpr_refcount refcount; + + grpc_iomgr_closure *read_cb; + grpc_iomgr_closure *write_cb; + gpr_slice read_slice; + gpr_slice_buffer *write_slices; + gpr_slice_buffer *read_slices; + + /* The IO Completion Port runs from another thread. We need some mechanism + to protect ourselves when requesting a shutdown. */ + gpr_mu mu; + int shutting_down; + + char *peer_string; +} grpc_tcp; + +static void tcp_free(grpc_tcp *tcp) { + grpc_winsocket_destroy(tcp->socket); + gpr_mu_destroy(&tcp->mu); + gpr_free(tcp->peer_string); + gpr_free(tcp); +} + +/*#define GRPC_TCP_REFCOUNT_DEBUG*/ +#ifdef GRPC_TCP_REFCOUNT_DEBUG +#define TCP_UNREF(tcp, reason) tcp_unref((tcp), (reason), __FILE__, __LINE__) +#define TCP_REF(tcp, reason) tcp_ref((tcp), (reason), __FILE__, __LINE__) +static void tcp_unref(grpc_tcp *tcp, const char *reason, const char *file, + int line) { + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "TCP unref %p : %s %d -> %d", tcp, + reason, tcp->refcount.count, tcp->refcount.count - 1); + if (gpr_unref(&tcp->refcount)) { + tcp_free(tcp); + } +} + +static void tcp_ref(grpc_tcp *tcp, const char *reason, const char *file, + int line) { + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "TCP ref %p : %s %d -> %d", tcp, + reason, tcp->refcount.count, tcp->refcount.count + 1); + gpr_ref(&tcp->refcount); +} +#else +#define TCP_UNREF(tcp, reason) tcp_unref((tcp)) +#define TCP_REF(tcp, reason) tcp_ref((tcp)) +static void tcp_unref(grpc_tcp *tcp) { + if (gpr_unref(&tcp->refcount)) { + tcp_free(tcp); + } +} + +static void tcp_ref(grpc_tcp *tcp) { gpr_ref(&tcp->refcount); } +#endif + +/* Asynchronous callback from the IOCP, or the background thread. */ +static int on_read(grpc_tcp *tcp, int success) { + grpc_winsocket *socket = tcp->socket; + gpr_slice sub; + gpr_slice *slice = NULL; + size_t nslices = 0; + grpc_winsocket_callback_info *info = &socket->read_info; + int do_abort = 0; + + if (success) { + if (socket->read_info.wsa_error != 0 && !tcp->shutting_down) { + if (socket->read_info.wsa_error != WSAECONNRESET) { + char *utf8_message = gpr_format_message(info->wsa_error); + gpr_log(GPR_ERROR, "ReadFile overlapped error: %s", utf8_message); + gpr_free(utf8_message); + } + success = 0; + gpr_slice_unref(tcp->read_slice); + } else { + if (info->bytes_transfered != 0 && !tcp->shutting_down) { + sub = gpr_slice_sub_no_ref(tcp->read_slice, 0, info->bytes_transfered); + gpr_slice_buffer_add(tcp->read_slices, sub); + success = 1; + } else { + gpr_slice_unref(tcp->read_slice); + success = 0; + } + } + } + + return success; +} + +static void on_read_cb(void *tcpp, int from_iocp) { + grpc_tcp *tcp = tcpp; + grpc_iomgr_closure *cb = tcp->read_cb; + int success = on_read(tcp, from_iocp); + tcp->read_cb = NULL; + TCP_UNREF(tcp, "read"); + if (cb) { + cb->cb(cb->cb_arg, success); + } +} + +static grpc_endpoint_op_status win_read(grpc_endpoint *ep, + gpr_slice_buffer *read_slices, + grpc_iomgr_closure *cb) { + grpc_tcp *tcp = (grpc_tcp *)ep; + grpc_winsocket *handle = tcp->socket; + grpc_winsocket_callback_info *info = &handle->read_info; + int status; + DWORD bytes_read = 0; + DWORD flags = 0; + WSABUF buffer; + + if (tcp->shutting_down) { + return GRPC_ENDPOINT_ERROR; + } + + tcp->read_cb = cb; + tcp->read_slices = read_slices; + gpr_slice_buffer_reset_and_unref(read_slices); + + tcp->read_slice = gpr_slice_malloc(8192); + + buffer.len = GPR_SLICE_LENGTH(tcp->read_slice); + buffer.buf = (char *)GPR_SLICE_START_PTR(tcp->read_slice); + + /* First let's try a synchronous, non-blocking read. */ + status = + WSARecv(tcp->socket->socket, &buffer, 1, &bytes_read, &flags, NULL, NULL); + info->wsa_error = status == 0 ? 0 : WSAGetLastError(); + + /* Did we get data immediately ? Yay. */ + if (info->wsa_error != WSAEWOULDBLOCK) { + int ok; + info->bytes_transfered = bytes_read; + ok = on_read(tcp, 1); + return ok ? GRPC_ENDPOINT_DONE : GRPC_ENDPOINT_ERROR; + } + + TCP_REF(tcp, "read"); + + /* Otherwise, let's retry, by queuing a read. */ + memset(&tcp->socket->read_info.overlapped, 0, sizeof(OVERLAPPED)); + status = WSARecv(tcp->socket->socket, &buffer, 1, &bytes_read, &flags, + &info->overlapped, NULL); + + if (status != 0) { + int wsa_error = WSAGetLastError(); + if (wsa_error != WSA_IO_PENDING) { + int ok; + info->wsa_error = wsa_error; + ok = on_read(tcp, 1); + return ok ? GRPC_ENDPOINT_DONE : GRPC_ENDPOINT_ERROR; + } + } + + grpc_socket_notify_on_read(tcp->socket, on_read_cb, tcp); + return GRPC_ENDPOINT_PENDING; +} + +/* Asynchronous callback from the IOCP, or the background thread. */ +static void on_write(void *tcpp, int success) { + grpc_tcp *tcp = (grpc_tcp *)tcpp; + grpc_winsocket *handle = tcp->socket; + grpc_winsocket_callback_info *info = &handle->write_info; + grpc_iomgr_closure *cb; + int do_abort = 0; + + gpr_mu_lock(&tcp->mu); + cb = tcp->write_cb; + tcp->write_cb = NULL; + gpr_mu_unlock(&tcp->mu); + + if (success) { + if (info->wsa_error != 0) { + if (info->wsa_error != WSAECONNRESET) { + char *utf8_message = gpr_format_message(info->wsa_error); + gpr_log(GPR_ERROR, "WSASend overlapped error: %s", utf8_message); + gpr_free(utf8_message); + } + success = 0; + } else { + GPR_ASSERT(info->bytes_transfered == tcp->write_slices->length); + } + } + + TCP_UNREF(tcp, "write"); + cb->cb(cb->cb_arg, success); +} + +/* Initiates a write. */ +static grpc_endpoint_op_status win_write(grpc_endpoint *ep, + gpr_slice_buffer *slices, + grpc_iomgr_closure *cb) { + grpc_tcp *tcp = (grpc_tcp *)ep; + grpc_winsocket *socket = tcp->socket; + grpc_winsocket_callback_info *info = &socket->write_info; + unsigned i; + DWORD bytes_sent; + int status; + WSABUF local_buffers[16]; + WSABUF *allocated = NULL; + WSABUF *buffers = local_buffers; + + if (tcp->shutting_down) { + return GRPC_ENDPOINT_ERROR; + } + + tcp->write_cb = cb; + tcp->write_slices = slices; + + if (tcp->write_slices->count > GPR_ARRAY_SIZE(local_buffers)) { + buffers = (WSABUF *)gpr_malloc(sizeof(WSABUF) * tcp->write_slices->count); + allocated = buffers; + } + + for (i = 0; i < tcp->write_slices->count; i++) { + buffers[i].len = GPR_SLICE_LENGTH(tcp->write_slices->slices[i]); + buffers[i].buf = (char *)GPR_SLICE_START_PTR(tcp->write_slices->slices[i]); + } + + /* First, let's try a synchronous, non-blocking write. */ + status = WSASend(socket->socket, buffers, tcp->write_slices->count, + &bytes_sent, 0, NULL, NULL); + info->wsa_error = status == 0 ? 0 : WSAGetLastError(); + + /* We would kind of expect to get a WSAEWOULDBLOCK here, especially on a busy + connection that has its send queue filled up. But if we don't, then we can + avoid doing an async write operation at all. */ + if (info->wsa_error != WSAEWOULDBLOCK) { + grpc_endpoint_op_status ret = GRPC_ENDPOINT_ERROR; + if (status == 0) { + ret = GRPC_ENDPOINT_DONE; + GPR_ASSERT(bytes_sent == tcp->write_slices->length); + } else { + if (socket->read_info.wsa_error != WSAECONNRESET) { + char *utf8_message = gpr_format_message(info->wsa_error); + gpr_log(GPR_ERROR, "WSASend error: %s", utf8_message); + gpr_free(utf8_message); + } + } + if (allocated) gpr_free(allocated); + return ret; + } + + TCP_REF(tcp, "write"); + + /* If we got a WSAEWOULDBLOCK earlier, then we need to re-do the same + operation, this time asynchronously. */ + memset(&socket->write_info.overlapped, 0, sizeof(OVERLAPPED)); + status = WSASend(socket->socket, buffers, tcp->write_slices->count, + &bytes_sent, 0, &socket->write_info.overlapped, NULL); + if (allocated) gpr_free(allocated); + + if (status != 0) { + int wsa_error = WSAGetLastError(); + if (wsa_error != WSA_IO_PENDING) { + TCP_UNREF(tcp, "write"); + return GRPC_ENDPOINT_ERROR; + } + } + + /* As all is now setup, we can now ask for the IOCP notification. It may + trigger the callback immediately however, but no matter. */ + grpc_socket_notify_on_write(socket, on_write, tcp); + return GRPC_ENDPOINT_PENDING; +} + +static void win_add_to_pollset(grpc_endpoint *ep, grpc_pollset *ps) { + grpc_tcp *tcp; + (void)ps; + tcp = (grpc_tcp *)ep; + grpc_iocp_add_socket(tcp->socket); +} + +static void win_add_to_pollset_set(grpc_endpoint *ep, grpc_pollset_set *pss) { + grpc_tcp *tcp; + (void)pss; + tcp = (grpc_tcp *)ep; + grpc_iocp_add_socket(tcp->socket); +} + +/* Initiates a shutdown of the TCP endpoint. This will queue abort callbacks + for the potential read and write operations. It is up to the caller to + guarantee this isn't called in parallel to a read or write request, so + we're not going to protect against these. However the IO Completion Port + callback will happen from another thread, so we need to protect against + concurrent access of the data structure in that regard. */ +static void win_shutdown(grpc_endpoint *ep) { + grpc_tcp *tcp = (grpc_tcp *)ep; + gpr_mu_lock(&tcp->mu); + /* At that point, what may happen is that we're already inside the IOCP + callback. See the comments in on_read and on_write. */ + tcp->shutting_down = 1; + grpc_winsocket_shutdown(tcp->socket); + gpr_mu_unlock(&tcp->mu); +} + +static void win_destroy(grpc_endpoint *ep) { + grpc_tcp *tcp = (grpc_tcp *)ep; + TCP_UNREF(tcp, "destroy"); +} + +static char *win_get_peer(grpc_endpoint *ep) { + grpc_tcp *tcp = (grpc_tcp *)ep; + return gpr_strdup(tcp->peer_string); +} + +static grpc_endpoint_vtable vtable = { + win_read, win_write, win_add_to_pollset, win_add_to_pollset_set, + win_shutdown, win_destroy, win_get_peer}; + +grpc_endpoint *grpc_tcp_create(grpc_winsocket *socket, char *peer_string) { + grpc_tcp *tcp = (grpc_tcp *)gpr_malloc(sizeof(grpc_tcp)); + memset(tcp, 0, sizeof(grpc_tcp)); + tcp->base.vtable = &vtable; + tcp->socket = socket; + gpr_mu_init(&tcp->mu); + gpr_ref_init(&tcp->refcount, 1); + tcp->peer_string = gpr_strdup(peer_string); + return &tcp->base; +} + +#endif /* GPR_WINSOCK_SOCKET */ diff --git a/src/core/iomgr/tcp_windows.h b/src/core/iomgr/tcp_windows.h new file mode 100644 index 00000000..deb3e482 --- /dev/null +++ b/src/core/iomgr/tcp_windows.h @@ -0,0 +1,57 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_IOMGR_TCP_WINDOWS_H +#define GRPC_INTERNAL_CORE_IOMGR_TCP_WINDOWS_H +/* + Low level TCP "bottom half" implementation, for use by transports built on + top of a TCP connection. + + Note that this file does not (yet) include APIs for creating the socket in + the first place. + + All calls passing slice transfer ownership of a slice refcount unless + otherwise specified. +*/ + +#include "src/core/iomgr/endpoint.h" +#include "src/core/iomgr/socket_windows.h" + +/* Create a tcp endpoint given a winsock handle. + * Takes ownership of the handle. + */ +grpc_endpoint *grpc_tcp_create(grpc_winsocket *socket, char *peer_string); + +int grpc_tcp_prepare_socket(SOCKET sock); + +#endif /* GRPC_INTERNAL_CORE_IOMGR_TCP_WINDOWS_H */ diff --git a/src/core/iomgr/time_averaged_stats.c b/src/core/iomgr/time_averaged_stats.c new file mode 100644 index 00000000..f881dde9 --- /dev/null +++ b/src/core/iomgr/time_averaged_stats.c @@ -0,0 +1,77 @@ +/* + * + * Copyright 2015, 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 "src/core/iomgr/time_averaged_stats.h" + +void grpc_time_averaged_stats_init(grpc_time_averaged_stats *stats, + double init_avg, double regress_weight, + double persistence_factor) { + stats->init_avg = init_avg; + stats->regress_weight = regress_weight; + stats->persistence_factor = persistence_factor; + stats->batch_total_value = 0; + stats->batch_num_samples = 0; + stats->aggregate_total_weight = 0; + stats->aggregate_weighted_avg = init_avg; +} + +void grpc_time_averaged_stats_add_sample(grpc_time_averaged_stats *stats, + double value) { + stats->batch_total_value += value; + ++stats->batch_num_samples; +} + +double grpc_time_averaged_stats_update_average( + grpc_time_averaged_stats *stats) { + /* Start with the current batch: */ + double weighted_sum = stats->batch_total_value; + double total_weight = stats->batch_num_samples; + if (stats->regress_weight > 0) { + /* Add in the regression towards init_avg_: */ + weighted_sum += stats->regress_weight * stats->init_avg; + total_weight += stats->regress_weight; + } + if (stats->persistence_factor > 0) { + /* Add in the persistence: */ + const double prev_sample_weight = + stats->persistence_factor * stats->aggregate_total_weight; + weighted_sum += prev_sample_weight * stats->aggregate_weighted_avg; + total_weight += prev_sample_weight; + } + stats->aggregate_weighted_avg = + (total_weight > 0) ? (weighted_sum / total_weight) : stats->init_avg; + stats->aggregate_total_weight = total_weight; + stats->batch_num_samples = 0; + stats->batch_total_value = 0; + return stats->aggregate_weighted_avg; +} diff --git a/src/core/iomgr/time_averaged_stats.h b/src/core/iomgr/time_averaged_stats.h new file mode 100644 index 00000000..e6dec1b4 --- /dev/null +++ b/src/core/iomgr/time_averaged_stats.h @@ -0,0 +1,88 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_IOMGR_TIME_AVERAGED_STATS_H +#define GRPC_INTERNAL_CORE_IOMGR_TIME_AVERAGED_STATS_H + +/* This tracks a time-decaying weighted average. It works by collecting + batches of samples and then mixing their average into a time-decaying + weighted mean. It is designed for batch operations where we do many adds + before updating the average. */ + +typedef struct { + /* The initial average value. This is the reported average until the first + grpc_time_averaged_stats_update_average call. If a positive regress_weight + is used, we also regress towards this value on each update. */ + double init_avg; + /* The sample weight of "init_avg" that is mixed in with each call to + grpc_time_averaged_stats_update_average. If the calls to + grpc_time_averaged_stats_add_sample stop, this will cause the average to + regress back to the mean. This should be non-negative. Set it to 0 to + disable the bias. A value of 1 has the effect of adding in 1 bonus sample + with value init_avg to each sample period. */ + double regress_weight; + /* This determines the rate of decay of the time-averaging from one period + to the next by scaling the aggregate_total_weight of samples from prior + periods when combining with the latest period. It should be in the range + [0,1]. A higher value adapts more slowly. With a value of 0.5, if the + batches each have k samples, the samples_in_avg_ will grow to 2 k, so the + weighting of the time average will eventually be 1/3 new batch and 2/3 + old average. */ + double persistence_factor; + + /* The total value of samples since the last UpdateAverage(). */ + double batch_total_value; + /* The number of samples since the last UpdateAverage(). */ + double batch_num_samples; + /* The time-decayed sum of batch_num_samples_ over previous batches. This is + the "weight" of the old aggregate_weighted_avg_ when updating the + average. */ + double aggregate_total_weight; + /* A time-decayed average of the (batch_total_value_ / batch_num_samples_), + computed by decaying the samples_in_avg_ weight in the weighted average. */ + double aggregate_weighted_avg; +} grpc_time_averaged_stats; + +/* See the comments on the members above for an explanation of init_avg, + regress_weight, and persistence_factor. */ +void grpc_time_averaged_stats_init(grpc_time_averaged_stats *stats, + double init_avg, double regress_weight, + double persistence_factor); +/* Add a sample to the current batch. */ +void grpc_time_averaged_stats_add_sample(grpc_time_averaged_stats *stats, + double value); +/* Complete a batch and compute the new estimate of the average sample + value. */ +double grpc_time_averaged_stats_update_average(grpc_time_averaged_stats *stats); + +#endif /* GRPC_INTERNAL_CORE_IOMGR_TIME_AVERAGED_STATS_H */ diff --git a/src/core/iomgr/udp_server.c b/src/core/iomgr/udp_server.c new file mode 100644 index 00000000..6429c38b --- /dev/null +++ b/src/core/iomgr/udp_server.c @@ -0,0 +1,440 @@ +/* + * + * Copyright 2015, 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. + * + */ + +/* FIXME: "posix" files shouldn't be depending on _GNU_SOURCE */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include + +#ifdef GPR_POSIX_SOCKET + +#include "src/core/iomgr/udp_server.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "src/core/iomgr/fd_posix.h" +#include "src/core/iomgr/pollset_posix.h" +#include "src/core/iomgr/resolve_address.h" +#include "src/core/iomgr/sockaddr_utils.h" +#include "src/core/iomgr/socket_utils_posix.h" +#include "src/core/support/string.h" +#include +#include +#include +#include +#include + +#define INIT_PORT_CAP 2 + +/* one listening port */ +typedef struct { + int fd; + grpc_fd *emfd; + grpc_udp_server *server; + union { + gpr_uint8 untyped[GRPC_MAX_SOCKADDR_SIZE]; + struct sockaddr sockaddr; + struct sockaddr_un un; + } addr; + int addr_len; + grpc_iomgr_closure read_closure; + grpc_iomgr_closure destroyed_closure; + grpc_udp_server_read_cb read_cb; +} server_port; + +static void unlink_if_unix_domain_socket(const struct sockaddr_un *un) { + struct stat st; + + if (stat(un->sun_path, &st) == 0 && (st.st_mode & S_IFMT) == S_IFSOCK) { + unlink(un->sun_path); + } +} + +/* the overall server */ +struct grpc_udp_server { + grpc_udp_server_cb cb; + void *cb_arg; + + gpr_mu mu; + gpr_cv cv; + + /* active port count: how many ports are actually still listening */ + size_t active_ports; + /* destroyed port count: how many ports are completely destroyed */ + size_t destroyed_ports; + + /* is this server shutting down? (boolean) */ + int shutdown; + + /* all listening ports */ + server_port *ports; + size_t nports; + size_t port_capacity; + + /* shutdown callback */ + void (*shutdown_complete)(void *); + void *shutdown_complete_arg; + + /* all pollsets interested in new connections */ + grpc_pollset **pollsets; + /* number of pollsets in the pollsets array */ + size_t pollset_count; +}; + +grpc_udp_server *grpc_udp_server_create(void) { + grpc_udp_server *s = gpr_malloc(sizeof(grpc_udp_server)); + gpr_mu_init(&s->mu); + gpr_cv_init(&s->cv); + s->active_ports = 0; + s->destroyed_ports = 0; + s->shutdown = 0; + s->cb = NULL; + s->cb_arg = NULL; + s->ports = gpr_malloc(sizeof(server_port) * INIT_PORT_CAP); + s->nports = 0; + s->port_capacity = INIT_PORT_CAP; + + return s; +} + +static void finish_shutdown(grpc_udp_server *s) { + s->shutdown_complete(s->shutdown_complete_arg); + + gpr_mu_destroy(&s->mu); + gpr_cv_destroy(&s->cv); + + gpr_free(s->ports); + gpr_free(s); +} + +static void destroyed_port(void *server, int success) { + grpc_udp_server *s = server; + gpr_mu_lock(&s->mu); + s->destroyed_ports++; + if (s->destroyed_ports == s->nports) { + gpr_mu_unlock(&s->mu); + finish_shutdown(s); + } else { + gpr_mu_unlock(&s->mu); + } +} + +static void dont_care_about_shutdown_completion(void *ignored) {} + +/* called when all listening endpoints have been shutdown, so no further + events will be received on them - at this point it's safe to destroy + things */ +static void deactivated_all_ports(grpc_udp_server *s) { + size_t i; + + /* delete ALL the things */ + gpr_mu_lock(&s->mu); + + if (!s->shutdown) { + gpr_mu_unlock(&s->mu); + return; + } + + if (s->nports) { + for (i = 0; i < s->nports; i++) { + server_port *sp = &s->ports[i]; + if (sp->addr.sockaddr.sa_family == AF_UNIX) { + unlink_if_unix_domain_socket(&sp->addr.un); + } + sp->destroyed_closure.cb = destroyed_port; + sp->destroyed_closure.cb_arg = s; + grpc_fd_orphan(sp->emfd, &sp->destroyed_closure, "udp_listener_shutdown"); + } + gpr_mu_unlock(&s->mu); + } else { + gpr_mu_unlock(&s->mu); + finish_shutdown(s); + } +} + +void grpc_udp_server_destroy( + grpc_udp_server *s, void (*shutdown_complete)(void *shutdown_complete_arg), + void *shutdown_complete_arg) { + size_t i; + gpr_mu_lock(&s->mu); + + GPR_ASSERT(!s->shutdown); + s->shutdown = 1; + + s->shutdown_complete = shutdown_complete + ? shutdown_complete + : dont_care_about_shutdown_completion; + s->shutdown_complete_arg = shutdown_complete_arg; + + /* shutdown all fd's */ + if (s->active_ports) { + for (i = 0; i < s->nports; i++) { + grpc_fd_shutdown(s->ports[i].emfd); + } + gpr_mu_unlock(&s->mu); + } else { + gpr_mu_unlock(&s->mu); + deactivated_all_ports(s); + } +} + +/* Prepare a recently-created socket for listening. */ +static int prepare_socket(int fd, const struct sockaddr *addr, int addr_len) { + struct sockaddr_storage sockname_temp; + socklen_t sockname_len; + int get_local_ip; + int rc; + + if (fd < 0) { + goto error; + } + + get_local_ip = 1; + rc = setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &get_local_ip, + sizeof(get_local_ip)); + if (rc == 0 && addr->sa_family == AF_INET6) { +#if !TARGET_OS_IPHONE + rc = setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &get_local_ip, + sizeof(get_local_ip)); +#endif + } + + if (bind(fd, addr, addr_len) < 0) { + char *addr_str; + grpc_sockaddr_to_string(&addr_str, addr, 0); + gpr_log(GPR_ERROR, "bind addr=%s: %s", addr_str, strerror(errno)); + gpr_free(addr_str); + goto error; + } + + sockname_len = sizeof(sockname_temp); + if (getsockname(fd, (struct sockaddr *)&sockname_temp, &sockname_len) < 0) { + goto error; + } + + return grpc_sockaddr_get_port((struct sockaddr *)&sockname_temp); + +error: + if (fd >= 0) { + close(fd); + } + return -1; +} + +/* event manager callback when reads are ready */ +static void on_read(void *arg, int success) { + server_port *sp = arg; + + if (success == 0) { + gpr_mu_lock(&sp->server->mu); + if (0 == --sp->server->active_ports) { + gpr_mu_unlock(&sp->server->mu); + deactivated_all_ports(sp->server); + } else { + gpr_mu_unlock(&sp->server->mu); + } + return; + } + + /* Tell the registered callback that data is available to read. */ + GPR_ASSERT(sp->read_cb); + sp->read_cb(sp->fd, sp->server->cb, sp->server->cb_arg); + + /* Re-arm the notification event so we get another chance to read. */ + grpc_fd_notify_on_read(sp->emfd, &sp->read_closure); +} + +static int add_socket_to_server(grpc_udp_server *s, int fd, + const struct sockaddr *addr, int addr_len, + grpc_udp_server_read_cb read_cb) { + server_port *sp; + int port; + char *addr_str; + char *name; + + port = prepare_socket(fd, addr, addr_len); + if (port >= 0) { + grpc_sockaddr_to_string(&addr_str, (struct sockaddr *)&addr, 1); + gpr_asprintf(&name, "udp-server-listener:%s", addr_str); + gpr_mu_lock(&s->mu); + GPR_ASSERT(!s->cb && "must add ports before starting server"); + /* append it to the list under a lock */ + if (s->nports == s->port_capacity) { + s->port_capacity *= 2; + s->ports = gpr_realloc(s->ports, sizeof(server_port) * s->port_capacity); + } + sp = &s->ports[s->nports++]; + sp->server = s; + sp->fd = fd; + sp->emfd = grpc_fd_create(fd, name); + memcpy(sp->addr.untyped, addr, addr_len); + sp->addr_len = addr_len; + sp->read_cb = read_cb; + GPR_ASSERT(sp->emfd); + gpr_mu_unlock(&s->mu); + } + + return port; +} + +int grpc_udp_server_add_port(grpc_udp_server *s, const void *addr, int addr_len, + grpc_udp_server_read_cb read_cb) { + int allocated_port1 = -1; + int allocated_port2 = -1; + unsigned i; + int fd; + grpc_dualstack_mode dsmode; + struct sockaddr_in6 addr6_v4mapped; + struct sockaddr_in wild4; + struct sockaddr_in6 wild6; + struct sockaddr_in addr4_copy; + struct sockaddr *allocated_addr = NULL; + struct sockaddr_storage sockname_temp; + socklen_t sockname_len; + int port; + + if (((struct sockaddr *)addr)->sa_family == AF_UNIX) { + unlink_if_unix_domain_socket(addr); + } + + /* Check if this is a wildcard port, and if so, try to keep the port the same + as some previously created listener. */ + if (grpc_sockaddr_get_port(addr) == 0) { + for (i = 0; i < s->nports; i++) { + sockname_len = sizeof(sockname_temp); + if (0 == getsockname(s->ports[i].fd, (struct sockaddr *)&sockname_temp, + &sockname_len)) { + port = grpc_sockaddr_get_port((struct sockaddr *)&sockname_temp); + if (port > 0) { + allocated_addr = malloc(addr_len); + memcpy(allocated_addr, addr, addr_len); + grpc_sockaddr_set_port(allocated_addr, port); + addr = allocated_addr; + break; + } + } + } + } + + if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { + addr = (const struct sockaddr *)&addr6_v4mapped; + addr_len = sizeof(addr6_v4mapped); + } + + /* Treat :: or 0.0.0.0 as a family-agnostic wildcard. */ + if (grpc_sockaddr_is_wildcard(addr, &port)) { + grpc_sockaddr_make_wildcards(port, &wild4, &wild6); + + /* Try listening on IPv6 first. */ + addr = (struct sockaddr *)&wild6; + addr_len = sizeof(wild6); + fd = grpc_create_dualstack_socket(addr, SOCK_DGRAM, IPPROTO_UDP, &dsmode); + allocated_port1 = add_socket_to_server(s, fd, addr, addr_len, read_cb); + if (fd >= 0 && dsmode == GRPC_DSMODE_DUALSTACK) { + goto done; + } + + /* If we didn't get a dualstack socket, also listen on 0.0.0.0. */ + if (port == 0 && allocated_port1 > 0) { + grpc_sockaddr_set_port((struct sockaddr *)&wild4, allocated_port1); + } + addr = (struct sockaddr *)&wild4; + addr_len = sizeof(wild4); + } + + fd = grpc_create_dualstack_socket(addr, SOCK_DGRAM, IPPROTO_UDP, &dsmode); + if (fd < 0) { + gpr_log(GPR_ERROR, "Unable to create socket: %s", strerror(errno)); + } + if (dsmode == GRPC_DSMODE_IPV4 && + grpc_sockaddr_is_v4mapped(addr, &addr4_copy)) { + addr = (struct sockaddr *)&addr4_copy; + addr_len = sizeof(addr4_copy); + } + allocated_port2 = add_socket_to_server(s, fd, addr, addr_len, read_cb); + +done: + gpr_free(allocated_addr); + return allocated_port1 >= 0 ? allocated_port1 : allocated_port2; +} + +int grpc_udp_server_get_fd(grpc_udp_server *s, unsigned index) { + return (index < s->nports) ? s->ports[index].fd : -1; +} + +void grpc_udp_server_start(grpc_udp_server *s, grpc_pollset **pollsets, + size_t pollset_count, + grpc_udp_server_cb new_transport_cb, void *cb_arg) { + size_t i, j; + GPR_ASSERT(new_transport_cb); + gpr_mu_lock(&s->mu); + GPR_ASSERT(!s->cb); + GPR_ASSERT(s->active_ports == 0); + s->cb = new_transport_cb; + s->cb_arg = cb_arg; + s->pollsets = pollsets; + for (i = 0; i < s->nports; i++) { + for (j = 0; j < pollset_count; j++) { + grpc_pollset_add_fd(pollsets[j], s->ports[i].emfd); + } + s->ports[i].read_closure.cb = on_read; + s->ports[i].read_closure.cb_arg = &s->ports[i]; + grpc_fd_notify_on_read(s->ports[i].emfd, &s->ports[i].read_closure); + s->active_ports++; + } + gpr_mu_unlock(&s->mu); +} + +/* TODO(rjshade): Add a test for this method. */ +void grpc_udp_server_write(server_port *sp, const char *buffer, size_t buf_len, + const struct sockaddr *peer_address) { + int rc; + rc = sendto(sp->fd, buffer, buf_len, 0, peer_address, sizeof(peer_address)); + if (rc < 0) { + gpr_log(GPR_ERROR, "Unable to send data: %s", strerror(errno)); + } +} + +#endif diff --git a/src/core/iomgr/udp_server.h b/src/core/iomgr/udp_server.h new file mode 100644 index 00000000..fcc4ba6e --- /dev/null +++ b/src/core/iomgr/udp_server.h @@ -0,0 +1,85 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_IOMGR_UDP_SERVER_H +#define GRPC_INTERNAL_CORE_IOMGR_UDP_SERVER_H + +#include "src/core/iomgr/endpoint.h" + +/* Forward decl of grpc_udp_server */ +typedef struct grpc_udp_server grpc_udp_server; + +/* New server callback: ep is the newly connected connection */ +typedef void (*grpc_udp_server_cb)(void *arg, grpc_endpoint *ep); + +/* Called when data is available to read from the socket. */ +typedef void (*grpc_udp_server_read_cb)(int fd, + grpc_udp_server_cb new_transport_cb, + void *cb_arg); + +/* Create a server, initially not bound to any ports */ +grpc_udp_server *grpc_udp_server_create(void); + +/* Start listening to bound ports */ +void grpc_udp_server_start(grpc_udp_server *server, grpc_pollset **pollsets, + size_t pollset_count, grpc_udp_server_cb cb, + void *cb_arg); + +int grpc_udp_server_get_fd(grpc_udp_server *s, unsigned index); + +/* Add a port to the server, returning port number on success, or negative + on failure. + + The :: and 0.0.0.0 wildcard addresses are treated identically, accepting + both IPv4 and IPv6 connections, but :: is the preferred style. This usually + creates one socket, but possibly two on systems which support IPv6, + but not dualstack sockets. */ + +/* TODO(ctiller): deprecate this, and make grpc_udp_server_add_ports to handle + all of the multiple socket port matching logic in one place */ +int grpc_udp_server_add_port(grpc_udp_server *s, const void *addr, int addr_len, + grpc_udp_server_read_cb read_cb); + +void grpc_udp_server_destroy(grpc_udp_server *server, + void (*shutdown_done)(void *shutdown_done_arg), + void *shutdown_done_arg); + +/* Write the contents of buffer to the underlying UDP socket. */ +/* +void grpc_udp_server_write(grpc_udp_server *s, + const char *buffer, + int buf_len, + const struct sockaddr* to); + */ + +#endif /* GRPC_INTERNAL_CORE_IOMGR_UDP_SERVER_H */ diff --git a/src/core/iomgr/wakeup_fd_eventfd.c b/src/core/iomgr/wakeup_fd_eventfd.c new file mode 100644 index 00000000..08fdc74f --- /dev/null +++ b/src/core/iomgr/wakeup_fd_eventfd.c @@ -0,0 +1,81 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_LINUX_EVENTFD + +#include +#include +#include + +#include "src/core/iomgr/wakeup_fd_posix.h" +#include + +static void eventfd_create(grpc_wakeup_fd *fd_info) { + int efd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); + /* TODO(klempner): Handle failure more gracefully */ + GPR_ASSERT(efd >= 0); + fd_info->read_fd = efd; + fd_info->write_fd = -1; +} + +static void eventfd_consume(grpc_wakeup_fd *fd_info) { + eventfd_t value; + int err; + do { + err = eventfd_read(fd_info->read_fd, &value); + } while (err < 0 && errno == EINTR); +} + +static void eventfd_wakeup(grpc_wakeup_fd *fd_info) { + int err; + do { + err = eventfd_write(fd_info->read_fd, 1); + } while (err < 0 && errno == EINTR); +} + +static void eventfd_destroy(grpc_wakeup_fd *fd_info) { + close(fd_info->read_fd); +} + +static int eventfd_check_availability(void) { + /* TODO(klempner): Actually check if eventfd is available */ + return 1; +} + +const grpc_wakeup_fd_vtable grpc_specialized_wakeup_fd_vtable = { + eventfd_create, eventfd_consume, eventfd_wakeup, eventfd_destroy, + eventfd_check_availability}; + +#endif /* GPR_LINUX_EVENTFD */ diff --git a/src/core/iomgr/wakeup_fd_nospecial.c b/src/core/iomgr/wakeup_fd_nospecial.c new file mode 100644 index 00000000..78d763c1 --- /dev/null +++ b/src/core/iomgr/wakeup_fd_nospecial.c @@ -0,0 +1,51 @@ +/* + * + * Copyright 2015, 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. + * + */ + +/* + * This is a dummy file to provide an invalid specialized_wakeup_fd_vtable on + * systems without anything better than pipe. + */ + +#include + +#ifdef GPR_POSIX_NO_SPECIAL_WAKEUP_FD + +#include "src/core/iomgr/wakeup_fd_posix.h" +#include + +static int check_availability_invalid(void) { return 0; } + +const grpc_wakeup_fd_vtable grpc_specialized_wakeup_fd_vtable = { + NULL, NULL, NULL, NULL, check_availability_invalid}; + +#endif /* GPR_POSIX_NO_SPECIAL_WAKEUP_FD */ diff --git a/src/core/iomgr/wakeup_fd_pipe.c b/src/core/iomgr/wakeup_fd_pipe.c new file mode 100644 index 00000000..bd643e80 --- /dev/null +++ b/src/core/iomgr/wakeup_fd_pipe.c @@ -0,0 +1,97 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_POSIX_WAKEUP_FD + +#include "src/core/iomgr/wakeup_fd_posix.h" + +#include +#include +#include + +#include "src/core/iomgr/socket_utils_posix.h" +#include + +static void pipe_init(grpc_wakeup_fd *fd_info) { + int pipefd[2]; + /* TODO(klempner): Make this nonfatal */ + GPR_ASSERT(0 == pipe(pipefd)); + GPR_ASSERT(grpc_set_socket_nonblocking(pipefd[0], 1)); + GPR_ASSERT(grpc_set_socket_nonblocking(pipefd[1], 1)); + fd_info->read_fd = pipefd[0]; + fd_info->write_fd = pipefd[1]; +} + +static void pipe_consume(grpc_wakeup_fd *fd_info) { + char buf[128]; + int r; + + for (;;) { + r = read(fd_info->read_fd, buf, sizeof(buf)); + if (r > 0) continue; + if (r == 0) return; + switch (errno) { + case EAGAIN: + return; + case EINTR: + continue; + default: + gpr_log(GPR_ERROR, "error reading pipe: %s", strerror(errno)); + return; + } + } +} + +static void pipe_wakeup(grpc_wakeup_fd *fd_info) { + char c = 0; + while (write(fd_info->write_fd, &c, 1) != 1 && errno == EINTR) + ; +} + +static void pipe_destroy(grpc_wakeup_fd *fd_info) { + close(fd_info->read_fd); + close(fd_info->write_fd); +} + +static int pipe_check_availability(void) { + /* Assume that pipes are always available. */ + return 1; +} + +const grpc_wakeup_fd_vtable grpc_pipe_wakeup_fd_vtable = { + pipe_init, pipe_consume, pipe_wakeup, pipe_destroy, + pipe_check_availability}; + +#endif /* GPR_POSIX_WAKUP_FD */ diff --git a/src/core/iomgr/wakeup_fd_pipe.h b/src/core/iomgr/wakeup_fd_pipe.h new file mode 100644 index 00000000..01a13a97 --- /dev/null +++ b/src/core/iomgr/wakeup_fd_pipe.h @@ -0,0 +1,41 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_IOMGR_WAKEUP_FD_PIPE_H +#define GRPC_INTERNAL_CORE_IOMGR_WAKEUP_FD_PIPE_H + +#include "src/core/iomgr/wakeup_fd_posix.h" + +extern grpc_wakeup_fd_vtable grpc_pipe_wakeup_fd_vtable; + +#endif /* GRPC_INTERNAL_CORE_IOMGR_WAKEUP_FD_PIPE_H */ diff --git a/src/core/iomgr/wakeup_fd_posix.c b/src/core/iomgr/wakeup_fd_posix.c new file mode 100644 index 00000000..d09fb78d --- /dev/null +++ b/src/core/iomgr/wakeup_fd_posix.c @@ -0,0 +1,74 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_POSIX_WAKEUP_FD + +#include "src/core/iomgr/wakeup_fd_posix.h" +#include "src/core/iomgr/wakeup_fd_pipe.h" +#include + +static const grpc_wakeup_fd_vtable *wakeup_fd_vtable = NULL; + +void grpc_wakeup_fd_global_init(void) { + if (grpc_specialized_wakeup_fd_vtable.check_availability()) { + wakeup_fd_vtable = &grpc_specialized_wakeup_fd_vtable; + } else { + wakeup_fd_vtable = &grpc_pipe_wakeup_fd_vtable; + } +} + +void grpc_wakeup_fd_global_init_force_fallback(void) { + wakeup_fd_vtable = &grpc_pipe_wakeup_fd_vtable; +} + +void grpc_wakeup_fd_global_destroy(void) { wakeup_fd_vtable = NULL; } + +void grpc_wakeup_fd_init(grpc_wakeup_fd *fd_info) { + wakeup_fd_vtable->init(fd_info); +} + +void grpc_wakeup_fd_consume_wakeup(grpc_wakeup_fd *fd_info) { + wakeup_fd_vtable->consume(fd_info); +} + +void grpc_wakeup_fd_wakeup(grpc_wakeup_fd *fd_info) { + wakeup_fd_vtable->wakeup(fd_info); +} + +void grpc_wakeup_fd_destroy(grpc_wakeup_fd *fd_info) { + wakeup_fd_vtable->destroy(fd_info); +} + +#endif /* GPR_POSIX_WAKEUP_FD */ diff --git a/src/core/iomgr/wakeup_fd_posix.h b/src/core/iomgr/wakeup_fd_posix.h new file mode 100644 index 00000000..b6c08690 --- /dev/null +++ b/src/core/iomgr/wakeup_fd_posix.h @@ -0,0 +1,99 @@ +/* + * + * Copyright 2015, 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. + * + */ + +/* + * wakeup_fd abstracts the concept of a file descriptor for the purpose of + * waking up a thread in select()/poll()/epoll_wait()/etc. + + * The poll() family of system calls provide a way for a thread to block until + * there is activity on one (or more) of a set of file descriptors. An + * application may wish to wake up this thread to do non file related work. The + * typical way to do this is to add a pipe to the set of file descriptors, then + * write to the pipe to wake up the thread in poll(). + * + * Linux has a lighter weight eventfd specifically designed for this purpose. + * wakeup_fd abstracts the difference between the two. + * + * Setup: + * 1. Before calling anything, call global_init() at least once. + * 1. Call grpc_wakeup_fd_create() to get a wakeup_fd. + * 2. Add the result of GRPC_WAKEUP_FD_FD to the set of monitored file + * descriptors for the poll() style API you are using. Monitor the file + * descriptor for readability. + * 3. To tear down, call grpc_wakeup_fd_destroy(). This closes the underlying + * file descriptor. + * + * Usage: + * 1. To wake up a polling thread, call grpc_wakeup_fd_wakeup() on a wakeup_fd + * it is monitoring. + * 2. If the polling thread was awakened by a wakeup_fd event, call + * grpc_wakeup_fd_consume_wakeup() on it. + */ +#ifndef GRPC_INTERNAL_CORE_IOMGR_WAKEUP_FD_POSIX_H +#define GRPC_INTERNAL_CORE_IOMGR_WAKEUP_FD_POSIX_H + +void grpc_wakeup_fd_global_init(void); +void grpc_wakeup_fd_global_destroy(void); + +/* Force using the fallback implementation. This is intended for testing + * purposes only.*/ +void grpc_wakeup_fd_global_init_force_fallback(void); + +typedef struct grpc_wakeup_fd grpc_wakeup_fd; + +typedef struct grpc_wakeup_fd_vtable { + void (*init)(grpc_wakeup_fd *fd_info); + void (*consume)(grpc_wakeup_fd *fd_info); + void (*wakeup)(grpc_wakeup_fd *fd_info); + void (*destroy)(grpc_wakeup_fd *fd_info); + /* Must be called before calling any other functions */ + int (*check_availability)(void); +} grpc_wakeup_fd_vtable; + +struct grpc_wakeup_fd { + int read_fd; + int write_fd; +}; + +#define GRPC_WAKEUP_FD_GET_READ_FD(fd_info) ((fd_info)->read_fd) + +void grpc_wakeup_fd_init(grpc_wakeup_fd *fd_info); +void grpc_wakeup_fd_consume_wakeup(grpc_wakeup_fd *fd_info); +void grpc_wakeup_fd_wakeup(grpc_wakeup_fd *fd_info); +void grpc_wakeup_fd_destroy(grpc_wakeup_fd *fd_info); + +/* Defined in some specialized implementation's .c file, or by + * wakeup_fd_nospecial.c if no such implementation exists. */ +extern const grpc_wakeup_fd_vtable grpc_specialized_wakeup_fd_vtable; + +#endif /* GRPC_INTERNAL_CORE_IOMGR_WAKEUP_FD_POSIX_H */ diff --git a/src/core/json/json.c b/src/core/json/json.c new file mode 100644 index 00000000..96e11eeb --- /dev/null +++ b/src/core/json/json.c @@ -0,0 +1,64 @@ +/* + * + * Copyright 2015, 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 + +#include + +#include "src/core/json/json.h" + +grpc_json *grpc_json_create(grpc_json_type type) { + grpc_json *json = gpr_malloc(sizeof(*json)); + memset(json, 0, sizeof(*json)); + json->type = type; + + return json; +} + +void grpc_json_destroy(grpc_json *json) { + while (json->child) { + grpc_json_destroy(json->child); + } + + if (json->next) { + json->next->prev = json->prev; + } + + if (json->prev) { + json->prev->next = json->next; + } else if (json->parent) { + json->parent->child = json->next; + } + + gpr_free(json); +} diff --git a/src/core/json/json.h b/src/core/json/json.h new file mode 100644 index 00000000..573584bf --- /dev/null +++ b/src/core/json/json.h @@ -0,0 +1,88 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_JSON_JSON_H +#define GRPC_INTERNAL_CORE_JSON_JSON_H + +#include + +#include "src/core/json/json_common.h" + +/* A tree-like structure to hold json values. The key and value pointers + * are not owned by it. + */ +typedef struct grpc_json { + struct grpc_json* next; + struct grpc_json* prev; + struct grpc_json* child; + struct grpc_json* parent; + + grpc_json_type type; + const char* key; + const char* value; +} grpc_json; + +/* The next two functions are going to parse the input string, and + * modify it in the process, in order to use its space to store + * all of the keys and values for the returned object tree. + * + * They assume UTF-8 input stream, and will output UTF-8 encoded + * strings in the tree. The input stream's UTF-8 isn't validated, + * as in, what you input is what you get as an output. + * + * All the keys and values in the grpc_json objects will be strings + * pointing at your input buffer. + * + * Delete the allocated tree afterward using grpc_json_destroy(). + */ +grpc_json* grpc_json_parse_string_with_len(char* input, size_t size); +grpc_json* grpc_json_parse_string(char* input); + +/* This function will create a new string using gpr_realloc, and will + * deserialize the grpc_json tree into it. It'll be zero-terminated, + * but will be allocated in chunks of 256 bytes. + * + * The indent parameter controls the way the output is formatted. + * If indent is 0, then newlines will be suppressed as well, and the + * output will be condensed at its maximum. + */ +char* grpc_json_dump_to_string(grpc_json* json, int indent); + +/* Use these to create or delete a grpc_json object. + * Deletion is recursive. We will not attempt to free any of the strings + * in any of the objects of that tree. + */ +grpc_json* grpc_json_create(grpc_json_type type); +void grpc_json_destroy(grpc_json* json); + +#endif /* GRPC_INTERNAL_CORE_JSON_JSON_H */ diff --git a/src/core/json/json_common.h b/src/core/json/json_common.h new file mode 100644 index 00000000..481695b3 --- /dev/null +++ b/src/core/json/json_common.h @@ -0,0 +1,49 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_JSON_JSON_COMMON_H +#define GRPC_INTERNAL_CORE_JSON_JSON_COMMON_H + +/* The various json types. */ +typedef enum { + GRPC_JSON_OBJECT, + GRPC_JSON_ARRAY, + GRPC_JSON_STRING, + GRPC_JSON_NUMBER, + GRPC_JSON_TRUE, + GRPC_JSON_FALSE, + GRPC_JSON_NULL, + GRPC_JSON_TOP_LEVEL +} grpc_json_type; + +#endif /* GRPC_INTERNAL_CORE_JSON_JSON_COMMON_H */ diff --git a/src/core/json/json_reader.c b/src/core/json/json_reader.c new file mode 100644 index 00000000..c22d4edd --- /dev/null +++ b/src/core/json/json_reader.c @@ -0,0 +1,659 @@ +/* + * + * Copyright 2015, 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 + +#include + +#include "src/core/json/json_reader.h" + +static void json_reader_string_clear(grpc_json_reader* reader) { + reader->vtable->string_clear(reader->userdata); +} + +static void json_reader_string_add_char(grpc_json_reader* reader, + gpr_uint32 c) { + reader->vtable->string_add_char(reader->userdata, c); +} + +static void json_reader_string_add_utf32(grpc_json_reader* reader, + gpr_uint32 utf32) { + reader->vtable->string_add_utf32(reader->userdata, utf32); +} + +static gpr_uint32 grpc_json_reader_read_char(grpc_json_reader* reader) { + return reader->vtable->read_char(reader->userdata); +} + +static void json_reader_container_begins(grpc_json_reader* reader, + grpc_json_type type) { + reader->vtable->container_begins(reader->userdata, type); +} + +static grpc_json_type grpc_json_reader_container_ends( + grpc_json_reader* reader) { + return reader->vtable->container_ends(reader->userdata); +} + +static void json_reader_set_key(grpc_json_reader* reader) { + reader->vtable->set_key(reader->userdata); +} + +static void json_reader_set_string(grpc_json_reader* reader) { + reader->vtable->set_string(reader->userdata); +} + +static int json_reader_set_number(grpc_json_reader* reader) { + return reader->vtable->set_number(reader->userdata); +} + +static void json_reader_set_true(grpc_json_reader* reader) { + reader->vtable->set_true(reader->userdata); +} + +static void json_reader_set_false(grpc_json_reader* reader) { + reader->vtable->set_false(reader->userdata); +} + +static void json_reader_set_null(grpc_json_reader* reader) { + reader->vtable->set_null(reader->userdata); +} + +/* Call this function to initialize the reader structure. */ +void grpc_json_reader_init(grpc_json_reader* reader, + grpc_json_reader_vtable* vtable, void* userdata) { + memset(reader, 0, sizeof(*reader)); + reader->vtable = vtable; + reader->userdata = userdata; + json_reader_string_clear(reader); + reader->state = GRPC_JSON_STATE_VALUE_BEGIN; +} + +int grpc_json_reader_is_complete(grpc_json_reader* reader) { + return ((reader->depth == 0) && + ((reader->state == GRPC_JSON_STATE_END) || + (reader->state == GRPC_JSON_STATE_VALUE_END))); +} + +grpc_json_reader_status grpc_json_reader_run(grpc_json_reader* reader) { + gpr_uint32 c, success; + + /* This state-machine is a strict implementation of ECMA-404 */ + for (;;) { + c = grpc_json_reader_read_char(reader); + switch (c) { + /* Let's process the error cases first. */ + case GRPC_JSON_READ_CHAR_ERROR: + return GRPC_JSON_READ_ERROR; + + case GRPC_JSON_READ_CHAR_EAGAIN: + return GRPC_JSON_EAGAIN; + + case GRPC_JSON_READ_CHAR_EOF: + if (grpc_json_reader_is_complete(reader)) { + return GRPC_JSON_DONE; + } else { + return GRPC_JSON_PARSE_ERROR; + } + break; + + /* Processing whitespaces. */ + case ' ': + case '\t': + case '\n': + case '\r': + switch (reader->state) { + case GRPC_JSON_STATE_OBJECT_KEY_BEGIN: + case GRPC_JSON_STATE_OBJECT_KEY_END: + case GRPC_JSON_STATE_VALUE_BEGIN: + case GRPC_JSON_STATE_VALUE_END: + case GRPC_JSON_STATE_END: + break; + + case GRPC_JSON_STATE_OBJECT_KEY_STRING: + case GRPC_JSON_STATE_VALUE_STRING: + if (c != ' ') return GRPC_JSON_PARSE_ERROR; + if (reader->unicode_high_surrogate != 0) + return GRPC_JSON_PARSE_ERROR; + json_reader_string_add_char(reader, c); + break; + + case GRPC_JSON_STATE_VALUE_NUMBER: + case GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL: + case GRPC_JSON_STATE_VALUE_NUMBER_ZERO: + case GRPC_JSON_STATE_VALUE_NUMBER_EPM: + success = (gpr_uint32)json_reader_set_number(reader); + if (!success) return GRPC_JSON_PARSE_ERROR; + json_reader_string_clear(reader); + reader->state = GRPC_JSON_STATE_VALUE_END; + break; + + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + /* Value, object or array terminations. */ + case ',': + case '}': + case ']': + switch (reader->state) { + case GRPC_JSON_STATE_OBJECT_KEY_STRING: + case GRPC_JSON_STATE_VALUE_STRING: + if (reader->unicode_high_surrogate != 0) + return GRPC_JSON_PARSE_ERROR; + json_reader_string_add_char(reader, c); + break; + + case GRPC_JSON_STATE_VALUE_NUMBER: + case GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL: + case GRPC_JSON_STATE_VALUE_NUMBER_ZERO: + case GRPC_JSON_STATE_VALUE_NUMBER_EPM: + success = (gpr_uint32)json_reader_set_number(reader); + if (!success) return GRPC_JSON_PARSE_ERROR; + json_reader_string_clear(reader); + reader->state = GRPC_JSON_STATE_VALUE_END; + /* The missing break here is intentional. */ + + case GRPC_JSON_STATE_VALUE_END: + case GRPC_JSON_STATE_OBJECT_KEY_BEGIN: + case GRPC_JSON_STATE_VALUE_BEGIN: + if (c == ',') { + if (reader->state != GRPC_JSON_STATE_VALUE_END) { + return GRPC_JSON_PARSE_ERROR; + } + if (reader->in_object) { + reader->state = GRPC_JSON_STATE_OBJECT_KEY_BEGIN; + } else { + reader->state = GRPC_JSON_STATE_VALUE_BEGIN; + } + } else { + if (reader->depth-- == 0) return GRPC_JSON_PARSE_ERROR; + if ((c == '}') && !reader->in_object) { + return GRPC_JSON_PARSE_ERROR; + } + if ((c == '}') && + (reader->state == GRPC_JSON_STATE_OBJECT_KEY_BEGIN) && + !reader->container_just_begun) { + return GRPC_JSON_PARSE_ERROR; + } + if ((c == ']') && !reader->in_array) return GRPC_JSON_PARSE_ERROR; + if ((c == ']') && + (reader->state == GRPC_JSON_STATE_VALUE_BEGIN) && + !reader->container_just_begun) { + return GRPC_JSON_PARSE_ERROR; + } + reader->state = GRPC_JSON_STATE_VALUE_END; + switch (grpc_json_reader_container_ends(reader)) { + case GRPC_JSON_OBJECT: + reader->in_object = 1; + reader->in_array = 0; + break; + case GRPC_JSON_ARRAY: + reader->in_object = 0; + reader->in_array = 1; + break; + case GRPC_JSON_TOP_LEVEL: + if (reader->depth != 0) return GRPC_JSON_INTERNAL_ERROR; + reader->in_object = 0; + reader->in_array = 0; + reader->state = GRPC_JSON_STATE_END; + break; + default: + return GRPC_JSON_INTERNAL_ERROR; + } + } + break; + + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + /* In-string escaping. */ + case '\\': + switch (reader->state) { + case GRPC_JSON_STATE_OBJECT_KEY_STRING: + reader->escaped_string_was_key = 1; + reader->state = GRPC_JSON_STATE_STRING_ESCAPE; + break; + + case GRPC_JSON_STATE_VALUE_STRING: + reader->escaped_string_was_key = 0; + reader->state = GRPC_JSON_STATE_STRING_ESCAPE; + break; + + /* This is the \\ case. */ + case GRPC_JSON_STATE_STRING_ESCAPE: + if (reader->unicode_high_surrogate != 0) + return GRPC_JSON_PARSE_ERROR; + json_reader_string_add_char(reader, '\\'); + if (reader->escaped_string_was_key) { + reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING; + } else { + reader->state = GRPC_JSON_STATE_VALUE_STRING; + } + break; + + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + default: + reader->container_just_begun = 0; + switch (reader->state) { + case GRPC_JSON_STATE_OBJECT_KEY_BEGIN: + if (c != '"') return GRPC_JSON_PARSE_ERROR; + reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING; + break; + + case GRPC_JSON_STATE_OBJECT_KEY_STRING: + if (reader->unicode_high_surrogate != 0) + return GRPC_JSON_PARSE_ERROR; + if (c == '"') { + reader->state = GRPC_JSON_STATE_OBJECT_KEY_END; + json_reader_set_key(reader); + json_reader_string_clear(reader); + } else { + if (c <= 0x001f) return GRPC_JSON_PARSE_ERROR; + json_reader_string_add_char(reader, c); + } + break; + + case GRPC_JSON_STATE_VALUE_STRING: + if (reader->unicode_high_surrogate != 0) + return GRPC_JSON_PARSE_ERROR; + if (c == '"') { + reader->state = GRPC_JSON_STATE_VALUE_END; + json_reader_set_string(reader); + json_reader_string_clear(reader); + } else { + if (c < 32) return GRPC_JSON_PARSE_ERROR; + json_reader_string_add_char(reader, c); + } + break; + + case GRPC_JSON_STATE_OBJECT_KEY_END: + if (c != ':') return GRPC_JSON_PARSE_ERROR; + reader->state = GRPC_JSON_STATE_VALUE_BEGIN; + break; + + case GRPC_JSON_STATE_VALUE_BEGIN: + switch (c) { + case 't': + reader->state = GRPC_JSON_STATE_VALUE_TRUE_R; + break; + + case 'f': + reader->state = GRPC_JSON_STATE_VALUE_FALSE_A; + break; + + case 'n': + reader->state = GRPC_JSON_STATE_VALUE_NULL_U; + break; + + case '"': + reader->state = GRPC_JSON_STATE_VALUE_STRING; + break; + + case '0': + json_reader_string_add_char(reader, c); + reader->state = GRPC_JSON_STATE_VALUE_NUMBER_ZERO; + break; + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + json_reader_string_add_char(reader, c); + reader->state = GRPC_JSON_STATE_VALUE_NUMBER; + break; + + case '{': + reader->container_just_begun = 1; + json_reader_container_begins(reader, GRPC_JSON_OBJECT); + reader->depth++; + reader->state = GRPC_JSON_STATE_OBJECT_KEY_BEGIN; + reader->in_object = 1; + reader->in_array = 0; + break; + + case '[': + reader->container_just_begun = 1; + json_reader_container_begins(reader, GRPC_JSON_ARRAY); + reader->depth++; + reader->in_object = 0; + reader->in_array = 1; + break; + } + break; + + case GRPC_JSON_STATE_STRING_ESCAPE: + if (reader->escaped_string_was_key) { + reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING; + } else { + reader->state = GRPC_JSON_STATE_VALUE_STRING; + } + if (reader->unicode_high_surrogate && c != 'u') + return GRPC_JSON_PARSE_ERROR; + switch (c) { + case '"': + case '/': + json_reader_string_add_char(reader, c); + break; + case 'b': + json_reader_string_add_char(reader, '\b'); + break; + case 'f': + json_reader_string_add_char(reader, '\f'); + break; + case 'n': + json_reader_string_add_char(reader, '\n'); + break; + case 'r': + json_reader_string_add_char(reader, '\r'); + break; + case 't': + json_reader_string_add_char(reader, '\t'); + break; + case 'u': + reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U1; + reader->unicode_char = 0; + break; + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + case GRPC_JSON_STATE_STRING_ESCAPE_U1: + case GRPC_JSON_STATE_STRING_ESCAPE_U2: + case GRPC_JSON_STATE_STRING_ESCAPE_U3: + case GRPC_JSON_STATE_STRING_ESCAPE_U4: + if ((c >= '0') && (c <= '9')) { + c -= '0'; + } else if ((c >= 'A') && (c <= 'F')) { + c -= 'A' - 10; + } else if ((c >= 'a') && (c <= 'f')) { + c -= 'a' - 10; + } else { + return GRPC_JSON_PARSE_ERROR; + } + reader->unicode_char = (gpr_uint16)(reader->unicode_char << 4); + reader->unicode_char = (gpr_uint16)(reader->unicode_char | c); + + switch (reader->state) { + case GRPC_JSON_STATE_STRING_ESCAPE_U1: + reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U2; + break; + case GRPC_JSON_STATE_STRING_ESCAPE_U2: + reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U3; + break; + case GRPC_JSON_STATE_STRING_ESCAPE_U3: + reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U4; + break; + case GRPC_JSON_STATE_STRING_ESCAPE_U4: + /* See grpc_json_writer_escape_string to have a description + * of what's going on here. + */ + if ((reader->unicode_char & 0xfc00) == 0xd800) { + /* high surrogate utf-16 */ + if (reader->unicode_high_surrogate != 0) + return GRPC_JSON_PARSE_ERROR; + reader->unicode_high_surrogate = reader->unicode_char; + } else if ((reader->unicode_char & 0xfc00) == 0xdc00) { + /* low surrogate utf-16 */ + gpr_uint32 utf32; + if (reader->unicode_high_surrogate == 0) + return GRPC_JSON_PARSE_ERROR; + utf32 = 0x10000; + utf32 += (gpr_uint32)( + (reader->unicode_high_surrogate - 0xd800) * 0x400); + utf32 += (gpr_uint32)(reader->unicode_char - 0xdc00); + json_reader_string_add_utf32(reader, utf32); + reader->unicode_high_surrogate = 0; + } else { + /* anything else */ + if (reader->unicode_high_surrogate != 0) + return GRPC_JSON_PARSE_ERROR; + json_reader_string_add_utf32(reader, reader->unicode_char); + } + if (reader->escaped_string_was_key) { + reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING; + } else { + reader->state = GRPC_JSON_STATE_VALUE_STRING; + } + break; + default: + return GRPC_JSON_INTERNAL_ERROR; + } + break; + + case GRPC_JSON_STATE_VALUE_NUMBER: + json_reader_string_add_char(reader, c); + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + break; + case 'e': + case 'E': + reader->state = GRPC_JSON_STATE_VALUE_NUMBER_E; + break; + case '.': + reader->state = GRPC_JSON_STATE_VALUE_NUMBER_DOT; + break; + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + case GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL: + json_reader_string_add_char(reader, c); + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + break; + case 'e': + case 'E': + reader->state = GRPC_JSON_STATE_VALUE_NUMBER_E; + break; + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + case GRPC_JSON_STATE_VALUE_NUMBER_ZERO: + if (c != '.') return GRPC_JSON_PARSE_ERROR; + json_reader_string_add_char(reader, c); + reader->state = GRPC_JSON_STATE_VALUE_NUMBER_DOT; + break; + + case GRPC_JSON_STATE_VALUE_NUMBER_DOT: + json_reader_string_add_char(reader, c); + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + reader->state = GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL; + break; + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + case GRPC_JSON_STATE_VALUE_NUMBER_E: + json_reader_string_add_char(reader, c); + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + reader->state = GRPC_JSON_STATE_VALUE_NUMBER_EPM; + break; + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + case GRPC_JSON_STATE_VALUE_NUMBER_EPM: + json_reader_string_add_char(reader, c); + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + break; + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + case GRPC_JSON_STATE_VALUE_TRUE_R: + if (c != 'r') return GRPC_JSON_PARSE_ERROR; + reader->state = GRPC_JSON_STATE_VALUE_TRUE_U; + break; + + case GRPC_JSON_STATE_VALUE_TRUE_U: + if (c != 'u') return GRPC_JSON_PARSE_ERROR; + reader->state = GRPC_JSON_STATE_VALUE_TRUE_E; + break; + + case GRPC_JSON_STATE_VALUE_TRUE_E: + if (c != 'e') return GRPC_JSON_PARSE_ERROR; + json_reader_set_true(reader); + reader->state = GRPC_JSON_STATE_VALUE_END; + break; + + case GRPC_JSON_STATE_VALUE_FALSE_A: + if (c != 'a') return GRPC_JSON_PARSE_ERROR; + reader->state = GRPC_JSON_STATE_VALUE_FALSE_L; + break; + + case GRPC_JSON_STATE_VALUE_FALSE_L: + if (c != 'l') return GRPC_JSON_PARSE_ERROR; + reader->state = GRPC_JSON_STATE_VALUE_FALSE_S; + break; + + case GRPC_JSON_STATE_VALUE_FALSE_S: + if (c != 's') return GRPC_JSON_PARSE_ERROR; + reader->state = GRPC_JSON_STATE_VALUE_FALSE_E; + break; + + case GRPC_JSON_STATE_VALUE_FALSE_E: + if (c != 'e') return GRPC_JSON_PARSE_ERROR; + json_reader_set_false(reader); + reader->state = GRPC_JSON_STATE_VALUE_END; + break; + + case GRPC_JSON_STATE_VALUE_NULL_U: + if (c != 'u') return GRPC_JSON_PARSE_ERROR; + reader->state = GRPC_JSON_STATE_VALUE_NULL_L1; + break; + + case GRPC_JSON_STATE_VALUE_NULL_L1: + if (c != 'l') return GRPC_JSON_PARSE_ERROR; + reader->state = GRPC_JSON_STATE_VALUE_NULL_L2; + break; + + case GRPC_JSON_STATE_VALUE_NULL_L2: + if (c != 'l') return GRPC_JSON_PARSE_ERROR; + json_reader_set_null(reader); + reader->state = GRPC_JSON_STATE_VALUE_END; + break; + + /* All of the VALUE_END cases are handled in the specialized case + * above. */ + case GRPC_JSON_STATE_VALUE_END: + switch (c) { + case ',': + case '}': + case ']': + return GRPC_JSON_INTERNAL_ERROR; + break; + + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + case GRPC_JSON_STATE_END: + return GRPC_JSON_PARSE_ERROR; + } + } + } + + return GRPC_JSON_INTERNAL_ERROR; +} diff --git a/src/core/json/json_reader.h b/src/core/json/json_reader.h new file mode 100644 index 00000000..4d5487f7 --- /dev/null +++ b/src/core/json/json_reader.h @@ -0,0 +1,160 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_JSON_JSON_READER_H +#define GRPC_INTERNAL_CORE_JSON_JSON_READER_H + +#include +#include "src/core/json/json_common.h" + +typedef enum { + GRPC_JSON_STATE_OBJECT_KEY_BEGIN, + GRPC_JSON_STATE_OBJECT_KEY_STRING, + GRPC_JSON_STATE_OBJECT_KEY_END, + GRPC_JSON_STATE_VALUE_BEGIN, + GRPC_JSON_STATE_VALUE_STRING, + GRPC_JSON_STATE_STRING_ESCAPE, + GRPC_JSON_STATE_STRING_ESCAPE_U1, + GRPC_JSON_STATE_STRING_ESCAPE_U2, + GRPC_JSON_STATE_STRING_ESCAPE_U3, + GRPC_JSON_STATE_STRING_ESCAPE_U4, + GRPC_JSON_STATE_VALUE_NUMBER, + GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL, + GRPC_JSON_STATE_VALUE_NUMBER_ZERO, + GRPC_JSON_STATE_VALUE_NUMBER_DOT, + GRPC_JSON_STATE_VALUE_NUMBER_E, + GRPC_JSON_STATE_VALUE_NUMBER_EPM, + GRPC_JSON_STATE_VALUE_TRUE_R, + GRPC_JSON_STATE_VALUE_TRUE_U, + GRPC_JSON_STATE_VALUE_TRUE_E, + GRPC_JSON_STATE_VALUE_FALSE_A, + GRPC_JSON_STATE_VALUE_FALSE_L, + GRPC_JSON_STATE_VALUE_FALSE_S, + GRPC_JSON_STATE_VALUE_FALSE_E, + GRPC_JSON_STATE_VALUE_NULL_U, + GRPC_JSON_STATE_VALUE_NULL_L1, + GRPC_JSON_STATE_VALUE_NULL_L2, + GRPC_JSON_STATE_VALUE_END, + GRPC_JSON_STATE_END +} grpc_json_reader_state; + +enum { + /* The first non-unicode value is 0x110000. But let's pick + * a value high enough to start our error codes from. These + * values are safe to return from the read_char function. + */ + GRPC_JSON_READ_CHAR_EOF = 0x7ffffff0, + GRPC_JSON_READ_CHAR_EAGAIN, + GRPC_JSON_READ_CHAR_ERROR +}; + +struct grpc_json_reader; + +typedef struct grpc_json_reader_vtable { + /* Clears your internal string scratchpad. */ + void (*string_clear)(void* userdata); + /* Adds a char to the string scratchpad. */ + void (*string_add_char)(void* userdata, gpr_uint32 c); + /* Adds a utf32 char to the string scratchpad. */ + void (*string_add_utf32)(void* userdata, gpr_uint32 c); + /* Reads a character from your input. May be utf-8, 16 or 32. */ + gpr_uint32 (*read_char)(void* userdata); + /* Starts a container of type GRPC_JSON_ARRAY or GRPC_JSON_OBJECT. */ + void (*container_begins)(void* userdata, grpc_json_type type); + /* Ends the current container. Must return the type of its parent. */ + grpc_json_type (*container_ends)(void* userdata); + /* Your internal string scratchpad is an object's key. */ + void (*set_key)(void* userdata); + /* Your internal string scratchpad is a string value. */ + void (*set_string)(void* userdata); + /* Your internal string scratchpad is a numerical value. Return 1 if valid. */ + int (*set_number)(void* userdata); + /* Sets the values true, false or null. */ + void (*set_true)(void* userdata); + void (*set_false)(void* userdata); + void (*set_null)(void* userdata); +} grpc_json_reader_vtable; + +typedef struct grpc_json_reader { + /* That structure is fully private, and initialized by grpc_json_reader_init. + * The definition is public so you can put it on your stack. + */ + + void* userdata; + grpc_json_reader_vtable* vtable; + int depth; + int in_object; + int in_array; + int escaped_string_was_key; + int container_just_begun; + gpr_uint16 unicode_char, unicode_high_surrogate; + grpc_json_reader_state state; +} grpc_json_reader; + +/* The return type of the parser. */ +typedef enum { + GRPC_JSON_DONE, /* The parser finished successfully. */ + GRPC_JSON_EAGAIN, /* The parser yields to get more data. */ + GRPC_JSON_READ_ERROR, /* The parser passes through a read error. */ + GRPC_JSON_PARSE_ERROR, /* The parser found an error in the json stream. */ + GRPC_JSON_INTERNAL_ERROR /* The parser got an internal error. */ +} grpc_json_reader_status; + +/* Call this function to start parsing the input. It will return the following: + * . GRPC_JSON_DONE if the input got eof, and the parsing finished + * successfully. + * . GRPC_JSON_EAGAIN if the read_char function returned again. Call the + * parser again as needed. It is okay to call the parser in polling mode, + * although a bit dull. + * . GRPC_JSON_READ_ERROR if the read_char function returned an error. The + * state isn't broken however, and the function can be called again if the + * error has been corrected. But please use the EAGAIN feature instead for + * consistency. + * . GRPC_JSON_PARSE_ERROR if the input was somehow invalid. + * . GRPC_JSON_INTERNAL_ERROR if the parser somehow ended into an invalid + * internal state. + */ +grpc_json_reader_status grpc_json_reader_run(grpc_json_reader* reader); + +/* Call this function to initialize the reader structure. */ +void grpc_json_reader_init(grpc_json_reader* reader, + grpc_json_reader_vtable* vtable, void* userdata); + +/* You may call this from the read_char callback if you don't know where is the + * end of your input stream, and you'd like the json reader to hint you that it + * has completed reading its input, so you can return an EOF to it. Note that + * there might still be trailing whitespaces after that point. + */ +int grpc_json_reader_is_complete(grpc_json_reader* reader); + +#endif /* GRPC_INTERNAL_CORE_JSON_JSON_READER_H */ diff --git a/src/core/json/json_string.c b/src/core/json/json_string.c new file mode 100644 index 00000000..e6622ec4 --- /dev/null +++ b/src/core/json/json_string.c @@ -0,0 +1,379 @@ +/* + * + * Copyright 2015, 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 +#include + +#include +#include + +#include "src/core/json/json.h" +#include "src/core/json/json_reader.h" +#include "src/core/json/json_writer.h" + +/* The json reader will construct a bunch of grpc_json objects and + * link them all up together in a tree-like structure that will represent + * the json data in memory. + * + * It also uses its own input as a scratchpad to store all of the decoded, + * unescaped strings. So we need to keep track of all these pointers in + * that opaque structure the reader will carry for us. + * + * Note that this works because the act of parsing json always reduces its + * input size, and never expands it. + */ +typedef struct { + grpc_json* top; + grpc_json* current_container; + grpc_json* current_value; + gpr_uint8* input; + gpr_uint8* key; + gpr_uint8* string; + gpr_uint8* string_ptr; + size_t remaining_input; +} json_reader_userdata; + +/* This json writer will put everything in a big string. + * The point is that we allocate that string in chunks of 256 bytes. + */ +typedef struct { + char* output; + size_t free_space; + size_t string_len; + size_t allocated; +} json_writer_userdata; + +/* This function checks if there's enough space left in the output buffer, + * and will enlarge it if necessary. We're only allocating chunks of 256 + * bytes at a time (or multiples thereof). + */ +static void json_writer_output_check(void* userdata, size_t needed) { + json_writer_userdata* state = userdata; + if (state->free_space >= needed) return; + needed -= state->free_space; + /* Round up by 256 bytes. */ + needed = (needed + 0xff) & ~0xffU; + state->output = gpr_realloc(state->output, state->allocated + needed); + state->free_space += needed; + state->allocated += needed; +} + +/* These are needed by the writer's implementation. */ +static void json_writer_output_char(void* userdata, char c) { + json_writer_userdata* state = userdata; + json_writer_output_check(userdata, 1); + state->output[state->string_len++] = c; + state->free_space--; +} + +static void json_writer_output_string_with_len(void* userdata, const char* str, + size_t len) { + json_writer_userdata* state = userdata; + json_writer_output_check(userdata, len); + memcpy(state->output + state->string_len, str, len); + state->string_len += len; + state->free_space -= len; +} + +static void json_writer_output_string(void* userdata, const char* str) { + size_t len = strlen(str); + json_writer_output_string_with_len(userdata, str, len); +} + +/* The reader asks us to clear our scratchpad. In our case, we'll simply mark + * the end of the current string, and advance our output pointer. + */ +static void json_reader_string_clear(void* userdata) { + json_reader_userdata* state = userdata; + if (state->string) { + GPR_ASSERT(state->string_ptr < state->input); + *state->string_ptr++ = 0; + } + state->string = state->string_ptr; +} + +static void json_reader_string_add_char(void* userdata, gpr_uint32 c) { + json_reader_userdata* state = userdata; + GPR_ASSERT(state->string_ptr < state->input); + GPR_ASSERT(c <= 0xff); + *state->string_ptr++ = (gpr_uint8)c; +} + +/* We are converting a UTF-32 character into UTF-8 here, + * as described by RFC3629. + */ +static void json_reader_string_add_utf32(void* userdata, gpr_uint32 c) { + if (c <= 0x7f) { + json_reader_string_add_char(userdata, c); + } else if (c <= 0x7ff) { + gpr_uint32 b1 = 0xc0 | ((c >> 6) & 0x1f); + gpr_uint32 b2 = 0x80 | (c & 0x3f); + json_reader_string_add_char(userdata, b1); + json_reader_string_add_char(userdata, b2); + } else if (c <= 0xffff) { + gpr_uint32 b1 = 0xe0 | ((c >> 12) & 0x0f); + gpr_uint32 b2 = 0x80 | ((c >> 6) & 0x3f); + gpr_uint32 b3 = 0x80 | (c & 0x3f); + json_reader_string_add_char(userdata, b1); + json_reader_string_add_char(userdata, b2); + json_reader_string_add_char(userdata, b3); + } else if (c <= 0x1fffff) { + gpr_uint32 b1 = 0xf0 | ((c >> 18) & 0x07); + gpr_uint32 b2 = 0x80 | ((c >> 12) & 0x3f); + gpr_uint32 b3 = 0x80 | ((c >> 6) & 0x3f); + gpr_uint32 b4 = 0x80 | (c & 0x3f); + json_reader_string_add_char(userdata, b1); + json_reader_string_add_char(userdata, b2); + json_reader_string_add_char(userdata, b3); + json_reader_string_add_char(userdata, b4); + } +} + +/* We consider that the input may be a zero-terminated string. So we + * can end up hitting eof before the end of the alleged string length. + */ +static gpr_uint32 json_reader_read_char(void* userdata) { + gpr_uint32 r; + json_reader_userdata* state = userdata; + + if (state->remaining_input == 0) return GRPC_JSON_READ_CHAR_EOF; + + r = *state->input++; + state->remaining_input--; + + if (r == 0) { + state->remaining_input = 0; + return GRPC_JSON_READ_CHAR_EOF; + } + + return r; +} + +/* Helper function to create a new grpc_json object and link it into + * our tree-in-progress inside our opaque structure. + */ +static grpc_json* json_create_and_link(void* userdata, grpc_json_type type) { + json_reader_userdata* state = userdata; + grpc_json* json = grpc_json_create(type); + + json->parent = state->current_container; + json->prev = state->current_value; + state->current_value = json; + + if (json->prev) { + json->prev->next = json; + } + if (json->parent) { + if (!json->parent->child) { + json->parent->child = json; + } + if (json->parent->type == GRPC_JSON_OBJECT) { + json->key = (char*)state->key; + } + } + if (!state->top) { + state->top = json; + } + + return json; +} + +static void json_reader_container_begins(void* userdata, grpc_json_type type) { + json_reader_userdata* state = userdata; + grpc_json* container; + + GPR_ASSERT(type == GRPC_JSON_ARRAY || type == GRPC_JSON_OBJECT); + + container = json_create_and_link(userdata, type); + state->current_container = container; + state->current_value = NULL; +} + +/* It's important to remember that the reader is mostly stateless, so it + * isn't trying to remember what the container was prior the one that just + * ends. Since we're keeping track of these for our own purpose, we are + * able to return that information back, which is useful for it to validate + * the input json stream. + * + * Also note that if we're at the top of the tree, and the last container + * ends, we have to return GRPC_JSON_TOP_LEVEL. + */ +static grpc_json_type json_reader_container_ends(void* userdata) { + grpc_json_type container_type = GRPC_JSON_TOP_LEVEL; + json_reader_userdata* state = userdata; + + GPR_ASSERT(state->current_container); + + state->current_value = state->current_container; + state->current_container = state->current_container->parent; + + if (state->current_container) { + container_type = state->current_container->type; + } + + return container_type; +} + +/* The next 3 functions basically are the reader asking us to use our string + * scratchpad for one of these 3 purposes. + * + * Note that in the set_number case, we're not going to try interpreting it. + * We'll keep it as a string, and leave it to the caller to evaluate it. + */ +static void json_reader_set_key(void* userdata) { + json_reader_userdata* state = userdata; + state->key = state->string; +} + +static void json_reader_set_string(void* userdata) { + json_reader_userdata* state = userdata; + grpc_json* json = json_create_and_link(userdata, GRPC_JSON_STRING); + json->value = (char*)state->string; +} + +static int json_reader_set_number(void* userdata) { + json_reader_userdata* state = userdata; + grpc_json* json = json_create_and_link(userdata, GRPC_JSON_NUMBER); + json->value = (char*)state->string; + return 1; +} + +/* The object types true, false and null are self-sufficient, and don't need + * any more information beside their type. + */ +static void json_reader_set_true(void* userdata) { + json_create_and_link(userdata, GRPC_JSON_TRUE); +} + +static void json_reader_set_false(void* userdata) { + json_create_and_link(userdata, GRPC_JSON_FALSE); +} + +static void json_reader_set_null(void* userdata) { + json_create_and_link(userdata, GRPC_JSON_NULL); +} + +static grpc_json_reader_vtable reader_vtable = { + json_reader_string_clear, json_reader_string_add_char, + json_reader_string_add_utf32, json_reader_read_char, + json_reader_container_begins, json_reader_container_ends, + json_reader_set_key, json_reader_set_string, + json_reader_set_number, json_reader_set_true, + json_reader_set_false, json_reader_set_null}; + +/* And finally, let's define our public API. */ +grpc_json* grpc_json_parse_string_with_len(char* input, size_t size) { + grpc_json_reader reader; + json_reader_userdata state; + grpc_json* json = NULL; + grpc_json_reader_status status; + + if (!input) return NULL; + + state.top = state.current_container = state.current_value = NULL; + state.string = state.key = NULL; + state.string_ptr = state.input = (gpr_uint8*)input; + state.remaining_input = size; + grpc_json_reader_init(&reader, &reader_vtable, &state); + + status = grpc_json_reader_run(&reader); + json = state.top; + + if ((status != GRPC_JSON_DONE) && json) { + grpc_json_destroy(json); + json = NULL; + } + + return json; +} + +#define UNBOUND_JSON_STRING_LENGTH 0x7fffffff + +grpc_json* grpc_json_parse_string(char* input) { + return grpc_json_parse_string_with_len(input, UNBOUND_JSON_STRING_LENGTH); +} + +static void json_dump_recursive(grpc_json_writer* writer, grpc_json* json, + int in_object) { + while (json) { + if (in_object) grpc_json_writer_object_key(writer, json->key); + + switch (json->type) { + case GRPC_JSON_OBJECT: + case GRPC_JSON_ARRAY: + grpc_json_writer_container_begins(writer, json->type); + if (json->child) + json_dump_recursive(writer, json->child, + json->type == GRPC_JSON_OBJECT); + grpc_json_writer_container_ends(writer, json->type); + break; + case GRPC_JSON_STRING: + grpc_json_writer_value_string(writer, json->value); + break; + case GRPC_JSON_NUMBER: + grpc_json_writer_value_raw(writer, json->value); + break; + case GRPC_JSON_TRUE: + grpc_json_writer_value_raw_with_len(writer, "true", 4); + break; + case GRPC_JSON_FALSE: + grpc_json_writer_value_raw_with_len(writer, "false", 5); + break; + case GRPC_JSON_NULL: + grpc_json_writer_value_raw_with_len(writer, "null", 4); + break; + default: + abort(); + } + json = json->next; + } +} + +static grpc_json_writer_vtable writer_vtable = { + json_writer_output_char, json_writer_output_string, + json_writer_output_string_with_len}; + +char* grpc_json_dump_to_string(grpc_json* json, int indent) { + grpc_json_writer writer; + json_writer_userdata state; + + state.output = NULL; + state.free_space = state.string_len = state.allocated = 0; + grpc_json_writer_init(&writer, indent, &writer_vtable, &state); + + json_dump_recursive(&writer, json, 0); + + json_writer_output_char(&state, 0); + + return state.output; +} diff --git a/src/core/json/json_writer.c b/src/core/json/json_writer.c new file mode 100644 index 00000000..ca9c8358 --- /dev/null +++ b/src/core/json/json_writer.c @@ -0,0 +1,260 @@ +/* + * + * Copyright 2015, 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 + +#include + +#include "src/core/json/json_writer.h" + +static void json_writer_output_char(grpc_json_writer* writer, char c) { + writer->vtable->output_char(writer->userdata, c); +} + +static void json_writer_output_string(grpc_json_writer* writer, + const char* str) { + writer->vtable->output_string(writer->userdata, str); +} + +static void json_writer_output_string_with_len(grpc_json_writer* writer, + const char* str, size_t len) { + writer->vtable->output_string_with_len(writer->userdata, str, len); +} + +void grpc_json_writer_init(grpc_json_writer* writer, int indent, + grpc_json_writer_vtable* vtable, void* userdata) { + memset(writer, 0, sizeof(*writer)); + writer->container_empty = 1; + writer->indent = indent; + writer->vtable = vtable; + writer->userdata = userdata; +} + +static void json_writer_output_indent(grpc_json_writer* writer) { + static const char spacesstr[] = + " " + " " + " " + " "; + + unsigned spaces = (unsigned)(writer->depth * writer->indent); + + if (writer->indent == 0) return; + + if (writer->got_key) { + json_writer_output_char(writer, ' '); + return; + } + + while (spaces >= (sizeof(spacesstr) - 1)) { + json_writer_output_string_with_len(writer, spacesstr, + sizeof(spacesstr) - 1); + spaces -= (unsigned)(sizeof(spacesstr) - 1); + } + + if (spaces == 0) return; + + json_writer_output_string_with_len( + writer, spacesstr + sizeof(spacesstr) - 1 - spaces, spaces); +} + +static void json_writer_value_end(grpc_json_writer* writer) { + if (writer->container_empty) { + writer->container_empty = 0; + if ((writer->indent == 0) || (writer->depth == 0)) return; + json_writer_output_char(writer, '\n'); + } else { + json_writer_output_char(writer, ','); + if (writer->indent == 0) return; + json_writer_output_char(writer, '\n'); + } +} + +static void json_writer_escape_utf16(grpc_json_writer* writer, + gpr_uint16 utf16) { + static const char hex[] = "0123456789abcdef"; + + json_writer_output_string_with_len(writer, "\\u", 2); + json_writer_output_char(writer, hex[(utf16 >> 12) & 0x0f]); + json_writer_output_char(writer, hex[(utf16 >> 8) & 0x0f]); + json_writer_output_char(writer, hex[(utf16 >> 4) & 0x0f]); + json_writer_output_char(writer, hex[(utf16)&0x0f]); +} + +static void json_writer_escape_string(grpc_json_writer* writer, + const char* string) { + json_writer_output_char(writer, '"'); + + for (;;) { + gpr_uint8 c = (gpr_uint8)*string++; + if (c == 0) { + break; + } else if ((c >= 32) && (c <= 126)) { + if ((c == '\\') || (c == '"')) json_writer_output_char(writer, '\\'); + json_writer_output_char(writer, (char)c); + } else if ((c < 32) || (c == 127)) { + switch (c) { + case '\b': + json_writer_output_string_with_len(writer, "\\b", 2); + break; + case '\f': + json_writer_output_string_with_len(writer, "\\f", 2); + break; + case '\n': + json_writer_output_string_with_len(writer, "\\n", 2); + break; + case '\r': + json_writer_output_string_with_len(writer, "\\r", 2); + break; + case '\t': + json_writer_output_string_with_len(writer, "\\t", 2); + break; + default: + json_writer_escape_utf16(writer, c); + break; + } + } else { + gpr_uint32 utf32 = 0; + int extra = 0; + int i; + int valid = 1; + if ((c & 0xe0) == 0xc0) { + utf32 = c & 0x1f; + extra = 1; + } else if ((c & 0xf0) == 0xe0) { + utf32 = c & 0x0f; + extra = 2; + } else if ((c & 0xf8) == 0xf0) { + utf32 = c & 0x07; + extra = 3; + } else { + break; + } + for (i = 0; i < extra; i++) { + utf32 <<= 6; + c = (gpr_uint8)(*string++); + /* Breaks out and bail on any invalid UTF-8 sequence, including \0. */ + if ((c & 0xc0) != 0x80) { + valid = 0; + break; + } + utf32 |= c & 0x3f; + } + if (!valid) break; + /* The range 0xd800 - 0xdfff is reserved by the surrogates ad vitam. + * Any other range is technically reserved for future usage, so if we + * don't want the software to break in the future, we have to allow + * anything else. The first non-unicode character is 0x110000. */ + if (((utf32 >= 0xd800) && (utf32 <= 0xdfff)) || (utf32 >= 0x110000)) + break; + if (utf32 >= 0x10000) { + /* If utf32 contains a character that is above 0xffff, it needs to be + * broken down into a utf-16 surrogate pair. A surrogate pair is first + * a high surrogate, followed by a low surrogate. Each surrogate holds + * 10 bits of usable data, thus allowing a total of 20 bits of data. + * The high surrogate marker is 0xd800, while the low surrogate marker + * is 0xdc00. The low 10 bits of each will be the usable data. + * + * After re-combining the 20 bits of data, one has to add 0x10000 to + * the resulting value, in order to obtain the original character. + * This is obviously because the range 0x0000 - 0xffff can be written + * without any special trick. + * + * Since 0x10ffff is the highest allowed character, we're working in + * the range 0x00000 - 0xfffff after we decrement it by 0x10000. + * That range is exactly 20 bits. + */ + utf32 -= 0x10000; + json_writer_escape_utf16(writer, (gpr_uint16)(0xd800 | (utf32 >> 10))); + json_writer_escape_utf16(writer, + (gpr_uint16)(0xdc00 | (utf32 & 0x3ff))); + } else { + json_writer_escape_utf16(writer, (gpr_uint16)utf32); + } + } + } + + json_writer_output_char(writer, '"'); +} + +void grpc_json_writer_container_begins(grpc_json_writer* writer, + grpc_json_type type) { + if (!writer->got_key) json_writer_value_end(writer); + json_writer_output_indent(writer); + json_writer_output_char(writer, type == GRPC_JSON_OBJECT ? '{' : '['); + writer->container_empty = 1; + writer->got_key = 0; + writer->depth++; +} + +void grpc_json_writer_container_ends(grpc_json_writer* writer, + grpc_json_type type) { + if (writer->indent && !writer->container_empty) + json_writer_output_char(writer, '\n'); + writer->depth--; + if (!writer->container_empty) json_writer_output_indent(writer); + json_writer_output_char(writer, type == GRPC_JSON_OBJECT ? '}' : ']'); + writer->container_empty = 0; + writer->got_key = 0; +} + +void grpc_json_writer_object_key(grpc_json_writer* writer, const char* string) { + json_writer_value_end(writer); + json_writer_output_indent(writer); + json_writer_escape_string(writer, string); + json_writer_output_char(writer, ':'); + writer->got_key = 1; +} + +void grpc_json_writer_value_raw(grpc_json_writer* writer, const char* string) { + if (!writer->got_key) json_writer_value_end(writer); + json_writer_output_indent(writer); + json_writer_output_string(writer, string); + writer->got_key = 0; +} + +void grpc_json_writer_value_raw_with_len(grpc_json_writer* writer, + const char* string, size_t len) { + if (!writer->got_key) json_writer_value_end(writer); + json_writer_output_indent(writer); + json_writer_output_string_with_len(writer, string, len); + writer->got_key = 0; +} + +void grpc_json_writer_value_string(grpc_json_writer* writer, + const char* string) { + if (!writer->got_key) json_writer_value_end(writer); + json_writer_output_indent(writer); + json_writer_escape_string(writer, string); + writer->got_key = 0; +} diff --git a/src/core/json/json_writer.h b/src/core/json/json_writer.h new file mode 100644 index 00000000..a299dfab --- /dev/null +++ b/src/core/json/json_writer.h @@ -0,0 +1,97 @@ +/* + * + * Copyright 2015, 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. + * + */ + +/* The idea of the writer is basically symmetrical of the reader. While the + * reader emits various calls to your code, the writer takes basically the + * same calls and emit json out of it. It doesn't try to make any check on + * the order of the calls you do on it. Meaning you can theorically force + * it to generate invalid json. + * + * Also, unlike the reader, the writer expects UTF-8 encoded input strings. + * These strings will be UTF-8 validated, and any invalid character will + * cut the conversion short, before any invalid UTF-8 sequence, thus forming + * a valid UTF-8 string overall. + */ + +#ifndef GRPC_INTERNAL_CORE_JSON_JSON_WRITER_H +#define GRPC_INTERNAL_CORE_JSON_JSON_WRITER_H + +#include + +#include "src/core/json/json_common.h" + +typedef struct grpc_json_writer_vtable { + /* Adds a character to the output stream. */ + void (*output_char)(void* userdata, char); + /* Adds a zero-terminated string to the output stream. */ + void (*output_string)(void* userdata, const char* str); + /* Adds a fixed-length string to the output stream. */ + void (*output_string_with_len)(void* userdata, const char* str, size_t len); + +} grpc_json_writer_vtable; + +typedef struct grpc_json_writer { + void* userdata; + grpc_json_writer_vtable* vtable; + int indent; + int depth; + int container_empty; + int got_key; +} grpc_json_writer; + +/* Call this to initialize your writer structure. The indent parameter is + * specifying the number of spaces to use for indenting the output. If you + * use indent=0, then the output will not have any newlines either, thus + * emitting a condensed json output. + */ +void grpc_json_writer_init(grpc_json_writer* writer, int indent, + grpc_json_writer_vtable* vtable, void* userdata); + +/* Signals the beginning of a container. */ +void grpc_json_writer_container_begins(grpc_json_writer* writer, + grpc_json_type type); +/* Signals the end of a container. */ +void grpc_json_writer_container_ends(grpc_json_writer* writer, + grpc_json_type type); +/* Writes down an object key for the next value. */ +void grpc_json_writer_object_key(grpc_json_writer* writer, const char* string); +/* Sets a raw value. Useful for numbers. */ +void grpc_json_writer_value_raw(grpc_json_writer* writer, const char* string); +/* Sets a raw value with its length. Useful for values like true or false. */ +void grpc_json_writer_value_raw_with_len(grpc_json_writer* writer, + const char* string, size_t len); +/* Sets a string value. It'll be escaped, and utf-8 validated. */ +void grpc_json_writer_value_string(grpc_json_writer* writer, + const char* string); + +#endif /* GRPC_INTERNAL_CORE_JSON_JSON_WRITER_H */ diff --git a/src/core/profiling/basic_timers.c b/src/core/profiling/basic_timers.c new file mode 100644 index 00000000..4b6a0d2f --- /dev/null +++ b/src/core/profiling/basic_timers.c @@ -0,0 +1,140 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GRPC_BASIC_PROFILER + +#include "src/core/profiling/timers.h" + +#include +#include +#include +#include +#include +#include + +typedef enum { + BEGIN = '{', + END = '}', + MARK = '.', + IMPORTANT = '!' +} marker_type; + +typedef struct grpc_timer_entry { + gpr_timespec tm; + int tag; + const char* tagstr; + marker_type type; + void* id; + const char* file; + int line; +} grpc_timer_entry; + +#define MAX_COUNT (1024 * 1024 / sizeof(grpc_timer_entry)) + +static __thread grpc_timer_entry log[MAX_COUNT]; +static __thread int count; + +static void log_report() { + int i; + for (i = 0; i < count; i++) { + grpc_timer_entry* entry = &(log[i]); + printf("GRPC_LAT_PROF %ld.%09d %p %c %d(%s) %p %s %d\n", + entry->tm.tv_sec, entry->tm.tv_nsec, + (void*)(gpr_intptr)gpr_thd_currentid(), entry->type, entry->tag, + entry->tagstr, entry->id, entry->file, entry->line); + } + + /* Now clear out the log */ + count = 0; +} + +static void grpc_timers_log_add(int tag, const char* tagstr, marker_type type, + void* id, const char* file, int line) { + grpc_timer_entry* entry; + + /* TODO (vpai) : Improve concurrency */ + if (count == MAX_COUNT) { + log_report(); + } + + entry = &log[count++]; + + entry->tm = gpr_now(GPR_CLOCK_PRECISE); + entry->tag = tag; + entry->tagstr = tagstr; + entry->type = type; + entry->id = id; + entry->file = file; + entry->line = line; +} + +/* Latency profiler API implementation. */ +void grpc_timer_add_mark(int tag, const char* tagstr, void* id, + const char* file, int line) { + if (tag < GRPC_PTAG_IGNORE_THRESHOLD) { + grpc_timers_log_add(tag, tagstr, MARK, id, file, line); + } +} + +void grpc_timer_add_important_mark(int tag, const char* tagstr, void* id, + const char* file, int line) { + if (tag < GRPC_PTAG_IGNORE_THRESHOLD) { + grpc_timers_log_add(tag, tagstr, IMPORTANT, id, file, line); + } +} + +void grpc_timer_begin(int tag, const char* tagstr, void* id, const char* file, + int line) { + if (tag < GRPC_PTAG_IGNORE_THRESHOLD) { + grpc_timers_log_add(tag, tagstr, BEGIN, id, file, line); + } +} + +void grpc_timer_end(int tag, const char* tagstr, void* id, const char* file, + int line) { + if (tag < GRPC_PTAG_IGNORE_THRESHOLD) { + grpc_timers_log_add(tag, tagstr, END, id, file, line); + } +} + +/* Basic profiler specific API functions. */ +void grpc_timers_global_init(void) {} + +void grpc_timers_global_destroy(void) {} + +#else /* !GRPC_BASIC_PROFILER */ +void grpc_timers_global_init(void) {} +void grpc_timers_global_destroy(void) {} +#endif /* GRPC_BASIC_PROFILER */ diff --git a/src/core/profiling/stap_probes.d b/src/core/profiling/stap_probes.d new file mode 100644 index 00000000..153de917 --- /dev/null +++ b/src/core/profiling/stap_probes.d @@ -0,0 +1,7 @@ +provider _stap { + probe add_mark(int tag); + probe add_important_mark(int tag); + probe timing_ns_begin(int tag); + probe timing_ns_end(int tag); +}; + diff --git a/src/core/profiling/stap_timers.c b/src/core/profiling/stap_timers.c new file mode 100644 index 00000000..99975163 --- /dev/null +++ b/src/core/profiling/stap_timers.c @@ -0,0 +1,65 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GRPC_STAP_PROFILER + +#include "src/core/profiling/timers.h" + +#include +/* Generated from src/core/profiling/stap_probes.d */ +#include "src/core/profiling/stap_probes.h" + +/* Latency profiler API implementation. */ +void grpc_timer_add_mark(int tag, const char* tagstr, void* id, + const char* file, int line) { + _STAP_ADD_MARK(tag); +} + +void grpc_timer_add_important_mark(int tag, const char* tagstr, void* id, + const char* file, int line) { + _STAP_ADD_IMPORTANT_MARK(tag); +} + +void grpc_timer_begin(int tag, const char* tagstr, void* id, const char* file, + int line) { + _STAP_TIMING_NS_BEGIN(tag); +} + +void grpc_timer_end(int tag, const char* tagstr, void* id, const char* file, + int line) { + _STAP_TIMING_NS_END(tag); +} + +#endif /* GRPC_STAP_PROFILER */ diff --git a/src/core/profiling/timers.h b/src/core/profiling/timers.h new file mode 100644 index 00000000..92dbab90 --- /dev/null +++ b/src/core/profiling/timers.h @@ -0,0 +1,146 @@ +/* + * + * Copyright 2015, 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_CORE_PROFILING_TIMERS_H +#define GRPC_CORE_PROFILING_TIMERS_H + +#ifdef __cplusplus +extern "C" { +#endif + +void grpc_timers_global_init(void); +void grpc_timers_global_destroy(void); + +void grpc_timer_add_mark(int tag, const char *tagstr, void *id, + const char *file, int line); +void grpc_timer_add_important_mark(int tag, const char *tagstr, void *id, + const char *file, int line); +void grpc_timer_begin(int tag, const char *tagstr, void *id, const char *file, + int line); +void grpc_timer_end(int tag, const char *tagstr, void *id, const char *file, + int line); + +enum grpc_profiling_tags { + /* Any GRPC_PTAG_* >= than the threshold won't generate any profiling mark. */ + GRPC_PTAG_IGNORE_THRESHOLD = 1000000, + + /* Re. Protos. */ + GRPC_PTAG_PROTO_SERIALIZE = 100 + GRPC_PTAG_IGNORE_THRESHOLD, + GRPC_PTAG_PROTO_DESERIALIZE = 101 + GRPC_PTAG_IGNORE_THRESHOLD, + + /* Re. sockets. */ + GRPC_PTAG_HANDLE_READ = 200 + GRPC_PTAG_IGNORE_THRESHOLD, + GRPC_PTAG_SENDMSG = 201 + GRPC_PTAG_IGNORE_THRESHOLD, + GRPC_PTAG_RECVMSG = 202 + GRPC_PTAG_IGNORE_THRESHOLD, + GRPC_PTAG_POLL_FINISHED = 203 + GRPC_PTAG_IGNORE_THRESHOLD, + GRPC_PTAG_TCP_CB_WRITE = 204 + GRPC_PTAG_IGNORE_THRESHOLD, + GRPC_PTAG_TCP_WRITE = 205 + GRPC_PTAG_IGNORE_THRESHOLD, + GRPC_PTAG_CALL_ON_DONE_RECV = 206 + GRPC_PTAG_IGNORE_THRESHOLD, + + /* C++ */ + GRPC_PTAG_CPP_CALL_CREATED = 300 + GRPC_PTAG_IGNORE_THRESHOLD, + GRPC_PTAG_CPP_PERFORM_OPS = 301 + GRPC_PTAG_IGNORE_THRESHOLD, + + /* Transports */ + GRPC_PTAG_HTTP2_UNLOCK = 401 + GRPC_PTAG_IGNORE_THRESHOLD, + GRPC_PTAG_HTTP2_UNLOCK_CLEANUP = 402 + GRPC_PTAG_IGNORE_THRESHOLD, + + /* > 1024 Unassigned reserved. For any miscellaneous use. + * Use addition to generate tags from this base or take advantage of the 10 + * zero'd bits for OR-ing. */ + GRPC_PTAG_OTHER_BASE = 1024 +}; + +#if !(defined(GRPC_STAP_PROFILER) + defined(GRPC_BASIC_PROFILER)) +/* No profiling. No-op all the things. */ +#define GRPC_TIMER_MARK(tag, id) \ + do { \ + } while (0) + +#define GRPC_TIMER_IMPORTANT_MARK(tag, id) \ + do { \ + } while (0) + +#define GRPC_TIMER_BEGIN(tag, id) \ + do { \ + } while (0) + +#define GRPC_TIMER_END(tag, id) \ + do { \ + } while (0) + +#else /* at least one profiler requested... */ +/* ... hopefully only one. */ +#if defined(GRPC_STAP_PROFILER) && defined(GRPC_BASIC_PROFILER) +#error "GRPC_STAP_PROFILER and GRPC_BASIC_PROFILER are mutually exclusive." +#endif + +/* Generic profiling interface. */ +#define GRPC_TIMER_MARK(tag, id) \ + if (tag < GRPC_PTAG_IGNORE_THRESHOLD) { \ + grpc_timer_add_mark(tag, #tag, ((void *)(gpr_intptr)(id)), __FILE__, \ + __LINE__); \ + } + +#define GRPC_TIMER_IMPORTANT_MARK(tag, id) \ + if (tag < GRPC_PTAG_IGNORE_THRESHOLD) { \ + grpc_timer_add_important_mark(tag, #tag, ((void *)(gpr_intptr)(id)), \ + __FILE__, __LINE__); \ + } + +#define GRPC_TIMER_BEGIN(tag, id) \ + if (tag < GRPC_PTAG_IGNORE_THRESHOLD) { \ + grpc_timer_begin(tag, #tag, ((void *)(gpr_intptr)(id)), __FILE__, \ + __LINE__); \ + } + +#define GRPC_TIMER_END(tag, id) \ + if (tag < GRPC_PTAG_IGNORE_THRESHOLD) { \ + grpc_timer_end(tag, #tag, ((void *)(gpr_intptr)(id)), __FILE__, __LINE__); \ + } + +#ifdef GRPC_STAP_PROFILER +/* Empty placeholder for now. */ +#endif /* GRPC_STAP_PROFILER */ + +#ifdef GRPC_BASIC_PROFILER +/* Empty placeholder for now. */ +#endif /* GRPC_BASIC_PROFILER */ + +#endif /* at least one profiler requested. */ + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_PROFILING_TIMERS_H */ diff --git a/src/core/security/auth_filters.h b/src/core/security/auth_filters.h new file mode 100644 index 00000000..c179b54b --- /dev/null +++ b/src/core/security/auth_filters.h @@ -0,0 +1,42 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_SECURITY_AUTH_FILTERS_H +#define GRPC_INTERNAL_CORE_SECURITY_AUTH_FILTERS_H + +#include "src/core/channel/channel_stack.h" + +extern const grpc_channel_filter grpc_client_auth_filter; +extern const grpc_channel_filter grpc_server_auth_filter; + +#endif /* GRPC_INTERNAL_CORE_SECURITY_AUTH_FILTERS_H */ diff --git a/src/core/security/base64.c b/src/core/security/base64.c new file mode 100644 index 00000000..8dfaef84 --- /dev/null +++ b/src/core/security/base64.c @@ -0,0 +1,231 @@ +/* + * + * Copyright 2015, 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 "src/core/security/base64.h" + +#include + +#include +#include +#include + +/* --- Constants. --- */ + +static const char base64_bytes[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 0x3E, -1, -1, -1, 0x3F, + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, -1, -1, + -1, 0x7F, -1, -1, -1, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, + 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, + 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, -1, -1, -1, -1, -1, + -1, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, + 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, + 0x31, 0x32, 0x33, -1, -1, -1, -1, -1}; + +static const char base64_url_unsafe_chars[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char base64_url_safe_chars[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + +#define GRPC_BASE64_PAD_CHAR '=' +#define GRPC_BASE64_PAD_BYTE 0x7F +#define GRPC_BASE64_MULTILINE_LINE_LEN 76 +#define GRPC_BASE64_MULTILINE_NUM_BLOCKS (GRPC_BASE64_MULTILINE_LINE_LEN / 4) + +/* --- base64 functions. --- */ + +char *grpc_base64_encode(const void *vdata, size_t data_size, int url_safe, + int multiline) { + const unsigned char *data = vdata; + const char *base64_chars = + url_safe ? base64_url_safe_chars : base64_url_unsafe_chars; + size_t result_projected_size = + 4 * ((data_size + 3) / 3) + + 2 * (multiline ? (data_size / (3 * GRPC_BASE64_MULTILINE_NUM_BLOCKS)) + : 0) + + 1; + char *result = gpr_malloc(result_projected_size); + char *current = result; + size_t num_blocks = 0; + size_t i = 0; + + /* Encode each block. */ + while (data_size >= 3) { + *current++ = base64_chars[(data[i] >> 2) & 0x3F]; + *current++ = + base64_chars[((data[i] & 0x03) << 4) | ((data[i + 1] >> 4) & 0x0F)]; + *current++ = + base64_chars[((data[i + 1] & 0x0F) << 2) | ((data[i + 2] >> 6) & 0x03)]; + *current++ = base64_chars[data[i + 2] & 0x3F]; + + data_size -= 3; + i += 3; + if (multiline && (++num_blocks == GRPC_BASE64_MULTILINE_NUM_BLOCKS)) { + *current++ = '\r'; + *current++ = '\n'; + num_blocks = 0; + } + } + + /* Take care of the tail. */ + if (data_size == 2) { + *current++ = base64_chars[(data[i] >> 2) & 0x3F]; + *current++ = + base64_chars[((data[i] & 0x03) << 4) | ((data[i + 1] >> 4) & 0x0F)]; + *current++ = base64_chars[(data[i + 1] & 0x0F) << 2]; + *current++ = GRPC_BASE64_PAD_CHAR; + } else if (data_size == 1) { + *current++ = base64_chars[(data[i] >> 2) & 0x3F]; + *current++ = base64_chars[(data[i] & 0x03) << 4]; + *current++ = GRPC_BASE64_PAD_CHAR; + *current++ = GRPC_BASE64_PAD_CHAR; + } + + GPR_ASSERT(current >= result); + GPR_ASSERT((gpr_uintptr)(current - result) < result_projected_size); + result[current - result] = '\0'; + return result; +} + +gpr_slice grpc_base64_decode(const char *b64, int url_safe) { + return grpc_base64_decode_with_len(b64, strlen(b64), url_safe); +} + +static void decode_one_char(const unsigned char *codes, unsigned char *result, + size_t *result_offset) { + gpr_uint32 packed = (codes[0] << 2) | (codes[1] >> 4); + result[(*result_offset)++] = (unsigned char)packed; +} + +static void decode_two_chars(const unsigned char *codes, unsigned char *result, + size_t *result_offset) { + gpr_uint32 packed = (codes[0] << 10) | (codes[1] << 4) | (codes[2] >> 2); + result[(*result_offset)++] = (unsigned char)(packed >> 8); + result[(*result_offset)++] = (unsigned char)(packed); +} + +static int decode_group(const unsigned char *codes, size_t num_codes, + unsigned char *result, size_t *result_offset) { + GPR_ASSERT(num_codes <= 4); + + /* Short end groups that may not have padding. */ + if (num_codes == 1) { + gpr_log(GPR_ERROR, "Invalid group. Must be at least 2 bytes."); + return 0; + } + if (num_codes == 2) { + decode_one_char(codes, result, result_offset); + return 1; + } + if (num_codes == 3) { + decode_two_chars(codes, result, result_offset); + return 1; + } + + /* Regular 4 byte groups with padding or not. */ + GPR_ASSERT(num_codes == 4); + if (codes[0] == GRPC_BASE64_PAD_BYTE || codes[1] == GRPC_BASE64_PAD_BYTE) { + gpr_log(GPR_ERROR, "Invalid padding detected."); + return 0; + } + if (codes[2] == GRPC_BASE64_PAD_BYTE) { + if (codes[3] == GRPC_BASE64_PAD_BYTE) { + decode_one_char(codes, result, result_offset); + } else { + gpr_log(GPR_ERROR, "Invalid padding detected."); + return 0; + } + } else if (codes[3] == GRPC_BASE64_PAD_BYTE) { + decode_two_chars(codes, result, result_offset); + } else { + /* No padding. */ + gpr_uint32 packed = + (codes[0] << 18) | (codes[1] << 12) | (codes[2] << 6) | codes[3]; + result[(*result_offset)++] = (unsigned char)(packed >> 16); + result[(*result_offset)++] = (unsigned char)(packed >> 8); + result[(*result_offset)++] = (unsigned char)(packed); + } + return 1; +} + +gpr_slice grpc_base64_decode_with_len(const char *b64, size_t b64_len, + int url_safe) { + gpr_slice result = gpr_slice_malloc(b64_len); + unsigned char *current = GPR_SLICE_START_PTR(result); + size_t result_size = 0; + unsigned char codes[4]; + size_t num_codes = 0; + + while (b64_len--) { + unsigned char c = (unsigned char)(*b64++); + signed char code; + if (c >= GPR_ARRAY_SIZE(base64_bytes)) continue; + if (url_safe) { + if (c == '+' || c == '/') { + gpr_log(GPR_ERROR, "Invalid character for url safe base64 %c", c); + goto fail; + } + if (c == '-') { + c = '+'; + } else if (c == '_') { + c = '/'; + } + } + code = base64_bytes[c]; + if (code == -1) { + if (c != '\r' && c != '\n') { + gpr_log(GPR_ERROR, "Invalid character %c", c); + goto fail; + } + } else { + codes[num_codes++] = (unsigned char)code; + if (num_codes == 4) { + if (!decode_group(codes, num_codes, current, &result_size)) goto fail; + num_codes = 0; + } + } + } + + if (num_codes != 0 && + !decode_group(codes, num_codes, current, &result_size)) { + goto fail; + } + GPR_SLICE_SET_LENGTH(result, result_size); + return result; + +fail: + gpr_slice_unref(result); + return gpr_empty_slice(); +} diff --git a/src/core/security/base64.h b/src/core/security/base64.h new file mode 100644 index 00000000..31ae9826 --- /dev/null +++ b/src/core/security/base64.h @@ -0,0 +1,52 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_SECURITY_BASE64_H +#define GRPC_INTERNAL_CORE_SECURITY_BASE64_H + +#include + +/* Encodes data using base64. It is the caller's responsability to free + the returned char * using gpr_free. Returns NULL on NULL input. */ +char *grpc_base64_encode(const void *data, size_t data_size, int url_safe, + int multiline); + +/* Decodes data according to the base64 specification. Returns an empty + slice in case of failure. */ +gpr_slice grpc_base64_decode(const char *b64, int url_safe); + +/* Same as above except that the length is provided by the caller. */ +gpr_slice grpc_base64_decode_with_len(const char *b64, size_t b64_len, + int url_safe); + +#endif /* GRPC_INTERNAL_CORE_SECURITY_BASE64_H */ diff --git a/src/core/security/client_auth_filter.c b/src/core/security/client_auth_filter.c new file mode 100644 index 00000000..d15a9673 --- /dev/null +++ b/src/core/security/client_auth_filter.c @@ -0,0 +1,355 @@ +/* + * + * Copyright 2015, 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 "src/core/security/auth_filters.h" + +#include + +#include +#include +#include + +#include "src/core/support/string.h" +#include "src/core/channel/channel_stack.h" +#include "src/core/security/security_context.h" +#include "src/core/security/security_connector.h" +#include "src/core/security/credentials.h" +#include "src/core/surface/call.h" + +#define MAX_CREDENTIALS_METADATA_COUNT 4 + +/* We can have a per-call credentials. */ +typedef struct { + grpc_credentials *creds; + grpc_mdstr *host; + grpc_mdstr *method; + /* pollset bound to this call; if we need to make external + network requests, they should be done under this pollset + so that work can progress when this call wants work to + progress */ + grpc_pollset *pollset; + grpc_transport_stream_op op; + size_t op_md_idx; + int sent_initial_metadata; + gpr_uint8 security_context_set; + grpc_linked_mdelem md_links[MAX_CREDENTIALS_METADATA_COUNT]; +} call_data; + +/* We can have a per-channel credentials. */ +typedef struct { + grpc_channel_security_connector *security_connector; + grpc_mdctx *md_ctx; + grpc_mdstr *authority_string; + grpc_mdstr *path_string; + grpc_mdstr *error_msg_key; + grpc_mdstr *status_key; +} channel_data; + +static void bubble_up_error(grpc_call_element *elem, grpc_status_code status, + const char *error_msg) { + call_data *calld = elem->call_data; + gpr_log(GPR_ERROR, "Client side authentication failure: %s", error_msg); + grpc_transport_stream_op_add_cancellation(&calld->op, status); + grpc_call_next_op(elem, &calld->op); +} + +static void on_credentials_metadata(void *user_data, + grpc_credentials_md *md_elems, + size_t num_md, + grpc_credentials_status status) { + grpc_call_element *elem = (grpc_call_element *)user_data; + call_data *calld = elem->call_data; + channel_data *chand = elem->channel_data; + grpc_transport_stream_op *op = &calld->op; + grpc_metadata_batch *mdb; + size_t i; + if (status != GRPC_CREDENTIALS_OK) { + bubble_up_error(elem, GRPC_STATUS_UNAUTHENTICATED, + "Credentials failed to get metadata."); + return; + } + GPR_ASSERT(num_md <= MAX_CREDENTIALS_METADATA_COUNT); + GPR_ASSERT(op->send_ops && op->send_ops->nops > calld->op_md_idx && + op->send_ops->ops[calld->op_md_idx].type == GRPC_OP_METADATA); + mdb = &op->send_ops->ops[calld->op_md_idx].data.metadata; + for (i = 0; i < num_md; i++) { + grpc_metadata_batch_add_tail( + mdb, &calld->md_links[i], + grpc_mdelem_from_slices(chand->md_ctx, gpr_slice_ref(md_elems[i].key), + gpr_slice_ref(md_elems[i].value))); + } + grpc_call_next_op(elem, op); +} + +static char *build_service_url(const char *url_scheme, call_data *calld) { + char *service_url; + char *service = gpr_strdup(grpc_mdstr_as_c_string(calld->method)); + char *last_slash = strrchr(service, '/'); + if (last_slash == NULL) { + gpr_log(GPR_ERROR, "No '/' found in fully qualified method name"); + service[0] = '\0'; + } else if (last_slash == service) { + /* No service part in fully qualified method name: will just be "/". */ + service[1] = '\0'; + } else { + *last_slash = '\0'; + } + if (url_scheme == NULL) url_scheme = ""; + gpr_asprintf(&service_url, "%s://%s%s", url_scheme, + grpc_mdstr_as_c_string(calld->host), service); + gpr_free(service); + return service_url; +} + +static void send_security_metadata(grpc_call_element *elem, + grpc_transport_stream_op *op) { + call_data *calld = elem->call_data; + channel_data *chand = elem->channel_data; + grpc_client_security_context *ctx = + (grpc_client_security_context *)op->context[GRPC_CONTEXT_SECURITY].value; + char *service_url = NULL; + grpc_credentials *channel_creds = + chand->security_connector->request_metadata_creds; + int channel_creds_has_md = + (channel_creds != NULL) && + grpc_credentials_has_request_metadata(channel_creds); + int call_creds_has_md = (ctx != NULL) && (ctx->creds != NULL) && + grpc_credentials_has_request_metadata(ctx->creds); + + if (!channel_creds_has_md && !call_creds_has_md) { + /* Skip sending metadata altogether. */ + grpc_call_next_op(elem, op); + return; + } + + if (channel_creds_has_md && call_creds_has_md) { + calld->creds = + grpc_composite_credentials_create(channel_creds, ctx->creds, NULL); + if (calld->creds == NULL) { + bubble_up_error(elem, GRPC_STATUS_INVALID_ARGUMENT, + "Incompatible credentials set on channel and call."); + return; + } + } else { + calld->creds = + grpc_credentials_ref(call_creds_has_md ? ctx->creds : channel_creds); + } + + service_url = + build_service_url(chand->security_connector->base.url_scheme, calld); + calld->op = *op; /* Copy op (originates from the caller's stack). */ + GPR_ASSERT(calld->pollset); + grpc_credentials_get_request_metadata( + calld->creds, calld->pollset, service_url, on_credentials_metadata, elem); + gpr_free(service_url); +} + +static void on_host_checked(void *user_data, grpc_security_status status) { + grpc_call_element *elem = (grpc_call_element *)user_data; + call_data *calld = elem->call_data; + + if (status == GRPC_SECURITY_OK) { + send_security_metadata(elem, &calld->op); + } else { + char *error_msg; + gpr_asprintf(&error_msg, "Invalid host %s set in :authority metadata.", + grpc_mdstr_as_c_string(calld->host)); + bubble_up_error(elem, GRPC_STATUS_INVALID_ARGUMENT, error_msg); + gpr_free(error_msg); + } +} + +/* Called either: + - in response to an API call (or similar) from above, to send something + - a network event (or similar) from below, to receive something + op contains type and call direction information, in addition to the data + that is being sent or received. */ +static void auth_start_transport_op(grpc_call_element *elem, + grpc_transport_stream_op *op) { + /* grab pointers to our data from the call element */ + call_data *calld = elem->call_data; + channel_data *chand = elem->channel_data; + grpc_linked_mdelem *l; + size_t i; + grpc_client_security_context *sec_ctx = NULL; + + if (calld->security_context_set == 0 && + op->cancel_with_status == GRPC_STATUS_OK) { + calld->security_context_set = 1; + GPR_ASSERT(op->context); + if (op->context[GRPC_CONTEXT_SECURITY].value == NULL) { + op->context[GRPC_CONTEXT_SECURITY].value = + grpc_client_security_context_create(); + op->context[GRPC_CONTEXT_SECURITY].destroy = + grpc_client_security_context_destroy; + } + sec_ctx = op->context[GRPC_CONTEXT_SECURITY].value; + GRPC_AUTH_CONTEXT_UNREF(sec_ctx->auth_context, "client auth filter"); + sec_ctx->auth_context = GRPC_AUTH_CONTEXT_REF( + chand->security_connector->base.auth_context, "client_auth_filter"); + } + + if (op->bind_pollset != NULL) { + calld->pollset = op->bind_pollset; + } + + if (op->send_ops != NULL && !calld->sent_initial_metadata) { + size_t nops = op->send_ops->nops; + grpc_stream_op *ops = op->send_ops->ops; + for (i = 0; i < nops; i++) { + grpc_stream_op *sop = &ops[i]; + if (sop->type != GRPC_OP_METADATA) continue; + calld->op_md_idx = i; + calld->sent_initial_metadata = 1; + for (l = sop->data.metadata.list.head; l != NULL; l = l->next) { + grpc_mdelem *md = l->md; + /* Pointer comparison is OK for md_elems created from the same context. + */ + if (md->key == chand->authority_string) { + if (calld->host != NULL) GRPC_MDSTR_UNREF(calld->host); + calld->host = GRPC_MDSTR_REF(md->value); + } else if (md->key == chand->path_string) { + if (calld->method != NULL) GRPC_MDSTR_UNREF(calld->method); + calld->method = GRPC_MDSTR_REF(md->value); + } + } + if (calld->host != NULL) { + grpc_security_status status; + const char *call_host = grpc_mdstr_as_c_string(calld->host); + calld->op = *op; /* Copy op (originates from the caller's stack). */ + status = grpc_channel_security_connector_check_call_host( + chand->security_connector, call_host, on_host_checked, elem); + if (status != GRPC_SECURITY_OK) { + if (status == GRPC_SECURITY_ERROR) { + char *error_msg; + gpr_asprintf(&error_msg, + "Invalid host %s set in :authority metadata.", + call_host); + bubble_up_error(elem, GRPC_STATUS_INVALID_ARGUMENT, error_msg); + gpr_free(error_msg); + } + return; /* early exit */ + } + } + send_security_metadata(elem, op); + return; /* early exit */ + } + } + + /* pass control up or down the stack */ + grpc_call_next_op(elem, op); +} + +/* Constructor for call_data */ +static void init_call_elem(grpc_call_element *elem, + const void *server_transport_data, + grpc_transport_stream_op *initial_op) { + call_data *calld = elem->call_data; + calld->creds = NULL; + calld->host = NULL; + calld->method = NULL; + calld->pollset = NULL; + calld->sent_initial_metadata = 0; + calld->security_context_set = 0; + + GPR_ASSERT(!initial_op || !initial_op->send_ops); +} + +/* Destructor for call_data */ +static void destroy_call_elem(grpc_call_element *elem) { + call_data *calld = elem->call_data; + grpc_credentials_unref(calld->creds); + if (calld->host != NULL) { + GRPC_MDSTR_UNREF(calld->host); + } + if (calld->method != NULL) { + GRPC_MDSTR_UNREF(calld->method); + } +} + +/* Constructor for channel_data */ +static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, + const grpc_channel_args *args, + grpc_mdctx *metadata_context, int is_first, + int is_last) { + grpc_security_connector *sc = grpc_find_security_connector_in_args(args); + /* grab pointers to our data from the channel element */ + channel_data *chand = elem->channel_data; + + /* The first and the last filters tend to be implemented differently to + handle the case that there's no 'next' filter to call on the up or down + path */ + GPR_ASSERT(!is_last); + GPR_ASSERT(sc != NULL); + + /* initialize members */ + GPR_ASSERT(sc->is_client_side); + chand->security_connector = + (grpc_channel_security_connector *)GRPC_SECURITY_CONNECTOR_REF( + sc, "client_auth_filter"); + chand->md_ctx = metadata_context; + chand->authority_string = + grpc_mdstr_from_string(chand->md_ctx, ":authority", 0); + chand->path_string = grpc_mdstr_from_string(chand->md_ctx, ":path", 0); + chand->error_msg_key = + grpc_mdstr_from_string(chand->md_ctx, "grpc-message", 0); + chand->status_key = grpc_mdstr_from_string(chand->md_ctx, "grpc-status", 0); +} + +/* Destructor for channel data */ +static void destroy_channel_elem(grpc_channel_element *elem) { + /* grab pointers to our data from the channel element */ + channel_data *chand = elem->channel_data; + grpc_channel_security_connector *ctx = chand->security_connector; + if (ctx != NULL) + GRPC_SECURITY_CONNECTOR_UNREF(&ctx->base, "client_auth_filter"); + if (chand->authority_string != NULL) { + GRPC_MDSTR_UNREF(chand->authority_string); + } + if (chand->error_msg_key != NULL) { + GRPC_MDSTR_UNREF(chand->error_msg_key); + } + if (chand->status_key != NULL) { + GRPC_MDSTR_UNREF(chand->status_key); + } + if (chand->path_string != NULL) { + GRPC_MDSTR_UNREF(chand->path_string); + } +} + +const grpc_channel_filter grpc_client_auth_filter = { + auth_start_transport_op, grpc_channel_next_op, + sizeof(call_data), init_call_elem, + destroy_call_elem, sizeof(channel_data), + init_channel_elem, destroy_channel_elem, + grpc_call_next_get_peer, "client-auth"}; diff --git a/src/core/security/credentials.c b/src/core/security/credentials.c new file mode 100644 index 00000000..a7644133 --- /dev/null +++ b/src/core/security/credentials.c @@ -0,0 +1,1187 @@ +/* + * + * Copyright 2015, 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 "src/core/security/credentials.h" + +#include +#include + +#include "src/core/channel/channel_args.h" +#include "src/core/channel/http_client_filter.h" +#include "src/core/json/json.h" +#include "src/core/httpcli/httpcli.h" +#include "src/core/iomgr/iomgr.h" +#include "src/core/support/string.h" + +#include +#include +#include +#include +#include + +/* -- Common. -- */ + +struct grpc_credentials_metadata_request { + grpc_credentials *creds; + grpc_credentials_metadata_cb cb; + grpc_iomgr_closure *on_simulated_token_fetch_done_closure; + void *user_data; +}; + +static grpc_credentials_metadata_request * +grpc_credentials_metadata_request_create(grpc_credentials *creds, + grpc_credentials_metadata_cb cb, + void *user_data) { + grpc_credentials_metadata_request *r = + gpr_malloc(sizeof(grpc_credentials_metadata_request)); + r->creds = grpc_credentials_ref(creds); + r->cb = cb; + r->on_simulated_token_fetch_done_closure = + gpr_malloc(sizeof(grpc_iomgr_closure)); + r->user_data = user_data; + return r; +} + +static void grpc_credentials_metadata_request_destroy( + grpc_credentials_metadata_request *r) { + grpc_credentials_unref(r->creds); + gpr_free(r->on_simulated_token_fetch_done_closure); + gpr_free(r); +} + +grpc_credentials *grpc_credentials_ref(grpc_credentials *creds) { + if (creds == NULL) return NULL; + gpr_ref(&creds->refcount); + return creds; +} + +void grpc_credentials_unref(grpc_credentials *creds) { + if (creds == NULL) return; + if (gpr_unref(&creds->refcount)) { + creds->vtable->destruct(creds); + gpr_free(creds); + } +} + +void grpc_credentials_release(grpc_credentials *creds) { + grpc_credentials_unref(creds); +} + +int grpc_credentials_has_request_metadata(grpc_credentials *creds) { + if (creds == NULL) return 0; + return creds->vtable->has_request_metadata(creds); +} + +int grpc_credentials_has_request_metadata_only(grpc_credentials *creds) { + if (creds == NULL) return 0; + return creds->vtable->has_request_metadata_only(creds); +} + +void grpc_credentials_get_request_metadata(grpc_credentials *creds, + grpc_pollset *pollset, + const char *service_url, + grpc_credentials_metadata_cb cb, + void *user_data) { + if (creds == NULL || !grpc_credentials_has_request_metadata(creds) || + creds->vtable->get_request_metadata == NULL) { + if (cb != NULL) { + cb(user_data, NULL, 0, GRPC_CREDENTIALS_OK); + } + return; + } + creds->vtable->get_request_metadata(creds, pollset, service_url, cb, + user_data); +} + +grpc_security_status grpc_credentials_create_security_connector( + grpc_credentials *creds, const char *target, const grpc_channel_args *args, + grpc_credentials *request_metadata_creds, + grpc_channel_security_connector **sc, grpc_channel_args **new_args) { + *new_args = NULL; + if (creds == NULL || creds->vtable->create_security_connector == NULL || + grpc_credentials_has_request_metadata_only(creds)) { + gpr_log(GPR_ERROR, + "Invalid credentials for creating a security connector."); + return GRPC_SECURITY_ERROR; + } + return creds->vtable->create_security_connector( + creds, target, args, request_metadata_creds, sc, new_args); +} + +grpc_server_credentials *grpc_server_credentials_ref( + grpc_server_credentials *creds) { + if (creds == NULL) return NULL; + gpr_ref(&creds->refcount); + return creds; +} + +void grpc_server_credentials_unref(grpc_server_credentials *creds) { + if (creds == NULL) return; + if (gpr_unref(&creds->refcount)) { + creds->vtable->destruct(creds); + if (creds->processor.destroy != NULL && creds->processor.state != NULL) { + creds->processor.destroy(creds->processor.state); + } + gpr_free(creds); + } +} + +void grpc_server_credentials_release(grpc_server_credentials *creds) { + grpc_server_credentials_unref(creds); +} + +grpc_security_status grpc_server_credentials_create_security_connector( + grpc_server_credentials *creds, grpc_security_connector **sc) { + if (creds == NULL || creds->vtable->create_security_connector == NULL) { + gpr_log(GPR_ERROR, "Server credentials cannot create security context."); + return GRPC_SECURITY_ERROR; + } + return creds->vtable->create_security_connector(creds, sc); +} + +void grpc_server_credentials_set_auth_metadata_processor( + grpc_server_credentials *creds, grpc_auth_metadata_processor processor) { + if (creds == NULL) return; + if (creds->processor.destroy != NULL && creds->processor.state != NULL) { + creds->processor.destroy(creds->processor.state); + } + creds->processor = processor; +} + +/* -- Ssl credentials. -- */ + +static void ssl_destruct(grpc_credentials *creds) { + grpc_ssl_credentials *c = (grpc_ssl_credentials *)creds; + if (c->config.pem_root_certs != NULL) gpr_free(c->config.pem_root_certs); + if (c->config.pem_private_key != NULL) gpr_free(c->config.pem_private_key); + if (c->config.pem_cert_chain != NULL) gpr_free(c->config.pem_cert_chain); +} + +static void ssl_server_destruct(grpc_server_credentials *creds) { + grpc_ssl_server_credentials *c = (grpc_ssl_server_credentials *)creds; + size_t i; + for (i = 0; i < c->config.num_key_cert_pairs; i++) { + if (c->config.pem_private_keys[i] != NULL) { + gpr_free(c->config.pem_private_keys[i]); + } + if (c->config.pem_cert_chains[i] != NULL) { + gpr_free(c->config.pem_cert_chains[i]); + } + } + if (c->config.pem_private_keys != NULL) gpr_free(c->config.pem_private_keys); + if (c->config.pem_private_keys_sizes != NULL) { + gpr_free(c->config.pem_private_keys_sizes); + } + if (c->config.pem_cert_chains != NULL) gpr_free(c->config.pem_cert_chains); + if (c->config.pem_cert_chains_sizes != NULL) { + gpr_free(c->config.pem_cert_chains_sizes); + } + if (c->config.pem_root_certs != NULL) gpr_free(c->config.pem_root_certs); +} + +static int ssl_has_request_metadata(const grpc_credentials *creds) { return 0; } + +static int ssl_has_request_metadata_only(const grpc_credentials *creds) { + return 0; +} + +static grpc_security_status ssl_create_security_connector( + grpc_credentials *creds, const char *target, const grpc_channel_args *args, + grpc_credentials *request_metadata_creds, + grpc_channel_security_connector **sc, grpc_channel_args **new_args) { + grpc_ssl_credentials *c = (grpc_ssl_credentials *)creds; + grpc_security_status status = GRPC_SECURITY_OK; + size_t i = 0; + const char *overridden_target_name = NULL; + grpc_arg arg; + + for (i = 0; args && i < args->num_args; i++) { + grpc_arg *arg = &args->args[i]; + if (strcmp(arg->key, GRPC_SSL_TARGET_NAME_OVERRIDE_ARG) == 0 && + arg->type == GRPC_ARG_STRING) { + overridden_target_name = arg->value.string; + break; + } + } + status = grpc_ssl_channel_security_connector_create( + request_metadata_creds, &c->config, target, overridden_target_name, sc); + if (status != GRPC_SECURITY_OK) { + return status; + } + arg.type = GRPC_ARG_STRING; + arg.key = GRPC_ARG_HTTP2_SCHEME; + arg.value.string = "https"; + *new_args = grpc_channel_args_copy_and_add(args, &arg, 1); + return status; +} + +static grpc_security_status ssl_server_create_security_connector( + grpc_server_credentials *creds, grpc_security_connector **sc) { + grpc_ssl_server_credentials *c = (grpc_ssl_server_credentials *)creds; + return grpc_ssl_server_security_connector_create(&c->config, sc); +} + +static grpc_credentials_vtable ssl_vtable = { + ssl_destruct, ssl_has_request_metadata, ssl_has_request_metadata_only, NULL, + ssl_create_security_connector}; + +static grpc_server_credentials_vtable ssl_server_vtable = { + ssl_server_destruct, ssl_server_create_security_connector}; + +static void ssl_copy_key_material(const char *input, unsigned char **output, + size_t *output_size) { + *output_size = strlen(input); + *output = gpr_malloc(*output_size); + memcpy(*output, input, *output_size); +} + +static void ssl_build_config(const char *pem_root_certs, + grpc_ssl_pem_key_cert_pair *pem_key_cert_pair, + grpc_ssl_config *config) { + if (pem_root_certs != NULL) { + ssl_copy_key_material(pem_root_certs, &config->pem_root_certs, + &config->pem_root_certs_size); + } + if (pem_key_cert_pair != NULL) { + GPR_ASSERT(pem_key_cert_pair->private_key != NULL); + GPR_ASSERT(pem_key_cert_pair->cert_chain != NULL); + ssl_copy_key_material(pem_key_cert_pair->private_key, + &config->pem_private_key, + &config->pem_private_key_size); + ssl_copy_key_material(pem_key_cert_pair->cert_chain, + &config->pem_cert_chain, + &config->pem_cert_chain_size); + } +} + +static void ssl_build_server_config( + const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs, + size_t num_key_cert_pairs, int force_client_auth, + grpc_ssl_server_config *config) { + size_t i; + config->force_client_auth = force_client_auth; + if (pem_root_certs != NULL) { + ssl_copy_key_material(pem_root_certs, &config->pem_root_certs, + &config->pem_root_certs_size); + } + if (num_key_cert_pairs > 0) { + GPR_ASSERT(pem_key_cert_pairs != NULL); + config->pem_private_keys = + gpr_malloc(num_key_cert_pairs * sizeof(unsigned char *)); + config->pem_cert_chains = + gpr_malloc(num_key_cert_pairs * sizeof(unsigned char *)); + config->pem_private_keys_sizes = + gpr_malloc(num_key_cert_pairs * sizeof(size_t)); + config->pem_cert_chains_sizes = + gpr_malloc(num_key_cert_pairs * sizeof(size_t)); + } + config->num_key_cert_pairs = num_key_cert_pairs; + for (i = 0; i < num_key_cert_pairs; i++) { + GPR_ASSERT(pem_key_cert_pairs[i].private_key != NULL); + GPR_ASSERT(pem_key_cert_pairs[i].cert_chain != NULL); + ssl_copy_key_material(pem_key_cert_pairs[i].private_key, + &config->pem_private_keys[i], + &config->pem_private_keys_sizes[i]); + ssl_copy_key_material(pem_key_cert_pairs[i].cert_chain, + &config->pem_cert_chains[i], + &config->pem_cert_chains_sizes[i]); + } +} + +grpc_credentials *grpc_ssl_credentials_create( + const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pair, + void *reserved) { + grpc_ssl_credentials *c = gpr_malloc(sizeof(grpc_ssl_credentials)); + GPR_ASSERT(reserved == NULL); + memset(c, 0, sizeof(grpc_ssl_credentials)); + c->base.type = GRPC_CREDENTIALS_TYPE_SSL; + c->base.vtable = &ssl_vtable; + gpr_ref_init(&c->base.refcount, 1); + ssl_build_config(pem_root_certs, pem_key_cert_pair, &c->config); + return &c->base; +} + +grpc_server_credentials *grpc_ssl_server_credentials_create( + const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs, + size_t num_key_cert_pairs, int force_client_auth, void *reserved) { + grpc_ssl_server_credentials *c = + gpr_malloc(sizeof(grpc_ssl_server_credentials)); + GPR_ASSERT(reserved == NULL); + memset(c, 0, sizeof(grpc_ssl_server_credentials)); + c->base.type = GRPC_CREDENTIALS_TYPE_SSL; + gpr_ref_init(&c->base.refcount, 1); + c->base.vtable = &ssl_server_vtable; + ssl_build_server_config(pem_root_certs, pem_key_cert_pairs, + num_key_cert_pairs, force_client_auth, &c->config); + return &c->base; +} + +/* -- Jwt credentials -- */ + +static void jwt_reset_cache(grpc_service_account_jwt_access_credentials *c) { + if (c->cached.jwt_md != NULL) { + grpc_credentials_md_store_unref(c->cached.jwt_md); + c->cached.jwt_md = NULL; + } + if (c->cached.service_url != NULL) { + gpr_free(c->cached.service_url); + c->cached.service_url = NULL; + } + c->cached.jwt_expiration = gpr_inf_past(GPR_CLOCK_REALTIME); +} + +static void jwt_destruct(grpc_credentials *creds) { + grpc_service_account_jwt_access_credentials *c = + (grpc_service_account_jwt_access_credentials *)creds; + grpc_auth_json_key_destruct(&c->key); + jwt_reset_cache(c); + gpr_mu_destroy(&c->cache_mu); +} + +static int jwt_has_request_metadata(const grpc_credentials *creds) { return 1; } + +static int jwt_has_request_metadata_only(const grpc_credentials *creds) { + return 1; +} + +static void jwt_get_request_metadata(grpc_credentials *creds, + grpc_pollset *pollset, + const char *service_url, + grpc_credentials_metadata_cb cb, + void *user_data) { + grpc_service_account_jwt_access_credentials *c = + (grpc_service_account_jwt_access_credentials *)creds; + gpr_timespec refresh_threshold = gpr_time_from_seconds( + GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS, GPR_TIMESPAN); + + /* See if we can return a cached jwt. */ + grpc_credentials_md_store *jwt_md = NULL; + { + gpr_mu_lock(&c->cache_mu); + if (c->cached.service_url != NULL && + strcmp(c->cached.service_url, service_url) == 0 && + c->cached.jwt_md != NULL && + (gpr_time_cmp(gpr_time_sub(c->cached.jwt_expiration, + gpr_now(GPR_CLOCK_REALTIME)), + refresh_threshold) > 0)) { + jwt_md = grpc_credentials_md_store_ref(c->cached.jwt_md); + } + gpr_mu_unlock(&c->cache_mu); + } + + if (jwt_md == NULL) { + char *jwt = NULL; + /* Generate a new jwt. */ + gpr_mu_lock(&c->cache_mu); + jwt_reset_cache(c); + jwt = grpc_jwt_encode_and_sign(&c->key, service_url, c->jwt_lifetime, NULL); + if (jwt != NULL) { + char *md_value; + gpr_asprintf(&md_value, "Bearer %s", jwt); + gpr_free(jwt); + c->cached.jwt_expiration = + gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), c->jwt_lifetime); + c->cached.service_url = gpr_strdup(service_url); + c->cached.jwt_md = grpc_credentials_md_store_create(1); + grpc_credentials_md_store_add_cstrings( + c->cached.jwt_md, GRPC_AUTHORIZATION_METADATA_KEY, md_value); + gpr_free(md_value); + jwt_md = grpc_credentials_md_store_ref(c->cached.jwt_md); + } + gpr_mu_unlock(&c->cache_mu); + } + + if (jwt_md != NULL) { + cb(user_data, jwt_md->entries, jwt_md->num_entries, GRPC_CREDENTIALS_OK); + grpc_credentials_md_store_unref(jwt_md); + } else { + cb(user_data, NULL, 0, GRPC_CREDENTIALS_ERROR); + } +} + +static grpc_credentials_vtable jwt_vtable = { + jwt_destruct, jwt_has_request_metadata, jwt_has_request_metadata_only, + jwt_get_request_metadata, NULL}; + +grpc_credentials * +grpc_service_account_jwt_access_credentials_create_from_auth_json_key( + grpc_auth_json_key key, gpr_timespec token_lifetime) { + grpc_service_account_jwt_access_credentials *c; + if (!grpc_auth_json_key_is_valid(&key)) { + gpr_log(GPR_ERROR, "Invalid input for jwt credentials creation"); + return NULL; + } + c = gpr_malloc(sizeof(grpc_service_account_jwt_access_credentials)); + memset(c, 0, sizeof(grpc_service_account_jwt_access_credentials)); + c->base.type = GRPC_CREDENTIALS_TYPE_JWT; + gpr_ref_init(&c->base.refcount, 1); + c->base.vtable = &jwt_vtable; + c->key = key; + c->jwt_lifetime = token_lifetime; + gpr_mu_init(&c->cache_mu); + jwt_reset_cache(c); + return &c->base; +} + +grpc_credentials *grpc_service_account_jwt_access_credentials_create( + const char *json_key, gpr_timespec token_lifetime, void *reserved) { + GPR_ASSERT(reserved == NULL); + return grpc_service_account_jwt_access_credentials_create_from_auth_json_key( + grpc_auth_json_key_create_from_string(json_key), token_lifetime); +} + +/* -- Oauth2TokenFetcher credentials -- */ + +static void oauth2_token_fetcher_destruct(grpc_credentials *creds) { + grpc_oauth2_token_fetcher_credentials *c = + (grpc_oauth2_token_fetcher_credentials *)creds; + grpc_credentials_md_store_unref(c->access_token_md); + gpr_mu_destroy(&c->mu); + grpc_httpcli_context_destroy(&c->httpcli_context); +} + +static int oauth2_token_fetcher_has_request_metadata( + const grpc_credentials *creds) { + return 1; +} + +static int oauth2_token_fetcher_has_request_metadata_only( + const grpc_credentials *creds) { + return 1; +} + +grpc_credentials_status +grpc_oauth2_token_fetcher_credentials_parse_server_response( + const grpc_httpcli_response *response, grpc_credentials_md_store **token_md, + gpr_timespec *token_lifetime) { + char *null_terminated_body = NULL; + char *new_access_token = NULL; + grpc_credentials_status status = GRPC_CREDENTIALS_OK; + grpc_json *json = NULL; + + if (response == NULL) { + gpr_log(GPR_ERROR, "Received NULL response."); + status = GRPC_CREDENTIALS_ERROR; + goto end; + } + + if (response->body_length > 0) { + null_terminated_body = gpr_malloc(response->body_length + 1); + null_terminated_body[response->body_length] = '\0'; + memcpy(null_terminated_body, response->body, response->body_length); + } + + if (response->status != 200) { + gpr_log(GPR_ERROR, "Call to http server ended with error %d [%s].", + response->status, + null_terminated_body != NULL ? null_terminated_body : ""); + status = GRPC_CREDENTIALS_ERROR; + goto end; + } else { + grpc_json *access_token = NULL; + grpc_json *token_type = NULL; + grpc_json *expires_in = NULL; + grpc_json *ptr; + json = grpc_json_parse_string(null_terminated_body); + if (json == NULL) { + gpr_log(GPR_ERROR, "Could not parse JSON from %s", null_terminated_body); + status = GRPC_CREDENTIALS_ERROR; + goto end; + } + if (json->type != GRPC_JSON_OBJECT) { + gpr_log(GPR_ERROR, "Response should be a JSON object"); + status = GRPC_CREDENTIALS_ERROR; + goto end; + } + for (ptr = json->child; ptr; ptr = ptr->next) { + if (strcmp(ptr->key, "access_token") == 0) { + access_token = ptr; + } else if (strcmp(ptr->key, "token_type") == 0) { + token_type = ptr; + } else if (strcmp(ptr->key, "expires_in") == 0) { + expires_in = ptr; + } + } + if (access_token == NULL || access_token->type != GRPC_JSON_STRING) { + gpr_log(GPR_ERROR, "Missing or invalid access_token in JSON."); + status = GRPC_CREDENTIALS_ERROR; + goto end; + } + if (token_type == NULL || token_type->type != GRPC_JSON_STRING) { + gpr_log(GPR_ERROR, "Missing or invalid token_type in JSON."); + status = GRPC_CREDENTIALS_ERROR; + goto end; + } + if (expires_in == NULL || expires_in->type != GRPC_JSON_NUMBER) { + gpr_log(GPR_ERROR, "Missing or invalid expires_in in JSON."); + status = GRPC_CREDENTIALS_ERROR; + goto end; + } + gpr_asprintf(&new_access_token, "%s %s", token_type->value, + access_token->value); + token_lifetime->tv_sec = strtol(expires_in->value, NULL, 10); + token_lifetime->tv_nsec = 0; + token_lifetime->clock_type = GPR_TIMESPAN; + if (*token_md != NULL) grpc_credentials_md_store_unref(*token_md); + *token_md = grpc_credentials_md_store_create(1); + grpc_credentials_md_store_add_cstrings( + *token_md, GRPC_AUTHORIZATION_METADATA_KEY, new_access_token); + status = GRPC_CREDENTIALS_OK; + } + +end: + if (status != GRPC_CREDENTIALS_OK && (*token_md != NULL)) { + grpc_credentials_md_store_unref(*token_md); + *token_md = NULL; + } + if (null_terminated_body != NULL) gpr_free(null_terminated_body); + if (new_access_token != NULL) gpr_free(new_access_token); + if (json != NULL) grpc_json_destroy(json); + return status; +} + +static void on_oauth2_token_fetcher_http_response( + void *user_data, const grpc_httpcli_response *response) { + grpc_credentials_metadata_request *r = + (grpc_credentials_metadata_request *)user_data; + grpc_oauth2_token_fetcher_credentials *c = + (grpc_oauth2_token_fetcher_credentials *)r->creds; + gpr_timespec token_lifetime; + grpc_credentials_status status; + + gpr_mu_lock(&c->mu); + status = grpc_oauth2_token_fetcher_credentials_parse_server_response( + response, &c->access_token_md, &token_lifetime); + if (status == GRPC_CREDENTIALS_OK) { + c->token_expiration = + gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), token_lifetime); + r->cb(r->user_data, c->access_token_md->entries, + c->access_token_md->num_entries, status); + } else { + c->token_expiration = gpr_inf_past(GPR_CLOCK_REALTIME); + r->cb(r->user_data, NULL, 0, status); + } + gpr_mu_unlock(&c->mu); + grpc_credentials_metadata_request_destroy(r); +} + +static void oauth2_token_fetcher_get_request_metadata( + grpc_credentials *creds, grpc_pollset *pollset, const char *service_url, + grpc_credentials_metadata_cb cb, void *user_data) { + grpc_oauth2_token_fetcher_credentials *c = + (grpc_oauth2_token_fetcher_credentials *)creds; + gpr_timespec refresh_threshold = gpr_time_from_seconds( + GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS, GPR_TIMESPAN); + grpc_credentials_md_store *cached_access_token_md = NULL; + { + gpr_mu_lock(&c->mu); + if (c->access_token_md != NULL && + (gpr_time_cmp( + gpr_time_sub(c->token_expiration, gpr_now(GPR_CLOCK_REALTIME)), + refresh_threshold) > 0)) { + cached_access_token_md = + grpc_credentials_md_store_ref(c->access_token_md); + } + gpr_mu_unlock(&c->mu); + } + if (cached_access_token_md != NULL) { + cb(user_data, cached_access_token_md->entries, + cached_access_token_md->num_entries, GRPC_CREDENTIALS_OK); + grpc_credentials_md_store_unref(cached_access_token_md); + } else { + c->fetch_func( + grpc_credentials_metadata_request_create(creds, cb, user_data), + &c->httpcli_context, pollset, on_oauth2_token_fetcher_http_response, + gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), refresh_threshold)); + } +} + +static void init_oauth2_token_fetcher(grpc_oauth2_token_fetcher_credentials *c, + grpc_fetch_oauth2_func fetch_func) { + memset(c, 0, sizeof(grpc_oauth2_token_fetcher_credentials)); + c->base.type = GRPC_CREDENTIALS_TYPE_OAUTH2; + gpr_ref_init(&c->base.refcount, 1); + gpr_mu_init(&c->mu); + c->token_expiration = gpr_inf_past(GPR_CLOCK_REALTIME); + c->fetch_func = fetch_func; + grpc_httpcli_context_init(&c->httpcli_context); +} + +/* -- GoogleComputeEngine credentials. -- */ + +static grpc_credentials_vtable compute_engine_vtable = { + oauth2_token_fetcher_destruct, oauth2_token_fetcher_has_request_metadata, + oauth2_token_fetcher_has_request_metadata_only, + oauth2_token_fetcher_get_request_metadata, NULL}; + +static void compute_engine_fetch_oauth2( + grpc_credentials_metadata_request *metadata_req, + grpc_httpcli_context *httpcli_context, grpc_pollset *pollset, + grpc_httpcli_response_cb response_cb, gpr_timespec deadline) { + grpc_httpcli_header header = {"Metadata-Flavor", "Google"}; + grpc_httpcli_request request; + memset(&request, 0, sizeof(grpc_httpcli_request)); + request.host = GRPC_COMPUTE_ENGINE_METADATA_HOST; + request.path = GRPC_COMPUTE_ENGINE_METADATA_TOKEN_PATH; + request.hdr_count = 1; + request.hdrs = &header; + grpc_httpcli_get(httpcli_context, pollset, &request, deadline, response_cb, + metadata_req); +} + +grpc_credentials *grpc_google_compute_engine_credentials_create( + void *reserved) { + grpc_oauth2_token_fetcher_credentials *c = + gpr_malloc(sizeof(grpc_oauth2_token_fetcher_credentials)); + GPR_ASSERT(reserved == NULL); + init_oauth2_token_fetcher(c, compute_engine_fetch_oauth2); + c->base.vtable = &compute_engine_vtable; + return &c->base; +} + +/* -- GoogleRefreshToken credentials. -- */ + +static void refresh_token_destruct(grpc_credentials *creds) { + grpc_google_refresh_token_credentials *c = + (grpc_google_refresh_token_credentials *)creds; + grpc_auth_refresh_token_destruct(&c->refresh_token); + oauth2_token_fetcher_destruct(&c->base.base); +} + +static grpc_credentials_vtable refresh_token_vtable = { + refresh_token_destruct, oauth2_token_fetcher_has_request_metadata, + oauth2_token_fetcher_has_request_metadata_only, + oauth2_token_fetcher_get_request_metadata, NULL}; + +static void refresh_token_fetch_oauth2( + grpc_credentials_metadata_request *metadata_req, + grpc_httpcli_context *httpcli_context, grpc_pollset *pollset, + grpc_httpcli_response_cb response_cb, gpr_timespec deadline) { + grpc_google_refresh_token_credentials *c = + (grpc_google_refresh_token_credentials *)metadata_req->creds; + grpc_httpcli_header header = {"Content-Type", + "application/x-www-form-urlencoded"}; + grpc_httpcli_request request; + char *body = NULL; + gpr_asprintf(&body, GRPC_REFRESH_TOKEN_POST_BODY_FORMAT_STRING, + c->refresh_token.client_id, c->refresh_token.client_secret, + c->refresh_token.refresh_token); + memset(&request, 0, sizeof(grpc_httpcli_request)); + request.host = GRPC_GOOGLE_OAUTH2_SERVICE_HOST; + request.path = GRPC_GOOGLE_OAUTH2_SERVICE_TOKEN_PATH; + request.hdr_count = 1; + request.hdrs = &header; + request.handshaker = &grpc_httpcli_ssl; + grpc_httpcli_post(httpcli_context, pollset, &request, body, strlen(body), + deadline, response_cb, metadata_req); + gpr_free(body); +} + +grpc_credentials * +grpc_refresh_token_credentials_create_from_auth_refresh_token( + grpc_auth_refresh_token refresh_token) { + grpc_google_refresh_token_credentials *c; + if (!grpc_auth_refresh_token_is_valid(&refresh_token)) { + gpr_log(GPR_ERROR, "Invalid input for refresh token credentials creation"); + return NULL; + } + c = gpr_malloc(sizeof(grpc_google_refresh_token_credentials)); + memset(c, 0, sizeof(grpc_google_refresh_token_credentials)); + init_oauth2_token_fetcher(&c->base, refresh_token_fetch_oauth2); + c->base.base.vtable = &refresh_token_vtable; + c->refresh_token = refresh_token; + return &c->base.base; +} + +grpc_credentials *grpc_google_refresh_token_credentials_create( + const char *json_refresh_token, void *reserved) { + GPR_ASSERT(reserved == NULL); + return grpc_refresh_token_credentials_create_from_auth_refresh_token( + grpc_auth_refresh_token_create_from_string(json_refresh_token)); +} + +/* -- Metadata-only credentials. -- */ + +static void md_only_test_destruct(grpc_credentials *creds) { + grpc_md_only_test_credentials *c = (grpc_md_only_test_credentials *)creds; + grpc_credentials_md_store_unref(c->md_store); +} + +static int md_only_test_has_request_metadata(const grpc_credentials *creds) { + return 1; +} + +static int md_only_test_has_request_metadata_only( + const grpc_credentials *creds) { + return 1; +} + +void on_simulated_token_fetch_done(void *user_data, int success) { + grpc_credentials_metadata_request *r = + (grpc_credentials_metadata_request *)user_data; + grpc_md_only_test_credentials *c = (grpc_md_only_test_credentials *)r->creds; + GPR_ASSERT(success); + r->cb(r->user_data, c->md_store->entries, c->md_store->num_entries, + GRPC_CREDENTIALS_OK); + grpc_credentials_metadata_request_destroy(r); +} + +static void md_only_test_get_request_metadata(grpc_credentials *creds, + grpc_pollset *pollset, + const char *service_url, + grpc_credentials_metadata_cb cb, + void *user_data) { + grpc_md_only_test_credentials *c = (grpc_md_only_test_credentials *)creds; + + if (c->is_async) { + grpc_credentials_metadata_request *cb_arg = + grpc_credentials_metadata_request_create(creds, cb, user_data); + grpc_iomgr_closure_init(cb_arg->on_simulated_token_fetch_done_closure, + on_simulated_token_fetch_done, cb_arg); + grpc_iomgr_add_callback(cb_arg->on_simulated_token_fetch_done_closure); + } else { + cb(user_data, c->md_store->entries, 1, GRPC_CREDENTIALS_OK); + } +} + +static grpc_credentials_vtable md_only_test_vtable = { + md_only_test_destruct, md_only_test_has_request_metadata, + md_only_test_has_request_metadata_only, md_only_test_get_request_metadata, + NULL}; + +grpc_credentials *grpc_md_only_test_credentials_create(const char *md_key, + const char *md_value, + int is_async) { + grpc_md_only_test_credentials *c = + gpr_malloc(sizeof(grpc_md_only_test_credentials)); + memset(c, 0, sizeof(grpc_md_only_test_credentials)); + c->base.type = GRPC_CREDENTIALS_TYPE_OAUTH2; + c->base.vtable = &md_only_test_vtable; + gpr_ref_init(&c->base.refcount, 1); + c->md_store = grpc_credentials_md_store_create(1); + grpc_credentials_md_store_add_cstrings(c->md_store, md_key, md_value); + c->is_async = is_async; + return &c->base; +} + +/* -- Oauth2 Access Token credentials. -- */ + +static void access_token_destruct(grpc_credentials *creds) { + grpc_access_token_credentials *c = (grpc_access_token_credentials *)creds; + grpc_credentials_md_store_unref(c->access_token_md); +} + +static int access_token_has_request_metadata(const grpc_credentials *creds) { + return 1; +} + +static int access_token_has_request_metadata_only( + const grpc_credentials *creds) { + return 1; +} + +static void access_token_get_request_metadata(grpc_credentials *creds, + grpc_pollset *pollset, + const char *service_url, + grpc_credentials_metadata_cb cb, + void *user_data) { + grpc_access_token_credentials *c = (grpc_access_token_credentials *)creds; + cb(user_data, c->access_token_md->entries, 1, GRPC_CREDENTIALS_OK); +} + +static grpc_credentials_vtable access_token_vtable = { + access_token_destruct, access_token_has_request_metadata, + access_token_has_request_metadata_only, access_token_get_request_metadata, + NULL}; + +grpc_credentials *grpc_access_token_credentials_create(const char *access_token, + void *reserved) { + grpc_access_token_credentials *c = + gpr_malloc(sizeof(grpc_access_token_credentials)); + char *token_md_value; + GPR_ASSERT(reserved == NULL); + memset(c, 0, sizeof(grpc_access_token_credentials)); + c->base.type = GRPC_CREDENTIALS_TYPE_OAUTH2; + c->base.vtable = &access_token_vtable; + gpr_ref_init(&c->base.refcount, 1); + c->access_token_md = grpc_credentials_md_store_create(1); + gpr_asprintf(&token_md_value, "Bearer %s", access_token); + grpc_credentials_md_store_add_cstrings( + c->access_token_md, GRPC_AUTHORIZATION_METADATA_KEY, token_md_value); + gpr_free(token_md_value); + return &c->base; +} + +/* -- Fake transport security credentials. -- */ + +static void fake_transport_security_credentials_destruct( + grpc_credentials *creds) { + /* Nothing to do here. */ +} + +static void fake_transport_security_server_credentials_destruct( + grpc_server_credentials *creds) { + /* Nothing to do here. */ +} + +static int fake_transport_security_has_request_metadata( + const grpc_credentials *creds) { + return 0; +} + +static int fake_transport_security_has_request_metadata_only( + const grpc_credentials *creds) { + return 0; +} + +static grpc_security_status fake_transport_security_create_security_connector( + grpc_credentials *c, const char *target, const grpc_channel_args *args, + grpc_credentials *request_metadata_creds, + grpc_channel_security_connector **sc, grpc_channel_args **new_args) { + *sc = grpc_fake_channel_security_connector_create(request_metadata_creds, 1); + return GRPC_SECURITY_OK; +} + +static grpc_security_status +fake_transport_security_server_create_security_connector( + grpc_server_credentials *c, grpc_security_connector **sc) { + *sc = grpc_fake_server_security_connector_create(); + return GRPC_SECURITY_OK; +} + +static grpc_credentials_vtable fake_transport_security_credentials_vtable = { + fake_transport_security_credentials_destruct, + fake_transport_security_has_request_metadata, + fake_transport_security_has_request_metadata_only, NULL, + fake_transport_security_create_security_connector}; + +static grpc_server_credentials_vtable + fake_transport_security_server_credentials_vtable = { + fake_transport_security_server_credentials_destruct, + fake_transport_security_server_create_security_connector}; + +grpc_credentials *grpc_fake_transport_security_credentials_create(void) { + grpc_credentials *c = gpr_malloc(sizeof(grpc_credentials)); + memset(c, 0, sizeof(grpc_credentials)); + c->type = GRPC_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY; + c->vtable = &fake_transport_security_credentials_vtable; + gpr_ref_init(&c->refcount, 1); + return c; +} + +grpc_server_credentials *grpc_fake_transport_security_server_credentials_create( + void) { + grpc_server_credentials *c = gpr_malloc(sizeof(grpc_server_credentials)); + memset(c, 0, sizeof(grpc_server_credentials)); + c->type = GRPC_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY; + gpr_ref_init(&c->refcount, 1); + c->vtable = &fake_transport_security_server_credentials_vtable; + return c; +} + +/* -- Composite credentials. -- */ + +typedef struct { + grpc_composite_credentials *composite_creds; + size_t creds_index; + grpc_credentials_md_store *md_elems; + char *service_url; + void *user_data; + grpc_pollset *pollset; + grpc_credentials_metadata_cb cb; +} grpc_composite_credentials_metadata_context; + +static void composite_destruct(grpc_credentials *creds) { + grpc_composite_credentials *c = (grpc_composite_credentials *)creds; + size_t i; + for (i = 0; i < c->inner.num_creds; i++) { + grpc_credentials_unref(c->inner.creds_array[i]); + } + gpr_free(c->inner.creds_array); +} + +static int composite_has_request_metadata(const grpc_credentials *creds) { + const grpc_composite_credentials *c = + (const grpc_composite_credentials *)creds; + size_t i; + for (i = 0; i < c->inner.num_creds; i++) { + if (grpc_credentials_has_request_metadata(c->inner.creds_array[i])) { + return 1; + } + } + return 0; +} + +static int composite_has_request_metadata_only(const grpc_credentials *creds) { + const grpc_composite_credentials *c = + (const grpc_composite_credentials *)creds; + size_t i; + for (i = 0; i < c->inner.num_creds; i++) { + if (!grpc_credentials_has_request_metadata_only(c->inner.creds_array[i])) { + return 0; + } + } + return 1; +} + +static void composite_md_context_destroy( + grpc_composite_credentials_metadata_context *ctx) { + grpc_credentials_md_store_unref(ctx->md_elems); + if (ctx->service_url != NULL) gpr_free(ctx->service_url); + gpr_free(ctx); +} + +static void composite_metadata_cb(void *user_data, + grpc_credentials_md *md_elems, size_t num_md, + grpc_credentials_status status) { + grpc_composite_credentials_metadata_context *ctx = + (grpc_composite_credentials_metadata_context *)user_data; + if (status != GRPC_CREDENTIALS_OK) { + ctx->cb(ctx->user_data, NULL, 0, status); + return; + } + + /* Copy the metadata in the context. */ + if (num_md > 0) { + size_t i; + for (i = 0; i < num_md; i++) { + grpc_credentials_md_store_add(ctx->md_elems, md_elems[i].key, + md_elems[i].value); + } + } + + /* See if we need to get some more metadata. */ + while (ctx->creds_index < ctx->composite_creds->inner.num_creds) { + grpc_credentials *inner_creds = + ctx->composite_creds->inner.creds_array[ctx->creds_index++]; + if (grpc_credentials_has_request_metadata(inner_creds)) { + grpc_credentials_get_request_metadata(inner_creds, ctx->pollset, + ctx->service_url, + composite_metadata_cb, ctx); + return; + } + } + + /* We're done!. */ + ctx->cb(ctx->user_data, ctx->md_elems->entries, ctx->md_elems->num_entries, + GRPC_CREDENTIALS_OK); + composite_md_context_destroy(ctx); +} + +static void composite_get_request_metadata(grpc_credentials *creds, + grpc_pollset *pollset, + const char *service_url, + grpc_credentials_metadata_cb cb, + void *user_data) { + grpc_composite_credentials *c = (grpc_composite_credentials *)creds; + grpc_composite_credentials_metadata_context *ctx; + if (!grpc_credentials_has_request_metadata(creds)) { + cb(user_data, NULL, 0, GRPC_CREDENTIALS_OK); + return; + } + ctx = gpr_malloc(sizeof(grpc_composite_credentials_metadata_context)); + memset(ctx, 0, sizeof(grpc_composite_credentials_metadata_context)); + ctx->service_url = gpr_strdup(service_url); + ctx->user_data = user_data; + ctx->cb = cb; + ctx->composite_creds = c; + ctx->pollset = pollset; + ctx->md_elems = grpc_credentials_md_store_create(c->inner.num_creds); + while (ctx->creds_index < c->inner.num_creds) { + grpc_credentials *inner_creds = c->inner.creds_array[ctx->creds_index++]; + if (grpc_credentials_has_request_metadata(inner_creds)) { + grpc_credentials_get_request_metadata(inner_creds, pollset, service_url, + composite_metadata_cb, ctx); + return; + } + } + GPR_ASSERT(0); /* Should have exited before. */ +} + +static grpc_security_status composite_create_security_connector( + grpc_credentials *creds, const char *target, const grpc_channel_args *args, + grpc_credentials *request_metadata_creds, + grpc_channel_security_connector **sc, grpc_channel_args **new_args) { + grpc_composite_credentials *c = (grpc_composite_credentials *)creds; + if (c->connector_creds == NULL) { + gpr_log(GPR_ERROR, + "Cannot create security connector, missing connector credentials."); + return GRPC_SECURITY_ERROR; + } + return grpc_credentials_create_security_connector(c->connector_creds, target, + args, creds, sc, new_args); +} + +static grpc_credentials_vtable composite_credentials_vtable = { + composite_destruct, composite_has_request_metadata, + composite_has_request_metadata_only, composite_get_request_metadata, + composite_create_security_connector}; + +static grpc_credentials_array get_creds_array(grpc_credentials **creds_addr) { + grpc_credentials_array result; + grpc_credentials *creds = *creds_addr; + result.creds_array = creds_addr; + result.num_creds = 1; + if (strcmp(creds->type, GRPC_CREDENTIALS_TYPE_COMPOSITE) == 0) { + result = *grpc_composite_credentials_get_credentials(creds); + } + return result; +} + +grpc_credentials *grpc_composite_credentials_create(grpc_credentials *creds1, + grpc_credentials *creds2, + void *reserved) { + size_t i; + size_t creds_array_byte_size; + grpc_credentials_array creds1_array; + grpc_credentials_array creds2_array; + grpc_composite_credentials *c; + GPR_ASSERT(reserved == NULL); + GPR_ASSERT(creds1 != NULL); + GPR_ASSERT(creds2 != NULL); + c = gpr_malloc(sizeof(grpc_composite_credentials)); + memset(c, 0, sizeof(grpc_composite_credentials)); + c->base.type = GRPC_CREDENTIALS_TYPE_COMPOSITE; + c->base.vtable = &composite_credentials_vtable; + gpr_ref_init(&c->base.refcount, 1); + creds1_array = get_creds_array(&creds1); + creds2_array = get_creds_array(&creds2); + c->inner.num_creds = creds1_array.num_creds + creds2_array.num_creds; + creds_array_byte_size = c->inner.num_creds * sizeof(grpc_credentials *); + c->inner.creds_array = gpr_malloc(creds_array_byte_size); + memset(c->inner.creds_array, 0, creds_array_byte_size); + for (i = 0; i < creds1_array.num_creds; i++) { + grpc_credentials *cur_creds = creds1_array.creds_array[i]; + if (!grpc_credentials_has_request_metadata_only(cur_creds)) { + if (c->connector_creds == NULL) { + c->connector_creds = cur_creds; + } else { + gpr_log(GPR_ERROR, "Cannot compose multiple connector credentials."); + goto fail; + } + } + c->inner.creds_array[i] = grpc_credentials_ref(cur_creds); + } + for (i = 0; i < creds2_array.num_creds; i++) { + grpc_credentials *cur_creds = creds2_array.creds_array[i]; + if (!grpc_credentials_has_request_metadata_only(cur_creds)) { + if (c->connector_creds == NULL) { + c->connector_creds = cur_creds; + } else { + gpr_log(GPR_ERROR, "Cannot compose multiple connector credentials."); + goto fail; + } + } + c->inner.creds_array[i + creds1_array.num_creds] = + grpc_credentials_ref(cur_creds); + } + return &c->base; + +fail: + grpc_credentials_unref(&c->base); + return NULL; +} + +const grpc_credentials_array *grpc_composite_credentials_get_credentials( + grpc_credentials *creds) { + const grpc_composite_credentials *c = + (const grpc_composite_credentials *)creds; + GPR_ASSERT(strcmp(creds->type, GRPC_CREDENTIALS_TYPE_COMPOSITE) == 0); + return &c->inner; +} + +grpc_credentials *grpc_credentials_contains_type( + grpc_credentials *creds, const char *type, + grpc_credentials **composite_creds) { + size_t i; + if (strcmp(creds->type, type) == 0) { + if (composite_creds != NULL) *composite_creds = NULL; + return creds; + } else if (strcmp(creds->type, GRPC_CREDENTIALS_TYPE_COMPOSITE) == 0) { + const grpc_credentials_array *inner_creds_array = + grpc_composite_credentials_get_credentials(creds); + for (i = 0; i < inner_creds_array->num_creds; i++) { + if (strcmp(type, inner_creds_array->creds_array[i]->type) == 0) { + if (composite_creds != NULL) *composite_creds = creds; + return inner_creds_array->creds_array[i]; + } + } + } + return NULL; +} + +/* -- IAM credentials. -- */ + +static void iam_destruct(grpc_credentials *creds) { + grpc_google_iam_credentials *c = (grpc_google_iam_credentials *)creds; + grpc_credentials_md_store_unref(c->iam_md); +} + +static int iam_has_request_metadata(const grpc_credentials *creds) { return 1; } + +static int iam_has_request_metadata_only(const grpc_credentials *creds) { + return 1; +} + +static void iam_get_request_metadata(grpc_credentials *creds, + grpc_pollset *pollset, + const char *service_url, + grpc_credentials_metadata_cb cb, + void *user_data) { + grpc_google_iam_credentials *c = (grpc_google_iam_credentials *)creds; + cb(user_data, c->iam_md->entries, c->iam_md->num_entries, + GRPC_CREDENTIALS_OK); +} + +static grpc_credentials_vtable iam_vtable = { + iam_destruct, iam_has_request_metadata, iam_has_request_metadata_only, + iam_get_request_metadata, NULL}; + +grpc_credentials *grpc_google_iam_credentials_create( + const char *token, const char *authority_selector, void *reserved) { + grpc_google_iam_credentials *c; + GPR_ASSERT(reserved == NULL); + GPR_ASSERT(token != NULL); + GPR_ASSERT(authority_selector != NULL); + c = gpr_malloc(sizeof(grpc_google_iam_credentials)); + memset(c, 0, sizeof(grpc_google_iam_credentials)); + c->base.type = GRPC_CREDENTIALS_TYPE_IAM; + c->base.vtable = &iam_vtable; + gpr_ref_init(&c->base.refcount, 1); + c->iam_md = grpc_credentials_md_store_create(2); + grpc_credentials_md_store_add_cstrings( + c->iam_md, GRPC_IAM_AUTHORIZATION_TOKEN_METADATA_KEY, token); + grpc_credentials_md_store_add_cstrings( + c->iam_md, GRPC_IAM_AUTHORITY_SELECTOR_METADATA_KEY, authority_selector); + return &c->base; +} diff --git a/src/core/security/credentials.h b/src/core/security/credentials.h new file mode 100644 index 00000000..8e4fed76 --- /dev/null +++ b/src/core/security/credentials.h @@ -0,0 +1,325 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_SECURITY_CREDENTIALS_H +#define GRPC_INTERNAL_CORE_SECURITY_CREDENTIALS_H + +#include "src/core/transport/stream_op.h" +#include +#include +#include + +#include "src/core/httpcli/httpcli.h" +#include "src/core/security/json_token.h" +#include "src/core/security/security_connector.h" + +struct grpc_httpcli_response; + +/* --- Constants. --- */ + +typedef enum { + GRPC_CREDENTIALS_OK = 0, + GRPC_CREDENTIALS_ERROR +} grpc_credentials_status; + +#define GRPC_FAKE_TRANSPORT_SECURITY_TYPE "fake" + +#define GRPC_CREDENTIALS_TYPE_SSL "Ssl" +#define GRPC_CREDENTIALS_TYPE_OAUTH2 "Oauth2" +#define GRPC_CREDENTIALS_TYPE_JWT "Jwt" +#define GRPC_CREDENTIALS_TYPE_IAM "Iam" +#define GRPC_CREDENTIALS_TYPE_COMPOSITE "Composite" +#define GRPC_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY "FakeTransportSecurity" + +#define GRPC_AUTHORIZATION_METADATA_KEY "Authorization" +#define GRPC_IAM_AUTHORIZATION_TOKEN_METADATA_KEY \ + "x-goog-iam-authorization-token" +#define GRPC_IAM_AUTHORITY_SELECTOR_METADATA_KEY "x-goog-iam-authority-selector" + +#define GRPC_GOOGLE_CLOUD_SDK_CONFIG_DIRECTORY "gcloud" +#define GRPC_GOOGLE_WELL_KNOWN_CREDENTIALS_FILE \ + "application_default_credentials.json" + +#define GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS 60 + +#define GRPC_COMPUTE_ENGINE_METADATA_HOST "metadata" +#define GRPC_COMPUTE_ENGINE_METADATA_TOKEN_PATH \ + "/computeMetadata/v1/instance/service-accounts/default/token" + +#define GRPC_GOOGLE_OAUTH2_SERVICE_HOST "www.googleapis.com" +#define GRPC_GOOGLE_OAUTH2_SERVICE_TOKEN_PATH "/oauth2/v3/token" + +#define GRPC_SERVICE_ACCOUNT_POST_BODY_PREFIX \ + "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&" \ + "assertion=" + +#define GRPC_REFRESH_TOKEN_POST_BODY_FORMAT_STRING \ + "client_id=%s&client_secret=%s&refresh_token=%s&grant_type=refresh_token" + +/* --- grpc_credentials_md. --- */ + +typedef struct { + gpr_slice key; + gpr_slice value; +} grpc_credentials_md; + +typedef struct { + grpc_credentials_md *entries; + size_t num_entries; + size_t allocated; + gpr_refcount refcount; +} grpc_credentials_md_store; + +grpc_credentials_md_store *grpc_credentials_md_store_create( + size_t initial_capacity); + +/* Will ref key and value. */ +void grpc_credentials_md_store_add(grpc_credentials_md_store *store, + gpr_slice key, gpr_slice value); +void grpc_credentials_md_store_add_cstrings(grpc_credentials_md_store *store, + const char *key, const char *value); +grpc_credentials_md_store *grpc_credentials_md_store_ref( + grpc_credentials_md_store *store); +void grpc_credentials_md_store_unref(grpc_credentials_md_store *store); + +/* --- grpc_credentials. --- */ + +/* Creates a fake transport security credentials object for testing. */ +grpc_credentials *grpc_fake_transport_security_credentials_create(void); +/* Creates a fake server transport security credentials object for testing. */ +grpc_server_credentials *grpc_fake_transport_security_server_credentials_create( + void); + +/* It is the caller's responsibility to gpr_free the result if not NULL. */ +char *grpc_get_well_known_google_credentials_file_path(void); + +typedef void (*grpc_credentials_metadata_cb)(void *user_data, + grpc_credentials_md *md_elems, + size_t num_md, + grpc_credentials_status status); + +typedef struct { + void (*destruct)(grpc_credentials *c); + int (*has_request_metadata)(const grpc_credentials *c); + int (*has_request_metadata_only)(const grpc_credentials *c); + void (*get_request_metadata)(grpc_credentials *c, grpc_pollset *pollset, + const char *service_url, + grpc_credentials_metadata_cb cb, + void *user_data); + grpc_security_status (*create_security_connector)( + grpc_credentials *c, const char *target, const grpc_channel_args *args, + grpc_credentials *request_metadata_creds, + grpc_channel_security_connector **sc, grpc_channel_args **new_args); +} grpc_credentials_vtable; + +struct grpc_credentials { + const grpc_credentials_vtable *vtable; + const char *type; + gpr_refcount refcount; +}; + +grpc_credentials *grpc_credentials_ref(grpc_credentials *creds); +void grpc_credentials_unref(grpc_credentials *creds); +int grpc_credentials_has_request_metadata(grpc_credentials *creds); +int grpc_credentials_has_request_metadata_only(grpc_credentials *creds); +void grpc_credentials_get_request_metadata(grpc_credentials *creds, + grpc_pollset *pollset, + const char *service_url, + grpc_credentials_metadata_cb cb, + void *user_data); + +/* Creates a security connector for the channel. May also create new channel + args for the channel to be used in place of the passed in const args if + returned non NULL. In that case the caller is responsible for destroying + new_args after channel creation. */ +grpc_security_status grpc_credentials_create_security_connector( + grpc_credentials *creds, const char *target, const grpc_channel_args *args, + grpc_credentials *request_metadata_creds, + grpc_channel_security_connector **sc, grpc_channel_args **new_args); + +typedef struct { + grpc_credentials **creds_array; + size_t num_creds; +} grpc_credentials_array; + +const grpc_credentials_array *grpc_composite_credentials_get_credentials( + grpc_credentials *composite_creds); + +/* Returns creds if creds is of the specified type or the inner creds of the + specified type (if found), if the creds is of type COMPOSITE. + If composite_creds is not NULL, *composite_creds will point to creds if of + type COMPOSITE in case of success. */ +grpc_credentials *grpc_credentials_contains_type( + grpc_credentials *creds, const char *type, + grpc_credentials **composite_creds); + +/* Exposed for testing only. */ +grpc_credentials_status +grpc_oauth2_token_fetcher_credentials_parse_server_response( + const struct grpc_httpcli_response *response, + grpc_credentials_md_store **token_md, gpr_timespec *token_lifetime); +void grpc_flush_cached_google_default_credentials(void); + +/* Metadata-only credentials with the specified key and value where + asynchronicity can be simulated for testing. */ +grpc_credentials *grpc_md_only_test_credentials_create(const char *md_key, + const char *md_value, + int is_async); + +/* Private constructor for jwt credentials from an already parsed json key. + Takes ownership of the key. */ +grpc_credentials * +grpc_service_account_jwt_access_credentials_create_from_auth_json_key( + grpc_auth_json_key key, gpr_timespec token_lifetime); + +/* Private constructor for refresh token credentials from an already parsed + refresh token. Takes ownership of the refresh token. */ +grpc_credentials *grpc_refresh_token_credentials_create_from_auth_refresh_token( + grpc_auth_refresh_token token); + +/* --- grpc_server_credentials. --- */ + +typedef struct { + void (*destruct)(grpc_server_credentials *c); + grpc_security_status (*create_security_connector)( + grpc_server_credentials *c, grpc_security_connector **sc); +} grpc_server_credentials_vtable; + + +/* TODO(jboeuf): Add a refcount. */ +struct grpc_server_credentials { + const grpc_server_credentials_vtable *vtable; + const char *type; + gpr_refcount refcount; + grpc_auth_metadata_processor processor; +}; + +grpc_security_status grpc_server_credentials_create_security_connector( + grpc_server_credentials *creds, grpc_security_connector **sc); + +grpc_server_credentials *grpc_server_credentials_ref( + grpc_server_credentials *creds); + +void grpc_server_credentials_unref(grpc_server_credentials *creds); + +/* -- Ssl credentials. -- */ + +typedef struct { + grpc_credentials base; + grpc_ssl_config config; +} grpc_ssl_credentials; + +typedef struct { + grpc_server_credentials base; + grpc_ssl_server_config config; +} grpc_ssl_server_credentials; + +/* -- Jwt credentials -- */ + +typedef struct { + grpc_credentials base; + + /* Have a simple cache for now with just 1 entry. We could have a map based on + the service_url for a more sophisticated one. */ + gpr_mu cache_mu; + struct { + grpc_credentials_md_store *jwt_md; + char *service_url; + gpr_timespec jwt_expiration; + } cached; + + grpc_auth_json_key key; + gpr_timespec jwt_lifetime; +} grpc_service_account_jwt_access_credentials; + +/* -- Oauth2TokenFetcher credentials -- + + This object is a base for credentials that need to acquire an oauth2 token + from an http service. */ + +typedef struct grpc_credentials_metadata_request + grpc_credentials_metadata_request; + +typedef void (*grpc_fetch_oauth2_func)(grpc_credentials_metadata_request *req, + grpc_httpcli_context *http_context, + grpc_pollset *pollset, + grpc_httpcli_response_cb response_cb, + gpr_timespec deadline); + +typedef struct { + grpc_credentials base; + gpr_mu mu; + grpc_credentials_md_store *access_token_md; + gpr_timespec token_expiration; + grpc_httpcli_context httpcli_context; + grpc_fetch_oauth2_func fetch_func; +} grpc_oauth2_token_fetcher_credentials; + +/* -- GoogleRefreshToken credentials. -- */ + +typedef struct { + grpc_oauth2_token_fetcher_credentials base; + grpc_auth_refresh_token refresh_token; +} grpc_google_refresh_token_credentials; + +/* -- Oauth2 Access Token credentials. -- */ + +typedef struct { + grpc_credentials base; + grpc_credentials_md_store *access_token_md; +} grpc_access_token_credentials; + +/* -- Metadata-only Test credentials. -- */ + +typedef struct { + grpc_credentials base; + grpc_credentials_md_store *md_store; + int is_async; +} grpc_md_only_test_credentials; + +/* -- GoogleIAM credentials. -- */ + +typedef struct { + grpc_credentials base; + grpc_credentials_md_store *iam_md; +} grpc_google_iam_credentials; + +/* -- Composite credentials. -- */ + +typedef struct { + grpc_credentials base; + grpc_credentials_array inner; + grpc_credentials *connector_creds; +} grpc_composite_credentials; + +#endif /* GRPC_INTERNAL_CORE_SECURITY_CREDENTIALS_H */ diff --git a/src/core/security/credentials_metadata.c b/src/core/security/credentials_metadata.c new file mode 100644 index 00000000..b8a132f1 --- /dev/null +++ b/src/core/security/credentials_metadata.c @@ -0,0 +1,101 @@ +/* + * + * Copyright 2015, 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 "src/core/security/credentials.h" + +#include + +#include + +static void store_ensure_capacity(grpc_credentials_md_store *store) { + if (store->num_entries == store->allocated) { + store->allocated = (store->allocated == 0) ? 1 : store->allocated * 2; + store->entries = gpr_realloc( + store->entries, store->allocated * sizeof(grpc_credentials_md)); + } +} + +grpc_credentials_md_store *grpc_credentials_md_store_create( + size_t initial_capacity) { + grpc_credentials_md_store *store = + gpr_malloc(sizeof(grpc_credentials_md_store)); + memset(store, 0, sizeof(grpc_credentials_md_store)); + if (initial_capacity > 0) { + store->entries = gpr_malloc(initial_capacity * sizeof(grpc_credentials_md)); + store->allocated = initial_capacity; + } + gpr_ref_init(&store->refcount, 1); + return store; +} + +void grpc_credentials_md_store_add(grpc_credentials_md_store *store, + gpr_slice key, gpr_slice value) { + if (store == NULL) return; + store_ensure_capacity(store); + store->entries[store->num_entries].key = gpr_slice_ref(key); + store->entries[store->num_entries].value = gpr_slice_ref(value); + store->num_entries++; +} + +void grpc_credentials_md_store_add_cstrings(grpc_credentials_md_store *store, + const char *key, + const char *value) { + if (store == NULL) return; + store_ensure_capacity(store); + store->entries[store->num_entries].key = gpr_slice_from_copied_string(key); + store->entries[store->num_entries].value = + gpr_slice_from_copied_string(value); + store->num_entries++; +} + +grpc_credentials_md_store *grpc_credentials_md_store_ref( + grpc_credentials_md_store *store) { + if (store == NULL) return NULL; + gpr_ref(&store->refcount); + return store; +} + +void grpc_credentials_md_store_unref(grpc_credentials_md_store *store) { + if (store == NULL) return; + if (gpr_unref(&store->refcount)) { + if (store->entries != NULL) { + size_t i; + for (i = 0; i < store->num_entries; i++) { + gpr_slice_unref(store->entries[i].key); + gpr_slice_unref(store->entries[i].value); + } + gpr_free(store->entries); + } + gpr_free(store); + } +} diff --git a/src/core/security/credentials_posix.c b/src/core/security/credentials_posix.c new file mode 100644 index 00000000..20f67a7f --- /dev/null +++ b/src/core/security/credentials_posix.c @@ -0,0 +1,61 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_POSIX_FILE + +#include "src/core/security/credentials.h" + +#include +#include +#include + +#include "src/core/support/env.h" +#include "src/core/support/string.h" + +char *grpc_get_well_known_google_credentials_file_path(void) { + char *result = NULL; + char *home = gpr_getenv("HOME"); + if (home == NULL) { + gpr_log(GPR_ERROR, "Could not get HOME environment variable."); + return NULL; + } + gpr_asprintf(&result, "%s/.config/%s/%s", home, + GRPC_GOOGLE_CLOUD_SDK_CONFIG_DIRECTORY, + GRPC_GOOGLE_WELL_KNOWN_CREDENTIALS_FILE); + gpr_free(home); + return result; +} + +#endif /* GPR_POSIX_FILE */ diff --git a/src/core/security/credentials_win32.c b/src/core/security/credentials_win32.c new file mode 100644 index 00000000..92dfd9bd --- /dev/null +++ b/src/core/security/credentials_win32.c @@ -0,0 +1,61 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_WIN32 + +#include "src/core/security/credentials.h" + +#include +#include +#include + +#include "src/core/support/env.h" +#include "src/core/support/string.h" + +char *grpc_get_well_known_google_credentials_file_path(void) { + char *result = NULL; + char *appdata_path = gpr_getenv("APPDATA"); + if (appdata_path == NULL) { + gpr_log(GPR_ERROR, "Could not get APPDATA environment variable."); + return NULL; + } + gpr_asprintf(&result, "%s/%s/%s", appdata_path, + GRPC_GOOGLE_CLOUD_SDK_CONFIG_DIRECTORY, + GRPC_GOOGLE_WELL_KNOWN_CREDENTIALS_FILE); + gpr_free(appdata_path); + return result; +} + +#endif /* GPR_WIN32 */ diff --git a/src/core/security/google_default_credentials.c b/src/core/security/google_default_credentials.c new file mode 100644 index 00000000..874dd59e --- /dev/null +++ b/src/core/security/google_default_credentials.c @@ -0,0 +1,225 @@ +/* + * + * Copyright 2015, 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 "src/core/security/credentials.h" + +#include + +#include +#include +#include + +#include "src/core/httpcli/httpcli.h" +#include "src/core/support/env.h" +#include "src/core/support/file.h" + +/* -- Constants. -- */ + +#define GRPC_COMPUTE_ENGINE_DETECTION_HOST "metadata.google.internal" + +/* -- Default credentials. -- */ + +static grpc_credentials *default_credentials = NULL; +static int compute_engine_detection_done = 0; +static gpr_mu g_mu; +static gpr_once g_once = GPR_ONCE_INIT; + +static void init_default_credentials(void) { gpr_mu_init(&g_mu); } + +typedef struct { + grpc_pollset pollset; + int is_done; + int success; +} compute_engine_detector; + +static void on_compute_engine_detection_http_response( + void *user_data, const grpc_httpcli_response *response) { + compute_engine_detector *detector = (compute_engine_detector *)user_data; + if (response != NULL && response->status == 200 && response->hdr_count > 0) { + /* Internet providers can return a generic response to all requests, so + it is necessary to check that metadata header is present also. */ + size_t i; + for (i = 0; i < response->hdr_count; i++) { + grpc_httpcli_header *header = &response->hdrs[i]; + if (strcmp(header->key, "Metadata-Flavor") == 0 && + strcmp(header->value, "Google") == 0) { + detector->success = 1; + break; + } + } + } + gpr_mu_lock(GRPC_POLLSET_MU(&detector->pollset)); + detector->is_done = 1; + grpc_pollset_kick(&detector->pollset, NULL); + gpr_mu_unlock(GRPC_POLLSET_MU(&detector->pollset)); +} + +static void destroy_pollset(void *p) { grpc_pollset_destroy(p); } + +static int is_stack_running_on_compute_engine(void) { + compute_engine_detector detector; + grpc_httpcli_request request; + grpc_httpcli_context context; + + /* The http call is local. If it takes more than one sec, it is for sure not + on compute engine. */ + gpr_timespec max_detection_delay = gpr_time_from_seconds(1, GPR_TIMESPAN); + + grpc_pollset_init(&detector.pollset); + detector.is_done = 0; + detector.success = 0; + + memset(&request, 0, sizeof(grpc_httpcli_request)); + request.host = GRPC_COMPUTE_ENGINE_DETECTION_HOST; + request.path = "/"; + + grpc_httpcli_context_init(&context); + + grpc_httpcli_get( + &context, &detector.pollset, &request, + gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), max_detection_delay), + on_compute_engine_detection_http_response, &detector); + + /* Block until we get the response. This is not ideal but this should only be + called once for the lifetime of the process by the default credentials. */ + gpr_mu_lock(GRPC_POLLSET_MU(&detector.pollset)); + while (!detector.is_done) { + grpc_pollset_worker worker; + grpc_pollset_work(&detector.pollset, &worker, gpr_now(GPR_CLOCK_MONOTONIC), + gpr_inf_future(GPR_CLOCK_MONOTONIC)); + } + gpr_mu_unlock(GRPC_POLLSET_MU(&detector.pollset)); + + grpc_httpcli_context_destroy(&context); + grpc_pollset_shutdown(&detector.pollset, destroy_pollset, &detector.pollset); + + return detector.success; +} + +/* Takes ownership of creds_path if not NULL. */ +static grpc_credentials *create_default_creds_from_path(char *creds_path) { + grpc_json *json = NULL; + grpc_auth_json_key key; + grpc_auth_refresh_token token; + grpc_credentials *result = NULL; + gpr_slice creds_data = gpr_empty_slice(); + int file_ok = 0; + if (creds_path == NULL) goto end; + creds_data = gpr_load_file(creds_path, 0, &file_ok); + if (!file_ok) goto end; + json = grpc_json_parse_string_with_len( + (char *)GPR_SLICE_START_PTR(creds_data), GPR_SLICE_LENGTH(creds_data)); + if (json == NULL) goto end; + + /* First, try an auth json key. */ + key = grpc_auth_json_key_create_from_json(json); + if (grpc_auth_json_key_is_valid(&key)) { + result = + grpc_service_account_jwt_access_credentials_create_from_auth_json_key( + key, grpc_max_auth_token_lifetime); + goto end; + } + + /* Then try a refresh token if the auth json key was invalid. */ + token = grpc_auth_refresh_token_create_from_json(json); + if (grpc_auth_refresh_token_is_valid(&token)) { + result = + grpc_refresh_token_credentials_create_from_auth_refresh_token(token); + goto end; + } + +end: + if (creds_path != NULL) gpr_free(creds_path); + gpr_slice_unref(creds_data); + if (json != NULL) grpc_json_destroy(json); + return result; +} + +grpc_credentials *grpc_google_default_credentials_create(void) { + grpc_credentials *result = NULL; + int serving_cached_credentials = 0; + gpr_once_init(&g_once, init_default_credentials); + + gpr_mu_lock(&g_mu); + + if (default_credentials != NULL) { + result = grpc_credentials_ref(default_credentials); + serving_cached_credentials = 1; + goto end; + } + + /* First, try the environment variable. */ + result = create_default_creds_from_path( + gpr_getenv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR)); + if (result != NULL) goto end; + + /* Then the well-known file. */ + result = create_default_creds_from_path( + grpc_get_well_known_google_credentials_file_path()); + if (result != NULL) goto end; + + /* At last try to see if we're on compute engine (do the detection only once + since it requires a network test). */ + if (!compute_engine_detection_done) { + int need_compute_engine_creds = is_stack_running_on_compute_engine(); + compute_engine_detection_done = 1; + if (need_compute_engine_creds) { + result = grpc_google_compute_engine_credentials_create(NULL); + } + } + +end: + if (!serving_cached_credentials && result != NULL) { + /* Blend with default ssl credentials and add a global reference so that it + can be cached and re-served. */ + grpc_credentials *ssl_creds = grpc_ssl_credentials_create(NULL, NULL, NULL); + default_credentials = grpc_credentials_ref( + grpc_composite_credentials_create(ssl_creds, result, NULL)); + GPR_ASSERT(default_credentials != NULL); + grpc_credentials_unref(ssl_creds); + grpc_credentials_unref(result); + result = default_credentials; + } + gpr_mu_unlock(&g_mu); + return result; +} + +void grpc_flush_cached_google_default_credentials(void) { + gpr_once_init(&g_once, init_default_credentials); + gpr_mu_lock(&g_mu); + if (default_credentials != NULL) { + grpc_credentials_unref(default_credentials); + default_credentials = NULL; + } + gpr_mu_unlock(&g_mu); +} diff --git a/src/core/security/json_token.c b/src/core/security/json_token.c new file mode 100644 index 00000000..021912f3 --- /dev/null +++ b/src/core/security/json_token.c @@ -0,0 +1,405 @@ +/* + * + * Copyright 2015, 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 "src/core/security/json_token.h" + +#include + +#include +#include +#include + +#include "src/core/security/base64.h" +#include "src/core/support/string.h" + +#include +#include +#include + +/* --- Constants. --- */ + +/* 1 hour max. */ +const gpr_timespec grpc_max_auth_token_lifetime = {3600, 0, GPR_TIMESPAN}; + +#define GRPC_JWT_RSA_SHA256_ALGORITHM "RS256" +#define GRPC_JWT_TYPE "JWT" + +/* --- Override for testing. --- */ + +static grpc_jwt_encode_and_sign_override g_jwt_encode_and_sign_override = NULL; + +/* --- grpc_auth_json_key. --- */ + +static const char *json_get_string_property(const grpc_json *json, + const char *prop_name) { + grpc_json *child; + for (child = json->child; child != NULL; child = child->next) { + if (strcmp(child->key, prop_name) == 0) break; + } + if (child == NULL || child->type != GRPC_JSON_STRING) { + gpr_log(GPR_ERROR, "Invalid or missing %s property.", prop_name); + return NULL; + } + return child->value; +} + +static int set_json_key_string_property(const grpc_json *json, + const char *prop_name, + char **json_key_field) { + const char *prop_value = json_get_string_property(json, prop_name); + if (prop_value == NULL) return 0; + *json_key_field = gpr_strdup(prop_value); + return 1; +} + +int grpc_auth_json_key_is_valid(const grpc_auth_json_key *json_key) { + return (json_key != NULL) && + strcmp(json_key->type, GRPC_AUTH_JSON_TYPE_INVALID); +} + +grpc_auth_json_key grpc_auth_json_key_create_from_json(const grpc_json *json) { + grpc_auth_json_key result; + BIO *bio = NULL; + const char *prop_value; + int success = 0; + + memset(&result, 0, sizeof(grpc_auth_json_key)); + result.type = GRPC_AUTH_JSON_TYPE_INVALID; + if (json == NULL) { + gpr_log(GPR_ERROR, "Invalid json."); + goto end; + } + + prop_value = json_get_string_property(json, "type"); + if (prop_value == NULL || + strcmp(prop_value, GRPC_AUTH_JSON_TYPE_SERVICE_ACCOUNT)) { + goto end; + } + result.type = GRPC_AUTH_JSON_TYPE_SERVICE_ACCOUNT; + + if (!set_json_key_string_property(json, "private_key_id", + &result.private_key_id) || + !set_json_key_string_property(json, "client_id", &result.client_id) || + !set_json_key_string_property(json, "client_email", + &result.client_email)) { + goto end; + } + + prop_value = json_get_string_property(json, "private_key"); + if (prop_value == NULL) { + goto end; + } + bio = BIO_new(BIO_s_mem()); + success = BIO_puts(bio, prop_value); + if ((success < 0) || ((size_t)success != strlen(prop_value))) { + gpr_log(GPR_ERROR, "Could not write into openssl BIO."); + goto end; + } + result.private_key = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, ""); + if (result.private_key == NULL) { + gpr_log(GPR_ERROR, "Could not deserialize private key."); + goto end; + } + success = 1; + +end: + if (bio != NULL) BIO_free(bio); + if (!success) grpc_auth_json_key_destruct(&result); + return result; +} + +grpc_auth_json_key grpc_auth_json_key_create_from_string( + const char *json_string) { + char *scratchpad = gpr_strdup(json_string); + grpc_json *json = grpc_json_parse_string(scratchpad); + grpc_auth_json_key result = grpc_auth_json_key_create_from_json(json); + if (json != NULL) grpc_json_destroy(json); + gpr_free(scratchpad); + return result; +} + +void grpc_auth_json_key_destruct(grpc_auth_json_key *json_key) { + if (json_key == NULL) return; + json_key->type = GRPC_AUTH_JSON_TYPE_INVALID; + if (json_key->client_id != NULL) { + gpr_free(json_key->client_id); + json_key->client_id = NULL; + } + if (json_key->private_key_id != NULL) { + gpr_free(json_key->private_key_id); + json_key->private_key_id = NULL; + } + if (json_key->client_email != NULL) { + gpr_free(json_key->client_email); + json_key->client_email = NULL; + } + if (json_key->private_key != NULL) { + RSA_free(json_key->private_key); + json_key->private_key = NULL; + } +} + +/* --- jwt encoding and signature. --- */ + +static grpc_json *create_child(grpc_json *brother, grpc_json *parent, + const char *key, const char *value, + grpc_json_type type) { + grpc_json *child = grpc_json_create(type); + if (brother) brother->next = child; + if (!parent->child) parent->child = child; + child->parent = parent; + child->value = value; + child->key = key; + return child; +} + +static char *encoded_jwt_header(const char *key_id, const char *algorithm) { + grpc_json *json = grpc_json_create(GRPC_JSON_OBJECT); + grpc_json *child = NULL; + char *json_str = NULL; + char *result = NULL; + + child = create_child(NULL, json, "alg", algorithm, GRPC_JSON_STRING); + child = create_child(child, json, "typ", GRPC_JWT_TYPE, GRPC_JSON_STRING); + create_child(child, json, "kid", key_id, GRPC_JSON_STRING); + + json_str = grpc_json_dump_to_string(json, 0); + result = grpc_base64_encode(json_str, strlen(json_str), 1, 0); + gpr_free(json_str); + grpc_json_destroy(json); + return result; +} + +static char *encoded_jwt_claim(const grpc_auth_json_key *json_key, + const char *audience, + gpr_timespec token_lifetime, const char *scope) { + grpc_json *json = grpc_json_create(GRPC_JSON_OBJECT); + grpc_json *child = NULL; + char *json_str = NULL; + char *result = NULL; + gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); + gpr_timespec expiration = gpr_time_add(now, token_lifetime); + char now_str[GPR_LTOA_MIN_BUFSIZE]; + char expiration_str[GPR_LTOA_MIN_BUFSIZE]; + if (gpr_time_cmp(token_lifetime, grpc_max_auth_token_lifetime) > 0) { + gpr_log(GPR_INFO, "Cropping token lifetime to maximum allowed value."); + expiration = gpr_time_add(now, grpc_max_auth_token_lifetime); + } + gpr_ltoa(now.tv_sec, now_str); + gpr_ltoa(expiration.tv_sec, expiration_str); + + child = + create_child(NULL, json, "iss", json_key->client_email, GRPC_JSON_STRING); + if (scope != NULL) { + child = create_child(child, json, "scope", scope, GRPC_JSON_STRING); + } else { + /* Unscoped JWTs need a sub field. */ + child = create_child(child, json, "sub", json_key->client_email, + GRPC_JSON_STRING); + } + + child = create_child(child, json, "aud", audience, GRPC_JSON_STRING); + child = create_child(child, json, "iat", now_str, GRPC_JSON_NUMBER); + create_child(child, json, "exp", expiration_str, GRPC_JSON_NUMBER); + + json_str = grpc_json_dump_to_string(json, 0); + result = grpc_base64_encode(json_str, strlen(json_str), 1, 0); + gpr_free(json_str); + grpc_json_destroy(json); + return result; +} + +static char *dot_concat_and_free_strings(char *str1, char *str2) { + size_t str1_len = strlen(str1); + size_t str2_len = strlen(str2); + size_t result_len = str1_len + 1 /* dot */ + str2_len; + char *result = gpr_malloc(result_len + 1 /* NULL terminated */); + char *current = result; + memcpy(current, str1, str1_len); + current += str1_len; + *(current++) = '.'; + memcpy(current, str2, str2_len); + current += str2_len; + GPR_ASSERT(current >= result); + GPR_ASSERT((gpr_uintptr)(current - result) == result_len); + *current = '\0'; + gpr_free(str1); + gpr_free(str2); + return result; +} + +const EVP_MD *openssl_digest_from_algorithm(const char *algorithm) { + if (strcmp(algorithm, GRPC_JWT_RSA_SHA256_ALGORITHM) == 0) { + return EVP_sha256(); + } else { + gpr_log(GPR_ERROR, "Unknown algorithm %s.", algorithm); + return NULL; + } +} + +char *compute_and_encode_signature(const grpc_auth_json_key *json_key, + const char *signature_algorithm, + const char *to_sign) { + const EVP_MD *md = openssl_digest_from_algorithm(signature_algorithm); + EVP_MD_CTX *md_ctx = NULL; + EVP_PKEY *key = EVP_PKEY_new(); + size_t sig_len = 0; + unsigned char *sig = NULL; + char *result = NULL; + if (md == NULL) return NULL; + md_ctx = EVP_MD_CTX_create(); + if (md_ctx == NULL) { + gpr_log(GPR_ERROR, "Could not create MD_CTX"); + goto end; + } + EVP_PKEY_set1_RSA(key, json_key->private_key); + if (EVP_DigestSignInit(md_ctx, NULL, md, NULL, key) != 1) { + gpr_log(GPR_ERROR, "DigestInit failed."); + goto end; + } + if (EVP_DigestSignUpdate(md_ctx, to_sign, strlen(to_sign)) != 1) { + gpr_log(GPR_ERROR, "DigestUpdate failed."); + goto end; + } + if (EVP_DigestSignFinal(md_ctx, NULL, &sig_len) != 1) { + gpr_log(GPR_ERROR, "DigestFinal (get signature length) failed."); + goto end; + } + sig = gpr_malloc(sig_len); + if (EVP_DigestSignFinal(md_ctx, sig, &sig_len) != 1) { + gpr_log(GPR_ERROR, "DigestFinal (signature compute) failed."); + goto end; + } + result = grpc_base64_encode(sig, sig_len, 1, 0); + +end: + if (key != NULL) EVP_PKEY_free(key); + if (md_ctx != NULL) EVP_MD_CTX_destroy(md_ctx); + if (sig != NULL) gpr_free(sig); + return result; +} + +char *grpc_jwt_encode_and_sign(const grpc_auth_json_key *json_key, + const char *audience, + gpr_timespec token_lifetime, const char *scope) { + if (g_jwt_encode_and_sign_override != NULL) { + return g_jwt_encode_and_sign_override(json_key, audience, token_lifetime, + scope); + } else { + const char *sig_algo = GRPC_JWT_RSA_SHA256_ALGORITHM; + char *to_sign = dot_concat_and_free_strings( + encoded_jwt_header(json_key->private_key_id, sig_algo), + encoded_jwt_claim(json_key, audience, token_lifetime, scope)); + char *sig = compute_and_encode_signature(json_key, sig_algo, to_sign); + if (sig == NULL) { + gpr_free(to_sign); + return NULL; + } + return dot_concat_and_free_strings(to_sign, sig); + } +} + +void grpc_jwt_encode_and_sign_set_override( + grpc_jwt_encode_and_sign_override func) { + g_jwt_encode_and_sign_override = func; +} + +/* --- grpc_auth_refresh_token --- */ + +int grpc_auth_refresh_token_is_valid( + const grpc_auth_refresh_token *refresh_token) { + return (refresh_token != NULL) && + strcmp(refresh_token->type, GRPC_AUTH_JSON_TYPE_INVALID); +} + +grpc_auth_refresh_token grpc_auth_refresh_token_create_from_json( + const grpc_json *json) { + grpc_auth_refresh_token result; + const char *prop_value; + int success = 0; + + memset(&result, 0, sizeof(grpc_auth_refresh_token)); + result.type = GRPC_AUTH_JSON_TYPE_INVALID; + if (json == NULL) { + gpr_log(GPR_ERROR, "Invalid json."); + goto end; + } + + prop_value = json_get_string_property(json, "type"); + if (prop_value == NULL || + strcmp(prop_value, GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER)) { + goto end; + } + result.type = GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER; + + if (!set_json_key_string_property(json, "client_secret", + &result.client_secret) || + !set_json_key_string_property(json, "client_id", &result.client_id) || + !set_json_key_string_property(json, "refresh_token", + &result.refresh_token)) { + goto end; + } + success = 1; + +end: + if (!success) grpc_auth_refresh_token_destruct(&result); + return result; +} + +grpc_auth_refresh_token grpc_auth_refresh_token_create_from_string( + const char *json_string) { + char *scratchpad = gpr_strdup(json_string); + grpc_json *json = grpc_json_parse_string(scratchpad); + grpc_auth_refresh_token result = + grpc_auth_refresh_token_create_from_json(json); + if (json != NULL) grpc_json_destroy(json); + gpr_free(scratchpad); + return result; +} + +void grpc_auth_refresh_token_destruct(grpc_auth_refresh_token *refresh_token) { + if (refresh_token == NULL) return; + refresh_token->type = GRPC_AUTH_JSON_TYPE_INVALID; + if (refresh_token->client_id != NULL) { + gpr_free(refresh_token->client_id); + refresh_token->client_id = NULL; + } + if (refresh_token->client_secret != NULL) { + gpr_free(refresh_token->client_secret); + refresh_token->client_secret = NULL; + } + if (refresh_token->refresh_token != NULL) { + gpr_free(refresh_token->refresh_token); + refresh_token->refresh_token = NULL; + } +} diff --git a/src/core/security/json_token.h b/src/core/security/json_token.h new file mode 100644 index 00000000..7e06864f --- /dev/null +++ b/src/core/security/json_token.h @@ -0,0 +1,118 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_SECURITY_JSON_TOKEN_H +#define GRPC_INTERNAL_CORE_SECURITY_JSON_TOKEN_H + +#include +#include + +#include "src/core/json/json.h" + +/* --- Constants. --- */ + +#define GRPC_JWT_OAUTH2_AUDIENCE "https://www.googleapis.com/oauth2/v3/token" + +#define GRPC_AUTH_JSON_TYPE_INVALID "invalid" +#define GRPC_AUTH_JSON_TYPE_SERVICE_ACCOUNT "service_account" +#define GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER "authorized_user" + +/* --- auth_json_key parsing. --- */ + +typedef struct { + const char *type; + char *private_key_id; + char *client_id; + char *client_email; + RSA *private_key; +} grpc_auth_json_key; + +/* Returns 1 if the object is valid, 0 otherwise. */ +int grpc_auth_json_key_is_valid(const grpc_auth_json_key *json_key); + +/* Creates a json_key object from string. Returns an invalid object if a parsing + error has been encountered. */ +grpc_auth_json_key grpc_auth_json_key_create_from_string( + const char *json_string); + +/* Creates a json_key object from parsed json. Returns an invalid object if a + parsing error has been encountered. */ +grpc_auth_json_key grpc_auth_json_key_create_from_json(const grpc_json *json); + +/* Destructs the object. */ +void grpc_auth_json_key_destruct(grpc_auth_json_key *json_key); + +/* --- json token encoding and signing. --- */ + +/* Caller is responsible for calling gpr_free on the returned value. May return + NULL on invalid input. The scope parameter may be NULL. */ +char *grpc_jwt_encode_and_sign(const grpc_auth_json_key *json_key, + const char *audience, + gpr_timespec token_lifetime, const char *scope); + +/* Override encode_and_sign function for testing. */ +typedef char *(*grpc_jwt_encode_and_sign_override)( + const grpc_auth_json_key *json_key, const char *audience, + gpr_timespec token_lifetime, const char *scope); + +/* Set a custom encode_and_sign override for testing. */ +void grpc_jwt_encode_and_sign_set_override( + grpc_jwt_encode_and_sign_override func); + +/* --- auth_refresh_token parsing. --- */ + +typedef struct { + const char *type; + char *client_id; + char *client_secret; + char *refresh_token; +} grpc_auth_refresh_token; + +/* Returns 1 if the object is valid, 0 otherwise. */ +int grpc_auth_refresh_token_is_valid( + const grpc_auth_refresh_token *refresh_token); + +/* Creates a refresh token object from string. Returns an invalid object if a + parsing error has been encountered. */ +grpc_auth_refresh_token grpc_auth_refresh_token_create_from_string( + const char *json_string); + +/* Creates a refresh token object from parsed json. Returns an invalid object if + a parsing error has been encountered. */ +grpc_auth_refresh_token grpc_auth_refresh_token_create_from_json( + const grpc_json *json); + +/* Destructs the object. */ +void grpc_auth_refresh_token_destruct(grpc_auth_refresh_token *refresh_token); + +#endif /* GRPC_INTERNAL_CORE_SECURITY_JSON_TOKEN_H */ diff --git a/src/core/security/jwt_verifier.c b/src/core/security/jwt_verifier.c new file mode 100644 index 00000000..38ad134a --- /dev/null +++ b/src/core/security/jwt_verifier.c @@ -0,0 +1,835 @@ +/* + * + * Copyright 2015, 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 disclaimser. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimser + * 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 "src/core/security/jwt_verifier.h" + +#include + +#include "src/core/httpcli/httpcli.h" +#include "src/core/security/base64.h" + +#include +#include +#include +#include +#include + +/* --- Utils. --- */ + +const char *grpc_jwt_verifier_status_to_string( + grpc_jwt_verifier_status status) { + switch (status) { + case GRPC_JWT_VERIFIER_OK: + return "OK"; + case GRPC_JWT_VERIFIER_BAD_SIGNATURE: + return "BAD_SIGNATURE"; + case GRPC_JWT_VERIFIER_BAD_FORMAT: + return "BAD_FORMAT"; + case GRPC_JWT_VERIFIER_BAD_AUDIENCE: + return "BAD_AUDIENCE"; + case GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR: + return "KEY_RETRIEVAL_ERROR"; + case GRPC_JWT_VERIFIER_TIME_CONSTRAINT_FAILURE: + return "TIME_CONSTRAINT_FAILURE"; + case GRPC_JWT_VERIFIER_GENERIC_ERROR: + return "GENERIC_ERROR"; + default: + return "UNKNOWN"; + } +} + +static const EVP_MD *evp_md_from_alg(const char *alg) { + if (strcmp(alg, "RS256") == 0) { + return EVP_sha256(); + } else if (strcmp(alg, "RS384") == 0) { + return EVP_sha384(); + } else if (strcmp(alg, "RS512") == 0) { + return EVP_sha512(); + } else { + return NULL; + } +} + +static grpc_json *parse_json_part_from_jwt(const char *str, size_t len, + gpr_slice *buffer) { + grpc_json *json; + + *buffer = grpc_base64_decode_with_len(str, len, 1); + if (GPR_SLICE_IS_EMPTY(*buffer)) { + gpr_log(GPR_ERROR, "Invalid base64."); + return NULL; + } + json = grpc_json_parse_string_with_len((char *)GPR_SLICE_START_PTR(*buffer), + GPR_SLICE_LENGTH(*buffer)); + if (json == NULL) { + gpr_slice_unref(*buffer); + gpr_log(GPR_ERROR, "JSON parsing error."); + } + return json; +} + +static const char *validate_string_field(const grpc_json *json, + const char *key) { + if (json->type != GRPC_JSON_STRING) { + gpr_log(GPR_ERROR, "Invalid %s field [%s]", key, json->value); + return NULL; + } + return json->value; +} + +static gpr_timespec validate_time_field(const grpc_json *json, + const char *key) { + gpr_timespec result = gpr_time_0(GPR_CLOCK_REALTIME); + if (json->type != GRPC_JSON_NUMBER) { + gpr_log(GPR_ERROR, "Invalid %s field [%s]", key, json->value); + return result; + } + result.tv_sec = strtol(json->value, NULL, 10); + return result; +} + +/* --- JOSE header. see http://tools.ietf.org/html/rfc7515#section-4 --- */ + +typedef struct { + const char *alg; + const char *kid; + const char *typ; + /* TODO(jboeuf): Add others as needed (jku, jwk, x5u, x5c and so on...). */ + gpr_slice buffer; +} jose_header; + +static void jose_header_destroy(jose_header *h) { + gpr_slice_unref(h->buffer); + gpr_free(h); +} + +/* Takes ownership of json and buffer. */ +static jose_header *jose_header_from_json(grpc_json *json, gpr_slice buffer) { + grpc_json *cur; + jose_header *h = gpr_malloc(sizeof(jose_header)); + memset(h, 0, sizeof(jose_header)); + h->buffer = buffer; + for (cur = json->child; cur != NULL; cur = cur->next) { + if (strcmp(cur->key, "alg") == 0) { + /* We only support RSA-1.5 signatures for now. + Beware of this if we add HMAC support: + https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/ + */ + if (cur->type != GRPC_JSON_STRING || strncmp(cur->value, "RS", 2) || + evp_md_from_alg(cur->value) == NULL) { + gpr_log(GPR_ERROR, "Invalid alg field [%s]", cur->value); + goto error; + } + h->alg = cur->value; + } else if (strcmp(cur->key, "typ") == 0) { + h->typ = validate_string_field(cur, "typ"); + if (h->typ == NULL) goto error; + } else if (strcmp(cur->key, "kid") == 0) { + h->kid = validate_string_field(cur, "kid"); + if (h->kid == NULL) goto error; + } + } + if (h->alg == NULL) { + gpr_log(GPR_ERROR, "Missing alg field."); + goto error; + } + grpc_json_destroy(json); + h->buffer = buffer; + return h; + +error: + grpc_json_destroy(json); + jose_header_destroy(h); + return NULL; +} + +/* --- JWT claims. see http://tools.ietf.org/html/rfc7519#section-4.1 */ + +struct grpc_jwt_claims { + /* Well known properties already parsed. */ + const char *sub; + const char *iss; + const char *aud; + const char *jti; + gpr_timespec iat; + gpr_timespec exp; + gpr_timespec nbf; + + grpc_json *json; + gpr_slice buffer; +}; + +void grpc_jwt_claims_destroy(grpc_jwt_claims *claims) { + grpc_json_destroy(claims->json); + gpr_slice_unref(claims->buffer); + gpr_free(claims); +} + +const grpc_json *grpc_jwt_claims_json(const grpc_jwt_claims *claims) { + if (claims == NULL) return NULL; + return claims->json; +} + +const char *grpc_jwt_claims_subject(const grpc_jwt_claims *claims) { + if (claims == NULL) return NULL; + return claims->sub; +} + +const char *grpc_jwt_claims_issuer(const grpc_jwt_claims *claims) { + if (claims == NULL) return NULL; + return claims->iss; +} + +const char *grpc_jwt_claims_id(const grpc_jwt_claims *claims) { + if (claims == NULL) return NULL; + return claims->jti; +} + +const char *grpc_jwt_claims_audience(const grpc_jwt_claims *claims) { + if (claims == NULL) return NULL; + return claims->aud; +} + +gpr_timespec grpc_jwt_claims_issued_at(const grpc_jwt_claims *claims) { + if (claims == NULL) return gpr_inf_past(GPR_CLOCK_REALTIME); + return claims->iat; +} + +gpr_timespec grpc_jwt_claims_expires_at(const grpc_jwt_claims *claims) { + if (claims == NULL) return gpr_inf_future(GPR_CLOCK_REALTIME); + return claims->exp; +} + +gpr_timespec grpc_jwt_claims_not_before(const grpc_jwt_claims *claims) { + if (claims == NULL) return gpr_inf_past(GPR_CLOCK_REALTIME); + return claims->nbf; +} + +/* Takes ownership of json and buffer even in case of failure. */ +grpc_jwt_claims *grpc_jwt_claims_from_json(grpc_json *json, gpr_slice buffer) { + grpc_json *cur; + grpc_jwt_claims *claims = gpr_malloc(sizeof(grpc_jwt_claims)); + memset(claims, 0, sizeof(grpc_jwt_claims)); + claims->json = json; + claims->buffer = buffer; + claims->iat = gpr_inf_past(GPR_CLOCK_REALTIME); + claims->nbf = gpr_inf_past(GPR_CLOCK_REALTIME); + claims->exp = gpr_inf_future(GPR_CLOCK_REALTIME); + + /* Per the spec, all fields are optional. */ + for (cur = json->child; cur != NULL; cur = cur->next) { + if (strcmp(cur->key, "sub") == 0) { + claims->sub = validate_string_field(cur, "sub"); + if (claims->sub == NULL) goto error; + } else if (strcmp(cur->key, "iss") == 0) { + claims->iss = validate_string_field(cur, "iss"); + if (claims->iss == NULL) goto error; + } else if (strcmp(cur->key, "aud") == 0) { + claims->aud = validate_string_field(cur, "aud"); + if (claims->aud == NULL) goto error; + } else if (strcmp(cur->key, "jti") == 0) { + claims->jti = validate_string_field(cur, "jti"); + if (claims->jti == NULL) goto error; + } else if (strcmp(cur->key, "iat") == 0) { + claims->iat = validate_time_field(cur, "iat"); + if (gpr_time_cmp(claims->iat, gpr_time_0(GPR_CLOCK_REALTIME)) == 0) + goto error; + } else if (strcmp(cur->key, "exp") == 0) { + claims->exp = validate_time_field(cur, "exp"); + if (gpr_time_cmp(claims->exp, gpr_time_0(GPR_CLOCK_REALTIME)) == 0) + goto error; + } else if (strcmp(cur->key, "nbf") == 0) { + claims->nbf = validate_time_field(cur, "nbf"); + if (gpr_time_cmp(claims->nbf, gpr_time_0(GPR_CLOCK_REALTIME)) == 0) + goto error; + } + } + return claims; + +error: + grpc_jwt_claims_destroy(claims); + return NULL; +} + +grpc_jwt_verifier_status grpc_jwt_claims_check(const grpc_jwt_claims *claims, + const char *audience) { + gpr_timespec skewed_now; + int audience_ok; + + GPR_ASSERT(claims != NULL); + + skewed_now = + gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_clock_skew); + if (gpr_time_cmp(skewed_now, claims->nbf) < 0) { + gpr_log(GPR_ERROR, "JWT is not valid yet."); + return GRPC_JWT_VERIFIER_TIME_CONSTRAINT_FAILURE; + } + skewed_now = + gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_clock_skew); + if (gpr_time_cmp(skewed_now, claims->exp) > 0) { + gpr_log(GPR_ERROR, "JWT is expired."); + return GRPC_JWT_VERIFIER_TIME_CONSTRAINT_FAILURE; + } + + if (audience == NULL) { + audience_ok = claims->aud == NULL; + } else { + audience_ok = claims->aud != NULL && strcmp(audience, claims->aud) == 0; + } + if (!audience_ok) { + gpr_log(GPR_ERROR, "Audience mismatch: expected %s and found %s.", + audience == NULL ? "NULL" : audience, + claims->aud == NULL ? "NULL" : claims->aud); + return GRPC_JWT_VERIFIER_BAD_AUDIENCE; + } + return GRPC_JWT_VERIFIER_OK; +} + +/* --- verifier_cb_ctx object. --- */ + +typedef struct { + grpc_jwt_verifier *verifier; + grpc_pollset *pollset; + jose_header *header; + grpc_jwt_claims *claims; + char *audience; + gpr_slice signature; + gpr_slice signed_data; + void *user_data; + grpc_jwt_verification_done_cb user_cb; +} verifier_cb_ctx; + +/* Takes ownership of the header, claims and signature. */ +static verifier_cb_ctx *verifier_cb_ctx_create( + grpc_jwt_verifier *verifier, grpc_pollset *pollset, jose_header *header, + grpc_jwt_claims *claims, const char *audience, gpr_slice signature, + const char *signed_jwt, size_t signed_jwt_len, void *user_data, + grpc_jwt_verification_done_cb cb) { + verifier_cb_ctx *ctx = gpr_malloc(sizeof(verifier_cb_ctx)); + memset(ctx, 0, sizeof(verifier_cb_ctx)); + ctx->verifier = verifier; + ctx->pollset = pollset; + ctx->header = header; + ctx->audience = gpr_strdup(audience); + ctx->claims = claims; + ctx->signature = signature; + ctx->signed_data = gpr_slice_from_copied_buffer(signed_jwt, signed_jwt_len); + ctx->user_data = user_data; + ctx->user_cb = cb; + return ctx; +} + +void verifier_cb_ctx_destroy(verifier_cb_ctx *ctx) { + if (ctx->audience != NULL) gpr_free(ctx->audience); + if (ctx->claims != NULL) grpc_jwt_claims_destroy(ctx->claims); + gpr_slice_unref(ctx->signature); + gpr_slice_unref(ctx->signed_data); + jose_header_destroy(ctx->header); + /* TODO: see what to do with claims... */ + gpr_free(ctx); +} + +/* --- grpc_jwt_verifier object. --- */ + +/* Clock skew defaults to one minute. */ +gpr_timespec grpc_jwt_verifier_clock_skew = {60, 0, GPR_TIMESPAN}; + +/* Max delay defaults to one minute. */ +gpr_timespec grpc_jwt_verifier_max_delay = {60, 0, GPR_TIMESPAN}; + +typedef struct { + char *email_domain; + char *key_url_prefix; +} email_key_mapping; + +struct grpc_jwt_verifier { + email_key_mapping *mappings; + size_t num_mappings; /* Should be very few, linear search ok. */ + size_t allocated_mappings; + grpc_httpcli_context http_ctx; +}; + +static grpc_json *json_from_http(const grpc_httpcli_response *response) { + grpc_json *json = NULL; + + if (response == NULL) { + gpr_log(GPR_ERROR, "HTTP response is NULL."); + return NULL; + } + if (response->status != 200) { + gpr_log(GPR_ERROR, "Call to http server failed with error %d.", + response->status); + return NULL; + } + + json = grpc_json_parse_string_with_len(response->body, response->body_length); + if (json == NULL) { + gpr_log(GPR_ERROR, "Invalid JSON found in response."); + } + return json; +} + +static const grpc_json *find_property_by_name(const grpc_json *json, + const char *name) { + const grpc_json *cur; + for (cur = json->child; cur != NULL; cur = cur->next) { + if (strcmp(cur->key, name) == 0) return cur; + } + return NULL; +} + +static EVP_PKEY *extract_pkey_from_x509(const char *x509_str) { + X509 *x509 = NULL; + EVP_PKEY *result = NULL; + BIO *bio = BIO_new(BIO_s_mem()); + BIO_write(bio, x509_str, strlen(x509_str)); + x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL); + if (x509 == NULL) { + gpr_log(GPR_ERROR, "Unable to parse x509 cert."); + goto end; + } + result = X509_get_pubkey(x509); + if (result == NULL) { + gpr_log(GPR_ERROR, "Cannot find public key in X509 cert."); + } + +end: + BIO_free(bio); + if (x509 != NULL) X509_free(x509); + return result; +} + +static BIGNUM *bignum_from_base64(const char *b64) { + BIGNUM *result = NULL; + gpr_slice bin; + + if (b64 == NULL) return NULL; + bin = grpc_base64_decode(b64, 1); + if (GPR_SLICE_IS_EMPTY(bin)) { + gpr_log(GPR_ERROR, "Invalid base64 for big num."); + return NULL; + } + result = BN_bin2bn(GPR_SLICE_START_PTR(bin), GPR_SLICE_LENGTH(bin), NULL); + gpr_slice_unref(bin); + return result; +} + +static EVP_PKEY *pkey_from_jwk(const grpc_json *json, const char *kty) { + const grpc_json *key_prop; + RSA *rsa = NULL; + EVP_PKEY *result = NULL; + + GPR_ASSERT(kty != NULL && json != NULL); + if (strcmp(kty, "RSA") != 0) { + gpr_log(GPR_ERROR, "Unsupported key type %s.", kty); + goto end; + } + rsa = RSA_new(); + if (rsa == NULL) { + gpr_log(GPR_ERROR, "Could not create rsa key."); + goto end; + } + for (key_prop = json->child; key_prop != NULL; key_prop = key_prop->next) { + if (strcmp(key_prop->key, "n") == 0) { + rsa->n = bignum_from_base64(validate_string_field(key_prop, "n")); + if (rsa->n == NULL) goto end; + } else if (strcmp(key_prop->key, "e") == 0) { + rsa->e = bignum_from_base64(validate_string_field(key_prop, "e")); + if (rsa->e == NULL) goto end; + } + } + if (rsa->e == NULL || rsa->n == NULL) { + gpr_log(GPR_ERROR, "Missing RSA public key field."); + goto end; + } + result = EVP_PKEY_new(); + EVP_PKEY_set1_RSA(result, rsa); /* uprefs rsa. */ + +end: + if (rsa != NULL) RSA_free(rsa); + return result; +} + +static EVP_PKEY *find_verification_key(const grpc_json *json, + const char *header_alg, + const char *header_kid) { + const grpc_json *jkey; + const grpc_json *jwk_keys; + /* Try to parse the json as a JWK set: + https://tools.ietf.org/html/rfc7517#section-5. */ + jwk_keys = find_property_by_name(json, "keys"); + if (jwk_keys == NULL) { + /* Use the google proprietary format which is: + { : , : , ... } */ + const grpc_json *cur = find_property_by_name(json, header_kid); + if (cur == NULL) return NULL; + return extract_pkey_from_x509(cur->value); + } + + if (jwk_keys->type != GRPC_JSON_ARRAY) { + gpr_log(GPR_ERROR, + "Unexpected value type of keys property in jwks key set."); + return NULL; + } + /* Key format is specified in: + https://tools.ietf.org/html/rfc7518#section-6. */ + for (jkey = jwk_keys->child; jkey != NULL; jkey = jkey->next) { + grpc_json *key_prop; + const char *alg = NULL; + const char *kid = NULL; + const char *kty = NULL; + + if (jkey->type != GRPC_JSON_OBJECT) continue; + for (key_prop = jkey->child; key_prop != NULL; key_prop = key_prop->next) { + if (strcmp(key_prop->key, "alg") == 0 && + key_prop->type == GRPC_JSON_STRING) { + alg = key_prop->value; + } else if (strcmp(key_prop->key, "kid") == 0 && + key_prop->type == GRPC_JSON_STRING) { + kid = key_prop->value; + } else if (strcmp(key_prop->key, "kty") == 0 && + key_prop->type == GRPC_JSON_STRING) { + kty = key_prop->value; + } + } + if (alg != NULL && kid != NULL && kty != NULL && + strcmp(kid, header_kid) == 0 && strcmp(alg, header_alg) == 0) { + return pkey_from_jwk(jkey, kty); + } + } + gpr_log(GPR_ERROR, + "Could not find matching key in key set for kid=%s and alg=%s", + header_kid, header_alg); + return NULL; +} + +static int verify_jwt_signature(EVP_PKEY *key, const char *alg, + gpr_slice signature, gpr_slice signed_data) { + EVP_MD_CTX *md_ctx = EVP_MD_CTX_create(); + const EVP_MD *md = evp_md_from_alg(alg); + int result = 0; + + GPR_ASSERT(md != NULL); /* Checked before. */ + if (md_ctx == NULL) { + gpr_log(GPR_ERROR, "Could not create EVP_MD_CTX."); + goto end; + } + if (EVP_DigestVerifyInit(md_ctx, NULL, md, NULL, key) != 1) { + gpr_log(GPR_ERROR, "EVP_DigestVerifyInit failed."); + goto end; + } + if (EVP_DigestVerifyUpdate(md_ctx, GPR_SLICE_START_PTR(signed_data), + GPR_SLICE_LENGTH(signed_data)) != 1) { + gpr_log(GPR_ERROR, "EVP_DigestVerifyUpdate failed."); + goto end; + } + if (EVP_DigestVerifyFinal(md_ctx, GPR_SLICE_START_PTR(signature), + GPR_SLICE_LENGTH(signature)) != 1) { + gpr_log(GPR_ERROR, "JWT signature verification failed."); + goto end; + } + result = 1; + +end: + if (md_ctx != NULL) EVP_MD_CTX_destroy(md_ctx); + return result; +} + +static void on_keys_retrieved(void *user_data, + const grpc_httpcli_response *response) { + grpc_json *json = json_from_http(response); + verifier_cb_ctx *ctx = (verifier_cb_ctx *)user_data; + EVP_PKEY *verification_key = NULL; + grpc_jwt_verifier_status status = GRPC_JWT_VERIFIER_GENERIC_ERROR; + grpc_jwt_claims *claims = NULL; + + if (json == NULL) { + status = GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR; + goto end; + } + verification_key = + find_verification_key(json, ctx->header->alg, ctx->header->kid); + if (verification_key == NULL) { + gpr_log(GPR_ERROR, "Could not find verification key with kid %s.", + ctx->header->kid); + status = GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR; + goto end; + } + + if (!verify_jwt_signature(verification_key, ctx->header->alg, ctx->signature, + ctx->signed_data)) { + status = GRPC_JWT_VERIFIER_BAD_SIGNATURE; + goto end; + } + + status = grpc_jwt_claims_check(ctx->claims, ctx->audience); + if (status == GRPC_JWT_VERIFIER_OK) { + /* Pass ownership. */ + claims = ctx->claims; + ctx->claims = NULL; + } + +end: + if (json != NULL) grpc_json_destroy(json); + if (verification_key != NULL) EVP_PKEY_free(verification_key); + ctx->user_cb(ctx->user_data, status, claims); + verifier_cb_ctx_destroy(ctx); +} + +static void on_openid_config_retrieved(void *user_data, + const grpc_httpcli_response *response) { + const grpc_json *cur; + grpc_json *json = json_from_http(response); + verifier_cb_ctx *ctx = (verifier_cb_ctx *)user_data; + grpc_httpcli_request req; + const char *jwks_uri; + + /* TODO(jboeuf): Cache the jwks_uri in order to avoid this hop next time.*/ + if (json == NULL) goto error; + cur = find_property_by_name(json, "jwks_uri"); + if (cur == NULL) { + gpr_log(GPR_ERROR, "Could not find jwks_uri in openid config."); + goto error; + } + jwks_uri = validate_string_field(cur, "jwks_uri"); + if (jwks_uri == NULL) goto error; + if (strstr(jwks_uri, "https://") != jwks_uri) { + gpr_log(GPR_ERROR, "Invalid non https jwks_uri: %s.", jwks_uri); + goto error; + } + jwks_uri += 8; + req.handshaker = &grpc_httpcli_ssl; + req.host = gpr_strdup(jwks_uri); + req.path = strchr(jwks_uri, '/'); + if (req.path == NULL) { + req.path = ""; + } else { + *(req.host + (req.path - jwks_uri)) = '\0'; + } + grpc_httpcli_get( + &ctx->verifier->http_ctx, ctx->pollset, &req, + gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_max_delay), + on_keys_retrieved, ctx); + grpc_json_destroy(json); + gpr_free(req.host); + return; + +error: + if (json != NULL) grpc_json_destroy(json); + ctx->user_cb(ctx->user_data, GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR, NULL); + verifier_cb_ctx_destroy(ctx); +} + +static email_key_mapping *verifier_get_mapping(grpc_jwt_verifier *v, + const char *email_domain) { + size_t i; + if (v->mappings == NULL) return NULL; + for (i = 0; i < v->num_mappings; i++) { + if (strcmp(email_domain, v->mappings[i].email_domain) == 0) { + return &v->mappings[i]; + } + } + return NULL; +} + +static void verifier_put_mapping(grpc_jwt_verifier *v, const char *email_domain, + const char *key_url_prefix) { + email_key_mapping *mapping = verifier_get_mapping(v, email_domain); + GPR_ASSERT(v->num_mappings < v->allocated_mappings); + if (mapping != NULL) { + gpr_free(mapping->key_url_prefix); + mapping->key_url_prefix = gpr_strdup(key_url_prefix); + return; + } + v->mappings[v->num_mappings].email_domain = gpr_strdup(email_domain); + v->mappings[v->num_mappings].key_url_prefix = gpr_strdup(key_url_prefix); + v->num_mappings++; + GPR_ASSERT(v->num_mappings <= v->allocated_mappings); +} + +/* Takes ownership of ctx. */ +static void retrieve_key_and_verify(verifier_cb_ctx *ctx) { + const char *at_sign; + grpc_httpcli_response_cb http_cb; + char *path_prefix = NULL; + const char *iss; + grpc_httpcli_request req; + memset(&req, 0, sizeof(grpc_httpcli_request)); + req.handshaker = &grpc_httpcli_ssl; + + GPR_ASSERT(ctx != NULL && ctx->header != NULL && ctx->claims != NULL); + iss = ctx->claims->iss; + if (ctx->header->kid == NULL) { + gpr_log(GPR_ERROR, "Missing kid in jose header."); + goto error; + } + if (iss == NULL) { + gpr_log(GPR_ERROR, "Missing iss in claims."); + goto error; + } + + /* This code relies on: + https://openid.net/specs/openid-connect-discovery-1_0.html + Nobody seems to implement the account/email/webfinger part 2. of the spec + so we will rely instead on email/url mappings if we detect such an issuer. + Part 4, on the other hand is implemented by both google and salesforce. */ + + /* Very non-sophisticated way to detect an email address. Should be good + enough for now... */ + at_sign = strchr(iss, '@'); + if (at_sign != NULL) { + email_key_mapping *mapping; + const char *email_domain = at_sign + 1; + GPR_ASSERT(ctx->verifier != NULL); + mapping = verifier_get_mapping(ctx->verifier, email_domain); + if (mapping == NULL) { + gpr_log(GPR_ERROR, "Missing mapping for issuer email."); + goto error; + } + req.host = gpr_strdup(mapping->key_url_prefix); + path_prefix = strchr(req.host, '/'); + if (path_prefix == NULL) { + gpr_asprintf(&req.path, "/%s", iss); + } else { + *(path_prefix++) = '\0'; + gpr_asprintf(&req.path, "/%s/%s", path_prefix, iss); + } + http_cb = on_keys_retrieved; + } else { + req.host = gpr_strdup(strstr(iss, "https://") == iss ? iss + 8 : iss); + path_prefix = strchr(req.host, '/'); + if (path_prefix == NULL) { + req.path = gpr_strdup(GRPC_OPENID_CONFIG_URL_SUFFIX); + } else { + *(path_prefix++) = 0; + gpr_asprintf(&req.path, "/%s%s", path_prefix, + GRPC_OPENID_CONFIG_URL_SUFFIX); + } + http_cb = on_openid_config_retrieved; + } + + grpc_httpcli_get( + &ctx->verifier->http_ctx, ctx->pollset, &req, + gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_max_delay), + http_cb, ctx); + gpr_free(req.host); + gpr_free(req.path); + return; + +error: + ctx->user_cb(ctx->user_data, GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR, NULL); + verifier_cb_ctx_destroy(ctx); +} + +void grpc_jwt_verifier_verify(grpc_jwt_verifier *verifier, + grpc_pollset *pollset, const char *jwt, + const char *audience, + grpc_jwt_verification_done_cb cb, + void *user_data) { + const char *dot = NULL; + grpc_json *json; + jose_header *header = NULL; + grpc_jwt_claims *claims = NULL; + gpr_slice header_buffer; + gpr_slice claims_buffer; + gpr_slice signature; + size_t signed_jwt_len; + const char *cur = jwt; + + GPR_ASSERT(verifier != NULL && jwt != NULL && audience != NULL && cb != NULL); + dot = strchr(cur, '.'); + if (dot == NULL) goto error; + json = parse_json_part_from_jwt(cur, dot - cur, &header_buffer); + if (json == NULL) goto error; + header = jose_header_from_json(json, header_buffer); + if (header == NULL) goto error; + + cur = dot + 1; + dot = strchr(cur, '.'); + if (dot == NULL) goto error; + json = parse_json_part_from_jwt(cur, dot - cur, &claims_buffer); + if (json == NULL) goto error; + claims = grpc_jwt_claims_from_json(json, claims_buffer); + if (claims == NULL) goto error; + + signed_jwt_len = (size_t)(dot - jwt); + cur = dot + 1; + signature = grpc_base64_decode(cur, 1); + if (GPR_SLICE_IS_EMPTY(signature)) goto error; + retrieve_key_and_verify( + verifier_cb_ctx_create(verifier, pollset, header, claims, audience, + signature, jwt, signed_jwt_len, user_data, cb)); + return; + +error: + if (header != NULL) jose_header_destroy(header); + if (claims != NULL) grpc_jwt_claims_destroy(claims); + cb(user_data, GRPC_JWT_VERIFIER_BAD_FORMAT, NULL); +} + +grpc_jwt_verifier *grpc_jwt_verifier_create( + const grpc_jwt_verifier_email_domain_key_url_mapping *mappings, + size_t num_mappings) { + grpc_jwt_verifier *v = gpr_malloc(sizeof(grpc_jwt_verifier)); + memset(v, 0, sizeof(grpc_jwt_verifier)); + grpc_httpcli_context_init(&v->http_ctx); + + /* We know at least of one mapping. */ + v->allocated_mappings = 1 + num_mappings; + v->mappings = gpr_malloc(v->allocated_mappings * sizeof(email_key_mapping)); + verifier_put_mapping(v, GRPC_GOOGLE_SERVICE_ACCOUNTS_EMAIL_DOMAIN, + GRPC_GOOGLE_SERVICE_ACCOUNTS_KEY_URL_PREFIX); + /* User-Provided mappings. */ + if (mappings != NULL) { + size_t i; + for (i = 0; i < num_mappings; i++) { + verifier_put_mapping(v, mappings[i].email_domain, + mappings[i].key_url_prefix); + } + } + return v; +} + +void grpc_jwt_verifier_destroy(grpc_jwt_verifier *v) { + size_t i; + if (v == NULL) return; + grpc_httpcli_context_destroy(&v->http_ctx); + if (v->mappings != NULL) { + for (i = 0; i < v->num_mappings; i++) { + gpr_free(v->mappings[i].email_domain); + gpr_free(v->mappings[i].key_url_prefix); + } + gpr_free(v->mappings); + } + gpr_free(v); +} diff --git a/src/core/security/jwt_verifier.h b/src/core/security/jwt_verifier.h new file mode 100644 index 00000000..7a32debf --- /dev/null +++ b/src/core/security/jwt_verifier.h @@ -0,0 +1,135 @@ +/* + * + * Copyright 2015, 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 disclaimser. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimser + * 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_INTERNAL_CORE_SECURITY_JWT_VERIFIER_H +#define GRPC_INTERNAL_CORE_SECURITY_JWT_VERIFIER_H + +#include "src/core/iomgr/pollset.h" +#include "src/core/json/json.h" + +#include +#include + +/* --- Constants. --- */ + +#define GRPC_OPENID_CONFIG_URL_SUFFIX "/.well-known/openid-configuration" +#define GRPC_GOOGLE_SERVICE_ACCOUNTS_EMAIL_DOMAIN \ + "developer.gserviceaccount.com" +#define GRPC_GOOGLE_SERVICE_ACCOUNTS_KEY_URL_PREFIX \ + "www.googleapis.com/robot/v1/metadata/x509" + +/* --- grpc_jwt_verifier_status. --- */ + +typedef enum { + GRPC_JWT_VERIFIER_OK = 0, + GRPC_JWT_VERIFIER_BAD_SIGNATURE, + GRPC_JWT_VERIFIER_BAD_FORMAT, + GRPC_JWT_VERIFIER_BAD_AUDIENCE, + GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR, + GRPC_JWT_VERIFIER_TIME_CONSTRAINT_FAILURE, + GRPC_JWT_VERIFIER_GENERIC_ERROR +} grpc_jwt_verifier_status; + +const char *grpc_jwt_verifier_status_to_string(grpc_jwt_verifier_status status); + +/* --- grpc_jwt_claims. --- */ + +typedef struct grpc_jwt_claims grpc_jwt_claims; + +void grpc_jwt_claims_destroy(grpc_jwt_claims *claims); + +/* Returns the whole JSON tree of the claims. */ +const grpc_json *grpc_jwt_claims_json(const grpc_jwt_claims *claims); + +/* Access to registered claims in https://tools.ietf.org/html/rfc7519#page-9 */ +const char *grpc_jwt_claims_subject(const grpc_jwt_claims *claims); +const char *grpc_jwt_claims_issuer(const grpc_jwt_claims *claims); +const char *grpc_jwt_claims_id(const grpc_jwt_claims *claims); +const char *grpc_jwt_claims_audience(const grpc_jwt_claims *claims); +gpr_timespec grpc_jwt_claims_issued_at(const grpc_jwt_claims *claims); +gpr_timespec grpc_jwt_claims_expires_at(const grpc_jwt_claims *claims); +gpr_timespec grpc_jwt_claims_not_before(const grpc_jwt_claims *claims); + +/* --- grpc_jwt_verifier. --- */ + +typedef struct grpc_jwt_verifier grpc_jwt_verifier; + +typedef struct { + /* The email domain is the part after the @ sign. */ + const char *email_domain; + + /* The key url prefix will be used to get the public key from the issuer: + https:/// + Therefore the key_url_prefix must NOT contain https://. */ + const char *key_url_prefix; +} grpc_jwt_verifier_email_domain_key_url_mapping; + +/* Globals to control the verifier. Not thread-safe. */ +extern gpr_timespec grpc_jwt_verifier_clock_skew; +extern gpr_timespec grpc_jwt_verifier_max_delay; + +/* The verifier can be created with some custom mappings to help with key + discovery in the case where the issuer is an email address. + mappings can be NULL in which case num_mappings MUST be 0. + A verifier object has one built-in mapping (unless overridden): + GRPC_GOOGLE_SERVICE_ACCOUNTS_EMAIL_DOMAIN -> + GRPC_GOOGLE_SERVICE_ACCOUNTS_KEY_URL_PREFIX.*/ +grpc_jwt_verifier *grpc_jwt_verifier_create( + const grpc_jwt_verifier_email_domain_key_url_mapping *mappings, + size_t num_mappings); + +/*The verifier must not be destroyed if there are still outstanding callbacks.*/ +void grpc_jwt_verifier_destroy(grpc_jwt_verifier *verifier); + +/* User provided callback that will be called when the verification of the JWT + is done (maybe in another thread). + It is the responsibility of the callee to call grpc_jwt_claims_destroy on + the claims. */ +typedef void (*grpc_jwt_verification_done_cb)(void *user_data, + grpc_jwt_verifier_status status, + grpc_jwt_claims *claims); + +/* Verifies for the JWT for the given expected audience. */ +void grpc_jwt_verifier_verify(grpc_jwt_verifier *verifier, + grpc_pollset *pollset, const char *jwt, + const char *audience, + grpc_jwt_verification_done_cb cb, + void *user_data); + +/* --- TESTING ONLY exposed functions. --- */ + +grpc_jwt_claims *grpc_jwt_claims_from_json(grpc_json *json, gpr_slice buffer); +grpc_jwt_verifier_status grpc_jwt_claims_check(const grpc_jwt_claims *claims, + const char *audience); + +#endif /* GRPC_INTERNAL_CORE_SECURITY_JWT_VERIFIER_H */ diff --git a/src/core/security/secure_endpoint.c b/src/core/security/secure_endpoint.c new file mode 100644 index 00000000..b696e384 --- /dev/null +++ b/src/core/security/secure_endpoint.c @@ -0,0 +1,393 @@ +/* + * + * Copyright 2015, 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 "src/core/security/secure_endpoint.h" +#include "src/core/support/string.h" +#include +#include +#include +#include +#include +#include "src/core/tsi/transport_security_interface.h" +#include "src/core/debug/trace.h" + +#define STAGING_BUFFER_SIZE 8192 + +typedef struct { + grpc_endpoint base; + grpc_endpoint *wrapped_ep; + struct tsi_frame_protector *protector; + gpr_mu protector_mu; + /* saved upper level callbacks and user_data. */ + grpc_iomgr_closure *read_cb; + grpc_iomgr_closure *write_cb; + grpc_iomgr_closure on_read; + gpr_slice_buffer *read_buffer; + gpr_slice_buffer source_buffer; + /* saved handshaker leftover data to unprotect. */ + gpr_slice_buffer leftover_bytes; + /* buffers for read and write */ + gpr_slice read_staging_buffer; + + gpr_slice write_staging_buffer; + gpr_slice_buffer output_buffer; + + gpr_refcount ref; +} secure_endpoint; + +int grpc_trace_secure_endpoint = 0; + +static void destroy(secure_endpoint *secure_ep) { + secure_endpoint *ep = secure_ep; + grpc_endpoint_destroy(ep->wrapped_ep); + tsi_frame_protector_destroy(ep->protector); + gpr_slice_buffer_destroy(&ep->leftover_bytes); + gpr_slice_unref(ep->read_staging_buffer); + gpr_slice_unref(ep->write_staging_buffer); + gpr_slice_buffer_destroy(&ep->output_buffer); + gpr_slice_buffer_destroy(&ep->source_buffer); + gpr_mu_destroy(&ep->protector_mu); + gpr_free(ep); +} + +/*#define GRPC_SECURE_ENDPOINT_REFCOUNT_DEBUG*/ +#ifdef GRPC_SECURE_ENDPOINT_REFCOUNT_DEBUG +#define SECURE_ENDPOINT_UNREF(ep, reason) \ + secure_endpoint_unref((ep), (reason), __FILE__, __LINE__) +#define SECURE_ENDPOINT_REF(ep, reason) \ + secure_endpoint_ref((ep), (reason), __FILE__, __LINE__) +static void secure_endpoint_unref(secure_endpoint *ep, const char *reason, + const char *file, int line) { + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "SECENDP unref %p : %s %d -> %d", + ep, reason, ep->ref.count, ep->ref.count - 1); + if (gpr_unref(&ep->ref)) { + destroy(ep); + } +} + +static void secure_endpoint_ref(secure_endpoint *ep, const char *reason, + const char *file, int line) { + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "SECENDP ref %p : %s %d -> %d", + ep, reason, ep->ref.count, ep->ref.count + 1); + gpr_ref(&ep->ref); +} +#else +#define SECURE_ENDPOINT_UNREF(ep, reason) secure_endpoint_unref((ep)) +#define SECURE_ENDPOINT_REF(ep, reason) secure_endpoint_ref((ep)) +static void secure_endpoint_unref(secure_endpoint *ep) { + if (gpr_unref(&ep->ref)) { + destroy(ep); + } +} + +static void secure_endpoint_ref(secure_endpoint *ep) { gpr_ref(&ep->ref); } +#endif + +static void flush_read_staging_buffer(secure_endpoint *ep, gpr_uint8 **cur, + gpr_uint8 **end) { + gpr_slice_buffer_add(ep->read_buffer, ep->read_staging_buffer); + ep->read_staging_buffer = gpr_slice_malloc(STAGING_BUFFER_SIZE); + *cur = GPR_SLICE_START_PTR(ep->read_staging_buffer); + *end = GPR_SLICE_END_PTR(ep->read_staging_buffer); +} + +static void call_read_cb(secure_endpoint *ep, int success) { + if (grpc_trace_secure_endpoint) { + size_t i; + for (i = 0; i < ep->read_buffer->count; i++) { + char *data = gpr_dump_slice(ep->read_buffer->slices[i], + GPR_DUMP_HEX | GPR_DUMP_ASCII); + gpr_log(GPR_DEBUG, "READ %p: %s", ep, data); + gpr_free(data); + } + } + ep->read_buffer = NULL; + ep->read_cb->cb(ep->read_cb->cb_arg, success); + SECURE_ENDPOINT_UNREF(ep, "read"); +} + +static int on_read(void *user_data, int success) { + unsigned i; + gpr_uint8 keep_looping = 0; + tsi_result result = TSI_OK; + secure_endpoint *ep = (secure_endpoint *)user_data; + gpr_uint8 *cur = GPR_SLICE_START_PTR(ep->read_staging_buffer); + gpr_uint8 *end = GPR_SLICE_END_PTR(ep->read_staging_buffer); + + if (!success) { + gpr_slice_buffer_reset_and_unref(ep->read_buffer); + return 0; + } + + /* TODO(yangg) check error, maybe bail out early */ + for (i = 0; i < ep->source_buffer.count; i++) { + gpr_slice encrypted = ep->source_buffer.slices[i]; + gpr_uint8 *message_bytes = GPR_SLICE_START_PTR(encrypted); + size_t message_size = GPR_SLICE_LENGTH(encrypted); + + while (message_size > 0 || keep_looping) { + size_t unprotected_buffer_size_written = (size_t)(end - cur); + size_t processed_message_size = message_size; + gpr_mu_lock(&ep->protector_mu); + result = tsi_frame_protector_unprotect(ep->protector, message_bytes, + &processed_message_size, cur, + &unprotected_buffer_size_written); + gpr_mu_unlock(&ep->protector_mu); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Decryption error: %s", + tsi_result_to_string(result)); + break; + } + message_bytes += processed_message_size; + message_size -= processed_message_size; + cur += unprotected_buffer_size_written; + + if (cur == end) { + flush_read_staging_buffer(ep, &cur, &end); + /* Force to enter the loop again to extract buffered bytes in protector. + The bytes could be buffered because of running out of staging_buffer. + If this happens at the end of all slices, doing another unprotect + avoids leaving data in the protector. */ + keep_looping = 1; + } else if (unprotected_buffer_size_written > 0) { + keep_looping = 1; + } else { + keep_looping = 0; + } + } + if (result != TSI_OK) break; + } + + if (cur != GPR_SLICE_START_PTR(ep->read_staging_buffer)) { + gpr_slice_buffer_add( + ep->read_buffer, + gpr_slice_split_head( + &ep->read_staging_buffer, + (size_t)(cur - GPR_SLICE_START_PTR(ep->read_staging_buffer)))); + } + + /* TODO(yangg) experiment with moving this block after read_cb to see if it + helps latency */ + gpr_slice_buffer_reset_and_unref(&ep->source_buffer); + + if (result != TSI_OK) { + gpr_slice_buffer_reset_and_unref(ep->read_buffer); + return 0; + } + + return 1; +} + +static void on_read_cb(void *user_data, int success) { + call_read_cb(user_data, on_read(user_data, success)); +} + +static grpc_endpoint_op_status endpoint_read(grpc_endpoint *secure_ep, + gpr_slice_buffer *slices, + grpc_iomgr_closure *cb) { + secure_endpoint *ep = (secure_endpoint *)secure_ep; + int immediate_read_success = -1; + ep->read_cb = cb; + ep->read_buffer = slices; + gpr_slice_buffer_reset_and_unref(ep->read_buffer); + + if (ep->leftover_bytes.count) { + gpr_slice_buffer_swap(&ep->leftover_bytes, &ep->source_buffer); + GPR_ASSERT(ep->leftover_bytes.count == 0); + return on_read(ep, 1) ? GRPC_ENDPOINT_DONE : GRPC_ENDPOINT_ERROR; + } + + SECURE_ENDPOINT_REF(ep, "read"); + + switch ( + grpc_endpoint_read(ep->wrapped_ep, &ep->source_buffer, &ep->on_read)) { + case GRPC_ENDPOINT_DONE: + immediate_read_success = on_read(ep, 1); + break; + case GRPC_ENDPOINT_PENDING: + return GRPC_ENDPOINT_PENDING; + case GRPC_ENDPOINT_ERROR: + immediate_read_success = on_read(ep, 0); + break; + } + + GPR_ASSERT(immediate_read_success != -1); + SECURE_ENDPOINT_UNREF(ep, "read"); + + return immediate_read_success ? GRPC_ENDPOINT_DONE : GRPC_ENDPOINT_ERROR; +} + +static void flush_write_staging_buffer(secure_endpoint *ep, gpr_uint8 **cur, + gpr_uint8 **end) { + gpr_slice_buffer_add(&ep->output_buffer, ep->write_staging_buffer); + ep->write_staging_buffer = gpr_slice_malloc(STAGING_BUFFER_SIZE); + *cur = GPR_SLICE_START_PTR(ep->write_staging_buffer); + *end = GPR_SLICE_END_PTR(ep->write_staging_buffer); +} + +static grpc_endpoint_op_status endpoint_write(grpc_endpoint *secure_ep, + gpr_slice_buffer *slices, + grpc_iomgr_closure *cb) { + unsigned i; + tsi_result result = TSI_OK; + secure_endpoint *ep = (secure_endpoint *)secure_ep; + gpr_uint8 *cur = GPR_SLICE_START_PTR(ep->write_staging_buffer); + gpr_uint8 *end = GPR_SLICE_END_PTR(ep->write_staging_buffer); + + gpr_slice_buffer_reset_and_unref(&ep->output_buffer); + + if (grpc_trace_secure_endpoint) { + for (i = 0; i < slices->count; i++) { + char *data = + gpr_dump_slice(slices->slices[i], GPR_DUMP_HEX | GPR_DUMP_ASCII); + gpr_log(GPR_DEBUG, "WRITE %p: %s", ep, data); + gpr_free(data); + } + } + + for (i = 0; i < slices->count; i++) { + gpr_slice plain = slices->slices[i]; + gpr_uint8 *message_bytes = GPR_SLICE_START_PTR(plain); + size_t message_size = GPR_SLICE_LENGTH(plain); + while (message_size > 0) { + size_t protected_buffer_size_to_send = (size_t)(end - cur); + size_t processed_message_size = message_size; + gpr_mu_lock(&ep->protector_mu); + result = tsi_frame_protector_protect(ep->protector, message_bytes, + &processed_message_size, cur, + &protected_buffer_size_to_send); + gpr_mu_unlock(&ep->protector_mu); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Encryption error: %s", + tsi_result_to_string(result)); + break; + } + message_bytes += processed_message_size; + message_size -= processed_message_size; + cur += protected_buffer_size_to_send; + + if (cur == end) { + flush_write_staging_buffer(ep, &cur, &end); + } + } + if (result != TSI_OK) break; + } + if (result == TSI_OK) { + size_t still_pending_size; + do { + size_t protected_buffer_size_to_send = (size_t)(end - cur); + gpr_mu_lock(&ep->protector_mu); + result = tsi_frame_protector_protect_flush(ep->protector, cur, + &protected_buffer_size_to_send, + &still_pending_size); + gpr_mu_unlock(&ep->protector_mu); + if (result != TSI_OK) break; + cur += protected_buffer_size_to_send; + if (cur == end) { + flush_write_staging_buffer(ep, &cur, &end); + } + } while (still_pending_size > 0); + if (cur != GPR_SLICE_START_PTR(ep->write_staging_buffer)) { + gpr_slice_buffer_add( + &ep->output_buffer, + gpr_slice_split_head( + &ep->write_staging_buffer, + (size_t)(cur - GPR_SLICE_START_PTR(ep->write_staging_buffer)))); + } + } + + if (result != TSI_OK) { + /* TODO(yangg) do different things according to the error type? */ + gpr_slice_buffer_reset_and_unref(&ep->output_buffer); + return GRPC_ENDPOINT_ERROR; + } + + return grpc_endpoint_write(ep->wrapped_ep, &ep->output_buffer, cb); +} + +static void endpoint_shutdown(grpc_endpoint *secure_ep) { + secure_endpoint *ep = (secure_endpoint *)secure_ep; + grpc_endpoint_shutdown(ep->wrapped_ep); +} + +static void endpoint_destroy(grpc_endpoint *secure_ep) { + secure_endpoint *ep = (secure_endpoint *)secure_ep; + SECURE_ENDPOINT_UNREF(ep, "destroy"); +} + +static void endpoint_add_to_pollset(grpc_endpoint *secure_ep, + grpc_pollset *pollset) { + secure_endpoint *ep = (secure_endpoint *)secure_ep; + grpc_endpoint_add_to_pollset(ep->wrapped_ep, pollset); +} + +static void endpoint_add_to_pollset_set(grpc_endpoint *secure_ep, + grpc_pollset_set *pollset_set) { + secure_endpoint *ep = (secure_endpoint *)secure_ep; + grpc_endpoint_add_to_pollset_set(ep->wrapped_ep, pollset_set); +} + +static char *endpoint_get_peer(grpc_endpoint *secure_ep) { + secure_endpoint *ep = (secure_endpoint *)secure_ep; + return grpc_endpoint_get_peer(ep->wrapped_ep); +} + +static const grpc_endpoint_vtable vtable = { + endpoint_read, endpoint_write, + endpoint_add_to_pollset, endpoint_add_to_pollset_set, + endpoint_shutdown, endpoint_destroy, + endpoint_get_peer}; + +grpc_endpoint *grpc_secure_endpoint_create( + struct tsi_frame_protector *protector, grpc_endpoint *transport, + gpr_slice *leftover_slices, size_t leftover_nslices) { + size_t i; + secure_endpoint *ep = (secure_endpoint *)gpr_malloc(sizeof(secure_endpoint)); + ep->base.vtable = &vtable; + ep->wrapped_ep = transport; + ep->protector = protector; + gpr_slice_buffer_init(&ep->leftover_bytes); + for (i = 0; i < leftover_nslices; i++) { + gpr_slice_buffer_add(&ep->leftover_bytes, + gpr_slice_ref(leftover_slices[i])); + } + ep->write_staging_buffer = gpr_slice_malloc(STAGING_BUFFER_SIZE); + ep->read_staging_buffer = gpr_slice_malloc(STAGING_BUFFER_SIZE); + gpr_slice_buffer_init(&ep->output_buffer); + gpr_slice_buffer_init(&ep->source_buffer); + ep->read_buffer = NULL; + grpc_iomgr_closure_init(&ep->on_read, on_read_cb, ep); + gpr_mu_init(&ep->protector_mu); + gpr_ref_init(&ep->ref, 1); + return &ep->base; +} diff --git a/src/core/security/secure_endpoint.h b/src/core/security/secure_endpoint.h new file mode 100644 index 00000000..c563bdd9 --- /dev/null +++ b/src/core/security/secure_endpoint.h @@ -0,0 +1,49 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_SECURITY_SECURE_ENDPOINT_H +#define GRPC_INTERNAL_CORE_SECURITY_SECURE_ENDPOINT_H + +#include "src/core/iomgr/endpoint.h" +#include + +struct tsi_frame_protector; + +extern int grpc_trace_secure_endpoint; + +/* Takes ownership of protector and to_wrap, and refs leftover_slices. */ +grpc_endpoint *grpc_secure_endpoint_create( + struct tsi_frame_protector *protector, grpc_endpoint *to_wrap, + gpr_slice *leftover_slices, size_t leftover_nslices); + +#endif /* GRPC_INTERNAL_CORE_SECURITY_SECURE_ENDPOINT_H */ diff --git a/src/core/security/secure_transport_setup.c b/src/core/security/secure_transport_setup.c new file mode 100644 index 00000000..bf007957 --- /dev/null +++ b/src/core/security/secure_transport_setup.c @@ -0,0 +1,312 @@ +/* + * + * Copyright 2015, 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 "src/core/security/secure_transport_setup.h" + +#include + +#include "src/core/security/secure_endpoint.h" +#include +#include +#include + +#define GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE 256 + +typedef struct { + grpc_security_connector *connector; + tsi_handshaker *handshaker; + unsigned char *handshake_buffer; + size_t handshake_buffer_size; + grpc_endpoint *wrapped_endpoint; + grpc_endpoint *secure_endpoint; + gpr_slice_buffer left_overs; + gpr_slice_buffer incoming; + gpr_slice_buffer outgoing; + grpc_secure_transport_setup_done_cb cb; + void *user_data; + grpc_iomgr_closure on_handshake_data_sent_to_peer; + grpc_iomgr_closure on_handshake_data_received_from_peer; +} grpc_secure_transport_setup; + +static void on_handshake_data_received_from_peer(void *setup, int success); + +static void on_handshake_data_sent_to_peer(void *setup, int success); + +static void secure_transport_setup_done(grpc_secure_transport_setup *s, + int is_success) { + if (is_success) { + s->cb(s->user_data, GRPC_SECURITY_OK, s->wrapped_endpoint, + s->secure_endpoint); + } else { + if (s->secure_endpoint != NULL) { + grpc_endpoint_shutdown(s->secure_endpoint); + grpc_endpoint_destroy(s->secure_endpoint); + } else { + grpc_endpoint_destroy(s->wrapped_endpoint); + } + s->cb(s->user_data, GRPC_SECURITY_ERROR, s->wrapped_endpoint, NULL); + } + if (s->handshaker != NULL) tsi_handshaker_destroy(s->handshaker); + if (s->handshake_buffer != NULL) gpr_free(s->handshake_buffer); + gpr_slice_buffer_destroy(&s->left_overs); + gpr_slice_buffer_destroy(&s->outgoing); + gpr_slice_buffer_destroy(&s->incoming); + GRPC_SECURITY_CONNECTOR_UNREF(s->connector, "secure_transport_setup"); + gpr_free(s); +} + +static void on_peer_checked(void *user_data, grpc_security_status status) { + grpc_secure_transport_setup *s = user_data; + tsi_frame_protector *protector; + tsi_result result; + if (status != GRPC_SECURITY_OK) { + gpr_log(GPR_ERROR, "Error checking peer."); + secure_transport_setup_done(s, 0); + return; + } + result = + tsi_handshaker_create_frame_protector(s->handshaker, NULL, &protector); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Frame protector creation failed with error %s.", + tsi_result_to_string(result)); + secure_transport_setup_done(s, 0); + return; + } + s->secure_endpoint = + grpc_secure_endpoint_create(protector, s->wrapped_endpoint, + s->left_overs.slices, s->left_overs.count); + s->left_overs.count = 0; + s->left_overs.length = 0; + secure_transport_setup_done(s, 1); + return; +} + +static void check_peer(grpc_secure_transport_setup *s) { + grpc_security_status peer_status; + tsi_peer peer; + tsi_result result = tsi_handshaker_extract_peer(s->handshaker, &peer); + + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Peer extraction failed with error %s", + tsi_result_to_string(result)); + secure_transport_setup_done(s, 0); + return; + } + peer_status = grpc_security_connector_check_peer(s->connector, peer, + on_peer_checked, s); + if (peer_status == GRPC_SECURITY_ERROR) { + gpr_log(GPR_ERROR, "Peer check failed."); + secure_transport_setup_done(s, 0); + return; + } else if (peer_status == GRPC_SECURITY_OK) { + on_peer_checked(s, peer_status); + } +} + +static void send_handshake_bytes_to_peer(grpc_secure_transport_setup *s) { + size_t offset = 0; + tsi_result result = TSI_OK; + gpr_slice to_send; + + do { + size_t to_send_size = s->handshake_buffer_size - offset; + result = tsi_handshaker_get_bytes_to_send_to_peer( + s->handshaker, s->handshake_buffer + offset, &to_send_size); + offset += to_send_size; + if (result == TSI_INCOMPLETE_DATA) { + s->handshake_buffer_size *= 2; + s->handshake_buffer = + gpr_realloc(s->handshake_buffer, s->handshake_buffer_size); + } + } while (result == TSI_INCOMPLETE_DATA); + + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Handshake failed with error %s", + tsi_result_to_string(result)); + secure_transport_setup_done(s, 0); + return; + } + + to_send = + gpr_slice_from_copied_buffer((const char *)s->handshake_buffer, offset); + gpr_slice_buffer_reset_and_unref(&s->outgoing); + gpr_slice_buffer_add(&s->outgoing, to_send); + /* TODO(klempner,jboeuf): This should probably use the client setup + deadline */ + switch (grpc_endpoint_write(s->wrapped_endpoint, &s->outgoing, + &s->on_handshake_data_sent_to_peer)) { + case GRPC_ENDPOINT_ERROR: + gpr_log(GPR_ERROR, "Could not send handshake data to peer."); + secure_transport_setup_done(s, 0); + break; + case GRPC_ENDPOINT_DONE: + on_handshake_data_sent_to_peer(s, 1); + break; + case GRPC_ENDPOINT_PENDING: + break; + } +} + +static void on_handshake_data_received_from_peer(void *setup, int success) { + grpc_secure_transport_setup *s = setup; + size_t consumed_slice_size = 0; + tsi_result result = TSI_OK; + size_t i; + size_t num_left_overs; + int has_left_overs_in_current_slice = 0; + + if (!success) { + gpr_log(GPR_ERROR, "Read failed."); + secure_transport_setup_done(s, 0); + return; + } + + for (i = 0; i < s->incoming.count; i++) { + consumed_slice_size = GPR_SLICE_LENGTH(s->incoming.slices[i]); + result = tsi_handshaker_process_bytes_from_peer( + s->handshaker, GPR_SLICE_START_PTR(s->incoming.slices[i]), + &consumed_slice_size); + if (!tsi_handshaker_is_in_progress(s->handshaker)) break; + } + + if (tsi_handshaker_is_in_progress(s->handshaker)) { + /* We may need more data. */ + if (result == TSI_INCOMPLETE_DATA) { + switch (grpc_endpoint_read(s->wrapped_endpoint, &s->incoming, + &s->on_handshake_data_received_from_peer)) { + case GRPC_ENDPOINT_DONE: + on_handshake_data_received_from_peer(s, 1); + break; + case GRPC_ENDPOINT_ERROR: + on_handshake_data_received_from_peer(s, 0); + break; + case GRPC_ENDPOINT_PENDING: + break; + } + return; + } else { + send_handshake_bytes_to_peer(s); + return; + } + } + + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Handshake failed with error %s", + tsi_result_to_string(result)); + secure_transport_setup_done(s, 0); + return; + } + + /* Handshake is done and successful this point. */ + has_left_overs_in_current_slice = + (consumed_slice_size < GPR_SLICE_LENGTH(s->incoming.slices[i])); + num_left_overs = + (has_left_overs_in_current_slice ? 1 : 0) + s->incoming.count - i - 1; + if (num_left_overs == 0) { + check_peer(s); + return; + } + /* Put the leftovers in our buffer (ownership transfered). */ + if (has_left_overs_in_current_slice) { + gpr_slice_buffer_add( + &s->left_overs, + gpr_slice_split_tail(&s->incoming.slices[i], consumed_slice_size)); + gpr_slice_unref( + s->incoming.slices[i]); /* split_tail above increments refcount. */ + } + gpr_slice_buffer_addn( + &s->left_overs, &s->incoming.slices[i + 1], + num_left_overs - (size_t)has_left_overs_in_current_slice); + check_peer(s); +} + +/* If setup is NULL, the setup is done. */ +static void on_handshake_data_sent_to_peer(void *setup, int success) { + grpc_secure_transport_setup *s = setup; + + /* Make sure that write is OK. */ + if (!success) { + gpr_log(GPR_ERROR, "Write failed."); + if (setup != NULL) secure_transport_setup_done(s, 0); + return; + } + + /* We may be done. */ + if (tsi_handshaker_is_in_progress(s->handshaker)) { + /* TODO(klempner,jboeuf): This should probably use the client setup + deadline */ + switch (grpc_endpoint_read(s->wrapped_endpoint, &s->incoming, + &s->on_handshake_data_received_from_peer)) { + case GRPC_ENDPOINT_ERROR: + on_handshake_data_received_from_peer(s, 0); + break; + case GRPC_ENDPOINT_PENDING: + break; + case GRPC_ENDPOINT_DONE: + on_handshake_data_received_from_peer(s, 1); + break; + } + } else { + check_peer(s); + } +} + +void grpc_setup_secure_transport(grpc_security_connector *connector, + grpc_endpoint *nonsecure_endpoint, + grpc_secure_transport_setup_done_cb cb, + void *user_data) { + grpc_security_status result = GRPC_SECURITY_OK; + grpc_secure_transport_setup *s = + gpr_malloc(sizeof(grpc_secure_transport_setup)); + memset(s, 0, sizeof(grpc_secure_transport_setup)); + result = grpc_security_connector_create_handshaker(connector, &s->handshaker); + if (result != GRPC_SECURITY_OK) { + secure_transport_setup_done(s, 0); + return; + } + s->connector = + GRPC_SECURITY_CONNECTOR_REF(connector, "secure_transport_setup"); + s->handshake_buffer_size = GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE; + s->handshake_buffer = gpr_malloc(s->handshake_buffer_size); + s->wrapped_endpoint = nonsecure_endpoint; + s->user_data = user_data; + s->cb = cb; + grpc_iomgr_closure_init(&s->on_handshake_data_sent_to_peer, + on_handshake_data_sent_to_peer, s); + grpc_iomgr_closure_init(&s->on_handshake_data_received_from_peer, + on_handshake_data_received_from_peer, s); + gpr_slice_buffer_init(&s->left_overs); + gpr_slice_buffer_init(&s->outgoing); + gpr_slice_buffer_init(&s->incoming); + send_handshake_bytes_to_peer(s); +} diff --git a/src/core/security/secure_transport_setup.h b/src/core/security/secure_transport_setup.h new file mode 100644 index 00000000..d9b80255 --- /dev/null +++ b/src/core/security/secure_transport_setup.h @@ -0,0 +1,53 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_SECURITY_SECURE_TRANSPORT_SETUP_H +#define GRPC_INTERNAL_CORE_SECURITY_SECURE_TRANSPORT_SETUP_H + +#include "src/core/iomgr/endpoint.h" +#include "src/core/security/security_connector.h" + +/* --- Secure transport setup --- */ + +/* Ownership of the secure_endpoint is transfered. */ +typedef void (*grpc_secure_transport_setup_done_cb)( + void *user_data, grpc_security_status status, + grpc_endpoint *wrapped_endpoint, grpc_endpoint *secure_endpoint); + +/* Calls the callback upon completion. */ +void grpc_setup_secure_transport(grpc_security_connector *connector, + grpc_endpoint *nonsecure_endpoint, + grpc_secure_transport_setup_done_cb cb, + void *user_data); + +#endif /* GRPC_INTERNAL_CORE_SECURITY_SECURE_TRANSPORT_SETUP_H */ diff --git a/src/core/security/security_connector.c b/src/core/security/security_connector.c new file mode 100644 index 00000000..ba9ac68c --- /dev/null +++ b/src/core/security/security_connector.c @@ -0,0 +1,680 @@ +/* + * + * Copyright 2015, 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 "src/core/security/security_connector.h" + +#include + +#include "src/core/security/credentials.h" +#include "src/core/security/secure_endpoint.h" +#include "src/core/security/security_context.h" +#include "src/core/support/env.h" +#include "src/core/support/file.h" +#include "src/core/support/string.h" +#include "src/core/transport/chttp2/alpn.h" + +#include +#include +#include +#include +#include +#include "src/core/tsi/fake_transport_security.h" +#include "src/core/tsi/ssl_transport_security.h" + +/* -- Constants. -- */ + +#ifndef INSTALL_PREFIX +static const char *installed_roots_path = "/usr/share/grpc/roots.pem"; +#else +static const char *installed_roots_path = + INSTALL_PREFIX "/share/grpc/roots.pem"; +#endif + +/* -- Cipher suites. -- */ + +/* Defines the cipher suites that we accept by default. All these cipher suites + are compliant with HTTP2. */ +#define GRPC_SSL_CIPHER_SUITES \ + "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-" \ + "SHA384:ECDHE-RSA-AES256-GCM-SHA384" + +static gpr_once cipher_suites_once = GPR_ONCE_INIT; +static const char *cipher_suites = NULL; + +static void init_cipher_suites(void) { + char *overridden = gpr_getenv("GRPC_SSL_CIPHER_SUITES"); + cipher_suites = overridden != NULL ? overridden : GRPC_SSL_CIPHER_SUITES; +} + +static const char *ssl_cipher_suites(void) { + gpr_once_init(&cipher_suites_once, init_cipher_suites); + return cipher_suites; +} + +/* -- Common methods. -- */ + +/* Returns the first property with that name. */ +const tsi_peer_property *tsi_peer_get_property_by_name(const tsi_peer *peer, + const char *name) { + size_t i; + if (peer == NULL) return NULL; + for (i = 0; i < peer->property_count; i++) { + const tsi_peer_property *property = &peer->properties[i]; + if (name == NULL && property->name == NULL) { + return property; + } + if (name != NULL && property->name != NULL && + strcmp(property->name, name) == 0) { + return property; + } + } + return NULL; +} + +grpc_security_status grpc_security_connector_create_handshaker( + grpc_security_connector *sc, tsi_handshaker **handshaker) { + if (sc == NULL || handshaker == NULL) return GRPC_SECURITY_ERROR; + return sc->vtable->create_handshaker(sc, handshaker); +} + +grpc_security_status grpc_security_connector_check_peer( + grpc_security_connector *sc, tsi_peer peer, grpc_security_check_cb cb, + void *user_data) { + if (sc == NULL) { + tsi_peer_destruct(&peer); + return GRPC_SECURITY_ERROR; + } + return sc->vtable->check_peer(sc, peer, cb, user_data); +} + +grpc_security_status grpc_channel_security_connector_check_call_host( + grpc_channel_security_connector *sc, const char *host, + grpc_security_check_cb cb, void *user_data) { + if (sc == NULL || sc->check_call_host == NULL) return GRPC_SECURITY_ERROR; + return sc->check_call_host(sc, host, cb, user_data); +} + +#ifdef GRPC_SECURITY_CONNECTOR_REFCOUNT_DEBUG +grpc_security_connector *grpc_security_connector_ref( + grpc_security_connector *sc, const char *file, int line, + const char *reason) { + if (sc == NULL) return NULL; + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "SECURITY_CONNECTOR:%p ref %d -> %d %s", sc, + (int)sc->refcount.count, (int)sc->refcount.count + 1, reason); +#else +grpc_security_connector *grpc_security_connector_ref( + grpc_security_connector *sc) { + if (sc == NULL) return NULL; +#endif + gpr_ref(&sc->refcount); + return sc; +} + +#ifdef GRPC_SECURITY_CONNECTOR_REFCOUNT_DEBUG +void grpc_security_connector_unref(grpc_security_connector *sc, + const char *file, int line, + const char *reason) { + if (sc == NULL) return; + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "SECURITY_CONNECTOR:%p unref %d -> %d %s", sc, + (int)sc->refcount.count, (int)sc->refcount.count - 1, reason); +#else +void grpc_security_connector_unref(grpc_security_connector *sc) { + if (sc == NULL) return; +#endif + if (gpr_unref(&sc->refcount)) sc->vtable->destroy(sc); +} + +static void connector_pointer_arg_destroy(void *p) { + GRPC_SECURITY_CONNECTOR_UNREF(p, "connector_pointer_arg"); +} + +static void *connector_pointer_arg_copy(void *p) { + return GRPC_SECURITY_CONNECTOR_REF(p, "connector_pointer_arg"); +} + +grpc_arg grpc_security_connector_to_arg(grpc_security_connector *sc) { + grpc_arg result; + result.type = GRPC_ARG_POINTER; + result.key = GRPC_SECURITY_CONNECTOR_ARG; + result.value.pointer.destroy = connector_pointer_arg_destroy; + result.value.pointer.copy = connector_pointer_arg_copy; + result.value.pointer.p = sc; + return result; +} + +grpc_security_connector *grpc_security_connector_from_arg(const grpc_arg *arg) { + if (strcmp(arg->key, GRPC_SECURITY_CONNECTOR_ARG)) return NULL; + if (arg->type != GRPC_ARG_POINTER) { + gpr_log(GPR_ERROR, "Invalid type %d for arg %s", arg->type, + GRPC_SECURITY_CONNECTOR_ARG); + return NULL; + } + return arg->value.pointer.p; +} + +grpc_security_connector *grpc_find_security_connector_in_args( + const grpc_channel_args *args) { + size_t i; + if (args == NULL) return NULL; + for (i = 0; i < args->num_args; i++) { + grpc_security_connector *sc = + grpc_security_connector_from_arg(&args->args[i]); + if (sc != NULL) return sc; + } + return NULL; +} + +static int check_request_metadata_creds(grpc_credentials *creds) { + if (creds != NULL && !grpc_credentials_has_request_metadata(creds)) { + gpr_log(GPR_ERROR, + "Incompatible credentials for channel security connector: needs to " + "set request metadata."); + return 0; + } + return 1; +} + +/* -- Fake implementation. -- */ + +typedef struct { + grpc_channel_security_connector base; + int call_host_check_is_async; +} grpc_fake_channel_security_connector; + +static void fake_channel_destroy(grpc_security_connector *sc) { + grpc_channel_security_connector *c = (grpc_channel_security_connector *)sc; + grpc_credentials_unref(c->request_metadata_creds); + GRPC_AUTH_CONTEXT_UNREF(sc->auth_context, "connector"); + gpr_free(sc); +} + +static void fake_server_destroy(grpc_security_connector *sc) { + GRPC_AUTH_CONTEXT_UNREF(sc->auth_context, "connector"); + gpr_free(sc); +} + +static grpc_security_status fake_channel_create_handshaker( + grpc_security_connector *sc, tsi_handshaker **handshaker) { + *handshaker = tsi_create_fake_handshaker(1); + return GRPC_SECURITY_OK; +} + +static grpc_security_status fake_server_create_handshaker( + grpc_security_connector *sc, tsi_handshaker **handshaker) { + *handshaker = tsi_create_fake_handshaker(0); + return GRPC_SECURITY_OK; +} + +static grpc_security_status fake_check_peer(grpc_security_connector *sc, + tsi_peer peer, + grpc_security_check_cb cb, + void *user_data) { + const char *prop_name; + grpc_security_status status = GRPC_SECURITY_OK; + if (peer.property_count != 1) { + gpr_log(GPR_ERROR, "Fake peers should only have 1 property."); + status = GRPC_SECURITY_ERROR; + goto end; + } + prop_name = peer.properties[0].name; + if (prop_name == NULL || + strcmp(prop_name, TSI_CERTIFICATE_TYPE_PEER_PROPERTY)) { + gpr_log(GPR_ERROR, "Unexpected property in fake peer: %s.", + prop_name == NULL ? "" : prop_name); + status = GRPC_SECURITY_ERROR; + goto end; + } + if (strncmp(peer.properties[0].value.data, TSI_FAKE_CERTIFICATE_TYPE, + peer.properties[0].value.length)) { + gpr_log(GPR_ERROR, "Invalid value for cert type property."); + status = GRPC_SECURITY_ERROR; + goto end; + } + GRPC_AUTH_CONTEXT_UNREF(sc->auth_context, "connector"); + sc->auth_context = grpc_auth_context_create(NULL); + grpc_auth_context_add_cstring_property( + sc->auth_context, GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME, + GRPC_FAKE_TRANSPORT_SECURITY_TYPE); + +end: + tsi_peer_destruct(&peer); + return status; +} + +static grpc_security_status fake_channel_check_call_host( + grpc_channel_security_connector *sc, const char *host, + grpc_security_check_cb cb, void *user_data) { + grpc_fake_channel_security_connector *c = + (grpc_fake_channel_security_connector *)sc; + if (c->call_host_check_is_async) { + cb(user_data, GRPC_SECURITY_OK); + return GRPC_SECURITY_PENDING; + } else { + return GRPC_SECURITY_OK; + } +} + +static grpc_security_connector_vtable fake_channel_vtable = { + fake_channel_destroy, fake_channel_create_handshaker, fake_check_peer}; + +static grpc_security_connector_vtable fake_server_vtable = { + fake_server_destroy, fake_server_create_handshaker, fake_check_peer}; + +grpc_channel_security_connector *grpc_fake_channel_security_connector_create( + grpc_credentials *request_metadata_creds, int call_host_check_is_async) { + grpc_fake_channel_security_connector *c = + gpr_malloc(sizeof(grpc_fake_channel_security_connector)); + memset(c, 0, sizeof(grpc_fake_channel_security_connector)); + gpr_ref_init(&c->base.base.refcount, 1); + c->base.base.is_client_side = 1; + c->base.base.url_scheme = GRPC_FAKE_SECURITY_URL_SCHEME; + c->base.base.vtable = &fake_channel_vtable; + GPR_ASSERT(check_request_metadata_creds(request_metadata_creds)); + c->base.request_metadata_creds = grpc_credentials_ref(request_metadata_creds); + c->base.check_call_host = fake_channel_check_call_host; + c->call_host_check_is_async = call_host_check_is_async; + return &c->base; +} + +grpc_security_connector *grpc_fake_server_security_connector_create(void) { + grpc_security_connector *c = gpr_malloc(sizeof(grpc_security_connector)); + memset(c, 0, sizeof(grpc_security_connector)); + gpr_ref_init(&c->refcount, 1); + c->is_client_side = 0; + c->vtable = &fake_server_vtable; + c->url_scheme = GRPC_FAKE_SECURITY_URL_SCHEME; + return c; +} + +/* --- Ssl implementation. --- */ + +typedef struct { + grpc_channel_security_connector base; + tsi_ssl_handshaker_factory *handshaker_factory; + char *target_name; + char *overridden_target_name; + tsi_peer peer; +} grpc_ssl_channel_security_connector; + +typedef struct { + grpc_security_connector base; + tsi_ssl_handshaker_factory *handshaker_factory; +} grpc_ssl_server_security_connector; + +static void ssl_channel_destroy(grpc_security_connector *sc) { + grpc_ssl_channel_security_connector *c = + (grpc_ssl_channel_security_connector *)sc; + grpc_credentials_unref(c->base.request_metadata_creds); + if (c->handshaker_factory != NULL) { + tsi_ssl_handshaker_factory_destroy(c->handshaker_factory); + } + if (c->target_name != NULL) gpr_free(c->target_name); + if (c->overridden_target_name != NULL) gpr_free(c->overridden_target_name); + tsi_peer_destruct(&c->peer); + GRPC_AUTH_CONTEXT_UNREF(sc->auth_context, "connector"); + gpr_free(sc); +} + +static void ssl_server_destroy(grpc_security_connector *sc) { + grpc_ssl_server_security_connector *c = + (grpc_ssl_server_security_connector *)sc; + if (c->handshaker_factory != NULL) { + tsi_ssl_handshaker_factory_destroy(c->handshaker_factory); + } + GRPC_AUTH_CONTEXT_UNREF(sc->auth_context, "connector"); + gpr_free(sc); +} + +static grpc_security_status ssl_create_handshaker( + tsi_ssl_handshaker_factory *handshaker_factory, int is_client, + const char *peer_name, tsi_handshaker **handshaker) { + tsi_result result = TSI_OK; + if (handshaker_factory == NULL) return GRPC_SECURITY_ERROR; + result = tsi_ssl_handshaker_factory_create_handshaker( + handshaker_factory, is_client ? peer_name : NULL, handshaker); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Handshaker creation failed with error %s.", + tsi_result_to_string(result)); + return GRPC_SECURITY_ERROR; + } + return GRPC_SECURITY_OK; +} + +static grpc_security_status ssl_channel_create_handshaker( + grpc_security_connector *sc, tsi_handshaker **handshaker) { + grpc_ssl_channel_security_connector *c = + (grpc_ssl_channel_security_connector *)sc; + return ssl_create_handshaker(c->handshaker_factory, 1, + c->overridden_target_name != NULL + ? c->overridden_target_name + : c->target_name, + handshaker); +} + +static grpc_security_status ssl_server_create_handshaker( + grpc_security_connector *sc, tsi_handshaker **handshaker) { + grpc_ssl_server_security_connector *c = + (grpc_ssl_server_security_connector *)sc; + return ssl_create_handshaker(c->handshaker_factory, 0, NULL, handshaker); +} + +static int ssl_host_matches_name(const tsi_peer *peer, const char *peer_name) { + char *allocated_name = NULL; + int r; + + if (strchr(peer_name, ':') != NULL) { + char *ignored_port; + gpr_split_host_port(peer_name, &allocated_name, &ignored_port); + gpr_free(ignored_port); + peer_name = allocated_name; + if (!peer_name) return 0; + } + r = tsi_ssl_peer_matches_name(peer, peer_name); + gpr_free(allocated_name); + return r; +} + +grpc_auth_context *tsi_ssl_peer_to_auth_context(const tsi_peer *peer) { + size_t i; + grpc_auth_context *ctx = NULL; + const char *peer_identity_property_name = NULL; + + /* The caller has checked the certificate type property. */ + GPR_ASSERT(peer->property_count >= 1); + ctx = grpc_auth_context_create(NULL); + grpc_auth_context_add_cstring_property( + ctx, GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME, + GRPC_SSL_TRANSPORT_SECURITY_TYPE); + for (i = 0; i < peer->property_count; i++) { + const tsi_peer_property *prop = &peer->properties[i]; + if (prop->name == NULL) continue; + if (strcmp(prop->name, TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY) == 0) { + /* If there is no subject alt name, have the CN as the identity. */ + if (peer_identity_property_name == NULL) { + peer_identity_property_name = GRPC_X509_CN_PROPERTY_NAME; + } + grpc_auth_context_add_property(ctx, GRPC_X509_CN_PROPERTY_NAME, + prop->value.data, prop->value.length); + } else if (strcmp(prop->name, + TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY) == 0) { + peer_identity_property_name = GRPC_X509_SAN_PROPERTY_NAME; + grpc_auth_context_add_property(ctx, GRPC_X509_SAN_PROPERTY_NAME, + prop->value.data, prop->value.length); + } + } + if (peer_identity_property_name != NULL) { + GPR_ASSERT(grpc_auth_context_set_peer_identity_property_name( + ctx, peer_identity_property_name) == 1); + } + return ctx; +} + +static grpc_security_status ssl_check_peer(grpc_security_connector *sc, + const char *peer_name, + const tsi_peer *peer) { + /* Check the ALPN. */ + const tsi_peer_property *p = + tsi_peer_get_property_by_name(peer, TSI_SSL_ALPN_SELECTED_PROTOCOL); + if (p == NULL) { + gpr_log(GPR_ERROR, "Missing selected ALPN property."); + return GRPC_SECURITY_ERROR; + } + if (!grpc_chttp2_is_alpn_version_supported(p->value.data, p->value.length)) { + gpr_log(GPR_ERROR, "Invalid ALPN value."); + return GRPC_SECURITY_ERROR; + } + + /* Check the peer name if specified. */ + if (peer_name != NULL && !ssl_host_matches_name(peer, peer_name)) { + gpr_log(GPR_ERROR, "Peer name %s is not in peer certificate", peer_name); + return GRPC_SECURITY_ERROR; + } + if (sc->auth_context != NULL) { + GRPC_AUTH_CONTEXT_UNREF(sc->auth_context, "connector"); + } + sc->auth_context = tsi_ssl_peer_to_auth_context(peer); + return GRPC_SECURITY_OK; +} + +static grpc_security_status ssl_channel_check_peer(grpc_security_connector *sc, + tsi_peer peer, + grpc_security_check_cb cb, + void *user_data) { + grpc_ssl_channel_security_connector *c = + (grpc_ssl_channel_security_connector *)sc; + grpc_security_status status; + tsi_peer_destruct(&c->peer); + c->peer = peer; + status = ssl_check_peer(sc, c->overridden_target_name != NULL + ? c->overridden_target_name + : c->target_name, + &peer); + return status; +} + +static grpc_security_status ssl_server_check_peer(grpc_security_connector *sc, + tsi_peer peer, + grpc_security_check_cb cb, + void *user_data) { + grpc_security_status status = ssl_check_peer(sc, NULL, &peer); + tsi_peer_destruct(&peer); + return status; +} + +static grpc_security_status ssl_channel_check_call_host( + grpc_channel_security_connector *sc, const char *host, + grpc_security_check_cb cb, void *user_data) { + grpc_ssl_channel_security_connector *c = + (grpc_ssl_channel_security_connector *)sc; + + if (ssl_host_matches_name(&c->peer, host)) return GRPC_SECURITY_OK; + + /* If the target name was overridden, then the original target_name was + 'checked' transitively during the previous peer check at the end of the + handshake. */ + if (c->overridden_target_name != NULL && strcmp(host, c->target_name) == 0) { + return GRPC_SECURITY_OK; + } else { + return GRPC_SECURITY_ERROR; + } +} + +static grpc_security_connector_vtable ssl_channel_vtable = { + ssl_channel_destroy, ssl_channel_create_handshaker, ssl_channel_check_peer}; + +static grpc_security_connector_vtable ssl_server_vtable = { + ssl_server_destroy, ssl_server_create_handshaker, ssl_server_check_peer}; + +static gpr_slice default_pem_root_certs; + +static void init_default_pem_root_certs(void) { + /* First try to load the roots from the environment. */ + char *default_root_certs_path = + gpr_getenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR); + if (default_root_certs_path == NULL) { + default_pem_root_certs = gpr_empty_slice(); + } else { + default_pem_root_certs = gpr_load_file(default_root_certs_path, 0, NULL); + gpr_free(default_root_certs_path); + } + + /* Fall back to installed certs if needed. */ + if (GPR_SLICE_IS_EMPTY(default_pem_root_certs)) { + default_pem_root_certs = gpr_load_file(installed_roots_path, 0, NULL); + } +} + +size_t grpc_get_default_ssl_roots(const unsigned char **pem_root_certs) { + /* TODO(jboeuf@google.com): Maybe revisit the approach which consists in + loading all the roots once for the lifetime of the process. */ + static gpr_once once = GPR_ONCE_INIT; + gpr_once_init(&once, init_default_pem_root_certs); + *pem_root_certs = GPR_SLICE_START_PTR(default_pem_root_certs); + return GPR_SLICE_LENGTH(default_pem_root_certs); +} + +grpc_security_status grpc_ssl_channel_security_connector_create( + grpc_credentials *request_metadata_creds, const grpc_ssl_config *config, + const char *target_name, const char *overridden_target_name, + grpc_channel_security_connector **sc) { + size_t num_alpn_protocols = grpc_chttp2_num_alpn_versions(); + const unsigned char **alpn_protocol_strings = + gpr_malloc(sizeof(const char *) * num_alpn_protocols); + unsigned char *alpn_protocol_string_lengths = + gpr_malloc(sizeof(unsigned char) * num_alpn_protocols); + tsi_result result = TSI_OK; + grpc_ssl_channel_security_connector *c; + size_t i; + const unsigned char *pem_root_certs; + size_t pem_root_certs_size; + char *port; + + for (i = 0; i < num_alpn_protocols; i++) { + alpn_protocol_strings[i] = + (const unsigned char *)grpc_chttp2_get_alpn_version_index(i); + alpn_protocol_string_lengths[i] = + (unsigned char)strlen(grpc_chttp2_get_alpn_version_index(i)); + } + + if (config == NULL || target_name == NULL) { + gpr_log(GPR_ERROR, "An ssl channel needs a config and a target name."); + goto error; + } + if (!check_request_metadata_creds(request_metadata_creds)) { + goto error; + } + if (config->pem_root_certs == NULL) { + pem_root_certs_size = grpc_get_default_ssl_roots(&pem_root_certs); + if (pem_root_certs == NULL || pem_root_certs_size == 0) { + gpr_log(GPR_ERROR, "Could not get default pem root certs."); + goto error; + } + } else { + pem_root_certs = config->pem_root_certs; + pem_root_certs_size = config->pem_root_certs_size; + } + + c = gpr_malloc(sizeof(grpc_ssl_channel_security_connector)); + memset(c, 0, sizeof(grpc_ssl_channel_security_connector)); + + gpr_ref_init(&c->base.base.refcount, 1); + c->base.base.vtable = &ssl_channel_vtable; + c->base.base.is_client_side = 1; + c->base.base.url_scheme = GRPC_SSL_URL_SCHEME; + c->base.request_metadata_creds = grpc_credentials_ref(request_metadata_creds); + c->base.check_call_host = ssl_channel_check_call_host; + gpr_split_host_port(target_name, &c->target_name, &port); + gpr_free(port); + if (overridden_target_name != NULL) { + c->overridden_target_name = gpr_strdup(overridden_target_name); + } + result = tsi_create_ssl_client_handshaker_factory( + config->pem_private_key, config->pem_private_key_size, + config->pem_cert_chain, config->pem_cert_chain_size, pem_root_certs, + pem_root_certs_size, ssl_cipher_suites(), alpn_protocol_strings, + alpn_protocol_string_lengths, (uint16_t)num_alpn_protocols, + &c->handshaker_factory); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.", + tsi_result_to_string(result)); + ssl_channel_destroy(&c->base.base); + *sc = NULL; + goto error; + } + *sc = &c->base; + gpr_free(alpn_protocol_strings); + gpr_free(alpn_protocol_string_lengths); + return GRPC_SECURITY_OK; + +error: + gpr_free(alpn_protocol_strings); + gpr_free(alpn_protocol_string_lengths); + return GRPC_SECURITY_ERROR; +} + +grpc_security_status grpc_ssl_server_security_connector_create( + const grpc_ssl_server_config *config, grpc_security_connector **sc) { + size_t num_alpn_protocols = grpc_chttp2_num_alpn_versions(); + const unsigned char **alpn_protocol_strings = + gpr_malloc(sizeof(const char *) * num_alpn_protocols); + unsigned char *alpn_protocol_string_lengths = + gpr_malloc(sizeof(unsigned char) * num_alpn_protocols); + tsi_result result = TSI_OK; + grpc_ssl_server_security_connector *c; + size_t i; + + for (i = 0; i < num_alpn_protocols; i++) { + alpn_protocol_strings[i] = + (const unsigned char *)grpc_chttp2_get_alpn_version_index(i); + alpn_protocol_string_lengths[i] = + (unsigned char)strlen(grpc_chttp2_get_alpn_version_index(i)); + } + + if (config == NULL || config->num_key_cert_pairs == 0) { + gpr_log(GPR_ERROR, "An SSL server needs a key and a cert."); + goto error; + } + c = gpr_malloc(sizeof(grpc_ssl_server_security_connector)); + memset(c, 0, sizeof(grpc_ssl_server_security_connector)); + + gpr_ref_init(&c->base.refcount, 1); + c->base.url_scheme = GRPC_SSL_URL_SCHEME; + c->base.vtable = &ssl_server_vtable; + result = tsi_create_ssl_server_handshaker_factory( + (const unsigned char **)config->pem_private_keys, + config->pem_private_keys_sizes, + (const unsigned char **)config->pem_cert_chains, + config->pem_cert_chains_sizes, config->num_key_cert_pairs, + config->pem_root_certs, config->pem_root_certs_size, + config->force_client_auth, ssl_cipher_suites(), alpn_protocol_strings, + alpn_protocol_string_lengths, (uint16_t)num_alpn_protocols, + &c->handshaker_factory); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.", + tsi_result_to_string(result)); + ssl_server_destroy(&c->base); + *sc = NULL; + goto error; + } + *sc = &c->base; + gpr_free(alpn_protocol_strings); + gpr_free(alpn_protocol_string_lengths); + return GRPC_SECURITY_OK; + +error: + gpr_free(alpn_protocol_strings); + gpr_free(alpn_protocol_string_lengths); + return GRPC_SECURITY_ERROR; +} diff --git a/src/core/security/security_connector.h b/src/core/security/security_connector.h new file mode 100644 index 00000000..2c9aa1c5 --- /dev/null +++ b/src/core/security/security_connector.h @@ -0,0 +1,223 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_SECURITY_SECURITY_CONNECTOR_H +#define GRPC_INTERNAL_CORE_SECURITY_SECURITY_CONNECTOR_H + +#include +#include "src/core/iomgr/endpoint.h" +#include "src/core/tsi/transport_security_interface.h" + +/* --- status enum. --- */ + +typedef enum { + GRPC_SECURITY_OK = 0, + GRPC_SECURITY_PENDING, + GRPC_SECURITY_ERROR +} grpc_security_status; + +/* --- URL schemes. --- */ + +#define GRPC_SSL_URL_SCHEME "https" +#define GRPC_FAKE_SECURITY_URL_SCHEME "http+fake_security" + +/* --- security_connector object. --- + + A security connector object represents away to configure the underlying + transport security mechanism and check the resulting trusted peer. */ + +typedef struct grpc_security_connector grpc_security_connector; + +#define GRPC_SECURITY_CONNECTOR_ARG "grpc.security_connector" + +typedef void (*grpc_security_check_cb)(void *user_data, + grpc_security_status status); + +typedef struct { + void (*destroy)(grpc_security_connector *sc); + grpc_security_status (*create_handshaker)(grpc_security_connector *sc, + tsi_handshaker **handshaker); + grpc_security_status (*check_peer)(grpc_security_connector *sc, tsi_peer peer, + grpc_security_check_cb cb, + void *user_data); +} grpc_security_connector_vtable; + +struct grpc_security_connector { + const grpc_security_connector_vtable *vtable; + gpr_refcount refcount; + int is_client_side; + const char *url_scheme; + grpc_auth_context *auth_context; /* Populated after the peer is checked. */ +}; + +/* Refcounting. */ +#ifdef GRPC_SECURITY_CONNECTOR_REFCOUNT_DEBUG +#define GRPC_SECURITY_CONNECTOR_REF(p, r) \ + grpc_security_connector_ref((p), __FILE__, __LINE__, (r)) +#define GRPC_SECURITY_CONNECTOR_UNREF(p, r) \ + grpc_security_connector_unref((p), __FILE__, __LINE__, (r)) +grpc_security_connector *grpc_security_connector_ref( + grpc_security_connector *policy, const char *file, int line, + const char *reason); +void grpc_security_connector_unref(grpc_security_connector *policy, + const char *file, int line, + const char *reason); +#else +#define GRPC_SECURITY_CONNECTOR_REF(p, r) grpc_security_connector_ref((p)) +#define GRPC_SECURITY_CONNECTOR_UNREF(p, r) grpc_security_connector_unref((p)) +grpc_security_connector *grpc_security_connector_ref( + grpc_security_connector *policy); +void grpc_security_connector_unref(grpc_security_connector *policy); +#endif + +/* Handshake creation. */ +grpc_security_status grpc_security_connector_create_handshaker( + grpc_security_connector *sc, tsi_handshaker **handshaker); + +/* Check the peer. + Implementations can choose to check the peer either synchronously or + asynchronously. In the first case, a successful call will return + GRPC_SECURITY_OK. In the asynchronous case, the call will return + GRPC_SECURITY_PENDING unless an error is detected early on. + Ownership of the peer is transfered. +*/ +grpc_security_status grpc_security_connector_check_peer( + grpc_security_connector *sc, tsi_peer peer, grpc_security_check_cb cb, + void *user_data); + +/* Util to encapsulate the connector in a channel arg. */ +grpc_arg grpc_security_connector_to_arg(grpc_security_connector *sc); + +/* Util to get the connector from a channel arg. */ +grpc_security_connector *grpc_security_connector_from_arg(const grpc_arg *arg); + +/* Util to find the connector from channel args. */ +grpc_security_connector *grpc_find_security_connector_in_args( + const grpc_channel_args *args); + +/* --- channel_security_connector object. --- + + A channel security connector object represents away to configure the + underlying transport security mechanism on the client side. */ + +typedef struct grpc_channel_security_connector grpc_channel_security_connector; + +struct grpc_channel_security_connector { + grpc_security_connector base; /* requires is_client_side to be non 0. */ + grpc_credentials *request_metadata_creds; + grpc_security_status (*check_call_host)(grpc_channel_security_connector *sc, + const char *host, + grpc_security_check_cb cb, + void *user_data); +}; + +/* Checks that the host that will be set for a call is acceptable. + Implementations can choose do the check either synchronously or + asynchronously. In the first case, a successful call will return + GRPC_SECURITY_OK. In the asynchronous case, the call will return + GRPC_SECURITY_PENDING unless an error is detected early on. */ +grpc_security_status grpc_channel_security_connector_check_call_host( + grpc_channel_security_connector *sc, const char *host, + grpc_security_check_cb cb, void *user_data); + +/* --- Creation security connectors. --- */ + +/* For TESTING ONLY! + Creates a fake connector that emulates real channel security. */ +grpc_channel_security_connector *grpc_fake_channel_security_connector_create( + grpc_credentials *request_metadata_creds, int call_host_check_is_async); + +/* For TESTING ONLY! + Creates a fake connector that emulates real server security. */ +grpc_security_connector *grpc_fake_server_security_connector_create(void); + +/* Config for ssl clients. */ +typedef struct { + unsigned char *pem_private_key; + size_t pem_private_key_size; + unsigned char *pem_cert_chain; + size_t pem_cert_chain_size; + unsigned char *pem_root_certs; + size_t pem_root_certs_size; +} grpc_ssl_config; + +/* Creates an SSL channel_security_connector. + - request_metadata_creds is the credentials object which metadata + will be sent with each request. This parameter can be NULL. + - config is the SSL config to be used for the SSL channel establishment. + - is_client should be 0 for a server or a non-0 value for a client. + - secure_peer_name is the secure peer name that should be checked in + grpc_channel_security_connector_check_peer. This parameter may be NULL in + which case the peer name will not be checked. Note that if this parameter + is not NULL, then, pem_root_certs should not be NULL either. + - sc is a pointer on the connector to be created. + This function returns GRPC_SECURITY_OK in case of success or a + specific error code otherwise. +*/ +grpc_security_status grpc_ssl_channel_security_connector_create( + grpc_credentials *request_metadata_creds, const grpc_ssl_config *config, + const char *target_name, const char *overridden_target_name, + grpc_channel_security_connector **sc); + +/* Gets the default ssl roots. */ +size_t grpc_get_default_ssl_roots(const unsigned char **pem_root_certs); + +/* Config for ssl servers. */ +typedef struct { + unsigned char **pem_private_keys; + size_t *pem_private_keys_sizes; + unsigned char **pem_cert_chains; + size_t *pem_cert_chains_sizes; + size_t num_key_cert_pairs; + unsigned char *pem_root_certs; + size_t pem_root_certs_size; + int force_client_auth; +} grpc_ssl_server_config; + +/* Creates an SSL server_security_connector. + - config is the SSL config to be used for the SSL channel establishment. + - sc is a pointer on the connector to be created. + This function returns GRPC_SECURITY_OK in case of success or a + specific error code otherwise. +*/ +grpc_security_status grpc_ssl_server_security_connector_create( + const grpc_ssl_server_config *config, grpc_security_connector **sc); + +/* Util. */ +const tsi_peer_property *tsi_peer_get_property_by_name(const tsi_peer *peer, + const char *name); + +/* Exposed for testing only. */ +grpc_auth_context *tsi_ssl_peer_to_auth_context(const tsi_peer *peer); + +#endif /* GRPC_INTERNAL_CORE_SECURITY_SECURITY_CONNECTOR_H */ diff --git a/src/core/security/security_context.c b/src/core/security/security_context.c new file mode 100644 index 00000000..95d80ba1 --- /dev/null +++ b/src/core/security/security_context.c @@ -0,0 +1,314 @@ +/* + * + * Copyright 2015, 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 + +#include "src/core/security/security_context.h" +#include "src/core/surface/call.h" +#include "src/core/support/string.h" + +#include +#include +#include +#include + +/* --- grpc_call --- */ + +grpc_call_error grpc_call_set_credentials(grpc_call *call, + grpc_credentials *creds) { + grpc_client_security_context *ctx = NULL; + if (!grpc_call_is_client(call)) { + gpr_log(GPR_ERROR, "Method is client-side only."); + return GRPC_CALL_ERROR_NOT_ON_SERVER; + } + if (creds != NULL && !grpc_credentials_has_request_metadata_only(creds)) { + gpr_log(GPR_ERROR, "Incompatible credentials to set on a call."); + return GRPC_CALL_ERROR; + } + ctx = (grpc_client_security_context *)grpc_call_context_get( + call, GRPC_CONTEXT_SECURITY); + if (ctx == NULL) { + ctx = grpc_client_security_context_create(); + ctx->creds = grpc_credentials_ref(creds); + grpc_call_context_set(call, GRPC_CONTEXT_SECURITY, ctx, + grpc_client_security_context_destroy); + } else { + grpc_credentials_unref(ctx->creds); + ctx->creds = grpc_credentials_ref(creds); + } + return GRPC_CALL_OK; +} + +grpc_auth_context *grpc_call_auth_context(grpc_call *call) { + void *sec_ctx = grpc_call_context_get(call, GRPC_CONTEXT_SECURITY); + if (sec_ctx == NULL) return NULL; + return grpc_call_is_client(call) + ? GRPC_AUTH_CONTEXT_REF( + ((grpc_client_security_context *)sec_ctx)->auth_context, + "grpc_call_auth_context client") + : GRPC_AUTH_CONTEXT_REF( + ((grpc_server_security_context *)sec_ctx)->auth_context, + "grpc_call_auth_context server"); +} + +void grpc_auth_context_release(grpc_auth_context *context) { + GRPC_AUTH_CONTEXT_UNREF(context, "grpc_auth_context_unref"); +} + +/* --- grpc_client_security_context --- */ + +grpc_client_security_context *grpc_client_security_context_create(void) { + grpc_client_security_context *ctx = + gpr_malloc(sizeof(grpc_client_security_context)); + memset(ctx, 0, sizeof(grpc_client_security_context)); + return ctx; +} + +void grpc_client_security_context_destroy(void *ctx) { + grpc_client_security_context *c = (grpc_client_security_context *)ctx; + grpc_credentials_unref(c->creds); + GRPC_AUTH_CONTEXT_UNREF(c->auth_context, "client_security_context"); + gpr_free(ctx); +} + +/* --- grpc_server_security_context --- */ + +grpc_server_security_context *grpc_server_security_context_create(void) { + grpc_server_security_context *ctx = + gpr_malloc(sizeof(grpc_server_security_context)); + memset(ctx, 0, sizeof(grpc_server_security_context)); + return ctx; +} + +void grpc_server_security_context_destroy(void *ctx) { + grpc_server_security_context *c = (grpc_server_security_context *)ctx; + GRPC_AUTH_CONTEXT_UNREF(c->auth_context, "server_security_context"); + gpr_free(ctx); +} + +/* --- grpc_auth_context --- */ + +static grpc_auth_property_iterator empty_iterator = {NULL, 0, NULL}; + +grpc_auth_context *grpc_auth_context_create(grpc_auth_context *chained) { + grpc_auth_context *ctx = gpr_malloc(sizeof(grpc_auth_context)); + memset(ctx, 0, sizeof(grpc_auth_context)); + gpr_ref_init(&ctx->refcount, 1); + if (chained != NULL) { + ctx->chained = GRPC_AUTH_CONTEXT_REF(chained, "chained"); + ctx->peer_identity_property_name = + ctx->chained->peer_identity_property_name; + } + return ctx; +} + +#ifdef GRPC_AUTH_CONTEXT_REFCOUNT_DEBUG +grpc_auth_context *grpc_auth_context_ref(grpc_auth_context *ctx, + const char *file, int line, + const char *reason) { + if (ctx == NULL) return NULL; + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "AUTH_CONTEXT:%p ref %d -> %d %s", ctx, (int)ctx->refcount.count, + (int)ctx->refcount.count + 1, reason); +#else +grpc_auth_context *grpc_auth_context_ref(grpc_auth_context *ctx) { + if (ctx == NULL) return NULL; +#endif + gpr_ref(&ctx->refcount); + return ctx; +} + +#ifdef GRPC_AUTH_CONTEXT_REFCOUNT_DEBUG +void grpc_auth_context_unref(grpc_auth_context *ctx, const char *file, int line, + const char *reason) { + if (ctx == NULL) return; + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "AUTH_CONTEXT:%p unref %d -> %d %s", ctx, (int)ctx->refcount.count, + (int)ctx->refcount.count - 1, reason); +#else +void grpc_auth_context_unref(grpc_auth_context *ctx) { + if (ctx == NULL) return; +#endif + if (gpr_unref(&ctx->refcount)) { + size_t i; + GRPC_AUTH_CONTEXT_UNREF(ctx->chained, "chained"); + if (ctx->properties.array != NULL) { + for (i = 0; i < ctx->properties.count; i++) { + grpc_auth_property_reset(&ctx->properties.array[i]); + } + gpr_free(ctx->properties.array); + } + gpr_free(ctx); + } +} + +const char *grpc_auth_context_peer_identity_property_name( + const grpc_auth_context *ctx) { + return ctx->peer_identity_property_name; +} + +int grpc_auth_context_set_peer_identity_property_name(grpc_auth_context *ctx, + const char *name) { + grpc_auth_property_iterator it = + grpc_auth_context_find_properties_by_name(ctx, name); + const grpc_auth_property *prop = grpc_auth_property_iterator_next(&it); + if (prop == NULL) { + gpr_log(GPR_ERROR, "Property name %s not found in auth context.", + name != NULL ? name : "NULL"); + return 0; + } + ctx->peer_identity_property_name = prop->name; + return 1; +} + +int grpc_auth_context_peer_is_authenticated(const grpc_auth_context *ctx) { + return ctx->peer_identity_property_name == NULL ? 0 : 1; +} + +grpc_auth_property_iterator grpc_auth_context_property_iterator( + const grpc_auth_context *ctx) { + grpc_auth_property_iterator it = empty_iterator; + if (ctx == NULL) return it; + it.ctx = ctx; + return it; +} + +const grpc_auth_property *grpc_auth_property_iterator_next( + grpc_auth_property_iterator *it) { + if (it == NULL || it->ctx == NULL) return NULL; + while (it->index == it->ctx->properties.count) { + if (it->ctx->chained == NULL) return NULL; + it->ctx = it->ctx->chained; + it->index = 0; + } + if (it->name == NULL) { + return &it->ctx->properties.array[it->index++]; + } else { + while (it->index < it->ctx->properties.count) { + const grpc_auth_property *prop = &it->ctx->properties.array[it->index++]; + GPR_ASSERT(prop->name != NULL); + if (strcmp(it->name, prop->name) == 0) { + return prop; + } + } + /* We could not find the name, try another round. */ + return grpc_auth_property_iterator_next(it); + } +} + +grpc_auth_property_iterator grpc_auth_context_find_properties_by_name( + const grpc_auth_context *ctx, const char *name) { + grpc_auth_property_iterator it = empty_iterator; + if (ctx == NULL || name == NULL) return empty_iterator; + it.ctx = ctx; + it.name = name; + return it; +} + +grpc_auth_property_iterator grpc_auth_context_peer_identity( + const grpc_auth_context *ctx) { + if (ctx == NULL) return empty_iterator; + return grpc_auth_context_find_properties_by_name( + ctx, ctx->peer_identity_property_name); +} + +static void ensure_auth_context_capacity(grpc_auth_context *ctx) { + if (ctx->properties.count == ctx->properties.capacity) { + ctx->properties.capacity = + GPR_MAX(ctx->properties.capacity + 8, ctx->properties.capacity * 2); + ctx->properties.array = + gpr_realloc(ctx->properties.array, + ctx->properties.capacity * sizeof(grpc_auth_property)); + } +} + +void grpc_auth_context_add_property(grpc_auth_context *ctx, const char *name, + const char *value, size_t value_length) { + grpc_auth_property *prop; + ensure_auth_context_capacity(ctx); + prop = &ctx->properties.array[ctx->properties.count++]; + prop->name = gpr_strdup(name); + prop->value = gpr_malloc(value_length + 1); + memcpy(prop->value, value, value_length); + prop->value[value_length] = '\0'; + prop->value_length = value_length; +} + +void grpc_auth_context_add_cstring_property(grpc_auth_context *ctx, + const char *name, + const char *value) { + grpc_auth_property *prop; + ensure_auth_context_capacity(ctx); + prop = &ctx->properties.array[ctx->properties.count++]; + prop->name = gpr_strdup(name); + prop->value = gpr_strdup(value); + prop->value_length = strlen(value); +} + +void grpc_auth_property_reset(grpc_auth_property *property) { + gpr_free(property->name); + gpr_free(property->value); + memset(property, 0, sizeof(grpc_auth_property)); +} + +grpc_arg grpc_auth_metadata_processor_to_arg(grpc_auth_metadata_processor *p) { + grpc_arg arg; + memset(&arg, 0, sizeof(grpc_arg)); + arg.type = GRPC_ARG_POINTER; + arg.key = GRPC_AUTH_METADATA_PROCESSOR_ARG; + arg.value.pointer.p = p; + return arg; +} + +grpc_auth_metadata_processor *grpc_auth_metadata_processor_from_arg( + const grpc_arg *arg) { + if (strcmp(arg->key, GRPC_AUTH_METADATA_PROCESSOR_ARG) != 0) return NULL; + if (arg->type != GRPC_ARG_POINTER) { + gpr_log(GPR_ERROR, "Invalid type %d for arg %s", arg->type, + GRPC_AUTH_METADATA_PROCESSOR_ARG); + return NULL; + } + return arg->value.pointer.p; +} + +grpc_auth_metadata_processor *grpc_find_auth_metadata_processor_in_args( + const grpc_channel_args *args) { + size_t i; + if (args == NULL) return NULL; + for (i = 0; i < args->num_args; i++) { + grpc_auth_metadata_processor *p = + grpc_auth_metadata_processor_from_arg(&args->args[i]); + if (p != NULL) return p; + } + return NULL; +} diff --git a/src/core/security/security_context.h b/src/core/security/security_context.h new file mode 100644 index 00000000..a9a03064 --- /dev/null +++ b/src/core/security/security_context.h @@ -0,0 +1,115 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_SECURITY_SECURITY_CONTEXT_H +#define GRPC_INTERNAL_CORE_SECURITY_SECURITY_CONTEXT_H + +#include "src/core/iomgr/pollset.h" +#include "src/core/security/credentials.h" + +/* --- grpc_auth_context --- + + High level authentication context object. Can optionally be chained. */ + +/* Property names are always NULL terminated. */ + +typedef struct { + grpc_auth_property *array; + size_t count; + size_t capacity; +} grpc_auth_property_array; + +struct grpc_auth_context { + struct grpc_auth_context *chained; + grpc_auth_property_array properties; + gpr_refcount refcount; + const char *peer_identity_property_name; + grpc_pollset *pollset; +}; + +/* Creation. */ +grpc_auth_context *grpc_auth_context_create(grpc_auth_context *chained); + +/* Refcounting. */ +#ifdef GRPC_AUTH_CONTEXT_REFCOUNT_DEBUG +#define GRPC_AUTH_CONTEXT_REF(p, r) \ + grpc_auth_context_ref((p), __FILE__, __LINE__, (r)) +#define GRPC_AUTH_CONTEXT_UNREF(p, r) \ + grpc_auth_context_unref((p), __FILE__, __LINE__, (r)) +grpc_auth_context *grpc_auth_context_ref(grpc_auth_context *policy, + const char *file, int line, + const char *reason); +void grpc_auth_context_unref(grpc_auth_context *policy, const char *file, + int line, const char *reason); +#else +#define GRPC_AUTH_CONTEXT_REF(p, r) grpc_auth_context_ref((p)) +#define GRPC_AUTH_CONTEXT_UNREF(p, r) grpc_auth_context_unref((p)) +grpc_auth_context *grpc_auth_context_ref(grpc_auth_context *policy); +void grpc_auth_context_unref(grpc_auth_context *policy); +#endif + +void grpc_auth_property_reset(grpc_auth_property *property); + +/* --- grpc_client_security_context --- + + Internal client-side security context. */ + +typedef struct { + grpc_credentials *creds; + grpc_auth_context *auth_context; +} grpc_client_security_context; + +grpc_client_security_context *grpc_client_security_context_create(void); +void grpc_client_security_context_destroy(void *ctx); + +/* --- grpc_server_security_context --- + + Internal server-side security context. */ + +typedef struct { + grpc_auth_context *auth_context; +} grpc_server_security_context; + +grpc_server_security_context *grpc_server_security_context_create(void); +void grpc_server_security_context_destroy(void *ctx); + +/* --- Auth metadata processing. --- */ +#define GRPC_AUTH_METADATA_PROCESSOR_ARG "grpc.auth_metadata_processor" + +grpc_arg grpc_auth_metadata_processor_to_arg(grpc_auth_metadata_processor *p); +grpc_auth_metadata_processor *grpc_auth_metadata_processor_from_arg( + const grpc_arg *arg); +grpc_auth_metadata_processor *grpc_find_auth_metadata_processor_in_args( + const grpc_channel_args *args); + +#endif /* GRPC_INTERNAL_CORE_SECURITY_SECURITY_CONTEXT_H */ diff --git a/src/core/security/server_auth_filter.c b/src/core/security/server_auth_filter.c new file mode 100644 index 00000000..d134201e --- /dev/null +++ b/src/core/security/server_auth_filter.c @@ -0,0 +1,271 @@ +/* + * + * Copyright 2015, 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 + +#include "src/core/security/auth_filters.h" +#include "src/core/security/security_connector.h" +#include "src/core/security/security_context.h" + +#include +#include + +typedef struct call_data { + gpr_uint8 got_client_metadata; + grpc_stream_op_buffer *recv_ops; + /* Closure to call when finished with the auth_on_recv hook. */ + grpc_iomgr_closure *on_done_recv; + /* Receive closures are chained: we inject this closure as the on_done_recv + up-call on transport_op, and remember to call our on_done_recv member after + handling it. */ + grpc_iomgr_closure auth_on_recv; + grpc_transport_stream_op transport_op; + grpc_metadata_array md; + const grpc_metadata *consumed_md; + size_t num_consumed_md; + grpc_stream_op *md_op; + grpc_auth_context *auth_context; +} call_data; + +typedef struct channel_data { + grpc_security_connector *security_connector; + grpc_auth_metadata_processor processor; + grpc_mdctx *mdctx; +} channel_data; + +static grpc_metadata_array metadata_batch_to_md_array( + const grpc_metadata_batch *batch) { + grpc_linked_mdelem *l; + grpc_metadata_array result; + grpc_metadata_array_init(&result); + for (l = batch->list.head; l != NULL; l = l->next) { + grpc_metadata *usr_md = NULL; + grpc_mdelem *md = l->md; + grpc_mdstr *key = md->key; + grpc_mdstr *value = md->value; + if (result.count == result.capacity) { + result.capacity = GPR_MAX(result.capacity + 8, result.capacity * 2); + result.metadata = + gpr_realloc(result.metadata, result.capacity * sizeof(grpc_metadata)); + } + usr_md = &result.metadata[result.count++]; + usr_md->key = grpc_mdstr_as_c_string(key); + usr_md->value = grpc_mdstr_as_c_string(value); + usr_md->value_length = GPR_SLICE_LENGTH(value->slice); + } + return result; +} + +static grpc_mdelem *remove_consumed_md(void *user_data, grpc_mdelem *md) { + grpc_call_element *elem = user_data; + call_data *calld = elem->call_data; + size_t i; + for (i = 0; i < calld->num_consumed_md; i++) { + const grpc_metadata *consumed_md = &calld->consumed_md[i]; + /* Maybe we could do a pointer comparison but we do not have any guarantee + that the metadata processor used the same pointers for consumed_md in the + callback. */ + if (GPR_SLICE_LENGTH(md->key->slice) != strlen(consumed_md->key) || + GPR_SLICE_LENGTH(md->value->slice) != consumed_md->value_length) { + continue; + } + if (memcmp(GPR_SLICE_START_PTR(md->key->slice), consumed_md->key, + GPR_SLICE_LENGTH(md->key->slice)) == 0 && + memcmp(GPR_SLICE_START_PTR(md->value->slice), consumed_md->value, + GPR_SLICE_LENGTH(md->value->slice)) == 0) { + return NULL; /* Delete. */ + } + } + return md; +} + +static void on_md_processing_done( + void *user_data, const grpc_metadata *consumed_md, size_t num_consumed_md, + const grpc_metadata *response_md, size_t num_response_md, + grpc_status_code status, const char *error_details) { + grpc_call_element *elem = user_data; + call_data *calld = elem->call_data; + + /* TODO(jboeuf): Implement support for response_md. */ + if (response_md != NULL && num_response_md > 0) { + gpr_log(GPR_INFO, + "response_md in auth metadata processing not supported for now. " + "Ignoring..."); + } + + if (status == GRPC_STATUS_OK) { + calld->consumed_md = consumed_md; + calld->num_consumed_md = num_consumed_md; + grpc_metadata_batch_filter(&calld->md_op->data.metadata, remove_consumed_md, + elem); + grpc_metadata_array_destroy(&calld->md); + calld->on_done_recv->cb(calld->on_done_recv->cb_arg, 1); + } else { + gpr_slice message; + grpc_metadata_array_destroy(&calld->md); + error_details = error_details != NULL + ? error_details + : "Authentication metadata processing failed."; + message = gpr_slice_from_copied_string(error_details); + grpc_sopb_reset(calld->recv_ops); + grpc_transport_stream_op_add_close(&calld->transport_op, status, &message); + grpc_call_next_op(elem, &calld->transport_op); + } +} + +static void auth_on_recv(void *user_data, int success) { + grpc_call_element *elem = user_data; + call_data *calld = elem->call_data; + channel_data *chand = elem->channel_data; + if (success) { + size_t i; + size_t nops = calld->recv_ops->nops; + grpc_stream_op *ops = calld->recv_ops->ops; + for (i = 0; i < nops; i++) { + grpc_stream_op *op = &ops[i]; + if (op->type != GRPC_OP_METADATA || calld->got_client_metadata) continue; + calld->got_client_metadata = 1; + if (chand->processor.process == NULL) continue; + calld->md_op = op; + calld->md = metadata_batch_to_md_array(&op->data.metadata); + chand->processor.process(chand->processor.state, calld->auth_context, + calld->md.metadata, calld->md.count, + on_md_processing_done, elem); + return; + } + } + calld->on_done_recv->cb(calld->on_done_recv->cb_arg, success); +} + +static void set_recv_ops_md_callbacks(grpc_call_element *elem, + grpc_transport_stream_op *op) { + call_data *calld = elem->call_data; + + if (op->recv_ops && !calld->got_client_metadata) { + /* substitute our callback for the higher callback */ + calld->recv_ops = op->recv_ops; + calld->on_done_recv = op->on_done_recv; + op->on_done_recv = &calld->auth_on_recv; + calld->transport_op = *op; + } +} + +/* Called either: + - in response to an API call (or similar) from above, to send something + - a network event (or similar) from below, to receive something + op contains type and call direction information, in addition to the data + that is being sent or received. */ +static void auth_start_transport_op(grpc_call_element *elem, + grpc_transport_stream_op *op) { + set_recv_ops_md_callbacks(elem, op); + grpc_call_next_op(elem, op); +} + +/* Constructor for call_data */ +static void init_call_elem(grpc_call_element *elem, + const void *server_transport_data, + grpc_transport_stream_op *initial_op) { + /* grab pointers to our data from the call element */ + call_data *calld = elem->call_data; + channel_data *chand = elem->channel_data; + grpc_server_security_context *server_ctx = NULL; + + /* initialize members */ + memset(calld, 0, sizeof(*calld)); + grpc_iomgr_closure_init(&calld->auth_on_recv, auth_on_recv, elem); + + GPR_ASSERT(initial_op && initial_op->context != NULL && + initial_op->context[GRPC_CONTEXT_SECURITY].value == NULL); + + /* Create a security context for the call and reference the auth context from + the channel. */ + if (initial_op->context[GRPC_CONTEXT_SECURITY].value != NULL) { + initial_op->context[GRPC_CONTEXT_SECURITY].destroy( + initial_op->context[GRPC_CONTEXT_SECURITY].value); + } + server_ctx = grpc_server_security_context_create(); + server_ctx->auth_context = + grpc_auth_context_create(chand->security_connector->auth_context); + server_ctx->auth_context->pollset = initial_op->bind_pollset; + initial_op->context[GRPC_CONTEXT_SECURITY].value = server_ctx; + initial_op->context[GRPC_CONTEXT_SECURITY].destroy = + grpc_server_security_context_destroy; + calld->auth_context = server_ctx->auth_context; + + /* Set the metadata callbacks. */ + set_recv_ops_md_callbacks(elem, initial_op); +} + +/* Destructor for call_data */ +static void destroy_call_elem(grpc_call_element *elem) {} + +/* Constructor for channel_data */ +static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, + const grpc_channel_args *args, grpc_mdctx *mdctx, + int is_first, int is_last) { + grpc_security_connector *sc = grpc_find_security_connector_in_args(args); + grpc_auth_metadata_processor *processor = + grpc_find_auth_metadata_processor_in_args(args); + /* grab pointers to our data from the channel element */ + channel_data *chand = elem->channel_data; + + /* The first and the last filters tend to be implemented differently to + handle the case that there's no 'next' filter to call on the up or down + path */ + GPR_ASSERT(!is_first); + GPR_ASSERT(!is_last); + GPR_ASSERT(sc != NULL); + GPR_ASSERT(processor != NULL); + + /* initialize members */ + GPR_ASSERT(!sc->is_client_side); + chand->security_connector = + GRPC_SECURITY_CONNECTOR_REF(sc, "server_auth_filter"); + chand->mdctx = mdctx; + chand->processor = *processor; +} + +/* Destructor for channel data */ +static void destroy_channel_elem(grpc_channel_element *elem) { + /* grab pointers to our data from the channel element */ + channel_data *chand = elem->channel_data; + GRPC_SECURITY_CONNECTOR_UNREF(chand->security_connector, + "server_auth_filter"); +} + +const grpc_channel_filter grpc_server_auth_filter = { + auth_start_transport_op, grpc_channel_next_op, + sizeof(call_data), init_call_elem, + destroy_call_elem, sizeof(channel_data), + init_channel_elem, destroy_channel_elem, + grpc_call_next_get_peer, "server-auth"}; diff --git a/src/core/security/server_secure_chttp2.c b/src/core/security/server_secure_chttp2.c new file mode 100644 index 00000000..4749f5f5 --- /dev/null +++ b/src/core/security/server_secure_chttp2.c @@ -0,0 +1,294 @@ +/* + * + * Copyright 2015, 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 + +#include + +#include "src/core/channel/channel_args.h" +#include "src/core/channel/http_server_filter.h" +#include "src/core/iomgr/endpoint.h" +#include "src/core/iomgr/resolve_address.h" +#include "src/core/iomgr/tcp_server.h" +#include "src/core/security/auth_filters.h" +#include "src/core/security/credentials.h" +#include "src/core/security/security_connector.h" +#include "src/core/security/security_context.h" +#include "src/core/security/secure_transport_setup.h" +#include "src/core/surface/server.h" +#include "src/core/transport/chttp2_transport.h" +#include +#include +#include +#include + +typedef struct tcp_endpoint_list { + grpc_endpoint *tcp_endpoint; + struct tcp_endpoint_list *next; +} tcp_endpoint_list; + +typedef struct grpc_server_secure_state { + grpc_server *server; + grpc_tcp_server *tcp; + grpc_security_connector *sc; + grpc_server_credentials *creds; + tcp_endpoint_list *handshaking_tcp_endpoints; + int is_shutdown; + gpr_mu mu; + gpr_refcount refcount; +} grpc_server_secure_state; + +static void state_ref(grpc_server_secure_state *state) { + gpr_ref(&state->refcount); +} + +static void state_unref(grpc_server_secure_state *state) { + if (gpr_unref(&state->refcount)) { + /* ensure all threads have unlocked */ + gpr_mu_lock(&state->mu); + gpr_mu_unlock(&state->mu); + /* clean up */ + GRPC_SECURITY_CONNECTOR_UNREF(state->sc, "server"); + grpc_server_credentials_unref(state->creds); + gpr_free(state); + } +} + +static void setup_transport(void *statep, grpc_transport *transport, + grpc_mdctx *mdctx) { + static grpc_channel_filter const *extra_filters[] = { + &grpc_server_auth_filter, &grpc_http_server_filter}; + grpc_server_secure_state *state = statep; + grpc_channel_args *args_copy; + grpc_arg args_to_add[2]; + args_to_add[0] = grpc_security_connector_to_arg(state->sc); + args_to_add[1] = + grpc_auth_metadata_processor_to_arg(&state->creds->processor); + args_copy = grpc_channel_args_copy_and_add( + grpc_server_get_channel_args(state->server), args_to_add, + GPR_ARRAY_SIZE(args_to_add)); + grpc_server_setup_transport(state->server, transport, extra_filters, + GPR_ARRAY_SIZE(extra_filters), mdctx, args_copy); + grpc_channel_args_destroy(args_copy); +} + +static int remove_tcp_from_list_locked(grpc_server_secure_state *state, + grpc_endpoint *tcp) { + tcp_endpoint_list *node = state->handshaking_tcp_endpoints; + tcp_endpoint_list *tmp = NULL; + if (node && node->tcp_endpoint == tcp) { + state->handshaking_tcp_endpoints = state->handshaking_tcp_endpoints->next; + gpr_free(node); + return 0; + } + while (node) { + if (node->next->tcp_endpoint == tcp) { + tmp = node->next; + node->next = node->next->next; + gpr_free(tmp); + return 0; + } + node = node->next; + } + return -1; +} + +static void on_secure_transport_setup_done(void *statep, + grpc_security_status status, + grpc_endpoint *wrapped_endpoint, + grpc_endpoint *secure_endpoint) { + grpc_server_secure_state *state = statep; + grpc_transport *transport; + grpc_mdctx *mdctx; + if (status == GRPC_SECURITY_OK) { + gpr_mu_lock(&state->mu); + remove_tcp_from_list_locked(state, wrapped_endpoint); + if (!state->is_shutdown) { + mdctx = grpc_mdctx_create(); + transport = grpc_create_chttp2_transport( + grpc_server_get_channel_args(state->server), secure_endpoint, mdctx, + 0); + setup_transport(state, transport, mdctx); + grpc_chttp2_transport_start_reading(transport, NULL, 0); + } else { + /* We need to consume this here, because the server may already have gone + * away. */ + grpc_endpoint_destroy(secure_endpoint); + } + gpr_mu_unlock(&state->mu); + } else { + gpr_mu_lock(&state->mu); + remove_tcp_from_list_locked(state, wrapped_endpoint); + gpr_mu_unlock(&state->mu); + gpr_log(GPR_ERROR, "Secure transport failed with error %d", status); + } + state_unref(state); +} + +static void on_accept(void *statep, grpc_endpoint *tcp) { + grpc_server_secure_state *state = statep; + tcp_endpoint_list *node; + state_ref(state); + node = gpr_malloc(sizeof(tcp_endpoint_list)); + node->tcp_endpoint = tcp; + gpr_mu_lock(&state->mu); + node->next = state->handshaking_tcp_endpoints; + state->handshaking_tcp_endpoints = node; + gpr_mu_unlock(&state->mu); + grpc_setup_secure_transport(state->sc, tcp, on_secure_transport_setup_done, + state); +} + +/* Server callback: start listening on our ports */ +static void start(grpc_server *server, void *statep, grpc_pollset **pollsets, + size_t pollset_count) { + grpc_server_secure_state *state = statep; + grpc_tcp_server_start(state->tcp, pollsets, pollset_count, on_accept, state); +} + +static void destroy_done(void *statep) { + grpc_server_secure_state *state = statep; + grpc_server_listener_destroy_done(state->server); + gpr_mu_lock(&state->mu); + while (state->handshaking_tcp_endpoints != NULL) { + grpc_endpoint_shutdown(state->handshaking_tcp_endpoints->tcp_endpoint); + remove_tcp_from_list_locked(state, + state->handshaking_tcp_endpoints->tcp_endpoint); + } + gpr_mu_unlock(&state->mu); + state_unref(state); +} + +/* Server callback: destroy the tcp listener (so we don't generate further + callbacks) */ +static void destroy(grpc_server *server, void *statep) { + grpc_server_secure_state *state = statep; + grpc_tcp_server *tcp; + gpr_mu_lock(&state->mu); + state->is_shutdown = 1; + tcp = state->tcp; + gpr_mu_unlock(&state->mu); + grpc_tcp_server_destroy(tcp, destroy_done, state); +} + +int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr, + grpc_server_credentials *creds) { + grpc_resolved_addresses *resolved = NULL; + grpc_tcp_server *tcp = NULL; + grpc_server_secure_state *state = NULL; + size_t i; + unsigned count = 0; + int port_num = -1; + int port_temp; + grpc_security_status status = GRPC_SECURITY_ERROR; + grpc_security_connector *sc = NULL; + + /* create security context */ + if (creds == NULL) goto error; + status = grpc_server_credentials_create_security_connector(creds, &sc); + if (status != GRPC_SECURITY_OK) { + gpr_log(GPR_ERROR, + "Unable to create secure server with credentials of type %s.", + creds->type); + goto error; + } + + /* resolve address */ + resolved = grpc_blocking_resolve_address(addr, "https"); + if (!resolved) { + goto error; + } + + tcp = grpc_tcp_server_create(); + if (!tcp) { + goto error; + } + + for (i = 0; i < resolved->naddrs; i++) { + port_temp = grpc_tcp_server_add_port( + tcp, (struct sockaddr *)&resolved->addrs[i].addr, + resolved->addrs[i].len); + if (port_temp >= 0) { + if (port_num == -1) { + port_num = port_temp; + } else { + GPR_ASSERT(port_num == port_temp); + } + count++; + } + } + if (count == 0) { + gpr_log(GPR_ERROR, "No address added out of total %d resolved", + resolved->naddrs); + goto error; + } + if (count != resolved->naddrs) { + gpr_log(GPR_ERROR, "Only %d addresses added out of total %d resolved", + count, resolved->naddrs); + /* if it's an error, don't we want to goto error; here ? */ + } + grpc_resolved_addresses_destroy(resolved); + + state = gpr_malloc(sizeof(*state)); + memset(state, 0, sizeof(*state)); + state->server = server; + state->tcp = tcp; + state->sc = sc; + state->creds = grpc_server_credentials_ref(creds); + + state->handshaking_tcp_endpoints = NULL; + state->is_shutdown = 0; + gpr_mu_init(&state->mu); + gpr_ref_init(&state->refcount, 1); + + /* Register with the server only upon success */ + grpc_server_add_listener(server, state, start, destroy); + + return port_num; + +/* Error path: cleanup and return */ +error: + if (sc) { + GRPC_SECURITY_CONNECTOR_UNREF(sc, "server"); + } + if (resolved) { + grpc_resolved_addresses_destroy(resolved); + } + if (tcp) { + grpc_tcp_server_destroy(tcp, NULL, NULL); + } + if (state) { + gpr_free(state); + } + return 0; +} diff --git a/src/core/statistics/census_init.c b/src/core/statistics/census_init.c new file mode 100644 index 00000000..e6306f5e --- /dev/null +++ b/src/core/statistics/census_init.c @@ -0,0 +1,48 @@ +/* + * + * Copyright 2015, 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 "src/core/statistics/census_interface.h" + +#include +#include "src/core/statistics/census_rpc_stats.h" +#include "src/core/statistics/census_tracing.h" + +void census_init(void) { + census_tracing_init(); + census_stats_store_init(); +} + +void census_shutdown(void) { + census_stats_store_shutdown(); + census_tracing_shutdown(); +} diff --git a/src/core/statistics/census_interface.h b/src/core/statistics/census_interface.h new file mode 100644 index 00000000..ac1ff248 --- /dev/null +++ b/src/core/statistics/census_interface.h @@ -0,0 +1,76 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_STATISTICS_CENSUS_INTERFACE_H +#define GRPC_INTERNAL_CORE_STATISTICS_CENSUS_INTERFACE_H + +#include + +/* Maximum length of an individual census trace annotation. */ +#define CENSUS_MAX_ANNOTATION_LENGTH 200 + +/* Structure of a census op id. Define as structure because 64bit integer is not + available on every platform for C89. */ +typedef struct census_op_id { + gpr_uint32 upper; + gpr_uint32 lower; +} census_op_id; + +typedef struct census_rpc_stats census_rpc_stats; + +/* Initializes Census library. No-op if Census is already initialized. */ +void census_init(void); + +/* Shutdown Census Library. */ +void census_shutdown(void); + +/* Annotates grpc method name on a census_op_id. The method name has the format + of /. Returns 0 iff + op_id and method_name are all valid. op_id is valid after its creation and + before calling census_tracing_end_op(). + + TODO(hongyu): Figure out valid characters set for service name and command + name and document requirements here.*/ +int census_add_method_tag(census_op_id op_id, const char* method_name); + +/* Annotates tracing information to a specific op_id. + Up to CENSUS_MAX_ANNOTATION_LENGTH bytes are recorded. */ +void census_tracing_print(census_op_id op_id, const char* annotation); + +/* Starts tracing for an RPC. Returns a locally unique census_op_id */ +census_op_id census_tracing_start_op(void); + +/* Ends tracing. Calling this function will invalidate the input op_id. */ +void census_tracing_end_op(census_op_id op_id); + +#endif /* GRPC_INTERNAL_CORE_STATISTICS_CENSUS_INTERFACE_H */ diff --git a/src/core/statistics/census_log.c b/src/core/statistics/census_log.c new file mode 100644 index 00000000..ec56ce38 --- /dev/null +++ b/src/core/statistics/census_log.c @@ -0,0 +1,604 @@ +/* + * + * Copyright 2015, 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. + * + */ + +/* Available log space is divided up in blocks of + CENSUS_LOG_2_MAX_RECORD_SIZE bytes. A block can be in one of the + following three data structures: + - Free blocks (free_block_list) + - Blocks with unread data (dirty_block_list) + - Blocks currently attached to cores (core_local_blocks[]) + + census_log_start_write() moves a block from core_local_blocks[] to the + end of dirty_block_list when block: + - is out-of-space OR + - has an incomplete record (an incomplete record occurs when a thread calls + census_log_start_write() and is context-switched before calling + census_log_end_write() + So, blocks in dirty_block_list are ordered, from oldest to newest, by time + when block is detached from the core. + + census_log_read_next() first iterates over dirty_block_list and then + core_local_blocks[]. It moves completely read blocks from dirty_block_list + to free_block_list. Blocks in core_local_blocks[] are not freed, even when + completely read. + + If log is configured to discard old records and free_block_list is empty, + census_log_start_write() iterates over dirty_block_list to allocate a + new block. It moves the oldest available block (no pending read/write) to + core_local_blocks[]. + + core_local_block_struct is used to implement a map from core id to the block + associated with that core. This mapping is advisory. It is possible that the + block returned by this mapping is no longer associated with that core. This + mapping is updated, lazily, by census_log_start_write(). + + Locking in block struct: + + Exclusive g_log.lock must be held before calling any functions operatong on + block structs except census_log_start_write() and + census_log_end_write(). + + Writes to a block are serialized via writer_lock. + census_log_start_write() acquires this lock and + census_log_end_write() releases it. On failure to acquire the lock, + writer allocates a new block for the current core and updates + core_local_block accordingly. + + Simultaneous read and write access is allowed. Reader can safely read up to + committed bytes (bytes_committed). + + reader_lock protects the block, currently being read, from getting recycled. + start_read() acquires reader_lock and end_read() releases the lock. + + Read/write access to a block is disabled via try_disable_access(). It returns + with both writer_lock and reader_lock held. These locks are subsequently + released by enable_access() to enable access to the block. + + A note on naming: Most function/struct names are prepended by cl_ + (shorthand for census_log). Further, functions that manipulate structures + include the name of the structure, which will be passed as the first + argument. E.g. cl_block_initialize() will initialize a cl_block. +*/ +#include "src/core/statistics/census_log.h" +#include +#include +#include +#include +#include +#include +#include +#include + +/* End of platform specific code */ + +typedef struct census_log_block_list_struct { + struct census_log_block_list_struct* next; + struct census_log_block_list_struct* prev; + struct census_log_block* block; +} cl_block_list_struct; + +typedef struct census_log_block { + /* Pointer to underlying buffer */ + char* buffer; + gpr_atm writer_lock; + gpr_atm reader_lock; + /* Keeps completely written bytes. Declared atomic because accessed + simultaneously by reader and writer. */ + gpr_atm bytes_committed; + /* Bytes already read */ + gpr_int32 bytes_read; + /* Links for list */ + cl_block_list_struct link; +/* We want this structure to be cacheline aligned. We assume the following + sizes for the various parts on 32/64bit systems: + type 32b size 64b size + char* 4 8 + 3x gpr_atm 12 24 + gpr_int32 4 8 (assumes padding) + cl_block_list_struct 12 24 + TOTAL 32 64 + + Depending on the size of our cacheline and the architecture, we + selectively add char buffering to this structure. The size is checked + via assert in census_log_initialize(). */ +#if defined(GPR_ARCH_64) +#define CL_BLOCK_PAD_SIZE (GPR_CACHELINE_SIZE - 64) +#else +#if defined(GPR_ARCH_32) +#define CL_BLOCK_PAD_SIZE (GPR_CACHELINE_SIZE - 32) +#else +#error "Unknown architecture" +#endif +#endif +#if CL_BLOCK_PAD_SIZE > 0 + char padding[CL_BLOCK_PAD_SIZE]; +#endif +} cl_block; + +/* A list of cl_blocks, doubly-linked through cl_block::link. */ +typedef struct census_log_block_list { + gpr_int32 count; /* Number of items in list. */ + cl_block_list_struct ht; /* head/tail of linked list. */ +} cl_block_list; + +/* Cacheline aligned block pointers to avoid false sharing. Block pointer must + be initialized via set_block(), before calling other functions */ +typedef struct census_log_core_local_block { + gpr_atm block; +/* Ensure cachline alignment: we assume sizeof(gpr_atm) == 4 or 8 */ +#if defined(GPR_ARCH_64) +#define CL_CORE_LOCAL_BLOCK_PAD_SIZE (GPR_CACHELINE_SIZE - 8) +#else +#if defined(GPR_ARCH_32) +#define CL_CORE_LOCAL_BLOCK_PAD_SIZE (GPR_CACHELINE_SIZE - 4) +#else +#error "Unknown architecture" +#endif +#endif +#if CL_CORE_LOCAL_BLOCK_PAD_SIZE > 0 + char padding[CL_CORE_LOCAL_BLOCK_PAD_SIZE]; +#endif +} cl_core_local_block; + +struct census_log { + int discard_old_records; + /* Number of cores (aka hardware-contexts) */ + unsigned num_cores; + /* number of CENSUS_LOG_2_MAX_RECORD_SIZE blocks in log */ + gpr_int32 num_blocks; + cl_block* blocks; /* Block metadata. */ + cl_core_local_block* core_local_blocks; /* Keeps core to block mappings. */ + gpr_mu lock; + int initialized; /* has log been initialized? */ + /* Keeps the state of the reader iterator. A value of 0 indicates that + iterator has reached the end. census_log_init_reader() resets the + value to num_core to restart iteration. */ + gpr_uint32 read_iterator_state; + /* Points to the block being read. If non-NULL, the block is locked for + reading (block_being_read_->reader_lock is held). */ + cl_block* block_being_read; + /* A non-zero value indicates that log is full. */ + gpr_atm is_full; + char* buffer; + cl_block_list free_block_list; + cl_block_list dirty_block_list; + gpr_atm out_of_space_count; +}; + +/* Single internal log */ +static struct census_log g_log; + +/* Functions that operate on an atomic memory location used as a lock */ + +/* Returns non-zero if lock is acquired */ +static int cl_try_lock(gpr_atm* lock) { return gpr_atm_acq_cas(lock, 0, 1); } + +static void cl_unlock(gpr_atm* lock) { gpr_atm_rel_store(lock, 0); } + +/* Functions that operate on cl_core_local_block's */ + +static void cl_core_local_block_set_block(cl_core_local_block* clb, + cl_block* block) { + gpr_atm_rel_store(&clb->block, (gpr_atm)block); +} + +static cl_block* cl_core_local_block_get_block(cl_core_local_block* clb) { + return (cl_block*)gpr_atm_acq_load(&clb->block); +} + +/* Functions that operate on cl_block_list_struct's */ + +static void cl_block_list_struct_initialize(cl_block_list_struct* bls, + cl_block* block) { + bls->next = bls->prev = bls; + bls->block = block; +} + +/* Functions that operate on cl_block_list's */ + +static void cl_block_list_initialize(cl_block_list* list) { + list->count = 0; + cl_block_list_struct_initialize(&list->ht, NULL); +} + +/* Returns head of *this, or NULL if empty. */ +static cl_block* cl_block_list_head(cl_block_list* list) { + return list->ht.next->block; +} + +/* Insert element *e after *pos. */ +static void cl_block_list_insert(cl_block_list* list, cl_block_list_struct* pos, + cl_block_list_struct* e) { + list->count++; + e->next = pos->next; + e->prev = pos; + e->next->prev = e; + e->prev->next = e; +} + +/* Insert block at the head of the list */ +static void cl_block_list_insert_at_head(cl_block_list* list, cl_block* block) { + cl_block_list_insert(list, &list->ht, &block->link); +} + +/* Insert block at the tail of the list */ +static void cl_block_list_insert_at_tail(cl_block_list* list, cl_block* block) { + cl_block_list_insert(list, list->ht.prev, &block->link); +} + +/* Removes block *b. Requires *b be in the list. */ +static void cl_block_list_remove(cl_block_list* list, cl_block* b) { + list->count--; + b->link.next->prev = b->link.prev; + b->link.prev->next = b->link.next; +} + +/* Functions that operate on cl_block's */ + +static void cl_block_initialize(cl_block* block, char* buffer) { + block->buffer = buffer; + gpr_atm_rel_store(&block->writer_lock, 0); + gpr_atm_rel_store(&block->reader_lock, 0); + gpr_atm_rel_store(&block->bytes_committed, 0); + block->bytes_read = 0; + cl_block_list_struct_initialize(&block->link, block); +} + +/* Guards against exposing partially written buffer to the reader. */ +static void cl_block_set_bytes_committed(cl_block* block, + gpr_int32 bytes_committed) { + gpr_atm_rel_store(&block->bytes_committed, bytes_committed); +} + +static gpr_int32 cl_block_get_bytes_committed(cl_block* block) { + return gpr_atm_acq_load(&block->bytes_committed); +} + +/* Tries to disable future read/write access to this block. Succeeds if: + - no in-progress write AND + - no in-progress read AND + - 'discard_data' set to true OR no unread data + On success, clears the block state and returns with writer_lock_ and + reader_lock_ held. These locks are released by a subsequent + cl_block_access_enable() call. */ +static int cl_block_try_disable_access(cl_block* block, int discard_data) { + if (!cl_try_lock(&block->writer_lock)) { + return 0; + } + if (!cl_try_lock(&block->reader_lock)) { + cl_unlock(&block->writer_lock); + return 0; + } + if (!discard_data && + (block->bytes_read != cl_block_get_bytes_committed(block))) { + cl_unlock(&block->reader_lock); + cl_unlock(&block->writer_lock); + return 0; + } + cl_block_set_bytes_committed(block, 0); + block->bytes_read = 0; + return 1; +} + +static void cl_block_enable_access(cl_block* block) { + cl_unlock(&block->reader_lock); + cl_unlock(&block->writer_lock); +} + +/* Returns with writer_lock held. */ +static void* cl_block_start_write(cl_block* block, size_t size) { + gpr_int32 bytes_committed; + if (!cl_try_lock(&block->writer_lock)) { + return NULL; + } + bytes_committed = cl_block_get_bytes_committed(block); + if (bytes_committed + size > CENSUS_LOG_MAX_RECORD_SIZE) { + cl_unlock(&block->writer_lock); + return NULL; + } + return block->buffer + bytes_committed; +} + +/* Releases writer_lock and increments committed bytes by 'bytes_written'. + 'bytes_written' must be <= 'size' specified in the corresponding + StartWrite() call. This function is thread-safe. */ +static void cl_block_end_write(cl_block* block, size_t bytes_written) { + cl_block_set_bytes_committed( + block, cl_block_get_bytes_committed(block) + bytes_written); + cl_unlock(&block->writer_lock); +} + +/* Returns a pointer to the first unread byte in buffer. The number of bytes + available are returned in 'bytes_available'. Acquires reader lock that is + released by a subsequent cl_block_end_read() call. Returns NULL if: + - read in progress + - no data available */ +static void* cl_block_start_read(cl_block* block, size_t* bytes_available) { + void* record; + if (!cl_try_lock(&block->reader_lock)) { + return NULL; + } + /* bytes_committed may change from under us. Use bytes_available to update + bytes_read below. */ + *bytes_available = cl_block_get_bytes_committed(block) - block->bytes_read; + if (*bytes_available == 0) { + cl_unlock(&block->reader_lock); + return NULL; + } + record = block->buffer + block->bytes_read; + block->bytes_read += *bytes_available; + return record; +} + +static void cl_block_end_read(cl_block* block) { + cl_unlock(&block->reader_lock); +} + +/* Internal functions operating on g_log */ + +/* Allocates a new free block (or recycles an available dirty block if log is + configured to discard old records). Returns NULL if out-of-space. */ +static cl_block* cl_allocate_block(void) { + cl_block* block = cl_block_list_head(&g_log.free_block_list); + if (block != NULL) { + cl_block_list_remove(&g_log.free_block_list, block); + return block; + } + if (!g_log.discard_old_records) { + /* No free block and log is configured to keep old records. */ + return NULL; + } + /* Recycle dirty block. Start from the oldest. */ + for (block = cl_block_list_head(&g_log.dirty_block_list); block != NULL; + block = block->link.next->block) { + if (cl_block_try_disable_access(block, 1 /* discard data */)) { + cl_block_list_remove(&g_log.dirty_block_list, block); + return block; + } + } + return NULL; +} + +/* Allocates a new block and updates core id => block mapping. 'old_block' + points to the block that the caller thinks is attached to + 'core_id'. 'old_block' may be NULL. Returns non-zero if: + - allocated a new block OR + - 'core_id' => 'old_block' mapping changed (another thread allocated a + block before lock was acquired). */ +static int cl_allocate_core_local_block(gpr_int32 core_id, + cl_block* old_block) { + /* Now that we have the lock, check if core-local mapping has changed. */ + cl_core_local_block* core_local_block = &g_log.core_local_blocks[core_id]; + cl_block* block = cl_core_local_block_get_block(core_local_block); + if ((block != NULL) && (block != old_block)) { + return 1; + } + if (block != NULL) { + cl_core_local_block_set_block(core_local_block, NULL); + cl_block_list_insert_at_tail(&g_log.dirty_block_list, block); + } + block = cl_allocate_block(); + if (block == NULL) { + gpr_atm_rel_store(&g_log.is_full, 1); + return 0; + } + cl_core_local_block_set_block(core_local_block, block); + cl_block_enable_access(block); + return 1; +} + +static cl_block* cl_get_block(void* record) { + gpr_uintptr p = (gpr_uintptr)((char*)record - g_log.buffer); + gpr_uintptr index = p >> CENSUS_LOG_2_MAX_RECORD_SIZE; + return &g_log.blocks[index]; +} + +/* Gets the next block to read and tries to free 'prev' block (if not NULL). + Returns NULL if reached the end. */ +static cl_block* cl_next_block_to_read(cl_block* prev) { + cl_block* block = NULL; + if (g_log.read_iterator_state == g_log.num_cores) { + /* We are traversing dirty list; find the next dirty block. */ + if (prev != NULL) { + /* Try to free the previous block if there is no unread data. This block + may have unread data if previously incomplete record completed between + read_next() calls. */ + block = prev->link.next->block; + if (cl_block_try_disable_access(prev, 0 /* do not discard data */)) { + cl_block_list_remove(&g_log.dirty_block_list, prev); + cl_block_list_insert_at_head(&g_log.free_block_list, prev); + gpr_atm_rel_store(&g_log.is_full, 0); + } + } else { + block = cl_block_list_head(&g_log.dirty_block_list); + } + if (block != NULL) { + return block; + } + /* We are done with the dirty list; moving on to core-local blocks. */ + } + while (g_log.read_iterator_state > 0) { + g_log.read_iterator_state--; + block = cl_core_local_block_get_block( + &g_log.core_local_blocks[g_log.read_iterator_state]); + if (block != NULL) { + return block; + } + } + return NULL; +} + +/* External functions: primary stats_log interface */ +void census_log_initialize(size_t size_in_mb, int discard_old_records) { + gpr_int32 ix; + /* Check cacheline alignment. */ + GPR_ASSERT(sizeof(cl_block) % GPR_CACHELINE_SIZE == 0); + GPR_ASSERT(sizeof(cl_core_local_block) % GPR_CACHELINE_SIZE == 0); + GPR_ASSERT(!g_log.initialized); + g_log.discard_old_records = discard_old_records; + g_log.num_cores = gpr_cpu_num_cores(); + /* Ensure at least as many blocks as there are cores. */ + g_log.num_blocks = GPR_MAX( + g_log.num_cores, (size_in_mb << 20) >> CENSUS_LOG_2_MAX_RECORD_SIZE); + gpr_mu_init(&g_log.lock); + g_log.read_iterator_state = 0; + g_log.block_being_read = NULL; + gpr_atm_rel_store(&g_log.is_full, 0); + g_log.core_local_blocks = (cl_core_local_block*)gpr_malloc_aligned( + g_log.num_cores * sizeof(cl_core_local_block), GPR_CACHELINE_SIZE_LOG); + memset(g_log.core_local_blocks, 0, + g_log.num_cores * sizeof(cl_core_local_block)); + g_log.blocks = (cl_block*)gpr_malloc_aligned( + g_log.num_blocks * sizeof(cl_block), GPR_CACHELINE_SIZE_LOG); + memset(g_log.blocks, 0, g_log.num_blocks * sizeof(cl_block)); + g_log.buffer = gpr_malloc(g_log.num_blocks * CENSUS_LOG_MAX_RECORD_SIZE); + memset(g_log.buffer, 0, g_log.num_blocks * CENSUS_LOG_MAX_RECORD_SIZE); + cl_block_list_initialize(&g_log.free_block_list); + cl_block_list_initialize(&g_log.dirty_block_list); + for (ix = 0; ix < g_log.num_blocks; ++ix) { + cl_block* block = g_log.blocks + ix; + cl_block_initialize(block, + g_log.buffer + (CENSUS_LOG_MAX_RECORD_SIZE * ix)); + cl_block_try_disable_access(block, 1 /* discard data */); + cl_block_list_insert_at_tail(&g_log.free_block_list, block); + } + gpr_atm_rel_store(&g_log.out_of_space_count, 0); + g_log.initialized = 1; +} + +void census_log_shutdown(void) { + GPR_ASSERT(g_log.initialized); + gpr_mu_destroy(&g_log.lock); + gpr_free_aligned(g_log.core_local_blocks); + g_log.core_local_blocks = NULL; + gpr_free_aligned(g_log.blocks); + g_log.blocks = NULL; + gpr_free(g_log.buffer); + g_log.buffer = NULL; + g_log.initialized = 0; +} + +void* census_log_start_write(size_t size) { + /* Used to bound number of times block allocation is attempted. */ + gpr_int32 attempts_remaining = g_log.num_blocks; + /* TODO(aveitch): move this inside the do loop when current_cpu is fixed */ + gpr_int32 core_id = gpr_cpu_current_cpu(); + GPR_ASSERT(g_log.initialized); + if (size > CENSUS_LOG_MAX_RECORD_SIZE) { + return NULL; + } + do { + int allocated; + void* record = NULL; + cl_block* block = + cl_core_local_block_get_block(&g_log.core_local_blocks[core_id]); + if (block && (record = cl_block_start_write(block, size))) { + return record; + } + /* Need to allocate a new block. We are here if: + - No block associated with the core OR + - Write in-progress on the block OR + - block is out of space */ + if (gpr_atm_acq_load(&g_log.is_full)) { + gpr_atm_no_barrier_fetch_add(&g_log.out_of_space_count, 1); + return NULL; + } + gpr_mu_lock(&g_log.lock); + allocated = cl_allocate_core_local_block(core_id, block); + gpr_mu_unlock(&g_log.lock); + if (!allocated) { + gpr_atm_no_barrier_fetch_add(&g_log.out_of_space_count, 1); + return NULL; + } + } while (attempts_remaining--); + /* Give up. */ + gpr_atm_no_barrier_fetch_add(&g_log.out_of_space_count, 1); + return NULL; +} + +void census_log_end_write(void* record, size_t bytes_written) { + GPR_ASSERT(g_log.initialized); + cl_block_end_write(cl_get_block(record), bytes_written); +} + +void census_log_init_reader(void) { + GPR_ASSERT(g_log.initialized); + gpr_mu_lock(&g_log.lock); + /* If a block is locked for reading unlock it. */ + if (g_log.block_being_read != NULL) { + cl_block_end_read(g_log.block_being_read); + g_log.block_being_read = NULL; + } + g_log.read_iterator_state = g_log.num_cores; + gpr_mu_unlock(&g_log.lock); +} + +const void* census_log_read_next(size_t* bytes_available) { + GPR_ASSERT(g_log.initialized); + gpr_mu_lock(&g_log.lock); + if (g_log.block_being_read != NULL) { + cl_block_end_read(g_log.block_being_read); + } + do { + g_log.block_being_read = cl_next_block_to_read(g_log.block_being_read); + if (g_log.block_being_read != NULL) { + void* record = + cl_block_start_read(g_log.block_being_read, bytes_available); + if (record != NULL) { + gpr_mu_unlock(&g_log.lock); + return record; + } + } + } while (g_log.block_being_read != NULL); + gpr_mu_unlock(&g_log.lock); + return NULL; +} + +size_t census_log_remaining_space(void) { + size_t space; + GPR_ASSERT(g_log.initialized); + gpr_mu_lock(&g_log.lock); + if (g_log.discard_old_records) { + /* Remaining space is not meaningful; just return the entire log space. */ + space = g_log.num_blocks << CENSUS_LOG_2_MAX_RECORD_SIZE; + } else { + space = g_log.free_block_list.count * CENSUS_LOG_MAX_RECORD_SIZE; + } + gpr_mu_unlock(&g_log.lock); + return space; +} + +int census_log_out_of_space_count(void) { + GPR_ASSERT(g_log.initialized); + return gpr_atm_acq_load(&g_log.out_of_space_count); +} diff --git a/src/core/statistics/census_log.h b/src/core/statistics/census_log.h new file mode 100644 index 00000000..60b6d597 --- /dev/null +++ b/src/core/statistics/census_log.h @@ -0,0 +1,91 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_STATISTICS_CENSUS_LOG_H +#define GRPC_INTERNAL_CORE_STATISTICS_CENSUS_LOG_H + +#include + +/* Maximum record size, in bytes. */ +#define CENSUS_LOG_2_MAX_RECORD_SIZE 14 /* 2^14 = 16KB */ +#define CENSUS_LOG_MAX_RECORD_SIZE (1 << CENSUS_LOG_2_MAX_RECORD_SIZE) + +/* Initialize the statistics logging subsystem with the given log size. A log + size of 0 will result in the smallest possible log for the platform + (approximately CENSUS_LOG_MAX_RECORD_SIZE * gpr_cpu_num_cores()). If + discard_old_records is non-zero, then new records will displace older ones + when the log is full. This function must be called before any other + census_log functions. +*/ +void census_log_initialize(size_t size_in_mb, int discard_old_records); + +/* Shutdown the logging subsystem. Caller must ensure that: + - no in progress or future call to any census_log functions + - no incomplete records +*/ +void census_log_shutdown(void); + +/* Allocates and returns a 'size' bytes record and marks it in use. A + subsequent census_log_end_write() marks the record complete. The + 'bytes_written' census_log_end_write() argument must be <= + 'size'. Returns NULL if out-of-space AND: + - log is configured to keep old records OR + - all blocks are pinned by incomplete records. +*/ +void* census_log_start_write(size_t size); + +void census_log_end_write(void* record, size_t bytes_written); + +/* census_log_read_next() iterates over blocks with data and for each block + returns a pointer to the first unread byte. The number of bytes that can be + read are returned in 'bytes_available'. Reader is expected to read all + available data. Reading the data consumes it i.e. it cannot be read again. + census_log_read_next() returns NULL if the end is reached i.e last block + is read. census_log_init_reader() starts the iteration or aborts the + current iteration. +*/ +void census_log_init_reader(void); +const void* census_log_read_next(size_t* bytes_available); + +/* Returns estimated remaining space across all blocks, in bytes. If log is + configured to discard old records, returns total log space. Otherwise, + returns space available in empty blocks (partially filled blocks are + treated as full). +*/ +size_t census_log_remaining_space(void); + +/* Returns the number of times gprc_stats_log_start_write() failed due to + out-of-space. */ +int census_log_out_of_space_count(void); + +#endif /* GRPC_INTERNAL_CORE_STATISTICS_CENSUS_LOG_H */ diff --git a/src/core/statistics/census_rpc_stats.c b/src/core/statistics/census_rpc_stats.c new file mode 100644 index 00000000..b836987c --- /dev/null +++ b/src/core/statistics/census_rpc_stats.c @@ -0,0 +1,252 @@ +/* + * + * Copyright 2015, 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 + +#include "src/core/statistics/census_interface.h" +#include "src/core/statistics/census_rpc_stats.h" +#include "src/core/statistics/hash_table.h" +#include "src/core/statistics/census_tracing.h" +#include "src/core/statistics/window_stats.h" +#include "src/core/support/murmur_hash.h" +#include "src/core/support/string.h" +#include +#include +#include + +#define NUM_INTERVALS 3 +#define MINUTE_INTERVAL 0 +#define HOUR_INTERVAL 1 +#define TOTAL_INTERVAL 2 + +/* for easier typing */ +typedef census_per_method_rpc_stats per_method_stats; + +/* Ensure mu is only initialized once. */ +static gpr_once g_stats_store_mu_init = GPR_ONCE_INIT; +/* Guards two stats stores. */ +static gpr_mu g_mu; +static census_ht* g_client_stats_store = NULL; +static census_ht* g_server_stats_store = NULL; + +static void init_mutex(void) { gpr_mu_init(&g_mu); } + +static void init_mutex_once(void) { + gpr_once_init(&g_stats_store_mu_init, init_mutex); +} + +static int cmp_str_keys(const void* k1, const void* k2) { + return strcmp((const char*)k1, (const char*)k2); +} + +/* TODO(hongyu): replace it with cityhash64 */ +static gpr_uint64 simple_hash(const void* k) { + size_t len = strlen(k); + gpr_uint64 higher = gpr_murmur_hash3((const char*)k, len / 2, 0); + return higher << 32 | + gpr_murmur_hash3((const char*)k + len / 2, len - len / 2, 0); +} + +static void delete_stats(void* stats) { + census_window_stats_destroy((struct census_window_stats*)stats); +} + +static void delete_key(void* key) { gpr_free(key); } + +static const census_ht_option ht_opt = { + CENSUS_HT_POINTER /* key type */, 1999 /* n_of_buckets */, + simple_hash /* hash function */, cmp_str_keys /* key comparator */, + delete_stats /* data deleter */, delete_key /* key deleter */ +}; + +static void init_rpc_stats(void* stats) { + memset(stats, 0, sizeof(census_rpc_stats)); +} + +static void stat_add_proportion(double p, void* base, const void* addme) { + census_rpc_stats* b = (census_rpc_stats*)base; + census_rpc_stats* a = (census_rpc_stats*)addme; + b->cnt += p * a->cnt; + b->rpc_error_cnt += p * a->rpc_error_cnt; + b->app_error_cnt += p * a->app_error_cnt; + b->elapsed_time_ms += p * a->elapsed_time_ms; + b->api_request_bytes += p * a->api_request_bytes; + b->wire_request_bytes += p * a->wire_request_bytes; + b->api_response_bytes += p * a->api_response_bytes; + b->wire_response_bytes += p * a->wire_response_bytes; +} + +static void stat_add(void* base, const void* addme) { + stat_add_proportion(1.0, base, addme); +} + +static gpr_timespec min_hour_total_intervals[3] = { + {60, 0}, {3600, 0}, {36000000, 0}}; + +static const census_window_stats_stat_info window_stats_settings = { + sizeof(census_rpc_stats), init_rpc_stats, stat_add, stat_add_proportion}; + +census_rpc_stats* census_rpc_stats_create_empty(void) { + census_rpc_stats* ret = + (census_rpc_stats*)gpr_malloc(sizeof(census_rpc_stats)); + memset(ret, 0, sizeof(census_rpc_stats)); + return ret; +} + +void census_aggregated_rpc_stats_set_empty(census_aggregated_rpc_stats* data) { + int i = 0; + for (i = 0; i < data->num_entries; i++) { + if (data->stats[i].method != NULL) { + gpr_free((void*)data->stats[i].method); + } + } + if (data->stats != NULL) { + gpr_free(data->stats); + } + data->num_entries = 0; + data->stats = NULL; +} + +static void record_stats(census_ht* store, census_op_id op_id, + const census_rpc_stats* stats) { + gpr_mu_lock(&g_mu); + if (store != NULL) { + census_trace_obj* trace = NULL; + census_internal_lock_trace_store(); + trace = census_get_trace_obj_locked(op_id); + if (trace != NULL) { + const char* method_name = census_get_trace_method_name(trace); + struct census_window_stats* window_stats = NULL; + census_ht_key key; + key.ptr = (void*)method_name; + window_stats = census_ht_find(store, key); + census_internal_unlock_trace_store(); + if (window_stats == NULL) { + window_stats = census_window_stats_create(3, min_hour_total_intervals, + 30, &window_stats_settings); + key.ptr = gpr_strdup(key.ptr); + census_ht_insert(store, key, (void*)window_stats); + } + census_window_stats_add(window_stats, gpr_now(GPR_CLOCK_REALTIME), stats); + } else { + census_internal_unlock_trace_store(); + } + } + gpr_mu_unlock(&g_mu); +} + +void census_record_rpc_client_stats(census_op_id op_id, + const census_rpc_stats* stats) { + record_stats(g_client_stats_store, op_id, stats); +} + +void census_record_rpc_server_stats(census_op_id op_id, + const census_rpc_stats* stats) { + record_stats(g_server_stats_store, op_id, stats); +} + +/* Get stats from input stats store */ +static void get_stats(census_ht* store, census_aggregated_rpc_stats* data) { + GPR_ASSERT(data != NULL); + if (data->num_entries != 0) { + census_aggregated_rpc_stats_set_empty(data); + } + gpr_mu_lock(&g_mu); + if (store != NULL) { + size_t n; + unsigned i, j; + gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); + census_ht_kv* kv = census_ht_get_all_elements(store, &n); + if (kv != NULL) { + data->num_entries = n; + data->stats = (per_method_stats*)gpr_malloc(sizeof(per_method_stats) * n); + for (i = 0; i < n; i++) { + census_window_stats_sums sums[NUM_INTERVALS]; + for (j = 0; j < NUM_INTERVALS; j++) { + sums[j].statistic = (void*)census_rpc_stats_create_empty(); + } + data->stats[i].method = gpr_strdup(kv[i].k.ptr); + census_window_stats_get_sums(kv[i].v, now, sums); + data->stats[i].minute_stats = + *(census_rpc_stats*)sums[MINUTE_INTERVAL].statistic; + data->stats[i].hour_stats = + *(census_rpc_stats*)sums[HOUR_INTERVAL].statistic; + data->stats[i].total_stats = + *(census_rpc_stats*)sums[TOTAL_INTERVAL].statistic; + for (j = 0; j < NUM_INTERVALS; j++) { + gpr_free(sums[j].statistic); + } + } + gpr_free(kv); + } + } + gpr_mu_unlock(&g_mu); +} + +void census_get_client_stats(census_aggregated_rpc_stats* data) { + get_stats(g_client_stats_store, data); +} + +void census_get_server_stats(census_aggregated_rpc_stats* data) { + get_stats(g_server_stats_store, data); +} + +void census_stats_store_init(void) { + init_mutex_once(); + gpr_mu_lock(&g_mu); + if (g_client_stats_store == NULL && g_server_stats_store == NULL) { + g_client_stats_store = census_ht_create(&ht_opt); + g_server_stats_store = census_ht_create(&ht_opt); + } else { + gpr_log(GPR_ERROR, "Census stats store already initialized."); + } + gpr_mu_unlock(&g_mu); +} + +void census_stats_store_shutdown(void) { + init_mutex_once(); + gpr_mu_lock(&g_mu); + if (g_client_stats_store != NULL) { + census_ht_destroy(g_client_stats_store); + g_client_stats_store = NULL; + } else { + gpr_log(GPR_ERROR, "Census server stats store not initialized."); + } + if (g_server_stats_store != NULL) { + census_ht_destroy(g_server_stats_store); + g_server_stats_store = NULL; + } else { + gpr_log(GPR_ERROR, "Census client stats store not initialized."); + } + gpr_mu_unlock(&g_mu); +} diff --git a/src/core/statistics/census_rpc_stats.h b/src/core/statistics/census_rpc_stats.h new file mode 100644 index 00000000..aec31c19 --- /dev/null +++ b/src/core/statistics/census_rpc_stats.h @@ -0,0 +1,101 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_STATISTICS_CENSUS_RPC_STATS_H +#define GRPC_INTERNAL_CORE_STATISTICS_CENSUS_RPC_STATS_H + +#include "src/core/statistics/census_interface.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct census_rpc_stats { + gpr_uint64 cnt; + gpr_uint64 rpc_error_cnt; + gpr_uint64 app_error_cnt; + double elapsed_time_ms; + double api_request_bytes; + double wire_request_bytes; + double api_response_bytes; + double wire_response_bytes; +}; + +/* Creates an empty rpc stats object on heap. */ +census_rpc_stats* census_rpc_stats_create_empty(void); + +typedef struct census_per_method_rpc_stats { + const char* method; + census_rpc_stats minute_stats; /* cumulative stats in the past minute */ + census_rpc_stats hour_stats; /* cumulative stats in the past hour */ + census_rpc_stats total_stats; /* cumulative stats from last gc */ +} census_per_method_rpc_stats; + +typedef struct census_aggregated_rpc_stats { + int num_entries; + census_per_method_rpc_stats* stats; +} census_aggregated_rpc_stats; + +/* Initializes an aggregated rpc stats object to an empty state. */ +void census_aggregated_rpc_stats_set_empty(census_aggregated_rpc_stats* data); + +/* Records client side stats of a rpc. */ +void census_record_rpc_client_stats(census_op_id op_id, + const census_rpc_stats* stats); + +/* Records server side stats of a rpc. */ +void census_record_rpc_server_stats(census_op_id op_id, + const census_rpc_stats* stats); + +/* The following two functions are intended for inprocess query of + per-service per-method stats from grpc implementations. */ + +/* Populates *data_map with server side aggregated per-service per-method + stats. + DO NOT CALL from outside of grpc code. */ +void census_get_server_stats(census_aggregated_rpc_stats* data_map); + +/* Populates *data_map with client side aggregated per-service per-method + stats. + DO NOT CALL from outside of grpc code. */ +void census_get_client_stats(census_aggregated_rpc_stats* data_map); + +void census_stats_store_init(void); +void census_stats_store_shutdown(void); + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_INTERNAL_CORE_STATISTICS_CENSUS_RPC_STATS_H */ diff --git a/src/core/statistics/census_tracing.c b/src/core/statistics/census_tracing.c new file mode 100644 index 00000000..f2a09dc0 --- /dev/null +++ b/src/core/statistics/census_tracing.c @@ -0,0 +1,240 @@ +/* + * + * Copyright 2015, 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 "src/core/statistics/census_interface.h" +#include "src/core/statistics/census_tracing.h" + +#include +#include + +#include "src/core/statistics/hash_table.h" +#include "src/core/support/string.h" +#include +#include +#include +#include + +void census_trace_obj_destroy(census_trace_obj* obj) { + census_trace_annotation* p = obj->annotations; + while (p != NULL) { + census_trace_annotation* next = p->next; + gpr_free(p); + p = next; + } + gpr_free(obj->method); + gpr_free(obj); +} + +static void delete_trace_obj(void* obj) { + census_trace_obj_destroy((census_trace_obj*)obj); +} + +static const census_ht_option ht_opt = { + CENSUS_HT_UINT64 /* key type*/, + 571 /* n_of_buckets */, + NULL /* hash */, + NULL /* compare_keys */, + delete_trace_obj /* delete data */, + NULL /* delete key */ +}; + +static gpr_once g_init_mutex_once = GPR_ONCE_INIT; +static gpr_mu g_mu; /* Guards following two static variables. */ +static census_ht* g_trace_store = NULL; +static gpr_uint64 g_id = 0; + +static census_ht_key op_id_as_key(census_op_id* id) { + return *(census_ht_key*)id; +} + +static gpr_uint64 op_id_2_uint64(census_op_id* id) { + gpr_uint64 ret; + memcpy(&ret, id, sizeof(census_op_id)); + return ret; +} + +static void init_mutex(void) { gpr_mu_init(&g_mu); } + +static void init_mutex_once(void) { + gpr_once_init(&g_init_mutex_once, init_mutex); +} + +census_op_id census_tracing_start_op(void) { + gpr_mu_lock(&g_mu); + { + census_trace_obj* ret = gpr_malloc(sizeof(census_trace_obj)); + memset(ret, 0, sizeof(census_trace_obj)); + g_id++; + memcpy(&ret->id, &g_id, sizeof(census_op_id)); + ret->rpc_stats.cnt = 1; + ret->ts = gpr_now(GPR_CLOCK_REALTIME); + census_ht_insert(g_trace_store, op_id_as_key(&ret->id), (void*)ret); + gpr_log(GPR_DEBUG, "Start tracing for id %lu", g_id); + gpr_mu_unlock(&g_mu); + return ret->id; + } +} + +int census_add_method_tag(census_op_id op_id, const char* method) { + int ret = 0; + census_trace_obj* trace = NULL; + gpr_mu_lock(&g_mu); + trace = census_ht_find(g_trace_store, op_id_as_key(&op_id)); + if (trace == NULL) { + ret = 1; + } else { + trace->method = gpr_strdup(method); + } + gpr_mu_unlock(&g_mu); + return ret; +} + +void census_tracing_print(census_op_id op_id, const char* anno_txt) { + census_trace_obj* trace = NULL; + gpr_mu_lock(&g_mu); + trace = census_ht_find(g_trace_store, op_id_as_key(&op_id)); + if (trace != NULL) { + census_trace_annotation* anno = gpr_malloc(sizeof(census_trace_annotation)); + anno->ts = gpr_now(GPR_CLOCK_REALTIME); + { + char* d = anno->txt; + const char* s = anno_txt; + int n = 0; + for (; n < CENSUS_MAX_ANNOTATION_LENGTH && *s != '\0'; ++n) { + *d++ = *s++; + } + *d = '\0'; + } + anno->next = trace->annotations; + trace->annotations = anno; + } + gpr_mu_unlock(&g_mu); +} + +void census_tracing_end_op(census_op_id op_id) { + census_trace_obj* trace = NULL; + gpr_mu_lock(&g_mu); + trace = census_ht_find(g_trace_store, op_id_as_key(&op_id)); + if (trace != NULL) { + trace->rpc_stats.elapsed_time_ms = gpr_timespec_to_micros( + gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME), trace->ts)); + gpr_log(GPR_DEBUG, "End tracing for id %lu, method %s, latency %f us", + op_id_2_uint64(&op_id), trace->method, + trace->rpc_stats.elapsed_time_ms); + census_ht_erase(g_trace_store, op_id_as_key(&op_id)); + } + gpr_mu_unlock(&g_mu); +} + +void census_tracing_init(void) { + init_mutex_once(); + gpr_mu_lock(&g_mu); + if (g_trace_store == NULL) { + g_id = 1; + g_trace_store = census_ht_create(&ht_opt); + } else { + gpr_log(GPR_ERROR, "Census trace store already initialized."); + } + gpr_mu_unlock(&g_mu); +} + +void census_tracing_shutdown(void) { + gpr_mu_lock(&g_mu); + if (g_trace_store != NULL) { + census_ht_destroy(g_trace_store); + g_trace_store = NULL; + } else { + gpr_log(GPR_ERROR, "Census trace store is not initialized."); + } + gpr_mu_unlock(&g_mu); +} + +void census_internal_lock_trace_store(void) { gpr_mu_lock(&g_mu); } + +void census_internal_unlock_trace_store(void) { gpr_mu_unlock(&g_mu); } + +census_trace_obj* census_get_trace_obj_locked(census_op_id op_id) { + if (g_trace_store == NULL) { + gpr_log(GPR_ERROR, "Census trace store is not initialized."); + return NULL; + } + return (census_trace_obj*)census_ht_find(g_trace_store, op_id_as_key(&op_id)); +} + +const char* census_get_trace_method_name(const census_trace_obj* trace) { + return trace->method; +} + +static census_trace_annotation* dup_annotation_chain( + census_trace_annotation* from) { + census_trace_annotation* ret = NULL; + census_trace_annotation** to = &ret; + for (; from != NULL; from = from->next) { + *to = gpr_malloc(sizeof(census_trace_annotation)); + memcpy(*to, from, sizeof(census_trace_annotation)); + to = &(*to)->next; + } + return ret; +} + +static census_trace_obj* trace_obj_dup(census_trace_obj* from) { + census_trace_obj* to = NULL; + GPR_ASSERT(from != NULL); + to = gpr_malloc(sizeof(census_trace_obj)); + to->id = from->id; + to->ts = from->ts; + to->rpc_stats = from->rpc_stats; + to->method = gpr_strdup(from->method); + to->annotations = dup_annotation_chain(from->annotations); + return to; +} + +census_trace_obj** census_get_active_ops(int* num_active_ops) { + census_trace_obj** ret = NULL; + gpr_mu_lock(&g_mu); + if (g_trace_store != NULL) { + size_t n = 0; + census_ht_kv* all_kvs = census_ht_get_all_elements(g_trace_store, &n); + *num_active_ops = (int)n; + if (n != 0) { + size_t i = 0; + ret = gpr_malloc(sizeof(census_trace_obj*) * n); + for (i = 0; i < n; i++) { + ret[i] = trace_obj_dup((census_trace_obj*)all_kvs[i].v); + } + } + gpr_free(all_kvs); + } + gpr_mu_unlock(&g_mu); + return ret; +} diff --git a/src/core/statistics/census_tracing.h b/src/core/statistics/census_tracing.h new file mode 100644 index 00000000..08305c24 --- /dev/null +++ b/src/core/statistics/census_tracing.h @@ -0,0 +1,96 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_STATISTICS_CENSUS_TRACING_H +#define GRPC_INTERNAL_CORE_STATISTICS_CENSUS_TRACING_H + +#include +#include "src/core/statistics/census_rpc_stats.h" + +/* WARNING: The data structures and APIs provided by this file are for GRPC + library's internal use ONLY. They might be changed in backward-incompatible + ways and are not subject to any deprecation policy. + They are not recommended for external use. + */ +#ifdef __cplusplus +extern "C" { +#endif + +/* Struct for a trace annotation. */ +typedef struct census_trace_annotation { + gpr_timespec ts; /* timestamp of the annotation */ + char txt[CENSUS_MAX_ANNOTATION_LENGTH + 1]; /* actual txt annotation */ + struct census_trace_annotation* next; +} census_trace_annotation; + +typedef struct census_trace_obj { + census_op_id id; + gpr_timespec ts; + census_rpc_stats rpc_stats; + char* method; + census_trace_annotation* annotations; +} census_trace_obj; + +/* Deletes trace object. */ +void census_trace_obj_destroy(census_trace_obj* obj); + +/* Initializes trace store. This function is thread safe. */ +void census_tracing_init(void); + +/* Shutsdown trace store. This function is thread safe. */ +void census_tracing_shutdown(void); + +/* Gets trace obj corresponding to the input op_id. Returns NULL if trace store + is not initialized or trace obj is not found. Requires trace store being + locked before calling this function. */ +census_trace_obj* census_get_trace_obj_locked(census_op_id op_id); + +/* The following two functions acquire and release the trace store global lock. + They are for census internal use only. */ +void census_internal_lock_trace_store(void); +void census_internal_unlock_trace_store(void); + +/* Gets method name associated with the input trace object. */ +const char* census_get_trace_method_name(const census_trace_obj* trace); + +/* Returns an array of pointers to trace objects of currently active operations + and fills in number of active operations. Returns NULL if there are no active + operations. + Caller owns the returned objects. */ +census_trace_obj** census_get_active_ops(int* num_active_ops); + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_INTERNAL_CORE_STATISTICS_CENSUS_TRACING_H */ diff --git a/src/core/statistics/hash_table.c b/src/core/statistics/hash_table.c new file mode 100644 index 00000000..56bdcc2f --- /dev/null +++ b/src/core/statistics/hash_table.c @@ -0,0 +1,303 @@ +/* + * + * Copyright 2015, 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 "src/core/statistics/hash_table.h" + +#include +#include + +#include +#include +#include + +#define CENSUS_HT_NUM_BUCKETS 1999 + +/* A single hash table data entry */ +typedef struct ht_entry { + census_ht_key key; + void* data; + struct ht_entry* next; +} ht_entry; + +/* hash table bucket */ +typedef struct bucket { + /* NULL if bucket is empty */ + ht_entry* next; + /* -1 if all buckets are empty. */ + gpr_int32 prev_non_empty_bucket; + /* -1 if all buckets are empty. */ + gpr_int32 next_non_empty_bucket; +} bucket; + +struct unresizable_hash_table { + /* Number of entries in the table */ + size_t size; + /* Number of buckets */ + gpr_uint32 num_buckets; + /* Array of buckets initialized at creation time. Memory consumption is + 16 bytes per bucket on a 64-bit platform. */ + bucket* buckets; + /* Index of the first non-empty bucket. -1 iff size == 0. */ + gpr_int32 first_non_empty_bucket; + /* Index of the last non_empty bucket. -1 iff size == 0. */ + gpr_int32 last_non_empty_bucket; + /* Immutable options of this hash table, initialized at creation time. */ + census_ht_option options; +}; + +typedef struct entry_locator { + gpr_int32 bucket_idx; + int is_first_in_chain; + int found; + ht_entry* prev_entry; +} entry_locator; + +/* Asserts if option is not valid. */ +void check_options(const census_ht_option* option) { + GPR_ASSERT(option != NULL); + GPR_ASSERT(option->num_buckets > 0); + GPR_ASSERT(option->key_type == CENSUS_HT_UINT64 || + option->key_type == CENSUS_HT_POINTER); + if (option->key_type == CENSUS_HT_UINT64) { + GPR_ASSERT(option->hash == NULL); + } else if (option->key_type == CENSUS_HT_POINTER) { + GPR_ASSERT(option->hash != NULL); + GPR_ASSERT(option->compare_keys != NULL); + } +} + +#define REMOVE_NEXT(options, ptr) \ + do { \ + ht_entry* tmp = (ptr)->next; \ + (ptr)->next = tmp->next; \ + delete_entry(options, tmp); \ + } while (0) + +static void delete_entry(const census_ht_option* opt, ht_entry* p) { + if (opt->delete_data != NULL) { + opt->delete_data(p->data); + } + if (opt->delete_key != NULL) { + opt->delete_key(p->key.ptr); + } + gpr_free(p); +} + +static gpr_uint64 hash(const census_ht_option* opt, census_ht_key key) { + return opt->key_type == CENSUS_HT_UINT64 ? key.val : opt->hash(key.ptr); +} + +census_ht* census_ht_create(const census_ht_option* option) { + int i; + census_ht* ret = NULL; + check_options(option); + ret = (census_ht*)gpr_malloc(sizeof(census_ht)); + ret->size = 0; + ret->num_buckets = option->num_buckets; + ret->buckets = (bucket*)gpr_malloc(sizeof(bucket) * ret->num_buckets); + ret->options = *option; + /* initialize each bucket */ + for (i = 0; i < ret->options.num_buckets; i++) { + ret->buckets[i].prev_non_empty_bucket = -1; + ret->buckets[i].next_non_empty_bucket = -1; + ret->buckets[i].next = NULL; + } + return ret; +} + +static gpr_int32 find_bucket_idx(const census_ht* ht, census_ht_key key) { + return hash(&ht->options, key) % ht->num_buckets; +} + +static int keys_match(const census_ht_option* opt, const ht_entry* p, + const census_ht_key key) { + GPR_ASSERT(opt->key_type == CENSUS_HT_UINT64 || + opt->key_type == CENSUS_HT_POINTER); + if (opt->key_type == CENSUS_HT_UINT64) return p->key.val == key.val; + return !opt->compare_keys((p->key).ptr, key.ptr); +} + +static entry_locator ht_find(const census_ht* ht, census_ht_key key) { + entry_locator loc = {0, 0, 0, NULL}; + gpr_int32 idx = 0; + ht_entry* ptr = NULL; + GPR_ASSERT(ht != NULL); + idx = find_bucket_idx(ht, key); + ptr = ht->buckets[idx].next; + if (ptr == NULL) { + /* bucket is empty */ + return loc; + } + if (keys_match(&ht->options, ptr, key)) { + loc.bucket_idx = idx; + loc.is_first_in_chain = 1; + loc.found = 1; + return loc; + } else { + for (; ptr->next != NULL; ptr = ptr->next) { + if (keys_match(&ht->options, ptr->next, key)) { + loc.bucket_idx = idx; + loc.is_first_in_chain = 0; + loc.found = 1; + loc.prev_entry = ptr; + return loc; + } + } + } + /* Could not find the key */ + return loc; +} + +void* census_ht_find(const census_ht* ht, census_ht_key key) { + entry_locator loc = ht_find(ht, key); + if (loc.found == 0) { + return NULL; + } + return loc.is_first_in_chain ? ht->buckets[loc.bucket_idx].next->data + : loc.prev_entry->next->data; +} + +void census_ht_insert(census_ht* ht, census_ht_key key, void* data) { + gpr_int32 idx = find_bucket_idx(ht, key); + ht_entry* ptr = NULL; + entry_locator loc = ht_find(ht, key); + if (loc.found) { + /* Replace old value with new value. */ + ptr = loc.is_first_in_chain ? ht->buckets[loc.bucket_idx].next + : loc.prev_entry->next; + if (ht->options.delete_data != NULL) { + ht->options.delete_data(ptr->data); + } + ptr->data = data; + return; + } + + /* first entry in the table. */ + if (ht->size == 0) { + ht->buckets[idx].next_non_empty_bucket = -1; + ht->buckets[idx].prev_non_empty_bucket = -1; + ht->first_non_empty_bucket = idx; + ht->last_non_empty_bucket = idx; + } else if (ht->buckets[idx].next == NULL) { + /* first entry in the bucket. */ + ht->buckets[ht->last_non_empty_bucket].next_non_empty_bucket = idx; + ht->buckets[idx].prev_non_empty_bucket = ht->last_non_empty_bucket; + ht->buckets[idx].next_non_empty_bucket = -1; + ht->last_non_empty_bucket = idx; + } + ptr = (ht_entry*)gpr_malloc(sizeof(ht_entry)); + ptr->key = key; + ptr->data = data; + ptr->next = ht->buckets[idx].next; + ht->buckets[idx].next = ptr; + ht->size++; +} + +void census_ht_erase(census_ht* ht, census_ht_key key) { + entry_locator loc = ht_find(ht, key); + if (loc.found == 0) { + /* noop if not found */ + return; + } + ht->size--; + if (loc.is_first_in_chain) { + bucket* b = &ht->buckets[loc.bucket_idx]; + GPR_ASSERT(b->next != NULL); + /* The only entry in the bucket */ + if (b->next->next == NULL) { + int prev = b->prev_non_empty_bucket; + int next = b->next_non_empty_bucket; + if (prev != -1) { + ht->buckets[prev].next_non_empty_bucket = next; + } else { + ht->first_non_empty_bucket = next; + } + if (next != -1) { + ht->buckets[next].prev_non_empty_bucket = prev; + } else { + ht->last_non_empty_bucket = prev; + } + } + REMOVE_NEXT(&ht->options, b); + } else { + GPR_ASSERT(loc.prev_entry->next != NULL); + REMOVE_NEXT(&ht->options, loc.prev_entry); + } +} + +/* Returns NULL if input table is empty. */ +census_ht_kv* census_ht_get_all_elements(const census_ht* ht, size_t* num) { + census_ht_kv* ret = NULL; + int i = 0; + gpr_int32 idx = -1; + GPR_ASSERT(ht != NULL && num != NULL); + *num = ht->size; + if (*num == 0) { + return NULL; + } + + ret = (census_ht_kv*)gpr_malloc(sizeof(census_ht_kv) * ht->size); + idx = ht->first_non_empty_bucket; + while (idx >= 0) { + ht_entry* ptr = ht->buckets[idx].next; + for (; ptr != NULL; ptr = ptr->next) { + ret[i].k = ptr->key; + ret[i].v = ptr->data; + i++; + } + idx = ht->buckets[idx].next_non_empty_bucket; + } + return ret; +} + +static void ht_delete_entry_chain(const census_ht_option* options, + ht_entry* first) { + if (first == NULL) { + return; + } + if (first->next != NULL) { + ht_delete_entry_chain(options, first->next); + } + delete_entry(options, first); +} + +void census_ht_destroy(census_ht* ht) { + unsigned i; + for (i = 0; i < ht->num_buckets; ++i) { + ht_delete_entry_chain(&ht->options, ht->buckets[i].next); + } + gpr_free(ht->buckets); + gpr_free(ht); +} + +size_t census_ht_get_size(const census_ht* ht) { return ht->size; } diff --git a/src/core/statistics/hash_table.h b/src/core/statistics/hash_table.h new file mode 100644 index 00000000..b7f8e11a --- /dev/null +++ b/src/core/statistics/hash_table.h @@ -0,0 +1,131 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_STATISTICS_HASH_TABLE_H +#define GRPC_INTERNAL_CORE_STATISTICS_HASH_TABLE_H + +#include + +#include + +/* A chain based hash table with fixed number of buckets. + Your probably shouldn't use this code directly. It is implemented for the + use case in census trace store and stats store, where number of entries in + the table is in the scale of upto several thousands, entries are added and + removed from the table very frequently (~100k/s), the frequency of find() + operations is roughly several times of the frequency of insert() and erase() + Comparing to find(), the insert(), erase() and get_all_entries() operations + are much less freqent (<1/s). + + Per bucket memory overhead is about (8 + sizeof(intptr_t) bytes. + Per entry memory overhead is about (8 + 2 * sizeof(intptr_t) bytes. + + All functions are not thread-safe. Synchronization will be provided in the + upper layer (in trace store and stats store). +*/ + +/* Opaque hash table struct */ +typedef struct unresizable_hash_table census_ht; + +/* Currently, the hash_table can take two types of keys. (uint64 for trace + store and const char* for stats store). */ +typedef union { + gpr_uint64 val; + void* ptr; +} census_ht_key; + +typedef enum census_ht_key_type { + CENSUS_HT_UINT64 = 0, + CENSUS_HT_POINTER = 1 +} census_ht_key_type; + +typedef struct census_ht_option { + /* Type of hash key */ + census_ht_key_type key_type; + /* Desired number of buckets, preferably a prime number */ + gpr_int32 num_buckets; + /* Fucntion to calculate uint64 hash value of the key. Only takes effect if + key_type is POINTER. */ + gpr_uint64 (*hash)(const void*); + /* Function to compare two keys, returns 0 iff equal. Only takes effect if + key_type is POINTER */ + int (*compare_keys)(const void* k1, const void* k2); + /* Value deleter. NULL if no specialized delete function is needed. */ + void (*delete_data)(void*); + /* Key deleter. NULL if table does not own the key. (e.g. key is part of the + value or key is not owned by the table.) */ + void (*delete_key)(void*); +} census_ht_option; + +/* Creates a hashtable with fixed number of buckets according to the settings + specified in 'options' arg. Function pointers "hash" and "compare_keys" must + be provided if key_type is POINTER. Asserts if fail to create. */ +census_ht* census_ht_create(const census_ht_option* options); + +/* Deletes hash table instance. Frees all dynamic memory owned by ht.*/ +void census_ht_destroy(census_ht* ht); + +/* Inserts the input key-val pair into hash_table. If an entry with the same key + exists in the table, the corresponding value will be overwritten by the input + val. */ +void census_ht_insert(census_ht* ht, census_ht_key key, void* val); + +/* Returns pointer to data, returns NULL if not found. */ +void* census_ht_find(const census_ht* ht, census_ht_key key); + +/* Erase hash table entry with input key. Noop if key is not found. */ +void census_ht_erase(census_ht* ht, census_ht_key key); + +typedef struct census_ht_kv { + census_ht_key k; + void* v; +} census_ht_kv; + +/* Returns an array of pointers to all values in the hash table. Order of the + elements can be arbitrary. Sets 'num' to the size of returned array. Caller + owns returned array. */ +census_ht_kv* census_ht_get_all_elements(const census_ht* ht, size_t* num); + +/* Returns number of elements kept. */ +size_t census_ht_get_size(const census_ht* ht); + +/* Functor applied on each key-value pair while iterating through entries in the + table. The functor should not mutate data. */ +typedef void (*census_ht_itr_cb)(census_ht_key key, const void* val_ptr, + void* state); + +/* Iterates through all key-value pairs in the hash_table. The callback function + should not invalidate data entries. */ +gpr_uint64 census_ht_for_all(const census_ht* ht, census_ht_itr_cb); + +#endif /* GRPC_INTERNAL_CORE_STATISTICS_HASH_TABLE_H */ diff --git a/src/core/statistics/window_stats.c b/src/core/statistics/window_stats.c new file mode 100644 index 00000000..a64e0805 --- /dev/null +++ b/src/core/statistics/window_stats.c @@ -0,0 +1,317 @@ +/* + * + * Copyright 2015, 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 "src/core/statistics/window_stats.h" +#include +#include +#include +#include +#include +#include +#include + +/* typedefs make typing long names easier. Use cws (for census_window_stats) */ +typedef census_window_stats_stat_info cws_stat_info; +typedef struct census_window_stats_sum cws_sum; + +/* Each interval is composed of a number of buckets, which hold a count of + entries and a single statistic */ +typedef struct census_window_stats_bucket { + gpr_int64 count; + void* statistic; +} cws_bucket; + +/* Each interval has a set of buckets, and the variables needed to keep + track of their current state */ +typedef struct census_window_stats_interval_stats { + /* The buckets. There will be 'granularity' + 1 of these. */ + cws_bucket* buckets; + /* Index of the bucket containing the smallest time interval. */ + int bottom_bucket; + /* The smallest time storable in the current window. */ + gpr_int64 bottom; + /* The largest time storable in the current window + 1ns */ + gpr_int64 top; + /* The width of each bucket in ns. */ + gpr_int64 width; +} cws_interval_stats; + +typedef struct census_window_stats { + /* Number of intervals. */ + int nintervals; + /* Number of buckets in each interval. 'granularity' + 1. */ + int nbuckets; + /* Record of stat_info. */ + cws_stat_info stat_info; + /* Stats for each interval. */ + cws_interval_stats* interval_stats; + /* The time the newset stat was recorded. */ + gpr_int64 newest_time; +} window_stats; + +/* Calculate an actual bucket index from a logical index 'IDX'. Other + parameters supply information on the interval struct and overall stats. */ +#define BUCKET_IDX(IS, IDX, WSTATS) \ + ((IS->bottom_bucket + (IDX)) % WSTATS->nbuckets) + +/* The maximum seconds value we can have in a valid timespec. More than this + will result in overflow in timespec_to_ns(). This works out to ~292 years. + TODO: consider using doubles instead of int64. */ +static gpr_int64 max_seconds = + (GPR_INT64_MAX - GPR_NS_PER_SEC) / GPR_NS_PER_SEC; + +static gpr_int64 timespec_to_ns(const gpr_timespec ts) { + if (ts.tv_sec > max_seconds) { + return GPR_INT64_MAX - 1; + } + return (gpr_int64)ts.tv_sec * GPR_NS_PER_SEC + ts.tv_nsec; +} + +static void cws_initialize_statistic(void* statistic, + const cws_stat_info* stat_info) { + if (stat_info->stat_initialize == NULL) { + memset(statistic, 0, stat_info->stat_size); + } else { + stat_info->stat_initialize(statistic); + } +} + +/* Create and initialize a statistic */ +static void* cws_create_statistic(const cws_stat_info* stat_info) { + void* stat = gpr_malloc(stat_info->stat_size); + cws_initialize_statistic(stat, stat_info); + return stat; +} + +window_stats* census_window_stats_create(int nintervals, + const gpr_timespec intervals[], + int granularity, + const cws_stat_info* stat_info) { + window_stats* ret; + int i; + /* validate inputs */ + GPR_ASSERT(nintervals > 0 && granularity > 2 && intervals != NULL && + stat_info != NULL); + for (i = 0; i < nintervals; i++) { + gpr_int64 ns = timespec_to_ns(intervals[i]); + GPR_ASSERT(intervals[i].tv_sec >= 0 && intervals[i].tv_nsec >= 0 && + intervals[i].tv_nsec < GPR_NS_PER_SEC && ns >= 100 && + granularity * 10 <= ns); + } + /* Allocate and initialize relevant data structures */ + ret = (window_stats*)gpr_malloc(sizeof(window_stats)); + ret->nintervals = nintervals; + ret->nbuckets = granularity + 1; + ret->stat_info = *stat_info; + ret->interval_stats = + (cws_interval_stats*)gpr_malloc(nintervals * sizeof(cws_interval_stats)); + for (i = 0; i < nintervals; i++) { + gpr_int64 size_ns = timespec_to_ns(intervals[i]); + cws_interval_stats* is = ret->interval_stats + i; + cws_bucket* buckets = is->buckets = + (cws_bucket*)gpr_malloc(ret->nbuckets * sizeof(cws_bucket)); + int b; + for (b = 0; b < ret->nbuckets; b++) { + buckets[b].statistic = cws_create_statistic(stat_info); + buckets[b].count = 0; + } + is->bottom_bucket = 0; + is->bottom = 0; + is->width = size_ns / granularity; + /* Check for possible overflow issues, and maximize interval size if the + user requested something large enough. */ + if ((GPR_INT64_MAX - is->width) > size_ns) { + is->top = size_ns + is->width; + } else { + is->top = GPR_INT64_MAX; + is->width = GPR_INT64_MAX / (granularity + 1); + } + /* If size doesn't divide evenly, we can have a width slightly too small; + better to have it slightly large. */ + if ((size_ns - (granularity + 1) * is->width) > 0) { + is->width += 1; + } + } + ret->newest_time = 0; + return ret; +} + +/* When we try adding a measurement above the current interval range, we + need to "shift" the buckets sufficiently to cover the new range. */ +static void cws_shift_buckets(const window_stats* wstats, + cws_interval_stats* is, gpr_int64 when_ns) { + int i; + /* number of bucket time widths to "shift" */ + int shift; + /* number of buckets to clear */ + int nclear; + GPR_ASSERT(when_ns >= is->top); + /* number of bucket time widths to "shift" */ + shift = ((when_ns - is->top) / is->width) + 1; + /* number of buckets to clear - limited by actual number of buckets */ + nclear = GPR_MIN(shift, wstats->nbuckets); + for (i = 0; i < nclear; i++) { + int b = BUCKET_IDX(is, i, wstats); + is->buckets[b].count = 0; + cws_initialize_statistic(is->buckets[b].statistic, &wstats->stat_info); + } + /* adjust top/bottom times and current bottom bucket */ + is->bottom_bucket = BUCKET_IDX(is, shift, wstats); + is->top += shift * is->width; + is->bottom += shift * is->width; +} + +void census_window_stats_add(window_stats* wstats, const gpr_timespec when, + const void* stat_value) { + int i; + gpr_int64 when_ns = timespec_to_ns(when); + GPR_ASSERT(wstats->interval_stats != NULL); + for (i = 0; i < wstats->nintervals; i++) { + cws_interval_stats* is = wstats->interval_stats + i; + cws_bucket* bucket; + if (when_ns < is->bottom) { /* Below smallest time in interval: drop */ + continue; + } + if (when_ns >= is->top) { /* above limit: shift buckets */ + cws_shift_buckets(wstats, is, when_ns); + } + /* Add the stat. */ + GPR_ASSERT(is->bottom <= when_ns && when_ns < is->top); + bucket = is->buckets + + BUCKET_IDX(is, (when_ns - is->bottom) / is->width, wstats); + bucket->count++; + wstats->stat_info.stat_add(bucket->statistic, stat_value); + } + if (when_ns > wstats->newest_time) { + wstats->newest_time = when_ns; + } +} + +/* Add a specific bucket contents to an accumulating total. */ +static void cws_add_bucket_to_sum(cws_sum* sum, const cws_bucket* bucket, + const cws_stat_info* stat_info) { + sum->count += bucket->count; + stat_info->stat_add(sum->statistic, bucket->statistic); +} + +/* Add a proportion to an accumulating sum. */ +static void cws_add_proportion_to_sum(double p, cws_sum* sum, + const cws_bucket* bucket, + const cws_stat_info* stat_info) { + sum->count += p * bucket->count; + stat_info->stat_add_proportion(p, sum->statistic, bucket->statistic); +} + +void census_window_stats_get_sums(const window_stats* wstats, + const gpr_timespec when, cws_sum sums[]) { + int i; + gpr_int64 when_ns = timespec_to_ns(when); + GPR_ASSERT(wstats->interval_stats != NULL); + for (i = 0; i < wstats->nintervals; i++) { + int when_bucket; + int new_bucket; + double last_proportion = 1.0; + double bottom_proportion; + cws_interval_stats* is = wstats->interval_stats + i; + cws_sum* sum = sums + i; + sum->count = 0; + cws_initialize_statistic(sum->statistic, &wstats->stat_info); + if (when_ns < is->bottom) { + continue; + } + if (when_ns >= is->top) { + cws_shift_buckets(wstats, is, when_ns); + } + /* Calculating the appropriate amount of which buckets to use can get + complicated. Essentially there are two cases: + 1) if the "top" bucket (new_bucket, where the newest additions to the + stats recorded are entered) corresponds to 'when', then we need + to take a proportion of it - (if when < newest_time) or the full + thing. We also (possibly) need to take a corresponding + proportion of the bottom bucket. + 2) Other cases, we just take a straight proportion. + */ + when_bucket = (when_ns - is->bottom) / is->width; + new_bucket = (wstats->newest_time - is->bottom) / is->width; + if (new_bucket == when_bucket) { + gpr_int64 bottom_bucket_time = is->bottom + when_bucket * is->width; + if (when_ns < wstats->newest_time) { + last_proportion = (double)(when_ns - bottom_bucket_time) / + (double)(wstats->newest_time - bottom_bucket_time); + bottom_proportion = + (double)(is->width - (when_ns - bottom_bucket_time)) / is->width; + } else { + bottom_proportion = + (double)(is->width - (wstats->newest_time - bottom_bucket_time)) / + is->width; + } + } else { + last_proportion = + (double)(when_ns + 1 - is->bottom - when_bucket * is->width) / + is->width; + bottom_proportion = 1.0 - last_proportion; + } + cws_add_proportion_to_sum(last_proportion, sum, + is->buckets + BUCKET_IDX(is, when_bucket, wstats), + &wstats->stat_info); + if (when_bucket != 0) { /* last bucket isn't also bottom bucket */ + int b; + /* Add all of "bottom" bucket if we are looking at a subset of the + full interval, or a proportion if we are adding full interval. */ + cws_add_proportion_to_sum( + (when_bucket == wstats->nbuckets - 1 ? bottom_proportion : 1.0), sum, + is->buckets + is->bottom_bucket, &wstats->stat_info); + /* Add all the remaining buckets (everything but top and bottom). */ + for (b = 1; b < when_bucket; b++) { + cws_add_bucket_to_sum(sum, is->buckets + BUCKET_IDX(is, b, wstats), + &wstats->stat_info); + } + } + } +} + +void census_window_stats_destroy(window_stats* wstats) { + int i; + GPR_ASSERT(wstats->interval_stats != NULL); + for (i = 0; i < wstats->nintervals; i++) { + int b; + for (b = 0; b < wstats->nbuckets; b++) { + gpr_free(wstats->interval_stats[i].buckets[b].statistic); + } + gpr_free(wstats->interval_stats[i].buckets); + } + gpr_free(wstats->interval_stats); + /* Ensure any use-after free triggers assert. */ + wstats->interval_stats = NULL; + gpr_free(wstats); +} diff --git a/src/core/statistics/window_stats.h b/src/core/statistics/window_stats.h new file mode 100644 index 00000000..0020f6b4 --- /dev/null +++ b/src/core/statistics/window_stats.h @@ -0,0 +1,173 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_STATISTICS_WINDOW_STATS_H +#define GRPC_INTERNAL_CORE_STATISTICS_WINDOW_STATS_H + +#include + +/* Keep rolling sums of a user-defined statistic (containing a number of + measurements) over a a number of time intervals ("windows"). For example, + you can use a window_stats object to answer questions such as + "Approximately how many RPCs/s did I receive over the past minute, and + approximately how many bytes did I send out over that period?". + + The type of data to record, and the time intervals to keep are specified + when creating the object via a call to census_window_stats_create(). + + A window's interval is divided into one or more "buckets"; the interval + must be divisible by the number of buckets. Internally, these buckets + control the granularity of window_stats' measurements. Increasing the + number of buckets lets the object respond more quickly to changes in the + overall rate of data added into the object, at the cost of additional + memory usage. + + Here's some code which keeps one minute/hour measurements for two values + (latency in seconds and bytes transferred), with each interval divided into + 4 buckets. + + typedef struct my_stat { + double latency; + int bytes; + } my_stat; + + void add_my_stat(void* base, const void* addme) { + my_stat* b = (my_stat*)base; + const my_stat* a = (const my_stat*)addme; + b->latency += a->latency; + b->bytes += a->bytes; + } + + void add_proportion_my_stat(double p, void* base, const void* addme) { + (my_stat*)result->latency += p * (const my_stat*)base->latency; + (my_stat*)result->bytes += p * (const my_stat*)base->bytes; + } + + #define kNumIntervals 2 + #define kMinInterval 0 + #define kHourInterval 1 + #define kNumBuckets 4 + + const struct census_window_stats_stat_info kMyStatInfo + = { sizeof(my_stat), NULL, add_my_stat, add_proportion_my_stat }; + gpr_timespec intervals[kNumIntervals] = {{60, 0}, {3600, 0}}; + my_stat stat; + my_stat sums[kNumIntervals]; + census_window_stats_sums result[kNumIntervals]; + struct census_window_stats* stats + = census_window_stats_create(kNumIntervals, intervals, kNumBuckets, + &kMyStatInfo); + // Record a new event, taking 15.3ms, transferring 1784 bytes. + stat.latency = 0.153; + stat.bytes = 1784; + census_window_stats_add(stats, gpr_now(GPR_CLOCK_REALTIME), &stat); + // Get sums and print them out + result[kMinInterval].statistic = &sums[kMinInterval]; + result[kHourInterval].statistic = &sums[kHourInterval]; + census_window_stats_get_sums(stats, gpr_now(GPR_CLOCK_REALTIME), result); + printf("%d events/min, average time %gs, average bytes %g\n", + result[kMinInterval].count, + (my_stat*)result[kMinInterval].statistic->latency / + result[kMinInterval].count, + (my_stat*)result[kMinInterval].statistic->bytes / + result[kMinInterval].count + ); + printf("%d events/hr, average time %gs, average bytes %g\n", + result[kHourInterval].count, + (my_stat*)result[kHourInterval].statistic->latency / + result[kHourInterval].count, + (my_stat*)result[kHourInterval].statistic->bytes / + result[kHourInterval].count + ); +*/ + +/* Opaque structure for representing window_stats object */ +struct census_window_stats; + +/* Information provided by API user on the information they want to record */ +typedef struct census_window_stats_stat_info { + /* Number of bytes in user-defined object. */ + size_t stat_size; + /* Function to initialize a user-defined statistics object. If this is set + * to NULL, then the object will be zero-initialized. */ + void (*stat_initialize)(void* stat); + /* Function to add one user-defined statistics object ('addme') to 'base' */ + void (*stat_add)(void* base, const void* addme); + /* As for previous function, but only add a proportion 'p'. This API will + currently only use 'p' values in the range [0,1], but other values are + possible in the future, and should be supported. */ + void (*stat_add_proportion)(double p, void* base, const void* addme); +} census_window_stats_stat_info; + +/* Create a new window_stats object. 'nintervals' is the number of + 'intervals', and must be >=1. 'granularity' is the number of buckets, with + a larger number using more memory, but providing greater accuracy of + results. 'granularity should be > 2. We also require that each interval be + at least 10 * 'granularity' nanoseconds in size. 'stat_info' contains + information about the statistic to be gathered. Intervals greater than ~192 + years will be treated as essentially infinite in size. This function will + GPR_ASSERT() if the object cannot be created or any of the parameters have + invalid values. This function is thread-safe. */ +struct census_window_stats* census_window_stats_create( + int nintervals, const gpr_timespec intervals[], int granularity, + const census_window_stats_stat_info* stat_info); + +/* Add a new measurement (in 'stat_value'), as of a given time ('when'). + This function is thread-compatible. */ +void census_window_stats_add(struct census_window_stats* wstats, + const gpr_timespec when, const void* stat_value); + +/* Structure used to record a single intervals sum for a given statistic */ +typedef struct census_window_stats_sum { + /* Total count of samples. Note that because some internal interpolation + is performed, the count of samples returned for each interval may not be an + integral value. */ + double count; + /* Sum for statistic */ + void* statistic; +} census_window_stats_sums; + +/* Retrieve a set of all values stored in a window_stats object 'wstats'. The + number of 'sums' MUST be the same as the number 'nintervals' used in + census_window_stats_create(). This function is thread-compatible. */ +void census_window_stats_get_sums(const struct census_window_stats* wstats, + const gpr_timespec when, + struct census_window_stats_sum sums[]); + +/* Destroy a window_stats object. Once this function has been called, the + object will no longer be usable from any of the above functions (and + calling them will most likely result in a NULL-pointer dereference or + assertion failure). This function is thread-compatible. */ +void census_window_stats_destroy(struct census_window_stats* wstats); + +#endif /* GRPC_INTERNAL_CORE_STATISTICS_WINDOW_STATS_H */ diff --git a/src/core/support/alloc.c b/src/core/support/alloc.c new file mode 100644 index 00000000..d2ed82e7 --- /dev/null +++ b/src/core/support/alloc.c @@ -0,0 +1,66 @@ +/* + * + * Copyright 2015, 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 + +#include +#include + +void *gpr_malloc(size_t size) { + void *p = malloc(size); + if (!p) { + abort(); + } + return p; +} + +void gpr_free(void *p) { free(p); } + +void *gpr_realloc(void *p, size_t size) { + p = realloc(p, size); + if (!p) { + abort(); + } + return p; +} + +void *gpr_malloc_aligned(size_t size, size_t alignment_log) { + size_t alignment = ((size_t)1) << alignment_log; + size_t extra = alignment - 1 + sizeof(void *); + void *p = gpr_malloc(size + extra); + void **ret = (void **)(((gpr_uintptr)p + extra) & ~(alignment - 1)); + ret[-1] = p; + return (void *)ret; +} + +void gpr_free_aligned(void *ptr) { free(((void **)ptr)[-1]); } diff --git a/src/core/support/cmdline.c b/src/core/support/cmdline.c new file mode 100644 index 00000000..45a3182f --- /dev/null +++ b/src/core/support/cmdline.c @@ -0,0 +1,323 @@ +/* + * + * Copyright 2015, 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 + +#include +#include +#include + +#include "src/core/support/string.h" +#include +#include +#include + +typedef enum { ARGTYPE_INT, ARGTYPE_BOOL, ARGTYPE_STRING } argtype; + +typedef struct arg { + const char *name; + const char *help; + argtype type; + void *value; + struct arg *next; +} arg; + +struct gpr_cmdline { + const char *description; + arg *args; + const char *argv0; + + const char *extra_arg_name; + const char *extra_arg_help; + void (*extra_arg)(void *user_data, const char *arg); + void *extra_arg_user_data; + + void (*state)(gpr_cmdline *cl, char *arg); + arg *cur_arg; +}; + +static void normal_state(gpr_cmdline *cl, char *arg); + +gpr_cmdline *gpr_cmdline_create(const char *description) { + gpr_cmdline *cl = gpr_malloc(sizeof(gpr_cmdline)); + memset(cl, 0, sizeof(gpr_cmdline)); + + cl->description = description; + cl->state = normal_state; + + return cl; +} + +void gpr_cmdline_destroy(gpr_cmdline *cl) { + while (cl->args) { + arg *a = cl->args; + cl->args = a->next; + gpr_free(a); + } + gpr_free(cl); +} + +static void add_arg(gpr_cmdline *cl, const char *name, const char *help, + argtype type, void *value) { + arg *a; + + for (a = cl->args; a; a = a->next) { + GPR_ASSERT(0 != strcmp(a->name, name)); + } + + a = gpr_malloc(sizeof(arg)); + memset(a, 0, sizeof(arg)); + a->name = name; + a->help = help; + a->type = type; + a->value = value; + a->next = cl->args; + cl->args = a; +} + +void gpr_cmdline_add_int(gpr_cmdline *cl, const char *name, const char *help, + int *value) { + add_arg(cl, name, help, ARGTYPE_INT, value); +} + +void gpr_cmdline_add_flag(gpr_cmdline *cl, const char *name, const char *help, + int *value) { + add_arg(cl, name, help, ARGTYPE_BOOL, value); +} + +void gpr_cmdline_add_string(gpr_cmdline *cl, const char *name, const char *help, + char **value) { + add_arg(cl, name, help, ARGTYPE_STRING, value); +} + +void gpr_cmdline_on_extra_arg( + gpr_cmdline *cl, const char *name, const char *help, + void (*on_extra_arg)(void *user_data, const char *arg), void *user_data) { + GPR_ASSERT(!cl->extra_arg); + GPR_ASSERT(on_extra_arg); + + cl->extra_arg = on_extra_arg; + cl->extra_arg_user_data = user_data; + cl->extra_arg_name = name; + cl->extra_arg_help = help; +} + +/* recursively descend argument list, adding the last element + to s first - so that arguments are added in the order they were + added to the list by api calls */ +static void add_args_to_usage(gpr_strvec *s, arg *a) { + char *tmp; + + if (!a) return; + add_args_to_usage(s, a->next); + + switch (a->type) { + case ARGTYPE_BOOL: + gpr_asprintf(&tmp, " [--%s|--no-%s]", a->name, a->name); + gpr_strvec_add(s, tmp); + break; + case ARGTYPE_STRING: + gpr_asprintf(&tmp, " [--%s=string]", a->name); + gpr_strvec_add(s, tmp); + break; + case ARGTYPE_INT: + gpr_asprintf(&tmp, " [--%s=int]", a->name); + gpr_strvec_add(s, tmp); + break; + } +} + +char *gpr_cmdline_usage_string(gpr_cmdline *cl, const char *argv0) { + /* TODO(ctiller): make this prettier */ + gpr_strvec s; + char *tmp; + const char *name = strrchr(argv0, '/'); + + if (name) { + name++; + } else { + name = argv0; + } + + gpr_strvec_init(&s); + + gpr_asprintf(&tmp, "Usage: %s", name); + gpr_strvec_add(&s, tmp); + add_args_to_usage(&s, cl->args); + if (cl->extra_arg) { + gpr_asprintf(&tmp, " [%s...]", cl->extra_arg_name); + gpr_strvec_add(&s, tmp); + } + gpr_strvec_add(&s, gpr_strdup("\n")); + + tmp = gpr_strvec_flatten(&s, NULL); + gpr_strvec_destroy(&s); + return tmp; +} + +static void print_usage_and_die(gpr_cmdline *cl) { + char *usage = gpr_cmdline_usage_string(cl, cl->argv0); + fprintf(stderr, "%s", usage); + gpr_free(usage); + exit(1); +} + +static void extra_state(gpr_cmdline *cl, char *arg) { + if (!cl->extra_arg) print_usage_and_die(cl); + cl->extra_arg(cl->extra_arg_user_data, arg); +} + +static arg *find_arg(gpr_cmdline *cl, char *name) { + arg *a; + + for (a = cl->args; a; a = a->next) { + if (0 == strcmp(a->name, name)) { + break; + } + } + + if (!a) { + fprintf(stderr, "Unknown argument: %s\n", name); + print_usage_and_die(cl); + } + + return a; +} + +static void value_state(gpr_cmdline *cl, char *arg) { + long intval; + char *end; + + GPR_ASSERT(cl->cur_arg); + + switch (cl->cur_arg->type) { + case ARGTYPE_INT: + intval = strtol(arg, &end, 0); + if (*end || intval < INT_MIN || intval > INT_MAX) { + fprintf(stderr, "expected integer, got '%s' for %s\n", arg, + cl->cur_arg->name); + print_usage_and_die(cl); + } + *(int *)cl->cur_arg->value = (int)intval; + break; + case ARGTYPE_BOOL: + if (0 == strcmp(arg, "1") || 0 == strcmp(arg, "true")) { + *(int *)cl->cur_arg->value = 1; + } else if (0 == strcmp(arg, "0") || 0 == strcmp(arg, "false")) { + *(int *)cl->cur_arg->value = 0; + } else { + fprintf(stderr, "expected boolean, got '%s' for %s\n", arg, + cl->cur_arg->name); + print_usage_and_die(cl); + } + break; + case ARGTYPE_STRING: + *(char **)cl->cur_arg->value = arg; + break; + } + + cl->state = normal_state; +} + +static void normal_state(gpr_cmdline *cl, char *arg) { + char *eq = NULL; + char *tmp = NULL; + char *arg_name = NULL; + + if (0 == strcmp(arg, "-help") || 0 == strcmp(arg, "--help") || + 0 == strcmp(arg, "-h")) { + print_usage_and_die(cl); + } + + cl->cur_arg = NULL; + + if (arg[0] == '-') { + if (arg[1] == '-') { + if (arg[2] == 0) { + /* handle '--' to move to just extra args */ + cl->state = extra_state; + return; + } + arg += 2; + } else { + arg += 1; + } + /* first byte of arg is now past the leading '-' or '--' */ + if (arg[0] == 'n' && arg[1] == 'o' && arg[2] == '-') { + /* arg is of the form '--no-foo' - it's a flag disable */ + arg += 3; + cl->cur_arg = find_arg(cl, arg); + if (cl->cur_arg->type != ARGTYPE_BOOL) { + fprintf(stderr, "%s is not a flag argument\n", arg); + print_usage_and_die(cl); + } + *(int *)cl->cur_arg->value = 0; + return; /* early out */ + } + eq = strchr(arg, '='); + if (eq != NULL) { + /* copy the string into a temp buffer and extract the name */ + tmp = arg_name = gpr_malloc((size_t)(eq - arg + 1)); + memcpy(arg_name, arg, (size_t)(eq - arg)); + arg_name[eq - arg] = 0; + } else { + arg_name = arg; + } + cl->cur_arg = find_arg(cl, arg_name); + if (eq != NULL) { + /* arg was of the type --foo=value, parse the value */ + value_state(cl, eq + 1); + } else if (cl->cur_arg->type != ARGTYPE_BOOL) { + /* flag types don't have a '--foo value' variant, other types do */ + cl->state = value_state; + } else { + /* flag parameter: just set the value */ + *(int *)cl->cur_arg->value = 1; + } + } else { + extra_state(cl, arg); + } + + gpr_free(tmp); +} + +void gpr_cmdline_parse(gpr_cmdline *cl, int argc, char **argv) { + int i; + + GPR_ASSERT(argc >= 1); + cl->argv0 = argv[0]; + + for (i = 1; i < argc; i++) { + cl->state(cl, argv[i]); + } +} diff --git a/src/core/support/cpu_iphone.c b/src/core/support/cpu_iphone.c new file mode 100644 index 00000000..82b49b47 --- /dev/null +++ b/src/core/support/cpu_iphone.c @@ -0,0 +1,49 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_CPU_IPHONE + +/* Probably 2 instead of 1, but see comment on gpr_cpu_current_cpu. */ +unsigned gpr_cpu_num_cores(void) { return 1; } + +/* Most code that's using this is using it to shard across work queues. So + unless profiling shows it's a problem or there appears a way to detect the + currently running CPU core, let's have it shard the default way. + Note that the interface in cpu.h lets gpr_cpu_num_cores return 0, but doing + it makes it impossible for gpr_cpu_current_cpu to satisfy its stated range, + and some code might be relying on it. */ +unsigned gpr_cpu_current_cpu(void) { return 0; } + +#endif /* GPR_CPU_IPHONE */ diff --git a/src/core/support/cpu_linux.c b/src/core/support/cpu_linux.c new file mode 100644 index 00000000..7af6a8f0 --- /dev/null +++ b/src/core/support/cpu_linux.c @@ -0,0 +1,78 @@ +/* + * + * Copyright 2015, 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 _GNU_SOURCE +#define _GNU_SOURCE +#endif /* _GNU_SOURCE */ + +#include + +#ifdef GPR_CPU_LINUX + +#include +#include +#include +#include + +#include +#include +#include + +static int ncpus = 0; + +static void init_num_cpus() { + /* This must be signed. sysconf returns -1 when the number cannot be + determined */ + ncpus = (int)sysconf(_SC_NPROCESSORS_ONLN); + if (ncpus < 1) { + gpr_log(GPR_ERROR, "Cannot determine number of CPUs: assuming 1"); + ncpus = 1; + } +} + +unsigned gpr_cpu_num_cores(void) { + static gpr_once once = GPR_ONCE_INIT; + gpr_once_init(&once, init_num_cpus); + return (unsigned)ncpus; +} + +unsigned gpr_cpu_current_cpu(void) { + int cpu = sched_getcpu(); + if (cpu < 0) { + gpr_log(GPR_ERROR, "Error determining current CPU: %s\n", strerror(errno)); + return 0; + } + return (unsigned)cpu; +} + +#endif /* GPR_CPU_LINUX */ diff --git a/src/core/support/cpu_posix.c b/src/core/support/cpu_posix.c new file mode 100644 index 00000000..99484e37 --- /dev/null +++ b/src/core/support/cpu_posix.c @@ -0,0 +1,77 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_CPU_POSIX + +#include +#include +#include + +#include +#include + +static __thread char magic_thread_local; + +static int ncpus = 0; + +static void init_ncpus() { + ncpus = sysconf(_SC_NPROCESSORS_ONLN); + if (ncpus < 1) { + gpr_log(GPR_ERROR, "Cannot determine number of CPUs: assuming 1"); + ncpus = 1; + } +} + +unsigned gpr_cpu_num_cores(void) { + static gpr_once once = GPR_ONCE_INIT; + gpr_once_init(&once, init_ncpus); + return ncpus; +} + +/* This is a cheap, but good enough, pointer hash for sharding things: */ +static size_t shard_ptr(const void *info) { + size_t x = (size_t)info; + return ((x >> 4) ^ (x >> 9) ^ (x >> 14)) % gpr_cpu_num_cores(); +} + +unsigned gpr_cpu_current_cpu(void) { + /* NOTE: there's no way I know to return the actual cpu index portably... + most code that's using this is using it to shard across work queues though, + so here we use thread identity instead to achieve a similar though not + identical effect */ + return shard_ptr(&magic_thread_local); +} + +#endif /* GPR_CPU_POSIX */ diff --git a/src/core/support/cpu_windows.c b/src/core/support/cpu_windows.c new file mode 100644 index 00000000..ce32eb0a --- /dev/null +++ b/src/core/support/cpu_windows.c @@ -0,0 +1,47 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_WIN32 +#include + +unsigned gpr_cpu_num_cores(void) { + SYSTEM_INFO si; + GetSystemInfo(&si); + return si.dwNumberOfProcessors; +} + +unsigned gpr_cpu_current_cpu(void) { return GetCurrentProcessorNumber(); } + +#endif /* GPR_WIN32 */ diff --git a/src/core/support/env.h b/src/core/support/env.h new file mode 100644 index 00000000..24172d86 --- /dev/null +++ b/src/core/support/env.h @@ -0,0 +1,60 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_SUPPORT_ENV_H +#define GRPC_INTERNAL_CORE_SUPPORT_ENV_H + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Env utility functions */ + +/* Gets the environment variable value with the specified name. + Returns a newly allocated string. It is the responsability of the caller to + gpr_free the return value if not NULL (which means that the environment + variable exists). */ +char *gpr_getenv(const char *name); + +/* Sets the the environment with the specified name to the specified value. */ +void gpr_setenv(const char *name, const char *value); + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_INTERNAL_CORE_SUPPORT_ENV_H */ diff --git a/src/core/support/env_linux.c b/src/core/support/env_linux.c new file mode 100644 index 00000000..2e03365e --- /dev/null +++ b/src/core/support/env_linux.c @@ -0,0 +1,62 @@ +/* + * + * Copyright 2015, 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. + * + */ + +/* for secure_getenv. */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include + +#ifdef GPR_LINUX_ENV + +#include "src/core/support/env.h" + +#include + +#include +#include + +#include "src/core/support/string.h" + +char *gpr_getenv(const char *name) { + char *result = secure_getenv(name); + return result == NULL ? result : gpr_strdup(result); +} + +void gpr_setenv(const char *name, const char *value) { + int res = setenv(name, value, 1); + GPR_ASSERT(res == 0); +} + +#endif /* GPR_LINUX_ENV */ diff --git a/src/core/support/env_posix.c b/src/core/support/env_posix.c new file mode 100644 index 00000000..1dd2af56 --- /dev/null +++ b/src/core/support/env_posix.c @@ -0,0 +1,57 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_POSIX_ENV + +#include "src/core/support/env.h" + +#include + +#include + +#include "src/core/support/string.h" +#include + +char *gpr_getenv(const char *name) { + char *result = getenv(name); + return result == NULL ? result : gpr_strdup(result); +} + +void gpr_setenv(const char *name, const char *value) { + int res = setenv(name, value, 1); + GPR_ASSERT(res == 0); +} + +#endif /* GPR_POSIX_ENV */ diff --git a/src/core/support/env_win32.c b/src/core/support/env_win32.c new file mode 100644 index 00000000..6b1ff102 --- /dev/null +++ b/src/core/support/env_win32.c @@ -0,0 +1,65 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_WIN32 + +#include "src/core/support/env.h" +#include "src/core/support/string.h" + +#include + +#include +#include +#include + +char *gpr_getenv(const char *name) { + size_t size; + char *result = NULL; + char *duplicated; + errno_t err; + + err = _dupenv_s(&result, &size, name); + if (err) return NULL; + duplicated = gpr_strdup(result); + free(result); + return duplicated; +} + +void gpr_setenv(const char *name, const char *value) { + errno_t res = _putenv_s(name, value); + GPR_ASSERT(res == 0); +} + +#endif /* GPR_WIN32 */ diff --git a/src/core/support/file.c b/src/core/support/file.c new file mode 100644 index 00000000..c1361d8a --- /dev/null +++ b/src/core/support/file.c @@ -0,0 +1,87 @@ +/* + * + * Copyright 2015, 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 "src/core/support/file.h" + +#include +#include + +#include +#include +#include + +#include "src/core/support/string.h" + +gpr_slice gpr_load_file(const char *filename, int add_null_terminator, + int *success) { + unsigned char *contents = NULL; + size_t contents_size = 0; + char *error_msg = NULL; + gpr_slice result = gpr_empty_slice(); + FILE *file = fopen(filename, "rb"); + size_t bytes_read = 0; + + if (file == NULL) { + gpr_asprintf(&error_msg, "Could not open file %s (error = %s).", filename, + strerror(errno)); + GPR_ASSERT(error_msg != NULL); + goto end; + } + fseek(file, 0, SEEK_END); + /* Converting to size_t on the assumption that it will not fail */ + contents_size = (size_t)ftell(file); + fseek(file, 0, SEEK_SET); + contents = gpr_malloc(contents_size + (add_null_terminator ? 1 : 0)); + bytes_read = fread(contents, 1, contents_size, file); + if (bytes_read < contents_size) { + GPR_ASSERT(ferror(file)); + gpr_asprintf(&error_msg, "Error %s occured while reading file %s.", + strerror(errno), filename); + GPR_ASSERT(error_msg != NULL); + goto end; + } + if (success != NULL) *success = 1; + if (add_null_terminator) { + contents[contents_size++] = 0; + } + result = gpr_slice_new(contents, contents_size, gpr_free); + +end: + if (error_msg != NULL) { + gpr_log(GPR_ERROR, "%s", error_msg); + gpr_free(error_msg); + if (success != NULL) *success = 0; + } + if (file != NULL) fclose(file); + return result; +} diff --git a/src/core/support/file.h b/src/core/support/file.h new file mode 100644 index 00000000..d8b7cea4 --- /dev/null +++ b/src/core/support/file.h @@ -0,0 +1,63 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_SUPPORT_FILE_H +#define GRPC_INTERNAL_CORE_SUPPORT_FILE_H + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* File utility functions */ + +/* Loads the content of a file into a slice. add_null_terminator will add + a NULL terminator if non-zero. The success parameter, if not NULL, + will be set to 1 in case of success and 0 in case of failure. */ +gpr_slice gpr_load_file(const char *filename, int add_null_terminator, + int *success); + +/* Creates a temporary file from a prefix. + If tmp_filename is not NULL, *tmp_filename is assigned the name of the + created file and it is the responsibility of the caller to gpr_free it + unless an error occurs in which case it will be set to NULL. */ +FILE *gpr_tmpfile(const char *prefix, char **tmp_filename); + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_INTERNAL_CORE_SUPPORT_FILE_H */ diff --git a/src/core/support/file_posix.c b/src/core/support/file_posix.c new file mode 100644 index 00000000..c11c0714 --- /dev/null +++ b/src/core/support/file_posix.c @@ -0,0 +1,85 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_POSIX_FILE + +#include "src/core/support/file.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include "src/core/support/string.h" + +FILE *gpr_tmpfile(const char *prefix, char **tmp_filename) { + FILE *result = NULL; + char *template; + int fd; + + if (tmp_filename != NULL) *tmp_filename = NULL; + + gpr_asprintf(&template, "/tmp/%s_XXXXXX", prefix); + GPR_ASSERT(template != NULL); + + fd = mkstemp(template); + if (fd == -1) { + gpr_log(GPR_ERROR, "mkstemp failed for template %s with error %s.", + template, strerror(errno)); + goto end; + } + result = fdopen(fd, "w+"); + if (result == NULL) { + gpr_log(GPR_ERROR, "Could not open file %s from fd %d (error = %s).", + template, fd, strerror(errno)); + unlink(template); + close(fd); + goto end; + } + +end: + if (result != NULL && tmp_filename != NULL) { + *tmp_filename = template; + } else { + gpr_free(template); + } + return result; +} + +#endif /* GPR_POSIX_FILE */ diff --git a/src/core/support/file_win32.c b/src/core/support/file_win32.c new file mode 100644 index 00000000..355744f7 --- /dev/null +++ b/src/core/support/file_win32.c @@ -0,0 +1,84 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_WIN32 + +#include +#include +#include +#include + +#include +#include +#include + +#include "src/core/support/file.h" +#include "src/core/support/string_win32.h" + +FILE *gpr_tmpfile(const char *prefix, char **tmp_filename_out) { + FILE *result = NULL; + LPTSTR template_string = NULL; + TCHAR tmp_path[MAX_PATH]; + TCHAR tmp_filename[MAX_PATH]; + DWORD status; + UINT success; + + if (tmp_filename_out != NULL) *tmp_filename_out = NULL; + + /* Convert our prefix to TCHAR. */ + template_string = gpr_char_to_tchar(prefix); + GPR_ASSERT(template_string); + + /* Get the path to the best temporary folder available. */ + status = GetTempPath(MAX_PATH, tmp_path); + if (status == 0 || status > MAX_PATH) goto end; + + /* Generate a unique filename with our template + temporary path. */ + success = GetTempFileName(tmp_path, template_string, 0, tmp_filename); + if (!success) goto end; + + /* Open a file there. */ + if (_tfopen_s(&result, tmp_filename, TEXT("wb+")) != 0) goto end; + +end: + if (result && tmp_filename_out) { + *tmp_filename_out = gpr_tchar_to_char(tmp_filename); + } + + gpr_free(template_string); + return result; +} + +#endif /* GPR_WIN32 */ diff --git a/src/core/support/histogram.c b/src/core/support/histogram.c new file mode 100644 index 00000000..78dbf986 --- /dev/null +++ b/src/core/support/histogram.c @@ -0,0 +1,244 @@ +/* + * + * Copyright 2015, 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 + +#include +#include +#include + +#include +#include +#include +#include + +/* Histograms are stored with exponentially increasing bucket sizes. + The first bucket is [0, m) where m = 1 + resolution + Bucket n (n>=1) contains [m**n, m**(n+1)) + There are sufficient buckets to reach max_bucket_start */ + +struct gpr_histogram { + /* Sum of all values seen so far */ + double sum; + /* Sum of squares of all values seen so far */ + double sum_of_squares; + /* number of values seen so far */ + double count; + /* m in the description */ + double multiplier; + double one_on_log_multiplier; + /* minimum value seen */ + double min_seen; + /* maximum value seen */ + double max_seen; + /* maximum representable value */ + double max_possible; + /* number of buckets */ + size_t num_buckets; + /* the buckets themselves */ + gpr_uint32 *buckets; +}; + +/* determine a bucket index given a value - does no bounds checking */ +static size_t bucket_for_unchecked(gpr_histogram *h, double x) { + return (size_t)(log(x) * h->one_on_log_multiplier); +} + +/* bounds checked version of the above */ +static size_t bucket_for(gpr_histogram *h, double x) { + size_t bucket = bucket_for_unchecked(h, GPR_CLAMP(x, 1.0, h->max_possible)); + GPR_ASSERT(bucket < h->num_buckets); + return bucket; +} + +/* at what value does a bucket start? */ +static double bucket_start(gpr_histogram *h, double x) { + return pow(h->multiplier, x); +} + +gpr_histogram *gpr_histogram_create(double resolution, + double max_bucket_start) { + gpr_histogram *h = gpr_malloc(sizeof(gpr_histogram)); + GPR_ASSERT(resolution > 0.0); + GPR_ASSERT(max_bucket_start > resolution); + h->sum = 0.0; + h->sum_of_squares = 0.0; + h->multiplier = 1.0 + resolution; + h->one_on_log_multiplier = 1.0 / log(1.0 + resolution); + h->max_possible = max_bucket_start; + h->count = 0.0; + h->min_seen = max_bucket_start; + h->max_seen = 0.0; + h->num_buckets = bucket_for_unchecked(h, max_bucket_start) + 1; + GPR_ASSERT(h->num_buckets > 1); + GPR_ASSERT(h->num_buckets < 100000000); + h->buckets = gpr_malloc(sizeof(gpr_uint32) * h->num_buckets); + memset(h->buckets, 0, sizeof(gpr_uint32) * h->num_buckets); + return h; +} + +void gpr_histogram_destroy(gpr_histogram *h) { + gpr_free(h->buckets); + gpr_free(h); +} + +void gpr_histogram_add(gpr_histogram *h, double x) { + h->sum += x; + h->sum_of_squares += x * x; + h->count++; + if (x < h->min_seen) { + h->min_seen = x; + } + if (x > h->max_seen) { + h->max_seen = x; + } + h->buckets[bucket_for(h, x)]++; +} + +int gpr_histogram_merge(gpr_histogram *dst, gpr_histogram *src) { + if ((dst->num_buckets != src->num_buckets) || + (dst->multiplier != src->multiplier)) { + /* Fail because these histograms don't match */ + return 0; + } + gpr_histogram_merge_contents(dst, src->buckets, src->num_buckets, + src->min_seen, src->max_seen, src->sum, + src->sum_of_squares, src->count); + return 1; +} + +void gpr_histogram_merge_contents(gpr_histogram *dst, const gpr_uint32 *data, + size_t data_count, double min_seen, + double max_seen, double sum, + double sum_of_squares, double count) { + size_t i; + GPR_ASSERT(dst->num_buckets == data_count); + dst->sum += sum; + dst->sum_of_squares += sum_of_squares; + dst->count += count; + if (min_seen < dst->min_seen) { + dst->min_seen = min_seen; + } + if (max_seen > dst->max_seen) { + dst->max_seen = max_seen; + } + for (i = 0; i < dst->num_buckets; i++) { + dst->buckets[i] += data[i]; + } +} + +static double threshold_for_count_below(gpr_histogram *h, double count_below) { + double count_so_far; + double lower_bound; + double upper_bound; + size_t lower_idx; + size_t upper_idx; + + if (h->count == 0) { + return 0.0; + } + + if (count_below <= 0) { + return h->min_seen; + } + if (count_below >= h->count) { + return h->max_seen; + } + + /* find the lowest bucket that gets us above count_below */ + count_so_far = 0.0; + for (lower_idx = 0; lower_idx < h->num_buckets; lower_idx++) { + count_so_far += h->buckets[lower_idx]; + if (count_so_far >= count_below) { + break; + } + } + if (count_so_far == count_below) { + /* this bucket hits the threshold exactly... we should be midway through + any run of zero values following the bucket */ + for (upper_idx = lower_idx + 1; upper_idx < h->num_buckets; upper_idx++) { + if (h->buckets[upper_idx]) { + break; + } + } + return (bucket_start(h, (double)lower_idx) + + bucket_start(h, (double)upper_idx)) / + 2.0; + } else { + /* treat values as uniform throughout the bucket, and find where this value + should lie */ + lower_bound = bucket_start(h, (double)lower_idx); + upper_bound = bucket_start(h, (double)(lower_idx + 1)); + return GPR_CLAMP(upper_bound - + (upper_bound - lower_bound) * + (count_so_far - count_below) / + h->buckets[lower_idx], + h->min_seen, h->max_seen); + } +} + +double gpr_histogram_percentile(gpr_histogram *h, double percentile) { + return threshold_for_count_below(h, h->count * percentile / 100.0); +} + +double gpr_histogram_mean(gpr_histogram *h) { + GPR_ASSERT(h->count); + return h->sum / h->count; +} + +double gpr_histogram_stddev(gpr_histogram *h) { + return sqrt(gpr_histogram_variance(h)); +} + +double gpr_histogram_variance(gpr_histogram *h) { + if (h->count == 0) return 0.0; + return (h->sum_of_squares * h->count - h->sum * h->sum) / + (h->count * h->count); +} + +double gpr_histogram_maximum(gpr_histogram *h) { return h->max_seen; } + +double gpr_histogram_minimum(gpr_histogram *h) { return h->min_seen; } + +double gpr_histogram_count(gpr_histogram *h) { return h->count; } + +double gpr_histogram_sum(gpr_histogram *h) { return h->sum; } + +double gpr_histogram_sum_of_squares(gpr_histogram *h) { + return h->sum_of_squares; +} + +const gpr_uint32 *gpr_histogram_get_contents(gpr_histogram *h, size_t *size) { + *size = h->num_buckets; + return h->buckets; +} diff --git a/src/core/support/host_port.c b/src/core/support/host_port.c new file mode 100644 index 00000000..a28f04df --- /dev/null +++ b/src/core/support/host_port.c @@ -0,0 +1,110 @@ +/* + * + * Copyright 2015, 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 + +#include + +#include "src/core/support/string.h" +#include +#include +#include + +int gpr_join_host_port(char **out, const char *host, int port) { + if (host[0] != '[' && strchr(host, ':') != NULL) { + /* IPv6 literals must be enclosed in brackets. */ + return gpr_asprintf(out, "[%s]:%d", host, port); + } else { + /* Ordinary non-bracketed host:port. */ + return gpr_asprintf(out, "%s:%d", host, port); + } +} + +int gpr_split_host_port(const char *name, char **host, char **port) { + const char *host_start; + size_t host_len; + const char *port_start; + + *host = NULL; + *port = NULL; + + if (name[0] == '[') { + /* Parse a bracketed host, typically an IPv6 literal. */ + const char *rbracket = strchr(name, ']'); + if (rbracket == NULL) { + /* Unmatched [ */ + return 0; + } + if (rbracket[1] == '\0') { + /* ] */ + port_start = NULL; + } else if (rbracket[1] == ':') { + /* ]: */ + port_start = rbracket + 2; + } else { + /* ] */ + return 0; + } + host_start = name + 1; + host_len = (size_t)(rbracket - host_start); + if (memchr(host_start, ':', host_len) == NULL) { + /* Require all bracketed hosts to contain a colon, because a hostname or + IPv4 address should never use brackets. */ + return 0; + } + } else { + const char *colon = strchr(name, ':'); + if (colon != NULL && strchr(colon + 1, ':') == NULL) { + /* Exactly 1 colon. Split into host:port. */ + host_start = name; + host_len = (size_t)(colon - name); + port_start = colon + 1; + } else { + /* 0 or 2+ colons. Bare hostname or IPv6 litearal. */ + host_start = name; + host_len = strlen(name); + port_start = NULL; + } + } + + /* Allocate return values. */ + *host = gpr_malloc(host_len + 1); + memcpy(*host, host_start, host_len); + (*host)[host_len] = '\0'; + + if (port_start != NULL) { + *port = gpr_strdup(port_start); + } + + return 1; +} diff --git a/src/core/support/log.c b/src/core/support/log.c new file mode 100644 index 00000000..f52c2035 --- /dev/null +++ b/src/core/support/log.c @@ -0,0 +1,65 @@ +/* + * + * Copyright 2015, 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 + +#include +#include + +extern void gpr_default_log(gpr_log_func_args *args); +static gpr_log_func g_log_func = gpr_default_log; + +const char *gpr_log_severity_string(gpr_log_severity severity) { + switch (severity) { + case GPR_LOG_SEVERITY_DEBUG: + return "D"; + case GPR_LOG_SEVERITY_INFO: + return "I"; + case GPR_LOG_SEVERITY_ERROR: + return "E"; + } + return "UNKNOWN"; +} + +void gpr_log_message(const char *file, int line, gpr_log_severity severity, + const char *message) { + gpr_log_func_args lfargs; + memset(&lfargs, 0, sizeof(lfargs)); + lfargs.file = file; + lfargs.line = line; + lfargs.severity = severity; + lfargs.message = message; + g_log_func(&lfargs); +} + +void gpr_set_log_function(gpr_log_func f) { g_log_func = f; } diff --git a/src/core/support/log_android.c b/src/core/support/log_android.c new file mode 100644 index 00000000..5d0c7d82 --- /dev/null +++ b/src/core/support/log_android.c @@ -0,0 +1,87 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_ANDROID + +#include +#include +#include +#include +#include +#include + +static android_LogPriority severity_to_log_priority(gpr_log_severity severity) { + switch (severity) { + case GPR_LOG_SEVERITY_DEBUG: + return ANDROID_LOG_DEBUG; + case GPR_LOG_SEVERITY_INFO: + return ANDROID_LOG_INFO; + case GPR_LOG_SEVERITY_ERROR: + return ANDROID_LOG_ERROR; + } + return ANDROID_LOG_DEFAULT; +} + +void gpr_log(const char *file, int line, gpr_log_severity severity, + const char *format, ...) { + char *message = NULL; + va_list args; + va_start(args, format); + vasprintf(&message, format, args); + va_end(args); + gpr_log_message(file, line, severity, message); + free(message); +} + +void gpr_default_log(gpr_log_func_args *args) { + char *final_slash; + const char *display_file; + char *output = NULL; + + final_slash = strrchr(args->file, '/'); + if (final_slash == NULL) + display_file = args->file; + else + display_file = final_slash + 1; + + asprintf(&output, "%s:%d] %s", display_file, args->line, args->message); + + __android_log_write(severity_to_log_priority(args->severity), "GRPC", output); + + /* allocated by asprintf => use free, not gpr_free */ + free(output); +} + +#endif /* GPR_ANDROID */ diff --git a/src/core/support/log_linux.c b/src/core/support/log_linux.c new file mode 100644 index 00000000..02f64d8b --- /dev/null +++ b/src/core/support/log_linux.c @@ -0,0 +1,103 @@ +/* + * + * Copyright 2015, 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 _POSIX_SOURCE +#define _POSIX_SOURCE +#endif + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include + +#ifdef GPR_LINUX + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static long gettid(void) { return syscall(__NR_gettid); } + +void gpr_log(const char *file, int line, gpr_log_severity severity, + const char *format, ...) { + char *message = NULL; + va_list args; + va_start(args, format); + if (vasprintf(&message, format, args) == -1) { + va_end(args); + return; + } + va_end(args); + gpr_log_message(file, line, severity, message); + free(message); +} + +void gpr_default_log(gpr_log_func_args *args) { + char *final_slash; + char *prefix; + const char *display_file; + char time_buffer[64]; + gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); + struct tm tm; + + final_slash = strrchr(args->file, '/'); + if (final_slash == NULL) + display_file = args->file; + else + display_file = final_slash + 1; + + if (!localtime_r(&now.tv_sec, &tm)) { + strcpy(time_buffer, "error:localtime"); + } else if (0 == + strftime(time_buffer, sizeof(time_buffer), "%m%d %H:%M:%S", &tm)) { + strcpy(time_buffer, "error:strftime"); + } + + gpr_asprintf(&prefix, "%s%s.%09d %7tu %s:%d]", + gpr_log_severity_string(args->severity), time_buffer, + (int)(now.tv_nsec), gettid(), display_file, args->line); + + fprintf(stderr, "%-60s %s\n", prefix, args->message); + gpr_free(prefix); +} + +#endif diff --git a/src/core/support/log_posix.c b/src/core/support/log_posix.c new file mode 100644 index 00000000..940ee20f --- /dev/null +++ b/src/core/support/log_posix.c @@ -0,0 +1,100 @@ +/* + * + * Copyright 2015, 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 + +#if defined(GPR_POSIX_LOG) + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static gpr_intptr gettid(void) { return (gpr_intptr)pthread_self(); } + +void gpr_log(const char *file, int line, gpr_log_severity severity, + const char *format, ...) { + char buf[64]; + char *allocated = NULL; + char *message = NULL; + int ret; + va_list args; + va_start(args, format); + ret = vsnprintf(buf, sizeof(buf), format, args); + va_end(args); + if (ret < 0) { + message = NULL; + } else if ((size_t)ret <= sizeof(buf) - 1) { + message = buf; + } else { + message = allocated = gpr_malloc(ret + 1); + va_start(args, format); + vsnprintf(message, ret + 1, format, args); + va_end(args); + } + gpr_log_message(file, line, severity, message); + gpr_free(allocated); +} + +void gpr_default_log(gpr_log_func_args *args) { + char *final_slash; + const char *display_file; + char time_buffer[64]; + gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); + struct tm tm; + + final_slash = strrchr(args->file, '/'); + if (final_slash == NULL) + display_file = args->file; + else + display_file = final_slash + 1; + + if (!localtime_r(&now.tv_sec, &tm)) { + strcpy(time_buffer, "error:localtime"); + } else if (0 == + strftime(time_buffer, sizeof(time_buffer), "%m%d %H:%M:%S", &tm)) { + strcpy(time_buffer, "error:strftime"); + } + + fprintf(stderr, "%s%s.%09d %7tu %s:%d] %s\n", + gpr_log_severity_string(args->severity), time_buffer, + (int)(now.tv_nsec), gettid(), display_file, args->line, + args->message); +} + +#endif /* defined(GPR_POSIX_LOG) */ diff --git a/src/core/support/log_win32.c b/src/core/support/log_win32.c new file mode 100644 index 00000000..b68239f8 --- /dev/null +++ b/src/core/support/log_win32.c @@ -0,0 +1,123 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_WIN32 + +#include +#include + +#include +#include +#include +#include +#include + +#include "src/core/support/string.h" +#include "src/core/support/string_win32.h" + +void gpr_log(const char *file, int line, gpr_log_severity severity, + const char *format, ...) { + char *message = NULL; + va_list args; + int ret; + + /* Determine the length. */ + va_start(args, format); + ret = _vscprintf(format, args); + va_end(args); + if (ret < 0) { + message = NULL; + } else { + /* Allocate a new buffer, with space for the NUL terminator. */ + size_t strp_buflen = (size_t)ret + 1; + message = gpr_malloc(strp_buflen); + + /* Print to the buffer. */ + va_start(args, format); + ret = vsnprintf_s(message, strp_buflen, _TRUNCATE, format, args); + va_end(args); + if ((size_t)ret != strp_buflen - 1) { + /* This should never happen. */ + gpr_free(message); + message = NULL; + } + } + + gpr_log_message(file, line, severity, message); + gpr_free(message); +} + +/* Simple starter implementation */ +void gpr_default_log(gpr_log_func_args *args) { + char *final_slash; + const char *display_file; + char time_buffer[64]; + gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); + struct tm tm; + + final_slash = strrchr(args->file, '\\'); + if (final_slash == NULL) + display_file = args->file; + else + display_file = final_slash + 1; + + if (localtime_s(&tm, &now.tv_sec)) { + strcpy(time_buffer, "error:localtime"); + } else if (0 == + strftime(time_buffer, sizeof(time_buffer), "%m%d %H:%M:%S", &tm)) { + strcpy(time_buffer, "error:strftime"); + } + + fprintf(stderr, "%s%s.%09u %5lu %s:%d] %s\n", + gpr_log_severity_string(args->severity), time_buffer, + (int)(now.tv_nsec), GetCurrentThreadId(), display_file, args->line, + args->message); +} + +char *gpr_format_message(DWORD messageid) { + LPTSTR tmessage; + char *message; + DWORD status = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, messageid, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)(&tmessage), 0, NULL); + if (status == 0) return gpr_strdup("Unable to retrieve error string"); + message = gpr_tchar_to_char(tmessage); + LocalFree(tmessage); + return message; +} + +#endif /* GPR_WIN32 */ diff --git a/src/core/support/murmur_hash.c b/src/core/support/murmur_hash.c new file mode 100644 index 00000000..37fdca82 --- /dev/null +++ b/src/core/support/murmur_hash.c @@ -0,0 +1,96 @@ +/* + * + * Copyright 2015, 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 "src/core/support/murmur_hash.h" + +#define ROTL32(x, r) ((x) << (r)) | ((x) >> (32 - (r))) + +#define FMIX32(h) \ + (h) ^= (h) >> 16; \ + (h) *= 0x85ebca6b; \ + (h) ^= (h) >> 13; \ + (h) *= 0xc2b2ae35; \ + (h) ^= (h) >> 16; + +/* Block read - if your platform needs to do endian-swapping or can only + handle aligned reads, do the conversion here */ +#define GETBLOCK32(p, i) (p)[(i)] + +gpr_uint32 gpr_murmur_hash3(const void *key, size_t len, gpr_uint32 seed) { + const gpr_uint8 *data = (const gpr_uint8 *)key; + const size_t nblocks = len / 4; + int i; + + gpr_uint32 h1 = seed; + gpr_uint32 k1; + + const gpr_uint32 c1 = 0xcc9e2d51; + const gpr_uint32 c2 = 0x1b873593; + + const gpr_uint32 *blocks = ((const gpr_uint32 *)key) + nblocks; + const gpr_uint8 *tail = (const gpr_uint8 *)(data + nblocks * 4); + + /* body */ + for (i = -(int)nblocks; i; i++) { + k1 = GETBLOCK32(blocks, i); + + k1 *= c1; + k1 = ROTL32(k1, 15); + k1 *= c2; + + h1 ^= k1; + h1 = ROTL32(h1, 13); + h1 = h1 * 5 + 0xe6546b64; + } + + k1 = 0; + + /* tail */ + switch (len & 3) { + case 3: + k1 ^= ((gpr_uint32)tail[2]) << 16; + case 2: + k1 ^= ((gpr_uint32)tail[1]) << 8; + case 1: + k1 ^= tail[0]; + k1 *= c1; + k1 = ROTL32(k1, 15); + k1 *= c2; + h1 ^= k1; + }; + + /* finalization */ + h1 ^= (gpr_uint32)len; + FMIX32(h1); + return h1; +} diff --git a/src/core/support/murmur_hash.h b/src/core/support/murmur_hash.h new file mode 100644 index 00000000..343fcb99 --- /dev/null +++ b/src/core/support/murmur_hash.h @@ -0,0 +1,44 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_SUPPORT_MURMUR_HASH_H +#define GRPC_INTERNAL_CORE_SUPPORT_MURMUR_HASH_H + +#include + +#include + +/* compute the hash of key (length len) */ +gpr_uint32 gpr_murmur_hash3(const void *key, size_t len, gpr_uint32 seed); + +#endif /* GRPC_INTERNAL_CORE_SUPPORT_MURMUR_HASH_H */ diff --git a/src/core/support/slice.c b/src/core/support/slice.c new file mode 100644 index 00000000..53024e88 --- /dev/null +++ b/src/core/support/slice.c @@ -0,0 +1,335 @@ +/* + * + * Copyright 2015, 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 +#include +#include + +#include + +gpr_slice gpr_empty_slice(void) { + gpr_slice out; + out.refcount = 0; + out.data.inlined.length = 0; + return out; +} + +gpr_slice gpr_slice_ref(gpr_slice slice) { + if (slice.refcount) { + slice.refcount->ref(slice.refcount); + } + return slice; +} + +void gpr_slice_unref(gpr_slice slice) { + if (slice.refcount) { + slice.refcount->unref(slice.refcount); + } +} + +/* gpr_slice_new support structures - we create a refcount object extended + with the user provided data pointer & destroy function */ +typedef struct new_slice_refcount { + gpr_slice_refcount rc; + gpr_refcount refs; + void (*user_destroy)(void *); + void *user_data; +} new_slice_refcount; + +static void new_slice_ref(void *p) { + new_slice_refcount *r = p; + gpr_ref(&r->refs); +} + +static void new_slice_unref(void *p) { + new_slice_refcount *r = p; + if (gpr_unref(&r->refs)) { + r->user_destroy(r->user_data); + gpr_free(r); + } +} + +gpr_slice gpr_slice_new(void *p, size_t len, void (*destroy)(void *)) { + gpr_slice slice; + new_slice_refcount *rc = gpr_malloc(sizeof(new_slice_refcount)); + gpr_ref_init(&rc->refs, 1); + rc->rc.ref = new_slice_ref; + rc->rc.unref = new_slice_unref; + rc->user_destroy = destroy; + rc->user_data = p; + + slice.refcount = &rc->rc; + slice.data.refcounted.bytes = p; + slice.data.refcounted.length = len; + return slice; +} + +/* gpr_slice_new_with_len support structures - we create a refcount object + extended with the user provided data pointer & destroy function */ +typedef struct new_with_len_slice_refcount { + gpr_slice_refcount rc; + gpr_refcount refs; + void *user_data; + size_t user_length; + void (*user_destroy)(void *, size_t); +} new_with_len_slice_refcount; + +static void new_with_len_ref(void *p) { + new_with_len_slice_refcount *r = p; + gpr_ref(&r->refs); +} + +static void new_with_len_unref(void *p) { + new_with_len_slice_refcount *r = p; + if (gpr_unref(&r->refs)) { + r->user_destroy(r->user_data, r->user_length); + gpr_free(r); + } +} + +gpr_slice gpr_slice_new_with_len(void *p, size_t len, + void (*destroy)(void *, size_t)) { + gpr_slice slice; + new_with_len_slice_refcount *rc = + gpr_malloc(sizeof(new_with_len_slice_refcount)); + gpr_ref_init(&rc->refs, 1); + rc->rc.ref = new_with_len_ref; + rc->rc.unref = new_with_len_unref; + rc->user_destroy = destroy; + rc->user_data = p; + rc->user_length = len; + + slice.refcount = &rc->rc; + slice.data.refcounted.bytes = p; + slice.data.refcounted.length = len; + return slice; +} + +gpr_slice gpr_slice_from_copied_buffer(const char *source, size_t length) { + gpr_slice slice = gpr_slice_malloc(length); + memcpy(GPR_SLICE_START_PTR(slice), source, length); + return slice; +} + +gpr_slice gpr_slice_from_copied_string(const char *source) { + return gpr_slice_from_copied_buffer(source, strlen(source)); +} + +typedef struct { + gpr_slice_refcount base; + gpr_refcount refs; +} malloc_refcount; + +static void malloc_ref(void *p) { + malloc_refcount *r = p; + gpr_ref(&r->refs); +} + +static void malloc_unref(void *p) { + malloc_refcount *r = p; + if (gpr_unref(&r->refs)) { + gpr_free(r); + } +} + +gpr_slice gpr_slice_malloc(size_t length) { + gpr_slice slice; + + if (length > sizeof(slice.data.inlined.bytes)) { + /* Memory layout used by the slice created here: + + +-----------+----------------------------------------------------------+ + | refcount | bytes | + +-----------+----------------------------------------------------------+ + + refcount is a malloc_refcount + bytes is an array of bytes of the requested length + Both parts are placed in the same allocation returned from gpr_malloc */ + malloc_refcount *rc = gpr_malloc(sizeof(malloc_refcount) + length); + + /* Initial refcount on rc is 1 - and it's up to the caller to release + this reference. */ + gpr_ref_init(&rc->refs, 1); + + rc->base.ref = malloc_ref; + rc->base.unref = malloc_unref; + + /* Build up the slice to be returned. */ + /* The slices refcount points back to the allocated block. */ + slice.refcount = &rc->base; + /* The data bytes are placed immediately after the refcount struct */ + slice.data.refcounted.bytes = (gpr_uint8 *)(rc + 1); + /* And the length of the block is set to the requested length */ + slice.data.refcounted.length = length; + } else { + /* small slice: just inline the data */ + slice.refcount = NULL; + slice.data.inlined.length = (gpr_uint8)length; + } + return slice; +} + +gpr_slice gpr_slice_sub_no_ref(gpr_slice source, size_t begin, size_t end) { + gpr_slice subset; + + GPR_ASSERT(end >= begin); + + if (source.refcount) { + /* Enforce preconditions */ + GPR_ASSERT(source.data.refcounted.length >= end); + + /* Build the result */ + subset.refcount = source.refcount; + /* Point into the source array */ + subset.data.refcounted.bytes = source.data.refcounted.bytes + begin; + subset.data.refcounted.length = end - begin; + } else { + /* Enforce preconditions */ + GPR_ASSERT(source.data.inlined.length >= end); + subset.refcount = NULL; + subset.data.inlined.length = (gpr_uint8)(end - begin); + memcpy(subset.data.inlined.bytes, source.data.inlined.bytes + begin, + end - begin); + } + return subset; +} + +gpr_slice gpr_slice_sub(gpr_slice source, size_t begin, size_t end) { + gpr_slice subset; + + if (end - begin <= sizeof(subset.data.inlined.bytes)) { + subset.refcount = NULL; + subset.data.inlined.length = (gpr_uint8)(end - begin); + memcpy(subset.data.inlined.bytes, GPR_SLICE_START_PTR(source) + begin, + end - begin); + } else { + subset = gpr_slice_sub_no_ref(source, begin, end); + /* Bump the refcount */ + subset.refcount->ref(subset.refcount); + } + return subset; +} + +gpr_slice gpr_slice_split_tail(gpr_slice *source, size_t split) { + gpr_slice tail; + + if (source->refcount == NULL) { + /* inlined data, copy it out */ + GPR_ASSERT(source->data.inlined.length >= split); + tail.refcount = NULL; + tail.data.inlined.length = (gpr_uint8)(source->data.inlined.length - split); + memcpy(tail.data.inlined.bytes, source->data.inlined.bytes + split, + tail.data.inlined.length); + source->data.inlined.length = (gpr_uint8)split; + } else { + size_t tail_length = source->data.refcounted.length - split; + GPR_ASSERT(source->data.refcounted.length >= split); + if (tail_length < sizeof(tail.data.inlined.bytes)) { + /* Copy out the bytes - it'll be cheaper than refcounting */ + tail.refcount = NULL; + tail.data.inlined.length = (gpr_uint8)tail_length; + memcpy(tail.data.inlined.bytes, source->data.refcounted.bytes + split, + tail_length); + } else { + /* Build the result */ + tail.refcount = source->refcount; + /* Bump the refcount */ + tail.refcount->ref(tail.refcount); + /* Point into the source array */ + tail.data.refcounted.bytes = source->data.refcounted.bytes + split; + tail.data.refcounted.length = tail_length; + } + source->data.refcounted.length = split; + } + + return tail; +} + +gpr_slice gpr_slice_split_head(gpr_slice *source, size_t split) { + gpr_slice head; + + if (source->refcount == NULL) { + GPR_ASSERT(source->data.inlined.length >= split); + + head.refcount = NULL; + head.data.inlined.length = (gpr_uint8)split; + memcpy(head.data.inlined.bytes, source->data.inlined.bytes, split); + source->data.inlined.length = + (gpr_uint8)(source->data.inlined.length - split); + memmove(source->data.inlined.bytes, source->data.inlined.bytes + split, + source->data.inlined.length); + } else if (split < sizeof(head.data.inlined.bytes)) { + GPR_ASSERT(source->data.refcounted.length >= split); + + head.refcount = NULL; + head.data.inlined.length = (gpr_uint8)split; + memcpy(head.data.inlined.bytes, source->data.refcounted.bytes, split); + source->data.refcounted.bytes += split; + source->data.refcounted.length -= split; + } else { + GPR_ASSERT(source->data.refcounted.length >= split); + + /* Build the result */ + head.refcount = source->refcount; + /* Bump the refcount */ + head.refcount->ref(head.refcount); + /* Point into the source array */ + head.data.refcounted.bytes = source->data.refcounted.bytes; + head.data.refcounted.length = split; + source->data.refcounted.bytes += split; + source->data.refcounted.length -= split; + } + + return head; +} + +int gpr_slice_cmp(gpr_slice a, gpr_slice b) { + int d = (int)(GPR_SLICE_LENGTH(a) - GPR_SLICE_LENGTH(b)); + if (d != 0) return d; + return memcmp(GPR_SLICE_START_PTR(a), GPR_SLICE_START_PTR(b), + GPR_SLICE_LENGTH(a)); +} + +int gpr_slice_str_cmp(gpr_slice a, const char *b) { + size_t b_length = strlen(b); + int d = (int)(GPR_SLICE_LENGTH(a) - b_length); + if (d != 0) return d; + return memcmp(GPR_SLICE_START_PTR(a), b, b_length); +} + +char *gpr_slice_to_cstring(gpr_slice slice) { + char *result = gpr_malloc(GPR_SLICE_LENGTH(slice) + 1); + memcpy(result, GPR_SLICE_START_PTR(slice), GPR_SLICE_LENGTH(slice)); + result[GPR_SLICE_LENGTH(slice)] = '\0'; + return result; +} diff --git a/src/core/support/slice_buffer.c b/src/core/support/slice_buffer.c new file mode 100644 index 00000000..6482ef9c --- /dev/null +++ b/src/core/support/slice_buffer.c @@ -0,0 +1,231 @@ +/* + * + * Copyright 2015, 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 + +#include + +#include +#include +#include + +/* grow a buffer; requires GRPC_SLICE_BUFFER_INLINE_ELEMENTS > 1 */ +#define GROW(x) (3 * (x) / 2) + +static void maybe_embiggen(gpr_slice_buffer *sb) { + if (sb->count == sb->capacity) { + sb->capacity = GROW(sb->capacity); + GPR_ASSERT(sb->capacity > sb->count); + if (sb->slices == sb->inlined) { + sb->slices = gpr_malloc(sb->capacity * sizeof(gpr_slice)); + memcpy(sb->slices, sb->inlined, sb->count * sizeof(gpr_slice)); + } else { + sb->slices = gpr_realloc(sb->slices, sb->capacity * sizeof(gpr_slice)); + } + } +} + +void gpr_slice_buffer_init(gpr_slice_buffer *sb) { + sb->count = 0; + sb->length = 0; + sb->capacity = GRPC_SLICE_BUFFER_INLINE_ELEMENTS; + sb->slices = sb->inlined; +} + +void gpr_slice_buffer_destroy(gpr_slice_buffer *sb) { + gpr_slice_buffer_reset_and_unref(sb); + if (sb->slices != sb->inlined) { + gpr_free(sb->slices); + } +} + +gpr_uint8 *gpr_slice_buffer_tiny_add(gpr_slice_buffer *sb, unsigned n) { + gpr_slice *back; + gpr_uint8 *out; + + sb->length += n; + + if (sb->count == 0) goto add_new; + back = &sb->slices[sb->count - 1]; + if (back->refcount) goto add_new; + if ((back->data.inlined.length + n) > sizeof(back->data.inlined.bytes)) + goto add_new; + out = back->data.inlined.bytes + back->data.inlined.length; + back->data.inlined.length = (gpr_uint8)(back->data.inlined.length + n); + return out; + +add_new: + maybe_embiggen(sb); + back = &sb->slices[sb->count]; + sb->count++; + back->refcount = NULL; + back->data.inlined.length = (gpr_uint8)n; + return back->data.inlined.bytes; +} + +size_t gpr_slice_buffer_add_indexed(gpr_slice_buffer *sb, gpr_slice s) { + size_t out = sb->count; + maybe_embiggen(sb); + sb->slices[out] = s; + sb->length += GPR_SLICE_LENGTH(s); + sb->count = out + 1; + return out; +} + +void gpr_slice_buffer_add(gpr_slice_buffer *sb, gpr_slice s) { + size_t n = sb->count; + /* if both the last slice in the slice buffer and the slice being added + are inlined (that is, that they carry their data inside the slice data + structure), and the back slice is not full, then concatenate directly + into the back slice, preventing many small slices being passed into + writes */ + if (!s.refcount && n) { + gpr_slice *back = &sb->slices[n - 1]; + if (!back->refcount && back->data.inlined.length < GPR_SLICE_INLINED_SIZE) { + if (s.data.inlined.length + back->data.inlined.length <= + GPR_SLICE_INLINED_SIZE) { + memcpy(back->data.inlined.bytes + back->data.inlined.length, + s.data.inlined.bytes, s.data.inlined.length); + back->data.inlined.length = + (gpr_uint8)(back->data.inlined.length + s.data.inlined.length); + } else { + size_t cp1 = GPR_SLICE_INLINED_SIZE - back->data.inlined.length; + memcpy(back->data.inlined.bytes + back->data.inlined.length, + s.data.inlined.bytes, cp1); + back->data.inlined.length = GPR_SLICE_INLINED_SIZE; + maybe_embiggen(sb); + back = &sb->slices[n]; + sb->count = n + 1; + back->refcount = NULL; + back->data.inlined.length = (gpr_uint8)(s.data.inlined.length - cp1); + memcpy(back->data.inlined.bytes, s.data.inlined.bytes + cp1, + s.data.inlined.length - cp1); + } + sb->length += s.data.inlined.length; + return; /* early out */ + } + } + gpr_slice_buffer_add_indexed(sb, s); +} + +void gpr_slice_buffer_addn(gpr_slice_buffer *sb, gpr_slice *s, size_t n) { + size_t i; + for (i = 0; i < n; i++) { + gpr_slice_buffer_add(sb, s[i]); + } +} + +void gpr_slice_buffer_pop(gpr_slice_buffer *sb) { + if (sb->count != 0) { + size_t count = --sb->count; + sb->length -= GPR_SLICE_LENGTH(sb->slices[count]); + } +} + +void gpr_slice_buffer_reset_and_unref(gpr_slice_buffer *sb) { + size_t i; + + for (i = 0; i < sb->count; i++) { + gpr_slice_unref(sb->slices[i]); + } + + sb->count = 0; + sb->length = 0; +} + +void gpr_slice_buffer_swap(gpr_slice_buffer *a, gpr_slice_buffer *b) { + GPR_SWAP(size_t, a->count, b->count); + GPR_SWAP(size_t, a->capacity, b->capacity); + GPR_SWAP(size_t, a->length, b->length); + + if (a->slices == a->inlined) { + if (b->slices == b->inlined) { + /* swap contents of inlined buffer */ + gpr_slice temp[GRPC_SLICE_BUFFER_INLINE_ELEMENTS]; + memcpy(temp, a->slices, b->count * sizeof(gpr_slice)); + memcpy(a->slices, b->slices, a->count * sizeof(gpr_slice)); + memcpy(b->slices, temp, b->count * sizeof(gpr_slice)); + } else { + /* a is inlined, b is not - copy a inlined into b, fix pointers */ + a->slices = b->slices; + b->slices = b->inlined; + memcpy(b->slices, a->inlined, b->count * sizeof(gpr_slice)); + } + } else if (b->slices == b->inlined) { + /* b is inlined, a is not - copy b inlined int a, fix pointers */ + b->slices = a->slices; + a->slices = a->inlined; + memcpy(a->slices, b->inlined, a->count * sizeof(gpr_slice)); + } else { + /* no inlining: easy swap */ + GPR_SWAP(gpr_slice *, a->slices, b->slices); + } +} + +void gpr_slice_buffer_move_into(gpr_slice_buffer *src, gpr_slice_buffer *dst) { + /* anything to move? */ + if (src->count == 0) { + return; + } + /* anything in dst? */ + if (dst->count == 0) { + gpr_slice_buffer_swap(src, dst); + return; + } + /* both buffers have data - copy, and reset src */ + gpr_slice_buffer_addn(dst, src->slices, src->count); + src->count = 0; + src->length = 0; +} + +void gpr_slice_buffer_trim_end(gpr_slice_buffer *sb, size_t n) { + GPR_ASSERT(n <= sb->length); + sb->length -= n; + for (;;) { + size_t idx = sb->count - 1; + gpr_slice slice = sb->slices[idx]; + size_t slice_len = GPR_SLICE_LENGTH(slice); + if (slice_len > n) { + sb->slices[idx] = gpr_slice_sub_no_ref(slice, 0, slice_len - n); + return; + } else if (slice_len == n) { + gpr_slice_unref(slice); + sb->count = idx; + return; + } else { + gpr_slice_unref(slice); + n -= slice_len; + sb->count = idx; + } + } +} diff --git a/src/core/support/stack_lockfree.c b/src/core/support/stack_lockfree.c new file mode 100644 index 00000000..27ecf622 --- /dev/null +++ b/src/core/support/stack_lockfree.c @@ -0,0 +1,175 @@ +/* + * + * Copyright 2015, 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 "src/core/support/stack_lockfree.h" + +#include +#include + +#include +#include +#include +#include + +/* The lockfree node structure is a single architecture-level + word that allows for an atomic CAS to set it up. */ +struct lockfree_node_contents { + /* next thing to look at. Actual index for head, next index otherwise */ + gpr_uint16 index; +#ifdef GPR_ARCH_64 + gpr_uint16 pad; + gpr_uint32 aba_ctr; +#else +#ifdef GPR_ARCH_32 + gpr_uint16 aba_ctr; +#else +#error Unsupported bit width architecture +#endif +#endif +}; + +/* Use a union to make sure that these are in the same bits as an atm word */ +typedef union lockfree_node { + gpr_atm atm; + struct lockfree_node_contents contents; +} lockfree_node; + +#define ENTRY_ALIGNMENT_BITS 3 /* make sure that entries aligned to 8-bytes */ +#define INVALID_ENTRY_INDEX \ + ((1 << 16) - 1) /* reserve this entry as invalid \ + */ + +struct gpr_stack_lockfree { + lockfree_node *entries; + lockfree_node head; /* An atomic entry describing curr head */ + +#ifndef NDEBUG + /* Bitmap of pushed entries to check for double-push or pop */ + gpr_atm pushed[(INVALID_ENTRY_INDEX + 1) / (8 * sizeof(gpr_atm))]; +#endif +}; + +gpr_stack_lockfree *gpr_stack_lockfree_create(int entries) { + gpr_stack_lockfree *stack; + stack = gpr_malloc(sizeof(*stack)); + /* Since we only allocate 16 bits to represent an entry number, + * make sure that we are within the desired range */ + /* Reserve the highest entry number as a dummy */ + GPR_ASSERT(entries < INVALID_ENTRY_INDEX); + stack->entries = gpr_malloc_aligned(entries * sizeof(stack->entries[0]), + ENTRY_ALIGNMENT_BITS); + /* Clear out all entries */ + memset(stack->entries, 0, entries * sizeof(stack->entries[0])); + memset(&stack->head, 0, sizeof(stack->head)); +#ifndef NDEBUG + memset(&stack->pushed, 0, sizeof(stack->pushed)); +#endif + + GPR_ASSERT(sizeof(stack->entries->atm) == sizeof(stack->entries->contents)); + + /* Point the head at reserved dummy entry */ + stack->head.contents.index = INVALID_ENTRY_INDEX; + return stack; +} + +void gpr_stack_lockfree_destroy(gpr_stack_lockfree *stack) { + gpr_free_aligned(stack->entries); + gpr_free(stack); +} + +int gpr_stack_lockfree_push(gpr_stack_lockfree *stack, int entry) { + lockfree_node head; + lockfree_node newhead; + lockfree_node curent; + lockfree_node newent; + + /* First fill in the entry's index and aba ctr for new head */ + newhead.contents.index = (gpr_uint16)entry; + /* Also post-increment the aba_ctr */ + curent.atm = gpr_atm_no_barrier_load(&stack->entries[entry].atm); + newhead.contents.aba_ctr = ++curent.contents.aba_ctr; + gpr_atm_no_barrier_store(&stack->entries[entry].atm, curent.atm); + +#ifndef NDEBUG + /* Check for double push */ + { + int pushed_index = entry / (8 * sizeof(gpr_atm)); + int pushed_bit = entry % (8 * sizeof(gpr_atm)); + gpr_atm old_val; + + old_val = gpr_atm_no_barrier_fetch_add(&stack->pushed[pushed_index], + (gpr_atm)(1UL << pushed_bit)); + GPR_ASSERT((old_val & (1UL << pushed_bit)) == 0); + } +#endif + + do { + /* Atomically get the existing head value for use */ + head.atm = gpr_atm_no_barrier_load(&(stack->head.atm)); + /* Point to it */ + newent.atm = gpr_atm_no_barrier_load(&stack->entries[entry].atm); + newent.contents.index = head.contents.index; + gpr_atm_no_barrier_store(&stack->entries[entry].atm, newent.atm); + } while (!gpr_atm_rel_cas(&(stack->head.atm), head.atm, newhead.atm)); + /* Use rel_cas above to make sure that entry index is set properly */ + return head.contents.index == INVALID_ENTRY_INDEX; +} + +int gpr_stack_lockfree_pop(gpr_stack_lockfree *stack) { + lockfree_node head; + lockfree_node newhead; + + do { + head.atm = gpr_atm_acq_load(&(stack->head.atm)); + if (head.contents.index == INVALID_ENTRY_INDEX) { + return -1; + } + newhead.atm = + gpr_atm_no_barrier_load(&(stack->entries[head.contents.index].atm)); + + } while (!gpr_atm_no_barrier_cas(&(stack->head.atm), head.atm, newhead.atm)); +#ifndef NDEBUG + /* Check for valid pop */ + { + int pushed_index = head.contents.index / (8 * sizeof(gpr_atm)); + int pushed_bit = head.contents.index % (8 * sizeof(gpr_atm)); + gpr_atm old_val; + + old_val = gpr_atm_no_barrier_fetch_add(&stack->pushed[pushed_index], + -(gpr_atm)(1UL << pushed_bit)); + GPR_ASSERT((old_val & (1UL << pushed_bit)) != 0); + } +#endif + + return head.contents.index; +} diff --git a/src/core/support/stack_lockfree.h b/src/core/support/stack_lockfree.h new file mode 100644 index 00000000..eec960fb --- /dev/null +++ b/src/core/support/stack_lockfree.h @@ -0,0 +1,51 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_SUPPORT_STACK_LOCKFREE_H +#define GRPC_INTERNAL_CORE_SUPPORT_STACK_LOCKFREE_H + +typedef struct gpr_stack_lockfree gpr_stack_lockfree; + +/* This stack must specify the maximum number of entries to track. + The current implementation only allows up to 65534 entries */ +gpr_stack_lockfree* gpr_stack_lockfree_create(int entries); +void gpr_stack_lockfree_destroy(gpr_stack_lockfree* stack); + +/* Pass in a valid entry number for the next stack entry */ +/* Returns 1 if this is the first element on the stack, 0 otherwise */ +int gpr_stack_lockfree_push(gpr_stack_lockfree*, int entry); + +/* Returns -1 on empty or the actual entry number */ +int gpr_stack_lockfree_pop(gpr_stack_lockfree* stack); + +#endif /* GRPC_INTERNAL_CORE_SUPPORT_STACK_LOCKFREE_H */ diff --git a/src/core/support/string.c b/src/core/support/string.c new file mode 100644 index 00000000..af0389ea --- /dev/null +++ b/src/core/support/string.c @@ -0,0 +1,275 @@ +/* + * + * Copyright 2015, 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 "src/core/support/string.h" + +#include +#include +#include + +#include +#include +#include +#include + +char *gpr_strdup(const char *src) { + char *dst; + size_t len; + + if (!src) { + return NULL; + } + + len = strlen(src) + 1; + dst = gpr_malloc(len); + + memcpy(dst, src, len); + + return dst; +} + +typedef struct { + size_t capacity; + size_t length; + char *data; +} dump_out; + +static dump_out dump_out_create(void) { + dump_out r = {0, 0, NULL}; + return r; +} + +static void dump_out_append(dump_out *out, char c) { + if (out->length == out->capacity) { + out->capacity = GPR_MAX(8, 2 * out->capacity); + out->data = gpr_realloc(out->data, out->capacity); + } + out->data[out->length++] = c; +} + +static void hexdump(dump_out *out, const char *buf, size_t len) { + static const char hex[16] = "0123456789abcdef"; + + const gpr_uint8 *const beg = (const gpr_uint8 *)buf; + const gpr_uint8 *const end = beg + len; + const gpr_uint8 *cur; + + for (cur = beg; cur != end; ++cur) { + if (cur != beg) dump_out_append(out, ' '); + dump_out_append(out, hex[*cur >> 4]); + dump_out_append(out, hex[*cur & 0xf]); + } +} + +static void asciidump(dump_out *out, const char *buf, size_t len) { + const gpr_uint8 *const beg = (const gpr_uint8 *)buf; + const gpr_uint8 *const end = beg + len; + const gpr_uint8 *cur; + int out_was_empty = (out->length == 0); + if (!out_was_empty) { + dump_out_append(out, ' '); + dump_out_append(out, '\''); + } + for (cur = beg; cur != end; ++cur) { + dump_out_append(out, isprint(*cur) ? *(char *)cur : '.'); + } + if (!out_was_empty) { + dump_out_append(out, '\''); + } +} + +char *gpr_dump(const char *buf, size_t len, gpr_uint32 flags) { + dump_out out = dump_out_create(); + if (flags & GPR_DUMP_HEX) { + hexdump(&out, buf, len); + } + if (flags & GPR_DUMP_ASCII) { + asciidump(&out, buf, len); + } + dump_out_append(&out, 0); + return out.data; +} + +char *gpr_dump_slice(gpr_slice s, gpr_uint32 flags) { + return gpr_dump((const char *)GPR_SLICE_START_PTR(s), GPR_SLICE_LENGTH(s), + flags); +} + +int gpr_parse_bytes_to_uint32(const char *buf, size_t len, gpr_uint32 *result) { + gpr_uint32 out = 0; + gpr_uint32 new; + size_t i; + + if (len == 0) return 0; /* must have some bytes */ + + for (i = 0; i < len; i++) { + if (buf[i] < '0' || buf[i] > '9') return 0; /* bad char */ + new = 10 * out + (gpr_uint32)(buf[i] - '0'); + if (new < out) return 0; /* overflow */ + out = new; + } + + *result = out; + return 1; +} + +void gpr_reverse_bytes(char *str, int len) { + char *p1, *p2; + for (p1 = str, p2 = str + len - 1; p2 > p1; ++p1, --p2) { + char temp = *p1; + *p1 = *p2; + *p2 = temp; + } +} + +int gpr_ltoa(long value, char *string) { + int i = 0; + int neg = value < 0; + + if (value == 0) { + string[0] = '0'; + string[1] = 0; + return 1; + } + + if (neg) value = -value; + while (value) { + string[i++] = (char)('0' + value % 10); + value /= 10; + } + if (neg) string[i++] = '-'; + gpr_reverse_bytes(string, i); + string[i] = 0; + return i; +} + +char *gpr_strjoin(const char **strs, size_t nstrs, size_t *final_length) { + return gpr_strjoin_sep(strs, nstrs, "", final_length); +} + +char *gpr_strjoin_sep(const char **strs, size_t nstrs, const char *sep, + size_t *final_length) { + const size_t sep_len = strlen(sep); + size_t out_length = 0; + size_t i; + char *out; + for (i = 0; i < nstrs; i++) { + out_length += strlen(strs[i]); + } + out_length += 1; /* null terminator */ + if (nstrs > 0) { + out_length += sep_len * (nstrs - 1); /* separators */ + } + out = gpr_malloc(out_length); + out_length = 0; + for (i = 0; i < nstrs; i++) { + const size_t slen = strlen(strs[i]); + if (i != 0) { + memcpy(out + out_length, sep, sep_len); + out_length += sep_len; + } + memcpy(out + out_length, strs[i], slen); + out_length += slen; + } + out[out_length] = 0; + if (final_length != NULL) { + *final_length = out_length; + } + return out; +} + +/** Finds the initial (\a begin) and final (\a end) offsets of the next + * substring from \a str + \a read_offset until the next \a sep or the end of \a + * str. + * + * Returns 1 and updates \a begin and \a end. Returns 0 otherwise. */ +static int slice_find_separator_offset(const gpr_slice str, const char *sep, + const size_t read_offset, size_t *begin, + size_t *end) { + size_t i; + const gpr_uint8 *str_ptr = GPR_SLICE_START_PTR(str) + read_offset; + const size_t str_len = GPR_SLICE_LENGTH(str) - read_offset; + const size_t sep_len = strlen(sep); + if (str_len < sep_len) { + return 0; + } + + for (i = 0; i <= str_len - sep_len; i++) { + if (memcmp(str_ptr + i, sep, sep_len) == 0) { + *begin = read_offset; + *end = read_offset + i; + return 1; + } + } + return 0; +} + +void gpr_slice_split(gpr_slice str, const char *sep, gpr_slice_buffer *dst) { + const size_t sep_len = strlen(sep); + size_t begin, end; + + GPR_ASSERT(sep_len > 0); + + if (slice_find_separator_offset(str, sep, 0, &begin, &end) != 0) { + do { + gpr_slice_buffer_add_indexed(dst, gpr_slice_sub(str, begin, end)); + } while (slice_find_separator_offset(str, sep, end + sep_len, &begin, + &end) != 0); + gpr_slice_buffer_add_indexed( + dst, gpr_slice_sub(str, end + sep_len, GPR_SLICE_LENGTH(str))); + } else { /* no sep found, add whole input */ + gpr_slice_buffer_add_indexed(dst, gpr_slice_ref(str)); + } +} + +void gpr_strvec_init(gpr_strvec *sv) { memset(sv, 0, sizeof(*sv)); } + +void gpr_strvec_destroy(gpr_strvec *sv) { + size_t i; + for (i = 0; i < sv->count; i++) { + gpr_free(sv->strs[i]); + } + gpr_free(sv->strs); +} + +void gpr_strvec_add(gpr_strvec *sv, char *str) { + if (sv->count == sv->capacity) { + sv->capacity = GPR_MAX(sv->capacity + 8, sv->capacity * 2); + sv->strs = gpr_realloc(sv->strs, sizeof(char *) * sv->capacity); + } + sv->strs[sv->count++] = str; +} + +char *gpr_strvec_flatten(gpr_strvec *sv, size_t *final_length) { + return gpr_strjoin((const char **)sv->strs, sv->count, final_length); +} diff --git a/src/core/support/string.h b/src/core/support/string.h new file mode 100644 index 00000000..a28e00fd --- /dev/null +++ b/src/core/support/string.h @@ -0,0 +1,111 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_SUPPORT_STRING_H +#define GRPC_INTERNAL_CORE_SUPPORT_STRING_H + +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* String utility functions */ + +/* Flags for gpr_dump function. */ +#define GPR_DUMP_HEX 0x00000001 +#define GPR_DUMP_ASCII 0x00000002 + +/* Converts array buf, of length len, into a C string according to the flags. + Result should be freed with gpr_free() */ +char *gpr_dump(const char *buf, size_t len, gpr_uint32 flags); + +/* Calls gpr_dump on a slice. */ +char *gpr_dump_slice(gpr_slice slice, gpr_uint32 flags); + +/* Parses an array of bytes into an integer (base 10). Returns 1 on success, + 0 on failure. */ +int gpr_parse_bytes_to_uint32(const char *data, size_t length, + gpr_uint32 *result); + +/* Minimum buffer size for calling ltoa */ +#define GPR_LTOA_MIN_BUFSIZE (3 * sizeof(long)) + +/* Convert a long to a string in base 10; returns the length of the + output string (or 0 on failure). + output must be at least GPR_LTOA_MIN_BUFSIZE bytes long. */ +int gpr_ltoa(long value, char *output); + +/* Reverse a run of bytes */ +void gpr_reverse_bytes(char *str, int len); + +/* Join a set of strings, returning the resulting string. + Total combined length (excluding null terminator) is returned in total_length + if it is non-null. */ +char *gpr_strjoin(const char **strs, size_t nstrs, size_t *total_length); + +/* Join a set of strings using a separator, returning the resulting string. + Total combined length (excluding null terminator) is returned in total_length + if it is non-null. */ +char *gpr_strjoin_sep(const char **strs, size_t nstrs, const char *sep, + size_t *total_length); + +/** Split \a str by the separator \a sep. Results are stored in \a dst, which + * should be a properly initialized instance. */ +void gpr_slice_split(gpr_slice str, const char *sep, gpr_slice_buffer *dst); + +/* A vector of strings... for building up a final string one piece at a time */ +typedef struct { + char **strs; + size_t count; + size_t capacity; +} gpr_strvec; + +/* Initialize/destroy */ +void gpr_strvec_init(gpr_strvec *strs); +void gpr_strvec_destroy(gpr_strvec *strs); +/* Add a string to a strvec, takes ownership of the string */ +void gpr_strvec_add(gpr_strvec *strs, char *add); +/* Return a joined string with all added substrings, optionally setting + total_length as per gpr_strjoin */ +char *gpr_strvec_flatten(gpr_strvec *strs, size_t *total_length); + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_INTERNAL_CORE_SUPPORT_STRING_H */ diff --git a/src/core/support/string_posix.c b/src/core/support/string_posix.c new file mode 100644 index 00000000..25c333db --- /dev/null +++ b/src/core/support/string_posix.c @@ -0,0 +1,86 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_POSIX_STRING + +#include +#include +#include + +#include + +int gpr_asprintf(char **strp, const char *format, ...) { + va_list args; + int ret; + char buf[64]; + size_t strp_buflen; + + /* Use a constant-sized buffer to determine the length. */ + va_start(args, format); + ret = vsnprintf(buf, sizeof(buf), format, args); + va_end(args); + if (ret < 0) { + *strp = NULL; + return -1; + } + + /* Allocate a new buffer, with space for the NUL terminator. */ + strp_buflen = (size_t)ret + 1; + if ((*strp = gpr_malloc(strp_buflen)) == NULL) { + /* This shouldn't happen, because gpr_malloc() calls abort(). */ + return -1; + } + + /* Return early if we have all the bytes. */ + if (strp_buflen <= sizeof(buf)) { + memcpy(*strp, buf, strp_buflen); + return ret; + } + + /* Try again using the larger buffer. */ + va_start(args, format); + ret = vsnprintf(*strp, strp_buflen, format, args); + va_end(args); + if ((size_t)ret == strp_buflen - 1) { + return ret; + } + + /* This should never happen. */ + gpr_free(*strp); + *strp = NULL; + return -1; +} + +#endif /* GPR_POSIX_STRING */ diff --git a/src/core/support/string_win32.c b/src/core/support/string_win32.c new file mode 100644 index 00000000..8ffb0a22 --- /dev/null +++ b/src/core/support/string_win32.c @@ -0,0 +1,107 @@ +/* + * + * Copyright 2015, 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. + * + */ + +/* Posix code for gpr snprintf support. */ + +#include + +#ifdef GPR_WIN32 + +#include +#include +#include + +#include + +#include "src/core/support/string.h" + +int gpr_asprintf(char **strp, const char *format, ...) { + va_list args; + int ret; + size_t strp_buflen; + + /* Determine the length. */ + va_start(args, format); + ret = _vscprintf(format, args); + va_end(args); + if (ret < 0) { + *strp = NULL; + return -1; + } + + /* Allocate a new buffer, with space for the NUL terminator. */ + strp_buflen = (size_t)ret + 1; + if ((*strp = gpr_malloc(strp_buflen)) == NULL) { + /* This shouldn't happen, because gpr_malloc() calls abort(). */ + return -1; + } + + /* Print to the buffer. */ + va_start(args, format); + ret = vsnprintf_s(*strp, strp_buflen, _TRUNCATE, format, args); + va_end(args); + if ((size_t)ret == strp_buflen - 1) { + return ret; + } + + /* This should never happen. */ + gpr_free(*strp); + *strp = NULL; + return -1; +} + +#if defined UNICODE || defined _UNICODE +LPTSTR gpr_char_to_tchar(LPCSTR input) { + LPTSTR ret; + int needed = MultiByteToWideChar(CP_UTF8, 0, input, -1, NULL, 0); + if (needed == 0) return NULL; + ret = gpr_malloc(needed * sizeof(TCHAR)); + MultiByteToWideChar(CP_UTF8, 0, input, -1, ret, needed); + return ret; +} + +LPSTR gpr_tchar_to_char(LPCTSTR input) { + LPSTR ret; + int needed = WideCharToMultiByte(CP_UTF8, 0, input, -1, NULL, 0, NULL, NULL); + if (needed == 0) return NULL; + ret = gpr_malloc(needed); + WideCharToMultiByte(CP_UTF8, 0, input, -1, ret, needed, NULL, NULL); + return ret; +} +#else +char *gpr_tchar_to_char(LPTSTR input) { return gpr_strdup(input); } + +char *gpr_char_to_tchar(LPTSTR input) { return gpr_strdup(input); } +#endif + +#endif /* GPR_WIN32 */ diff --git a/src/core/support/string_win32.h b/src/core/support/string_win32.h new file mode 100644 index 00000000..e3043656 --- /dev/null +++ b/src/core/support/string_win32.h @@ -0,0 +1,47 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_SUPPORT_STRING_WIN32_H +#define GRPC_INTERNAL_CORE_SUPPORT_STRING_WIN32_H + +#include + +#ifdef GPR_WIN32 + +/* These allocate new strings using gpr_malloc to convert from and to utf-8. */ +LPTSTR gpr_char_to_tchar(LPCSTR input); +LPSTR gpr_tchar_to_char(LPCTSTR input); + +#endif /* GPR_WIN32 */ + +#endif /* GRPC_INTERNAL_CORE_SUPPORT_STRING_WIN32_H */ diff --git a/src/core/support/subprocess_posix.c b/src/core/support/subprocess_posix.c new file mode 100644 index 00000000..171054e4 --- /dev/null +++ b/src/core/support/subprocess_posix.c @@ -0,0 +1,112 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_POSIX_SUBPROCESS + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +struct gpr_subprocess { + int pid; + int joined; +}; + +const char *gpr_subprocess_binary_extension() { return ""; } + +gpr_subprocess *gpr_subprocess_create(int argc, const char **argv) { + gpr_subprocess *r; + int pid; + char **exec_args; + + pid = fork(); + if (pid == -1) { + return NULL; + } else if (pid == 0) { + exec_args = gpr_malloc(((size_t)argc + 1) * sizeof(char *)); + memcpy(exec_args, argv, (size_t)argc * sizeof(char *)); + exec_args[argc] = NULL; + execv(exec_args[0], exec_args); + /* if we reach here, an error has occurred */ + gpr_log(GPR_ERROR, "execv '%s' failed: %s", exec_args[0], strerror(errno)); + _exit(1); + return NULL; + } else { + r = gpr_malloc(sizeof(gpr_subprocess)); + memset(r, 0, sizeof(*r)); + r->pid = pid; + return r; + } +} + +void gpr_subprocess_destroy(gpr_subprocess *p) { + if (!p->joined) { + kill(p->pid, SIGKILL); + gpr_subprocess_join(p); + } + gpr_free(p); +} + +int gpr_subprocess_join(gpr_subprocess *p) { + int status; +retry: + if (waitpid(p->pid, &status, 0) == -1) { + if (errno == EINTR) { + goto retry; + } + gpr_log(GPR_ERROR, "waitpid failed: %s", strerror(errno)); + return -1; + } + return status; +} + +void gpr_subprocess_interrupt(gpr_subprocess *p) { + if (!p->joined) { + kill(p->pid, SIGINT); + } +} + +#endif /* GPR_POSIX_SUBPROCESS */ diff --git a/src/core/support/sync.c b/src/core/support/sync.c new file mode 100644 index 00000000..d3cf77fa --- /dev/null +++ b/src/core/support/sync.c @@ -0,0 +1,122 @@ +/* + * + * Copyright 2015, 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. + * + */ + +/* Generic implementation of synchronization primitives. */ + +#include +#include +#include + +/* Number of mutexes to allocate for events, to avoid lock contention. + Should be a prime. */ +enum { event_sync_partitions = 31 }; + +/* Events are partitioned by address to avoid lock contention. */ +static struct sync_array_s { + gpr_mu mu; + gpr_cv cv; +} sync_array[event_sync_partitions]; + +/* This routine is executed once on first use, via event_once */ +static gpr_once event_once = GPR_ONCE_INIT; +static void event_initialize(void) { + int i; + for (i = 0; i != event_sync_partitions; i++) { + gpr_mu_init(&sync_array[i].mu); + gpr_cv_init(&sync_array[i].cv); + } +} + +/* Hash ev into an element of sync_array[]. */ +static struct sync_array_s *hash(gpr_event *ev) { + return &sync_array[((gpr_uintptr)ev) % event_sync_partitions]; +} + +void gpr_event_init(gpr_event *ev) { + gpr_once_init(&event_once, &event_initialize); + ev->state = 0; +} + +void gpr_event_set(gpr_event *ev, void *value) { + struct sync_array_s *s = hash(ev); + gpr_mu_lock(&s->mu); + GPR_ASSERT(gpr_atm_acq_load(&ev->state) == 0); + gpr_atm_rel_store(&ev->state, (gpr_atm)value); + gpr_cv_broadcast(&s->cv); + gpr_mu_unlock(&s->mu); + GPR_ASSERT(value != NULL); +} + +void *gpr_event_get(gpr_event *ev) { + return (void *)gpr_atm_acq_load(&ev->state); +} + +void *gpr_event_wait(gpr_event *ev, gpr_timespec abs_deadline) { + void *result = (void *)gpr_atm_acq_load(&ev->state); + if (result == NULL) { + struct sync_array_s *s = hash(ev); + gpr_mu_lock(&s->mu); + do { + result = (void *)gpr_atm_acq_load(&ev->state); + } while (result == NULL && !gpr_cv_wait(&s->cv, &s->mu, abs_deadline)); + gpr_mu_unlock(&s->mu); + } + return result; +} + +void gpr_ref_init(gpr_refcount *r, int n) { gpr_atm_rel_store(&r->count, n); } + +void gpr_ref(gpr_refcount *r) { gpr_atm_no_barrier_fetch_add(&r->count, 1); } + +void gpr_refn(gpr_refcount *r, int n) { + gpr_atm_no_barrier_fetch_add(&r->count, n); +} + +int gpr_unref(gpr_refcount *r) { + gpr_atm prior = gpr_atm_full_fetch_add(&r->count, -1); + GPR_ASSERT(prior > 0); + return prior == 1; +} + +void gpr_stats_init(gpr_stats_counter *c, gpr_intptr n) { + gpr_atm_rel_store(&c->value, n); +} + +void gpr_stats_inc(gpr_stats_counter *c, gpr_intptr inc) { + gpr_atm_no_barrier_fetch_add(&c->value, inc); +} + +gpr_intptr gpr_stats_read(const gpr_stats_counter *c) { + /* don't need acquire-load, but we have no no-barrier load yet */ + return gpr_atm_acq_load(&c->value); +} diff --git a/src/core/support/sync_posix.c b/src/core/support/sync_posix.c new file mode 100644 index 00000000..6f078cd4 --- /dev/null +++ b/src/core/support/sync_posix.c @@ -0,0 +1,92 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_POSIX_SYNC + +#include +#include +#include +#include +#include + +void gpr_mu_init(gpr_mu *mu) { GPR_ASSERT(pthread_mutex_init(mu, NULL) == 0); } + +void gpr_mu_destroy(gpr_mu *mu) { GPR_ASSERT(pthread_mutex_destroy(mu) == 0); } + +void gpr_mu_lock(gpr_mu *mu) { GPR_ASSERT(pthread_mutex_lock(mu) == 0); } + +void gpr_mu_unlock(gpr_mu *mu) { GPR_ASSERT(pthread_mutex_unlock(mu) == 0); } + +int gpr_mu_trylock(gpr_mu *mu) { + int err = pthread_mutex_trylock(mu); + GPR_ASSERT(err == 0 || err == EBUSY); + return err == 0; +} + +/*----------------------------------------*/ + +void gpr_cv_init(gpr_cv *cv) { GPR_ASSERT(pthread_cond_init(cv, NULL) == 0); } + +void gpr_cv_destroy(gpr_cv *cv) { GPR_ASSERT(pthread_cond_destroy(cv) == 0); } + +int gpr_cv_wait(gpr_cv *cv, gpr_mu *mu, gpr_timespec abs_deadline) { + int err = 0; + if (gpr_time_cmp(abs_deadline, gpr_inf_future(abs_deadline.clock_type)) == + 0) { + err = pthread_cond_wait(cv, mu); + } else { + struct timespec abs_deadline_ts; + abs_deadline = gpr_convert_clock_type(abs_deadline, GPR_CLOCK_REALTIME); + abs_deadline_ts.tv_sec = abs_deadline.tv_sec; + abs_deadline_ts.tv_nsec = abs_deadline.tv_nsec; + err = pthread_cond_timedwait(cv, mu, &abs_deadline_ts); + } + GPR_ASSERT(err == 0 || err == ETIMEDOUT || err == EAGAIN); + return err == ETIMEDOUT; +} + +void gpr_cv_signal(gpr_cv *cv) { GPR_ASSERT(pthread_cond_signal(cv) == 0); } + +void gpr_cv_broadcast(gpr_cv *cv) { + GPR_ASSERT(pthread_cond_broadcast(cv) == 0); +} + +/*----------------------------------------*/ + +void gpr_once_init(gpr_once *once, void (*init_function)(void)) { + GPR_ASSERT(pthread_once(once, init_function) == 0); +} + +#endif /* GRP_POSIX_SYNC */ diff --git a/src/core/support/sync_win32.c b/src/core/support/sync_win32.c new file mode 100644 index 00000000..f5464770 --- /dev/null +++ b/src/core/support/sync_win32.c @@ -0,0 +1,128 @@ +/* + * + * Copyright 2015, 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. + * + */ + +/* Win32 code for gpr synchronization support. */ + +#include + +#ifdef GPR_WIN32 + +#include +#include +#include + +void gpr_mu_init(gpr_mu *mu) { + InitializeCriticalSection(&mu->cs); + mu->locked = 0; +} + +void gpr_mu_destroy(gpr_mu *mu) { DeleteCriticalSection(&mu->cs); } + +void gpr_mu_lock(gpr_mu *mu) { + EnterCriticalSection(&mu->cs); + GPR_ASSERT(!mu->locked); + mu->locked = 1; +} + +void gpr_mu_unlock(gpr_mu *mu) { + mu->locked = 0; + LeaveCriticalSection(&mu->cs); +} + +int gpr_mu_trylock(gpr_mu *mu) { + int result = TryEnterCriticalSection(&mu->cs); + if (result) { + if (mu->locked) { /* This thread already holds the lock. */ + LeaveCriticalSection(&mu->cs); /* Decrement lock count. */ + result = 0; /* Indicate failure */ + } + mu->locked = 1; + } + return result; +} + +/*----------------------------------------*/ + +void gpr_cv_init(gpr_cv *cv) { InitializeConditionVariable(cv); } + +void gpr_cv_destroy(gpr_cv *cv) { + /* Condition variables don't need destruction in Win32. */ +} + +int gpr_cv_wait(gpr_cv *cv, gpr_mu *mu, gpr_timespec abs_deadline) { + int timeout = 0; + DWORD timeout_max_ms; + mu->locked = 0; + if (gpr_time_cmp(abs_deadline, gpr_inf_future(abs_deadline.clock_type)) == + 0) { + SleepConditionVariableCS(cv, &mu->cs, INFINITE); + } else { + gpr_timespec now = gpr_now(abs_deadline.clock_type); + gpr_int64 now_ms = (gpr_int64)now.tv_sec * 1000 + now.tv_nsec / 1000000; + gpr_int64 deadline_ms = + (gpr_int64)abs_deadline.tv_sec * 1000 + abs_deadline.tv_nsec / 1000000; + if (now_ms >= deadline_ms) { + timeout = 1; + } else { + timeout_max_ms = (DWORD)min(deadline_ms - now_ms, INFINITE - 1); + timeout = (SleepConditionVariableCS(cv, &mu->cs, timeout_max_ms) == 0 && + GetLastError() == ERROR_TIMEOUT); + } + } + mu->locked = 1; + return timeout; +} + +void gpr_cv_signal(gpr_cv *cv) { WakeConditionVariable(cv); } + +void gpr_cv_broadcast(gpr_cv *cv) { WakeAllConditionVariable(cv); } + +/*----------------------------------------*/ + +static void *dummy; +struct run_once_func_arg { + void (*init_function)(void); +}; +static BOOL CALLBACK run_once_func(gpr_once *once, void *v, void **pv) { + struct run_once_func_arg *arg = v; + (*arg->init_function)(); + return 1; +} + +void gpr_once_init(gpr_once *once, void (*init_function)(void)) { + struct run_once_func_arg arg; + arg.init_function = init_function; + InitOnceExecuteOnce(once, run_once_func, &arg, &dummy); +} + +#endif /* GPR_WIN32 */ diff --git a/src/core/support/thd.c b/src/core/support/thd.c new file mode 100644 index 00000000..32c0db5b --- /dev/null +++ b/src/core/support/thd.c @@ -0,0 +1,64 @@ +/* + * + * Copyright 2015, 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. + * + */ + +/* Posix implementation for gpr threads. */ + +#include + +#include + +enum { GPR_THD_JOINABLE = 1 }; + +gpr_thd_options gpr_thd_options_default(void) { + gpr_thd_options options; + memset(&options, 0, sizeof(options)); + return options; +} + +void gpr_thd_options_set_detached(gpr_thd_options *options) { + options->flags &= ~GPR_THD_JOINABLE; +} + +void gpr_thd_options_set_joinable(gpr_thd_options *options) { + options->flags |= GPR_THD_JOINABLE; +} + +int gpr_thd_options_is_detached(const gpr_thd_options *options) { + if (!options) return 1; + return (options->flags & GPR_THD_JOINABLE) == 0; +} + +int gpr_thd_options_is_joinable(const gpr_thd_options *options) { + if (!options) return 0; + return (options->flags & GPR_THD_JOINABLE) == GPR_THD_JOINABLE; +} diff --git a/src/core/support/thd_internal.h b/src/core/support/thd_internal.h new file mode 100644 index 00000000..1508c469 --- /dev/null +++ b/src/core/support/thd_internal.h @@ -0,0 +1,39 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_SUPPORT_THD_INTERNAL_H +#define GRPC_INTERNAL_CORE_SUPPORT_THD_INTERNAL_H + +/* Internal interfaces between modules within the gpr support library. */ + +#endif /* GRPC_INTERNAL_CORE_SUPPORT_THD_INTERNAL_H */ diff --git a/src/core/support/thd_posix.c b/src/core/support/thd_posix.c new file mode 100644 index 00000000..c36d94d0 --- /dev/null +++ b/src/core/support/thd_posix.c @@ -0,0 +1,91 @@ +/* + * + * Copyright 2015, 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. + * + */ + +/* Posix implementation for gpr threads. */ + +#include + +#ifdef GPR_POSIX_SYNC + +#include +#include +#include +#include +#include +#include +#include + +struct thd_arg { + void (*body)(void *arg); /* body of a thread */ + void *arg; /* argument to a thread */ +}; + +/* Body of every thread started via gpr_thd_new. */ +static void *thread_body(void *v) { + struct thd_arg a = *(struct thd_arg *)v; + gpr_free(v); + (*a.body)(a.arg); + return NULL; +} + +int gpr_thd_new(gpr_thd_id *t, void (*thd_body)(void *arg), void *arg, + const gpr_thd_options *options) { + int thread_started; + pthread_attr_t attr; + pthread_t p; + struct thd_arg *a = gpr_malloc(sizeof(*a)); + a->body = thd_body; + a->arg = arg; + + GPR_ASSERT(pthread_attr_init(&attr) == 0); + if (gpr_thd_options_is_detached(options)) { + GPR_ASSERT(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) == + 0); + } else { + GPR_ASSERT(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE) == + 0); + } + thread_started = (pthread_create(&p, &attr, &thread_body, a) == 0); + GPR_ASSERT(pthread_attr_destroy(&attr) == 0); + if (!thread_started) { + gpr_free(a); + } + *t = (gpr_thd_id)p; + return thread_started; +} + +gpr_thd_id gpr_thd_currentid(void) { return (gpr_thd_id)pthread_self(); } + +void gpr_thd_join(gpr_thd_id t) { pthread_join((pthread_t)t, NULL); } + +#endif /* GPR_POSIX_SYNC */ diff --git a/src/core/support/thd_win32.c b/src/core/support/thd_win32.c new file mode 100644 index 00000000..a9db180c --- /dev/null +++ b/src/core/support/thd_win32.c @@ -0,0 +1,117 @@ +/* + * + * Copyright 2015, 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. + * + */ + +/* Windows implementation for gpr threads. */ + +#include + +#ifdef GPR_WIN32 + +#include +#include +#include +#include + +#if defined(_MSC_VER) +#define thread_local __declspec(thread) +#elif defined(__GNUC__) +#define thread_local __thread +#else +#error "Unknown compiler - please file a bug report" +#endif + +struct thd_info { + void (*body)(void *arg); /* body of a thread */ + void *arg; /* argument to a thread */ + HANDLE join_event; /* if joinable, the join event */ + int joinable; /* true if not detached */ +}; + +static thread_local struct thd_info *g_thd_info; + +/* Destroys a thread info */ +static void destroy_thread(struct thd_info *t) { + if (t->joinable) CloseHandle(t->join_event); + gpr_free(t); +} + +/* Body of every thread started via gpr_thd_new. */ +static DWORD WINAPI thread_body(void *v) { + g_thd_info = (struct thd_info *)v; + g_thd_info->body(g_thd_info->arg); + if (g_thd_info->joinable) { + BOOL ret = SetEvent(g_thd_info->join_event); + GPR_ASSERT(ret); + } else { + destroy_thread(g_thd_info); + } + return 0; +} + +int gpr_thd_new(gpr_thd_id *t, void (*thd_body)(void *arg), void *arg, + const gpr_thd_options *options) { + HANDLE handle; + struct thd_info *info = gpr_malloc(sizeof(*info)); + info->body = thd_body; + info->arg = arg; + *t = 0; + if (gpr_thd_options_is_joinable(options)) { + info->joinable = 1; + info->join_event = CreateEvent(NULL, FALSE, FALSE, NULL); + if (info->join_event == NULL) { + gpr_free(info); + return 0; + } + } else { + info->joinable = 0; + } + handle = CreateThread(NULL, 64 * 1024, thread_body, info, 0, NULL); + if (handle == NULL) { + destroy_thread(info); + } else { + *t = (gpr_thd_id)info; + CloseHandle(handle); + } + return handle != NULL; +} + +gpr_thd_id gpr_thd_currentid(void) { return (gpr_thd_id)g_thd_info; } + +void gpr_thd_join(gpr_thd_id t) { + struct thd_info *info = (struct thd_info *)t; + DWORD ret = WaitForSingleObject(info->join_event, INFINITE); + GPR_ASSERT(ret == WAIT_OBJECT_0); + destroy_thread(info); +} + +#endif /* GPR_WIN32 */ diff --git a/src/core/support/time.c b/src/core/support/time.c new file mode 100644 index 00000000..929adac9 --- /dev/null +++ b/src/core/support/time.c @@ -0,0 +1,320 @@ +/* + * + * Copyright 2015, 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. + * + */ + +/* Generic implementation of time calls. */ + +#include +#include +#include +#include +#include + +int gpr_time_cmp(gpr_timespec a, gpr_timespec b) { + int cmp = (a.tv_sec > b.tv_sec) - (a.tv_sec < b.tv_sec); + GPR_ASSERT(a.clock_type == b.clock_type); + if (cmp == 0) { + cmp = (a.tv_nsec > b.tv_nsec) - (a.tv_nsec < b.tv_nsec); + } + return cmp; +} + +gpr_timespec gpr_time_min(gpr_timespec a, gpr_timespec b) { + return gpr_time_cmp(a, b) < 0 ? a : b; +} + +gpr_timespec gpr_time_max(gpr_timespec a, gpr_timespec b) { + return gpr_time_cmp(a, b) > 0 ? a : b; +} + +/* There's no standard TIME_T_MIN and TIME_T_MAX, so we construct them. The + following assumes that signed types are two's-complement and that bytes are + 8 bits. */ + +/* The top bit of integral type t. */ +#define TOP_BIT_OF_TYPE(t) (((gpr_uintmax)1) << ((8 * sizeof(t)) - 1)) + +/* Return whether integral type t is signed. */ +#define TYPE_IS_SIGNED(t) (((t)1) > (t) ~(t)0) + +/* The minimum and maximum value of integral type t. */ +#define TYPE_MIN(t) ((t)(TYPE_IS_SIGNED(t) ? TOP_BIT_OF_TYPE(t) : 0)) +#define TYPE_MAX(t) \ + ((t)(TYPE_IS_SIGNED(t) ? (TOP_BIT_OF_TYPE(t) - 1) \ + : ((TOP_BIT_OF_TYPE(t) - 1) << 1) + 1)) + +gpr_timespec gpr_time_0(gpr_clock_type type) { + gpr_timespec out; + out.tv_sec = 0; + out.tv_nsec = 0; + out.clock_type = type; + return out; +} + +gpr_timespec gpr_inf_future(gpr_clock_type type) { + gpr_timespec out; + out.tv_sec = TYPE_MAX(time_t); + out.tv_nsec = 0; + out.clock_type = type; + return out; +} + +gpr_timespec gpr_inf_past(gpr_clock_type type) { + gpr_timespec out; + out.tv_sec = TYPE_MIN(time_t); + out.tv_nsec = 0; + out.clock_type = type; + return out; +} + +/* TODO(ctiller): consider merging _nanos, _micros, _millis into a single + function for maintainability. Similarly for _seconds, _minutes, and _hours */ + +gpr_timespec gpr_time_from_nanos(long ns, gpr_clock_type type) { + gpr_timespec result; + result.clock_type = type; + if (ns == LONG_MAX) { + result = gpr_inf_future(type); + } else if (ns == LONG_MIN) { + result = gpr_inf_past(type); + } else if (ns >= 0) { + result.tv_sec = ns / GPR_NS_PER_SEC; + result.tv_nsec = (int)(ns - result.tv_sec * GPR_NS_PER_SEC); + } else { + /* Calculation carefully formulated to avoid any possible under/overflow. */ + result.tv_sec = (-(999999999 - (ns + GPR_NS_PER_SEC)) / GPR_NS_PER_SEC) - 1; + result.tv_nsec = (int)(ns - result.tv_sec * GPR_NS_PER_SEC); + } + return result; +} + +gpr_timespec gpr_time_from_micros(long us, gpr_clock_type type) { + gpr_timespec result; + result.clock_type = type; + if (us == LONG_MAX) { + result = gpr_inf_future(type); + } else if (us == LONG_MIN) { + result = gpr_inf_past(type); + } else if (us >= 0) { + result.tv_sec = us / 1000000; + result.tv_nsec = (int)((us - result.tv_sec * 1000000) * 1000); + } else { + /* Calculation carefully formulated to avoid any possible under/overflow. */ + result.tv_sec = (-(999999 - (us + 1000000)) / 1000000) - 1; + result.tv_nsec = (int)((us - result.tv_sec * 1000000) * 1000); + } + return result; +} + +gpr_timespec gpr_time_from_millis(long ms, gpr_clock_type type) { + gpr_timespec result; + result.clock_type = type; + if (ms == LONG_MAX) { + result = gpr_inf_future(type); + } else if (ms == LONG_MIN) { + result = gpr_inf_past(type); + } else if (ms >= 0) { + result.tv_sec = ms / 1000; + result.tv_nsec = (int)((ms - result.tv_sec * 1000) * 1000000); + } else { + /* Calculation carefully formulated to avoid any possible under/overflow. */ + result.tv_sec = (-(999 - (ms + 1000)) / 1000) - 1; + result.tv_nsec = (int)((ms - result.tv_sec * 1000) * 1000000); + } + return result; +} + +gpr_timespec gpr_time_from_seconds(long s, gpr_clock_type type) { + gpr_timespec result; + result.clock_type = type; + if (s == LONG_MAX) { + result = gpr_inf_future(type); + } else if (s == LONG_MIN) { + result = gpr_inf_past(type); + } else { + result.tv_sec = s; + result.tv_nsec = 0; + } + return result; +} + +gpr_timespec gpr_time_from_minutes(long m, gpr_clock_type type) { + gpr_timespec result; + result.clock_type = type; + if (m >= LONG_MAX / 60) { + result = gpr_inf_future(type); + } else if (m <= LONG_MIN / 60) { + result = gpr_inf_past(type); + } else { + result.tv_sec = m * 60; + result.tv_nsec = 0; + } + return result; +} + +gpr_timespec gpr_time_from_hours(long h, gpr_clock_type type) { + gpr_timespec result; + result.clock_type = type; + if (h >= LONG_MAX / 3600) { + result = gpr_inf_future(type); + } else if (h <= LONG_MIN / 3600) { + result = gpr_inf_past(type); + } else { + result.tv_sec = h * 3600; + result.tv_nsec = 0; + } + return result; +} + +gpr_timespec gpr_time_add(gpr_timespec a, gpr_timespec b) { + gpr_timespec sum; + int inc = 0; + GPR_ASSERT(b.clock_type == GPR_TIMESPAN); + sum.clock_type = a.clock_type; + sum.tv_nsec = a.tv_nsec + b.tv_nsec; + if (sum.tv_nsec >= GPR_NS_PER_SEC) { + sum.tv_nsec -= GPR_NS_PER_SEC; + inc++; + } + if (a.tv_sec == TYPE_MAX(time_t) || a.tv_sec == TYPE_MIN(time_t)) { + sum = a; + } else if (b.tv_sec == TYPE_MAX(time_t) || + (b.tv_sec >= 0 && a.tv_sec >= TYPE_MAX(time_t) - b.tv_sec)) { + sum = gpr_inf_future(sum.clock_type); + } else if (b.tv_sec == TYPE_MIN(time_t) || + (b.tv_sec <= 0 && a.tv_sec <= TYPE_MIN(time_t) - b.tv_sec)) { + sum = gpr_inf_past(sum.clock_type); + } else { + sum.tv_sec = a.tv_sec + b.tv_sec; + if (inc != 0 && sum.tv_sec == TYPE_MAX(time_t) - 1) { + sum = gpr_inf_future(sum.clock_type); + } else { + sum.tv_sec += inc; + } + } + return sum; +} + +gpr_timespec gpr_time_sub(gpr_timespec a, gpr_timespec b) { + gpr_timespec diff; + int dec = 0; + if (b.clock_type == GPR_TIMESPAN) { + diff.clock_type = a.clock_type; + } else { + GPR_ASSERT(a.clock_type == b.clock_type); + diff.clock_type = GPR_TIMESPAN; + } + diff.tv_nsec = a.tv_nsec - b.tv_nsec; + if (diff.tv_nsec < 0) { + diff.tv_nsec += GPR_NS_PER_SEC; + dec++; + } + if (a.tv_sec == TYPE_MAX(time_t) || a.tv_sec == TYPE_MIN(time_t)) { + diff = a; + } else if (b.tv_sec == TYPE_MIN(time_t) || + (b.tv_sec <= 0 && a.tv_sec >= TYPE_MAX(time_t) + b.tv_sec)) { + diff = gpr_inf_future(GPR_CLOCK_REALTIME); + } else if (b.tv_sec == TYPE_MAX(time_t) || + (b.tv_sec >= 0 && a.tv_sec <= TYPE_MIN(time_t) + b.tv_sec)) { + diff = gpr_inf_past(GPR_CLOCK_REALTIME); + } else { + diff.tv_sec = a.tv_sec - b.tv_sec; + if (dec != 0 && diff.tv_sec == TYPE_MIN(time_t) + 1) { + diff = gpr_inf_past(GPR_CLOCK_REALTIME); + } else { + diff.tv_sec -= dec; + } + } + return diff; +} + +int gpr_time_similar(gpr_timespec a, gpr_timespec b, gpr_timespec threshold) { + int cmp_ab; + + GPR_ASSERT(a.clock_type == b.clock_type); + GPR_ASSERT(threshold.clock_type == GPR_TIMESPAN); + + cmp_ab = gpr_time_cmp(a, b); + if (cmp_ab == 0) return 1; + if (cmp_ab < 0) { + return gpr_time_cmp(gpr_time_sub(b, a), threshold) <= 0; + } else { + return gpr_time_cmp(gpr_time_sub(a, b), threshold) <= 0; + } +} + +gpr_int32 gpr_time_to_millis(gpr_timespec t) { + if (t.tv_sec >= 2147483) { + if (t.tv_sec == 2147483 && t.tv_nsec < 648 * GPR_NS_PER_MS) { + return 2147483 * GPR_MS_PER_SEC + t.tv_nsec / GPR_NS_PER_MS; + } + return 2147483647; + } else if (t.tv_sec <= -2147483) { + /* TODO(ctiller): correct handling here (it's so far in the past do we + care?) */ + return -2147483647; + } else { + return (gpr_int32)(t.tv_sec * GPR_MS_PER_SEC + t.tv_nsec / GPR_NS_PER_MS); + } +} + +double gpr_timespec_to_micros(gpr_timespec t) { + return (double)t.tv_sec * GPR_US_PER_SEC + t.tv_nsec * 1e-3; +} + +gpr_timespec gpr_convert_clock_type(gpr_timespec t, gpr_clock_type clock_type) { + if (t.clock_type == clock_type) { + return t; + } + + if (t.tv_nsec == 0) { + if (t.tv_sec == TYPE_MAX(time_t)) { + t.clock_type = clock_type; + return t; + } + if (t.tv_sec == TYPE_MIN(time_t)) { + t.clock_type = clock_type; + return t; + } + } + + if (clock_type == GPR_TIMESPAN) { + return gpr_time_sub(t, gpr_now(t.clock_type)); + } + + if (t.clock_type == GPR_TIMESPAN) { + return gpr_time_add(gpr_now(clock_type), t); + } + + return gpr_time_add(gpr_now(clock_type), + gpr_time_sub(t, gpr_now(t.clock_type))); +} diff --git a/src/core/support/time_posix.c b/src/core/support/time_posix.c new file mode 100644 index 00000000..a2744002 --- /dev/null +++ b/src/core/support/time_posix.c @@ -0,0 +1,146 @@ +/* + * + * Copyright 2015, 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 +#include + +#ifdef GPR_POSIX_TIME + +#include +#include +#include +#include +#include + +static struct timespec timespec_from_gpr(gpr_timespec gts) { + struct timespec rv; + rv.tv_sec = gts.tv_sec; + rv.tv_nsec = gts.tv_nsec; + return rv; +} + +#if _POSIX_TIMERS > 0 +static gpr_timespec gpr_from_timespec(struct timespec ts, + gpr_clock_type clock) { + gpr_timespec rv; + rv.tv_sec = ts.tv_sec; + rv.tv_nsec = (int)ts.tv_nsec; + rv.clock_type = clock; + return rv; +} + +/** maps gpr_clock_type --> clockid_t for clock_gettime */ +static clockid_t clockid_for_gpr_clock[] = {CLOCK_MONOTONIC, CLOCK_REALTIME}; + +void gpr_time_init(void) {} + +gpr_timespec gpr_now(gpr_clock_type clock) { + struct timespec now; + GPR_ASSERT(clock != GPR_TIMESPAN); + if (clock == GPR_CLOCK_PRECISE) { + gpr_timespec ret; + gpr_precise_clock_now(&ret); + return ret; + } else { + clock_gettime(clockid_for_gpr_clock[clock], &now); + return gpr_from_timespec(now, clock); + } +} +#else +/* For some reason Apple's OSes haven't implemented clock_gettime. */ + +#include +#include +#include + +static double g_time_scale; +static uint64_t g_time_start; + +void gpr_time_init(void) { + mach_timebase_info_data_t tb = {0, 1}; + mach_timebase_info(&tb); + g_time_scale = tb.numer; + g_time_scale /= tb.denom; + g_time_start = mach_absolute_time(); +} + +gpr_timespec gpr_now(gpr_clock_type clock) { + gpr_timespec now; + struct timeval now_tv; + double now_dbl; + + now.clock_type = clock; + switch (clock) { + case GPR_CLOCK_REALTIME: + gettimeofday(&now_tv, NULL); + now.tv_sec = now_tv.tv_sec; + now.tv_nsec = now_tv.tv_usec * 1000; + break; + case GPR_CLOCK_MONOTONIC: + now_dbl = (mach_absolute_time() - g_time_start) * g_time_scale; + now.tv_sec = now_dbl * 1e-9; + now.tv_nsec = now_dbl - now.tv_sec * 1e9; + break; + case GPR_CLOCK_PRECISE: + gpr_precise_clock_now(&now); + break; + case GPR_TIMESPAN: + abort(); + } + + return now; +} +#endif + +void gpr_sleep_until(gpr_timespec until) { + gpr_timespec now; + gpr_timespec delta; + struct timespec delta_ts; + + for (;;) { + /* We could simplify by using clock_nanosleep instead, but it might be + * slightly less portable. */ + now = gpr_now(until.clock_type); + if (gpr_time_cmp(until, now) <= 0) { + return; + } + + delta = gpr_time_sub(until, now); + delta_ts = timespec_from_gpr(delta); + if (nanosleep(&delta_ts, NULL) == 0) { + break; + } + } +} + +#endif /* GPR_POSIX_TIME */ diff --git a/src/core/support/time_precise.h b/src/core/support/time_precise.h new file mode 100644 index 00000000..574ebb84 --- /dev/null +++ b/src/core/support/time_precise.h @@ -0,0 +1,93 @@ +/* + * + * Copyright 2015, 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_CORE_SUPPORT_TIME_PRECISE_H_ +#define GRPC_CORE_SUPPORT_TIME_PRECISE_H_ + +#include +#include +#include + +#ifdef GRPC_TIMERS_RDTSC +#if defined(__i386__) +static void gpr_get_cycle_counter(long long int *clk) { + long long int ret; + __asm__ volatile("rdtsc" : "=A"(ret)); + *clk = ret; +} + +// ---------------------------------------------------------------- +#elif defined(__x86_64__) || defined(__amd64__) +static void gpr_get_cycle_counter(long long int *clk) { + unsigned long long low, high; + __asm__ volatile("rdtsc" : "=a"(low), "=d"(high)); + *clk = (high << 32) | low; +} +#endif + +static gpr_once precise_clock_init = GPR_ONCE_INIT; +static long long cycles_per_second = 0; +static void gpr_precise_clock_init() { + time_t start = time(NULL); + gpr_precise_clock start_cycle; + gpr_precise_clock end_cycle; + while (time(NULL) == start) + ; + gpr_get_cycle_counter(&start_cycle); + while (time(NULL) == start + 1) + ; + gpr_get_cycle_counter(&end_cycle); + cycles_per_second = end_cycle - start_cycle; +} + +static double grpc_precise_clock_scaling_factor() { + gpr_once_init(&precise_clock_init, grpc_precise_clock_init); + return 1e6 / cycles_per_second; +} + +static void gpr_precise_clock_now(gpr_timespec *clk) { + long long int counter; + gpr_get_cycle_counter(&counter); + clk->clock = GPR_CLOCK_REALTIME; + clk->tv_sec = counter / cycles_per_second; + clk->tv_nsec = counter % cycles_per_second; +} + +#else /* GRPC_TIMERS_RDTSC */ +static void gpr_precise_clock_now(gpr_timespec *clk) { + *clk = gpr_now(GPR_CLOCK_REALTIME); + clk->clock_type = GPR_CLOCK_PRECISE; +} +#endif /* GRPC_TIMERS_RDTSC */ + +#endif /* GRPC_CORE_SUPPORT_TIME_PRECISE_ */ diff --git a/src/core/support/time_win32.c b/src/core/support/time_win32.c new file mode 100644 index 00000000..f7948554 --- /dev/null +++ b/src/core/support/time_win32.c @@ -0,0 +1,99 @@ +/* + * + * Copyright 2015, 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. + * + */ + +/* Win32 code for gpr time support. */ + +#include + +#ifdef GPR_WIN32 + +#include +#include +#include + +static LARGE_INTEGER g_start_time; +static double g_time_scale; + +void gpr_time_init(void) { + LARGE_INTEGER frequency; + QueryPerformanceFrequency(&frequency); + QueryPerformanceCounter(&g_start_time); + g_time_scale = 1.0 / frequency.QuadPart; +} + +gpr_timespec gpr_now(gpr_clock_type clock) { + gpr_timespec now_tv; + struct _timeb now_tb; + LARGE_INTEGER timestamp; + double now_dbl; + now_tv.clock_type = clock; + switch (clock) { + case GPR_CLOCK_REALTIME: + _ftime_s(&now_tb); + now_tv.tv_sec = now_tb.time; + now_tv.tv_nsec = now_tb.millitm * 1000000; + break; + case GPR_CLOCK_MONOTONIC: + QueryPerformanceCounter(×tamp); + now_dbl = (timestamp.QuadPart - g_start_time.QuadPart) * g_time_scale; + now_tv.tv_sec = (time_t)now_dbl; + now_tv.tv_nsec = (int)((now_dbl - (double)now_tv.tv_sec) * 1e9); + break; + case GPR_CLOCK_PRECISE: + gpr_precise_clock_now(&now_tv); + break; + } + return now_tv; +} + +void gpr_sleep_until(gpr_timespec until) { + gpr_timespec now; + gpr_timespec delta; + DWORD sleep_millis; + + for (;;) { + /* We could simplify by using clock_nanosleep instead, but it might be + * slightly less portable. */ + now = gpr_now(until.clock_type); + if (gpr_time_cmp(until, now) <= 0) { + return; + } + + delta = gpr_time_sub(until, now); + sleep_millis = + (DWORD)delta.tv_sec * GPR_MS_PER_SEC + delta.tv_nsec / GPR_NS_PER_MS; + Sleep(sleep_millis); + } +} + +#endif /* GPR_WIN32 */ diff --git a/src/core/support/tls_pthread.c b/src/core/support/tls_pthread.c new file mode 100644 index 00000000..2d28226f --- /dev/null +++ b/src/core/support/tls_pthread.c @@ -0,0 +1,45 @@ +/* + * + * Copyright 2015, 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 + +#ifdef GPR_PTHREAD_TLS + +#include + +gpr_intptr gpr_tls_set(struct gpr_pthread_thread_local *tls, gpr_intptr value) { + GPR_ASSERT(0 == pthread_setspecific(tls->key, (void *)value)); + return value; +} + +#endif /* GPR_PTHREAD_TLS */ diff --git a/src/core/surface/byte_buffer.c b/src/core/surface/byte_buffer.c new file mode 100644 index 00000000..a930949f --- /dev/null +++ b/src/core/surface/byte_buffer.c @@ -0,0 +1,100 @@ +/* + * + * Copyright 2015, 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 +#include +#include + +grpc_byte_buffer *grpc_raw_byte_buffer_create(gpr_slice *slices, + size_t nslices) { + return grpc_raw_compressed_byte_buffer_create(slices, nslices, + GRPC_COMPRESS_NONE); +} + +grpc_byte_buffer *grpc_raw_compressed_byte_buffer_create( + gpr_slice *slices, size_t nslices, grpc_compression_algorithm compression) { + size_t i; + grpc_byte_buffer *bb = malloc(sizeof(grpc_byte_buffer)); + bb->type = GRPC_BB_RAW; + bb->data.raw.compression = compression; + gpr_slice_buffer_init(&bb->data.raw.slice_buffer); + for (i = 0; i < nslices; i++) { + gpr_slice_ref(slices[i]); + gpr_slice_buffer_add(&bb->data.raw.slice_buffer, slices[i]); + } + return bb; +} + +grpc_byte_buffer *grpc_raw_byte_buffer_from_reader( + grpc_byte_buffer_reader *reader) { + grpc_byte_buffer *bb = malloc(sizeof(grpc_byte_buffer)); + gpr_slice slice; + bb->type = GRPC_BB_RAW; + bb->data.raw.compression = GRPC_COMPRESS_NONE; + gpr_slice_buffer_init(&bb->data.raw.slice_buffer); + + while (grpc_byte_buffer_reader_next(reader, &slice)) { + gpr_slice_buffer_add(&bb->data.raw.slice_buffer, slice); + } + return bb; +} + +grpc_byte_buffer *grpc_byte_buffer_copy(grpc_byte_buffer *bb) { + switch (bb->type) { + case GRPC_BB_RAW: + return grpc_raw_byte_buffer_create(bb->data.raw.slice_buffer.slices, + bb->data.raw.slice_buffer.count); + } + gpr_log(GPR_INFO, "should never get here"); + abort(); + return NULL; +} + +void grpc_byte_buffer_destroy(grpc_byte_buffer *bb) { + if (!bb) return; + switch (bb->type) { + case GRPC_BB_RAW: + gpr_slice_buffer_destroy(&bb->data.raw.slice_buffer); + break; + } + free(bb); +} + +size_t grpc_byte_buffer_length(grpc_byte_buffer *bb) { + switch (bb->type) { + case GRPC_BB_RAW: + return bb->data.raw.slice_buffer.length; + } + gpr_log(GPR_ERROR, "should never reach here"); + abort(); +} diff --git a/src/core/surface/byte_buffer_queue.c b/src/core/surface/byte_buffer_queue.c new file mode 100644 index 00000000..e47dc4f4 --- /dev/null +++ b/src/core/surface/byte_buffer_queue.c @@ -0,0 +1,97 @@ +/* + * + * Copyright 2015, 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 "src/core/surface/byte_buffer_queue.h" +#include +#include + +static void bba_destroy(grpc_bbq_array *array, size_t start_pos) { + size_t i; + for (i = start_pos; i < array->count; i++) { + grpc_byte_buffer_destroy(array->data[i]); + } + gpr_free(array->data); +} + +/* Append an operation to an array, expanding as needed */ +static void bba_push(grpc_bbq_array *a, grpc_byte_buffer *buffer) { + if (a->count == a->capacity) { + a->capacity = GPR_MAX(a->capacity * 2, 8); + a->data = gpr_realloc(a->data, sizeof(grpc_byte_buffer *) * a->capacity); + } + a->data[a->count++] = buffer; +} + +void grpc_bbq_destroy(grpc_byte_buffer_queue *q) { + bba_destroy(&q->filling, 0); + bba_destroy(&q->draining, q->drain_pos); +} + +int grpc_bbq_empty(grpc_byte_buffer_queue *q) { + return (q->drain_pos == q->draining.count && q->filling.count == 0); +} + +void grpc_bbq_push(grpc_byte_buffer_queue *q, grpc_byte_buffer *buffer) { + q->bytes += grpc_byte_buffer_length(buffer); + bba_push(&q->filling, buffer); +} + +void grpc_bbq_flush(grpc_byte_buffer_queue *q) { + grpc_byte_buffer *bb; + while ((bb = grpc_bbq_pop(q))) { + grpc_byte_buffer_destroy(bb); + } +} + +size_t grpc_bbq_bytes(grpc_byte_buffer_queue *q) { return q->bytes; } + +grpc_byte_buffer *grpc_bbq_pop(grpc_byte_buffer_queue *q) { + grpc_bbq_array temp_array; + grpc_byte_buffer *out; + + if (q->drain_pos == q->draining.count) { + if (q->filling.count == 0) { + return NULL; + } + q->draining.count = 0; + q->drain_pos = 0; + /* swap arrays */ + temp_array = q->filling; + q->filling = q->draining; + q->draining = temp_array; + } + + out = q->draining.data[q->drain_pos++]; + q->bytes -= grpc_byte_buffer_length(out); + return out; +} diff --git a/src/core/surface/byte_buffer_queue.h b/src/core/surface/byte_buffer_queue.h new file mode 100644 index 00000000..2c3b22d2 --- /dev/null +++ b/src/core/surface/byte_buffer_queue.h @@ -0,0 +1,62 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_SURFACE_BYTE_BUFFER_QUEUE_H +#define GRPC_INTERNAL_CORE_SURFACE_BYTE_BUFFER_QUEUE_H + +#include + +/* TODO(ctiller): inline an element or two into this struct to avoid per-call + allocations */ +typedef struct { + grpc_byte_buffer **data; + size_t count; + size_t capacity; +} grpc_bbq_array; + +/* should be initialized by zeroing memory */ +typedef struct { + size_t drain_pos; + grpc_bbq_array filling; + grpc_bbq_array draining; + size_t bytes; +} grpc_byte_buffer_queue; + +void grpc_bbq_destroy(grpc_byte_buffer_queue *q); +grpc_byte_buffer *grpc_bbq_pop(grpc_byte_buffer_queue *q); +void grpc_bbq_flush(grpc_byte_buffer_queue *q); +int grpc_bbq_empty(grpc_byte_buffer_queue *q); +void grpc_bbq_push(grpc_byte_buffer_queue *q, grpc_byte_buffer *bb); +size_t grpc_bbq_bytes(grpc_byte_buffer_queue *q); + +#endif /* GRPC_INTERNAL_CORE_SURFACE_BYTE_BUFFER_QUEUE_H */ diff --git a/src/core/surface/byte_buffer_reader.c b/src/core/surface/byte_buffer_reader.c new file mode 100644 index 00000000..283db838 --- /dev/null +++ b/src/core/surface/byte_buffer_reader.c @@ -0,0 +1,105 @@ +/* + * + * Copyright 2015, 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 + +#include +#include +#include +#include +#include +#include + +#include "src/core/compression/message_compress.h" + +static int is_compressed(grpc_byte_buffer *buffer) { + switch (buffer->type) { + case GRPC_BB_RAW: + if (buffer->data.raw.compression == GRPC_COMPRESS_NONE) { + return 0 /* GPR_FALSE */; + } + break; + } + return 1 /* GPR_TRUE */; +} + +void grpc_byte_buffer_reader_init(grpc_byte_buffer_reader *reader, + grpc_byte_buffer *buffer) { + gpr_slice_buffer decompressed_slices_buffer; + reader->buffer_in = buffer; + switch (reader->buffer_in->type) { + case GRPC_BB_RAW: + gpr_slice_buffer_init(&decompressed_slices_buffer); + if (is_compressed(reader->buffer_in)) { + grpc_msg_decompress(reader->buffer_in->data.raw.compression, + &reader->buffer_in->data.raw.slice_buffer, + &decompressed_slices_buffer); + reader->buffer_out = + grpc_raw_byte_buffer_create(decompressed_slices_buffer.slices, + decompressed_slices_buffer.count); + gpr_slice_buffer_destroy(&decompressed_slices_buffer); + } else { /* not compressed, use the input buffer as output */ + reader->buffer_out = reader->buffer_in; + } + reader->current.index = 0; + break; + } +} + +void grpc_byte_buffer_reader_destroy(grpc_byte_buffer_reader *reader) { + switch (reader->buffer_in->type) { + case GRPC_BB_RAW: + /* keeping the same if-else structure as in the init function */ + if (is_compressed(reader->buffer_in)) { + grpc_byte_buffer_destroy(reader->buffer_out); + } + break; + } +} + +int grpc_byte_buffer_reader_next(grpc_byte_buffer_reader *reader, + gpr_slice *slice) { + switch (reader->buffer_in->type) { + case GRPC_BB_RAW: { + gpr_slice_buffer *slice_buffer; + slice_buffer = &reader->buffer_out->data.raw.slice_buffer; + if (reader->current.index < slice_buffer->count) { + *slice = gpr_slice_ref(slice_buffer->slices[reader->current.index]); + reader->current.index += 1; + return 1; + } + break; + } + } + return 0; +} diff --git a/src/core/surface/call.c b/src/core/surface/call.c new file mode 100644 index 00000000..a8b4d65f --- /dev/null +++ b/src/core/surface/call.c @@ -0,0 +1,1751 @@ +/* + * + * Copyright 2015, 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 +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "src/core/channel/channel_stack.h" +#include "src/core/iomgr/alarm.h" +#include "src/core/profiling/timers.h" +#include "src/core/support/string.h" +#include "src/core/surface/byte_buffer_queue.h" +#include "src/core/surface/call.h" +#include "src/core/surface/channel.h" +#include "src/core/surface/completion_queue.h" + +/** The maximum number of completions possible. + Based upon the maximum number of individually queueable ops in the batch + api: + - initial metadata send + - message send + - status/close send (depending on client/server) + - initial metadata recv + - message recv + - status/close recv (depending on client/server) */ +#define MAX_CONCURRENT_COMPLETIONS 6 + +typedef enum { REQ_INITIAL = 0, REQ_READY, REQ_DONE } req_state; + +typedef enum { + SEND_NOTHING, + SEND_INITIAL_METADATA, + SEND_BUFFERED_INITIAL_METADATA, + SEND_MESSAGE, + SEND_BUFFERED_MESSAGE, + SEND_TRAILING_METADATA_AND_FINISH, + SEND_FINISH +} send_action; + +typedef struct { + grpc_ioreq_completion_func on_complete; + void *user_data; + int success; +} completed_request; + +/* See request_set in grpc_call below for a description */ +#define REQSET_EMPTY 'X' +#define REQSET_DONE 'Y' + +#define MAX_SEND_INITIAL_METADATA_COUNT 3 + +typedef struct { + /* Overall status of the operation: starts OK, may degrade to + non-OK */ + gpr_uint8 success; + /* a bit mask of which request ops are needed (1u << opid) */ + gpr_uint16 need_mask; + /* a bit mask of which request ops are now completed */ + gpr_uint16 complete_mask; + /* Completion function to call at the end of the operation */ + grpc_ioreq_completion_func on_complete; + void *user_data; +} reqinfo_master; + +/* Status data for a request can come from several sources; this + enumerates them all, and acts as a priority sorting for which + status to return to the application - earlier entries override + later ones */ +typedef enum { + /* Status came from the application layer overriding whatever + the wire says */ + STATUS_FROM_API_OVERRIDE = 0, + /* Status was created by some internal channel stack operation */ + STATUS_FROM_CORE, + /* Status came from 'the wire' - or somewhere below the surface + layer */ + STATUS_FROM_WIRE, + /* Status came from the server sending status */ + STATUS_FROM_SERVER_STATUS, + STATUS_SOURCE_COUNT +} status_source; + +typedef struct { + gpr_uint8 is_set; + grpc_status_code code; + grpc_mdstr *details; +} received_status; + +/* How far through the GRPC stream have we read? */ +typedef enum { + /* We are still waiting for initial metadata to complete */ + READ_STATE_INITIAL = 0, + /* We have gotten initial metadata, and are reading either + messages or trailing metadata */ + READ_STATE_GOT_INITIAL_METADATA, + /* The stream is closed for reading */ + READ_STATE_READ_CLOSED, + /* The stream is closed for reading & writing */ + READ_STATE_STREAM_CLOSED +} read_state; + +typedef enum { + WRITE_STATE_INITIAL = 0, + WRITE_STATE_STARTED, + WRITE_STATE_WRITE_CLOSED +} write_state; + +struct grpc_call { + grpc_completion_queue *cq; + grpc_channel *channel; + grpc_call *parent; + grpc_call *first_child; + grpc_mdctx *metadata_context; + /* TODO(ctiller): share with cq if possible? */ + gpr_mu mu; + gpr_mu completion_mu; + + /* how far through the stream have we read? */ + read_state read_state; + /* how far through the stream have we written? */ + write_state write_state; + /* client or server call */ + gpr_uint8 is_client; + /* is the alarm set */ + gpr_uint8 have_alarm; + /* are we currently performing a send operation */ + gpr_uint8 sending; + /* are we currently performing a recv operation */ + gpr_uint8 receiving; + /* are we currently completing requests */ + gpr_uint8 completing; + /** has grpc_call_destroy been called */ + gpr_uint8 destroy_called; + /* pairs with completed_requests */ + gpr_uint8 num_completed_requests; + /* are we currently reading a message? */ + gpr_uint8 reading_message; + /* have we bound a pollset yet? */ + gpr_uint8 bound_pollset; + /* is an error status set */ + gpr_uint8 error_status_set; + /** should the alarm be cancelled */ + gpr_uint8 cancel_alarm; + /** bitmask of allocated completion events in completions */ + gpr_uint8 allocated_completions; + /** flag indicating that cancellation is inherited */ + gpr_uint8 cancellation_is_inherited; + + /* flags with bits corresponding to write states allowing us to determine + what was sent */ + gpr_uint16 last_send_contains; + /* cancel with this status on the next outgoing transport op */ + grpc_status_code cancel_with_status; + + /* Active ioreqs. + request_set and request_data contain one element per active ioreq + operation. + + request_set[op] is an integer specifying a set of operations to which + the request belongs: + - if it is < GRPC_IOREQ_OP_COUNT, then this operation is pending + completion, and the integer represents to which group of operations + the ioreq belongs. Each group is represented by one master, and the + integer in request_set is an index into masters to find the master + data. + - if it is REQSET_EMPTY, the ioreq op is inactive and available to be + started + - finally, if request_set[op] is REQSET_DONE, then the operation is + complete and unavailable to be started again + + request_data[op] is the request data as supplied by the initiator of + a request, and is valid iff request_set[op] <= GRPC_IOREQ_OP_COUNT. + The set fields are as per the request type specified by op. + + Finally, one element of masters is set per active _set_ of ioreq + operations. It describes work left outstanding, result status, and + what work to perform upon operation completion. As one ioreq of each + op type can be active at once, by convention we choose the first element + of the group to be the master -- ie the master of in-progress operation + op is masters[request_set[op]]. This allows constant time allocation + and a strong upper bound of a count of masters to be calculated. */ + gpr_uint8 request_set[GRPC_IOREQ_OP_COUNT]; + grpc_ioreq_data request_data[GRPC_IOREQ_OP_COUNT]; + gpr_uint32 request_flags[GRPC_IOREQ_OP_COUNT]; + reqinfo_master masters[GRPC_IOREQ_OP_COUNT]; + + /* Dynamic array of ioreq's that have completed: the count of + elements is queued in num_completed_requests. + This list is built up under lock(), and flushed entirely during + unlock(). + We know the upper bound of the number of elements as we can only + have one ioreq of each type active at once. */ + completed_request completed_requests[GRPC_IOREQ_OP_COUNT]; + /* Incoming buffer of messages */ + grpc_byte_buffer_queue incoming_queue; + /* Buffered read metadata waiting to be returned to the application. + Element 0 is initial metadata, element 1 is trailing metadata. */ + grpc_metadata_array buffered_metadata[2]; + /* All metadata received - unreffed at once at the end of the call */ + grpc_mdelem **owned_metadata; + size_t owned_metadata_count; + size_t owned_metadata_capacity; + + /* Received call statuses from various sources */ + received_status status[STATUS_SOURCE_COUNT]; + + /* Compression algorithm for the call */ + grpc_compression_algorithm compression_algorithm; + + /* Supported encodings (compression algorithms), a bitset */ + gpr_uint32 encodings_accepted_by_peer; + + /* Contexts for various subsystems (security, tracing, ...). */ + grpc_call_context_element context[GRPC_CONTEXT_COUNT]; + + /* Deadline alarm - if have_alarm is non-zero */ + grpc_alarm alarm; + + /* Call refcount - to keep the call alive during asynchronous operations */ + gpr_refcount internal_refcount; + + grpc_linked_mdelem send_initial_metadata[MAX_SEND_INITIAL_METADATA_COUNT]; + grpc_linked_mdelem status_link; + grpc_linked_mdelem details_link; + size_t send_initial_metadata_count; + gpr_timespec send_deadline; + + grpc_stream_op_buffer send_ops; + grpc_stream_op_buffer recv_ops; + grpc_stream_state recv_state; + + gpr_slice_buffer incoming_message; + gpr_uint32 incoming_message_length; + gpr_uint32 incoming_message_flags; + grpc_iomgr_closure destroy_closure; + grpc_iomgr_closure on_done_recv; + grpc_iomgr_closure on_done_send; + grpc_iomgr_closure on_done_bind; + + /** completion events - for completion queue use */ + grpc_cq_completion completions[MAX_CONCURRENT_COMPLETIONS]; + + /** siblings: children of the same parent form a list, and this list is + protected under + parent->mu */ + grpc_call *sibling_next; + grpc_call *sibling_prev; +}; + +#define CALL_STACK_FROM_CALL(call) ((grpc_call_stack *)((call) + 1)) +#define CALL_FROM_CALL_STACK(call_stack) (((grpc_call *)(call_stack)) - 1) +#define CALL_ELEM_FROM_CALL(call, idx) \ + grpc_call_stack_element(CALL_STACK_FROM_CALL(call), idx) +#define CALL_FROM_TOP_ELEM(top_elem) \ + CALL_FROM_CALL_STACK(grpc_call_stack_from_top_element(top_elem)) + +static void set_deadline_alarm(grpc_call *call, gpr_timespec deadline); +static void call_on_done_recv(void *call, int success); +static void call_on_done_send(void *call, int success); +static int fill_send_ops(grpc_call *call, grpc_transport_stream_op *op); +static void execute_op(grpc_call *call, grpc_transport_stream_op *op); +static void recv_metadata(grpc_call *call, grpc_metadata_batch *metadata); +static void finish_read_ops(grpc_call *call); +static grpc_call_error cancel_with_status(grpc_call *c, grpc_status_code status, + const char *description); +static void finished_loose_op(void *call, int success); + +static void lock(grpc_call *call); +static void unlock(grpc_call *call); + +grpc_call *grpc_call_create(grpc_channel *channel, grpc_call *parent_call, + gpr_uint32 propagation_mask, + grpc_completion_queue *cq, + const void *server_transport_data, + grpc_mdelem **add_initial_metadata, + size_t add_initial_metadata_count, + gpr_timespec send_deadline) { + size_t i; + grpc_transport_stream_op initial_op; + grpc_transport_stream_op *initial_op_ptr = NULL; + grpc_channel_stack *channel_stack = grpc_channel_get_channel_stack(channel); + grpc_call *call = + gpr_malloc(sizeof(grpc_call) + channel_stack->call_stack_size); + memset(call, 0, sizeof(grpc_call)); + gpr_mu_init(&call->mu); + gpr_mu_init(&call->completion_mu); + call->channel = channel; + call->cq = cq; + if (cq != NULL) { + GRPC_CQ_INTERNAL_REF(cq, "bind"); + } + call->parent = parent_call; + call->is_client = server_transport_data == NULL; + for (i = 0; i < GRPC_IOREQ_OP_COUNT; i++) { + call->request_set[i] = REQSET_EMPTY; + } + if (call->is_client) { + call->request_set[GRPC_IOREQ_SEND_TRAILING_METADATA] = REQSET_DONE; + call->request_set[GRPC_IOREQ_SEND_STATUS] = REQSET_DONE; + } + GPR_ASSERT(add_initial_metadata_count < MAX_SEND_INITIAL_METADATA_COUNT); + for (i = 0; i < add_initial_metadata_count; i++) { + call->send_initial_metadata[i].md = add_initial_metadata[i]; + } + call->send_initial_metadata_count = add_initial_metadata_count; + call->send_deadline = send_deadline; + GRPC_CHANNEL_INTERNAL_REF(channel, "call"); + call->metadata_context = grpc_channel_get_metadata_context(channel); + grpc_sopb_init(&call->send_ops); + grpc_sopb_init(&call->recv_ops); + gpr_slice_buffer_init(&call->incoming_message); + grpc_iomgr_closure_init(&call->on_done_recv, call_on_done_recv, call); + grpc_iomgr_closure_init(&call->on_done_send, call_on_done_send, call); + grpc_iomgr_closure_init(&call->on_done_bind, finished_loose_op, call); + /* dropped in destroy and when READ_STATE_STREAM_CLOSED received */ + gpr_ref_init(&call->internal_refcount, 2); + /* server hack: start reads immediately so we can get initial metadata. + TODO(ctiller): figure out a cleaner solution */ + if (!call->is_client) { + memset(&initial_op, 0, sizeof(initial_op)); + initial_op.recv_ops = &call->recv_ops; + initial_op.recv_state = &call->recv_state; + initial_op.on_done_recv = &call->on_done_recv; + initial_op.context = call->context; + call->receiving = 1; + GRPC_CALL_INTERNAL_REF(call, "receiving"); + initial_op_ptr = &initial_op; + } + grpc_call_stack_init(channel_stack, server_transport_data, initial_op_ptr, + CALL_STACK_FROM_CALL(call)); + if (parent_call != NULL) { + GRPC_CALL_INTERNAL_REF(parent_call, "child"); + GPR_ASSERT(call->is_client); + GPR_ASSERT(!parent_call->is_client); + + gpr_mu_lock(&parent_call->mu); + + if (propagation_mask & GRPC_PROPAGATE_DEADLINE) { + send_deadline = gpr_time_min( + gpr_convert_clock_type(send_deadline, + parent_call->send_deadline.clock_type), + parent_call->send_deadline); + } + /* for now GRPC_PROPAGATE_TRACING_CONTEXT *MUST* be passed with + * GRPC_PROPAGATE_STATS_CONTEXT */ + /* TODO(ctiller): This should change to use the appropriate census start_op + * call. */ + if (propagation_mask & GRPC_PROPAGATE_CENSUS_TRACING_CONTEXT) { + GPR_ASSERT(propagation_mask & GRPC_PROPAGATE_CENSUS_STATS_CONTEXT); + grpc_call_context_set(call, GRPC_CONTEXT_TRACING, + parent_call->context[GRPC_CONTEXT_TRACING].value, + NULL); + } else { + GPR_ASSERT(propagation_mask & GRPC_PROPAGATE_CENSUS_STATS_CONTEXT); + } + if (propagation_mask & GRPC_PROPAGATE_CANCELLATION) { + call->cancellation_is_inherited = 1; + } + + if (parent_call->first_child == NULL) { + parent_call->first_child = call; + call->sibling_next = call->sibling_prev = call; + } else { + call->sibling_next = parent_call->first_child; + call->sibling_prev = parent_call->first_child->sibling_prev; + call->sibling_next->sibling_prev = call->sibling_prev->sibling_next = + call; + } + + gpr_mu_unlock(&parent_call->mu); + } + if (gpr_time_cmp(send_deadline, gpr_inf_future(send_deadline.clock_type)) != + 0) { + set_deadline_alarm(call, send_deadline); + } + return call; +} + +void grpc_call_set_completion_queue(grpc_call *call, + grpc_completion_queue *cq) { + lock(call); + call->cq = cq; + if (cq) { + GRPC_CQ_INTERNAL_REF(cq, "bind"); + } + unlock(call); +} + +grpc_completion_queue *grpc_call_get_completion_queue(grpc_call *call) { + return call->cq; +} + +static grpc_cq_completion *allocate_completion(grpc_call *call) { + gpr_uint8 i; + gpr_mu_lock(&call->completion_mu); + for (i = 0; i < GPR_ARRAY_SIZE(call->completions); i++) { + if (call->allocated_completions & (1u << i)) { + continue; + } + call->allocated_completions |= 1u << i; + gpr_mu_unlock(&call->completion_mu); + return &call->completions[i]; + } + gpr_log(GPR_ERROR, "should never reach here"); + abort(); +} + +static void done_completion(void *call, grpc_cq_completion *completion) { + grpc_call *c = call; + gpr_mu_lock(&c->completion_mu); + c->allocated_completions &= ~(1u << (completion - c->completions)); + gpr_mu_unlock(&c->completion_mu); + GRPC_CALL_INTERNAL_UNREF(c, "completion", 1); +} + +#ifdef GRPC_CALL_REF_COUNT_DEBUG +void grpc_call_internal_ref(grpc_call *c, const char *reason) { + gpr_log(GPR_DEBUG, "CALL: ref %p %d -> %d [%s]", c, + c->internal_refcount.count, c->internal_refcount.count + 1, reason); +#else +void grpc_call_internal_ref(grpc_call *c) { +#endif + gpr_ref(&c->internal_refcount); +} + +static void destroy_call(void *call, int ignored_success) { + size_t i; + grpc_call *c = call; + grpc_call_stack_destroy(CALL_STACK_FROM_CALL(c)); + GRPC_CHANNEL_INTERNAL_UNREF(c->channel, "call"); + gpr_mu_destroy(&c->mu); + gpr_mu_destroy(&c->completion_mu); + for (i = 0; i < STATUS_SOURCE_COUNT; i++) { + if (c->status[i].details) { + GRPC_MDSTR_UNREF(c->status[i].details); + } + } + for (i = 0; i < c->owned_metadata_count; i++) { + GRPC_MDELEM_UNREF(c->owned_metadata[i]); + } + gpr_free(c->owned_metadata); + for (i = 0; i < GPR_ARRAY_SIZE(c->buffered_metadata); i++) { + gpr_free(c->buffered_metadata[i].metadata); + } + for (i = 0; i < c->send_initial_metadata_count; i++) { + GRPC_MDELEM_UNREF(c->send_initial_metadata[i].md); + } + for (i = 0; i < GRPC_CONTEXT_COUNT; i++) { + if (c->context[i].destroy) { + c->context[i].destroy(c->context[i].value); + } + } + grpc_sopb_destroy(&c->send_ops); + grpc_sopb_destroy(&c->recv_ops); + grpc_bbq_destroy(&c->incoming_queue); + gpr_slice_buffer_destroy(&c->incoming_message); + if (c->cq) { + GRPC_CQ_INTERNAL_UNREF(c->cq, "bind"); + } + gpr_free(c); +} + +#ifdef GRPC_CALL_REF_COUNT_DEBUG +void grpc_call_internal_unref(grpc_call *c, const char *reason, + int allow_immediate_deletion) { + gpr_log(GPR_DEBUG, "CALL: unref %p %d -> %d [%s]", c, + c->internal_refcount.count, c->internal_refcount.count - 1, reason); +#else +void grpc_call_internal_unref(grpc_call *c, int allow_immediate_deletion) { +#endif + if (gpr_unref(&c->internal_refcount)) { + if (allow_immediate_deletion) { + destroy_call(c, 1); + } else { + c->destroy_closure.cb = destroy_call; + c->destroy_closure.cb_arg = c; + grpc_iomgr_add_callback(&c->destroy_closure); + } + } +} + +static void set_status_code(grpc_call *call, status_source source, + gpr_uint32 status) { + if (call->status[source].is_set) return; + + call->status[source].is_set = 1; + call->status[source].code = status; + call->error_status_set = status != GRPC_STATUS_OK; + + if (status != GRPC_STATUS_OK && !grpc_bbq_empty(&call->incoming_queue)) { + grpc_bbq_flush(&call->incoming_queue); + } +} + +static void set_compression_algorithm(grpc_call *call, + grpc_compression_algorithm algo) { + call->compression_algorithm = algo; +} + +grpc_compression_algorithm grpc_call_get_compression_algorithm( + const grpc_call *call) { + return call->compression_algorithm; +} + +static void set_encodings_accepted_by_peer( + grpc_call *call, const gpr_slice accept_encoding_slice) { + size_t i; + grpc_compression_algorithm algorithm; + gpr_slice_buffer accept_encoding_parts; + + gpr_slice_buffer_init(&accept_encoding_parts); + gpr_slice_split(accept_encoding_slice, ", ", &accept_encoding_parts); + + /* No need to zero call->encodings_accepted_by_peer: grpc_call_create already + * zeroes the whole grpc_call */ + /* Always support no compression */ + GPR_BITSET(&call->encodings_accepted_by_peer, GRPC_COMPRESS_NONE); + for (i = 0; i < accept_encoding_parts.count; i++) { + const gpr_slice *accept_encoding_entry_slice = + &accept_encoding_parts.slices[i]; + if (grpc_compression_algorithm_parse( + (const char *)GPR_SLICE_START_PTR(*accept_encoding_entry_slice), + GPR_SLICE_LENGTH(*accept_encoding_entry_slice), &algorithm)) { + GPR_BITSET(&call->encodings_accepted_by_peer, algorithm); + } else { + char *accept_encoding_entry_str = + gpr_dump_slice(*accept_encoding_entry_slice, GPR_DUMP_ASCII); + gpr_log(GPR_ERROR, + "Invalid entry in accept encoding metadata: '%s'. Ignoring.", + accept_encoding_entry_str); + gpr_free(accept_encoding_entry_str); + } + } +} + +gpr_uint32 grpc_call_get_encodings_accepted_by_peer(grpc_call *call) { + return call->encodings_accepted_by_peer; +} + +gpr_uint32 grpc_call_get_message_flags(const grpc_call *call) { + return call->incoming_message_flags; +} + +static void set_status_details(grpc_call *call, status_source source, + grpc_mdstr *status) { + if (call->status[source].details != NULL) { + GRPC_MDSTR_UNREF(call->status[source].details); + } + call->status[source].details = status; +} + +static int is_op_live(grpc_call *call, grpc_ioreq_op op) { + gpr_uint8 set = call->request_set[op]; + reqinfo_master *master; + if (set >= GRPC_IOREQ_OP_COUNT) return 0; + master = &call->masters[set]; + return (master->complete_mask & (1u << op)) == 0; +} + +static void lock(grpc_call *call) { gpr_mu_lock(&call->mu); } + +static int need_more_data(grpc_call *call) { + if (call->read_state == READ_STATE_STREAM_CLOSED) return 0; + /* TODO(ctiller): this needs some serious cleanup */ + return is_op_live(call, GRPC_IOREQ_RECV_INITIAL_METADATA) || + (is_op_live(call, GRPC_IOREQ_RECV_MESSAGE) && + grpc_bbq_empty(&call->incoming_queue)) || + is_op_live(call, GRPC_IOREQ_RECV_TRAILING_METADATA) || + is_op_live(call, GRPC_IOREQ_RECV_STATUS) || + is_op_live(call, GRPC_IOREQ_RECV_STATUS_DETAILS) || + (is_op_live(call, GRPC_IOREQ_RECV_CLOSE) && + grpc_bbq_empty(&call->incoming_queue)) || + (call->write_state == WRITE_STATE_INITIAL && !call->is_client) || + (call->cancel_with_status != GRPC_STATUS_OK) || call->destroy_called; +} + +static void unlock(grpc_call *call) { + grpc_transport_stream_op op; + completed_request completed_requests[GRPC_IOREQ_OP_COUNT]; + int completing_requests = 0; + int start_op = 0; + int i; + const gpr_uint32 MAX_RECV_PEEK_AHEAD = 65536; + size_t buffered_bytes; + int cancel_alarm = 0; + + memset(&op, 0, sizeof(op)); + + op.cancel_with_status = call->cancel_with_status; + start_op = op.cancel_with_status != GRPC_STATUS_OK; + call->cancel_with_status = GRPC_STATUS_OK; /* reset */ + + cancel_alarm = call->cancel_alarm; + call->cancel_alarm = 0; + + if (!call->receiving && need_more_data(call)) { + if (grpc_bbq_empty(&call->incoming_queue) && call->reading_message) { + op.max_recv_bytes = call->incoming_message_length - + call->incoming_message.length + MAX_RECV_PEEK_AHEAD; + } else { + buffered_bytes = grpc_bbq_bytes(&call->incoming_queue); + if (buffered_bytes > MAX_RECV_PEEK_AHEAD) { + op.max_recv_bytes = 0; + } else { + op.max_recv_bytes = MAX_RECV_PEEK_AHEAD - buffered_bytes; + } + } + /* TODO(ctiller): 1024 is basically to cover a bug + I don't understand yet */ + if (op.max_recv_bytes > 1024) { + op.recv_ops = &call->recv_ops; + op.recv_state = &call->recv_state; + op.on_done_recv = &call->on_done_recv; + call->receiving = 1; + GRPC_CALL_INTERNAL_REF(call, "receiving"); + start_op = 1; + } + } + + if (!call->sending) { + if (fill_send_ops(call, &op)) { + call->sending = 1; + GRPC_CALL_INTERNAL_REF(call, "sending"); + start_op = 1; + } + } + + if (!call->bound_pollset && call->cq && (!call->is_client || start_op)) { + call->bound_pollset = 1; + op.bind_pollset = grpc_cq_pollset(call->cq); + start_op = 1; + } + + if (!call->completing && call->num_completed_requests != 0) { + completing_requests = call->num_completed_requests; + memcpy(completed_requests, call->completed_requests, + sizeof(completed_requests)); + call->num_completed_requests = 0; + call->completing = 1; + GRPC_CALL_INTERNAL_REF(call, "completing"); + } + + gpr_mu_unlock(&call->mu); + + if (cancel_alarm) { + grpc_alarm_cancel(&call->alarm); + } + + if (start_op) { + execute_op(call, &op); + } + + if (completing_requests > 0) { + for (i = 0; i < completing_requests; i++) { + completed_requests[i].on_complete(call, completed_requests[i].success, + completed_requests[i].user_data); + } + lock(call); + call->completing = 0; + unlock(call); + GRPC_CALL_INTERNAL_UNREF(call, "completing", 0); + } +} + +static void get_final_status(grpc_call *call, grpc_ioreq_data out) { + int i; + for (i = 0; i < STATUS_SOURCE_COUNT; i++) { + if (call->status[i].is_set) { + out.recv_status.set_value(call->status[i].code, + out.recv_status.user_data); + return; + } + } + if (call->is_client) { + out.recv_status.set_value(GRPC_STATUS_UNKNOWN, out.recv_status.user_data); + } else { + out.recv_status.set_value(GRPC_STATUS_OK, out.recv_status.user_data); + } +} + +static void get_final_details(grpc_call *call, grpc_ioreq_data out) { + int i; + for (i = 0; i < STATUS_SOURCE_COUNT; i++) { + if (call->status[i].is_set) { + if (call->status[i].details) { + gpr_slice details = call->status[i].details->slice; + size_t len = GPR_SLICE_LENGTH(details); + if (len + 1 > *out.recv_status_details.details_capacity) { + *out.recv_status_details.details_capacity = GPR_MAX( + len + 1, *out.recv_status_details.details_capacity * 3 / 2); + *out.recv_status_details.details = + gpr_realloc(*out.recv_status_details.details, + *out.recv_status_details.details_capacity); + } + memcpy(*out.recv_status_details.details, GPR_SLICE_START_PTR(details), + len); + (*out.recv_status_details.details)[len] = 0; + } else { + goto no_details; + } + return; + } + } + +no_details: + if (0 == *out.recv_status_details.details_capacity) { + *out.recv_status_details.details_capacity = 8; + *out.recv_status_details.details = + gpr_malloc(*out.recv_status_details.details_capacity); + } + **out.recv_status_details.details = 0; +} + +static void finish_live_ioreq_op(grpc_call *call, grpc_ioreq_op op, + int success) { + completed_request *cr; + gpr_uint8 master_set = call->request_set[op]; + reqinfo_master *master; + size_t i; + /* ioreq is live: we need to do something */ + master = &call->masters[master_set]; + master->complete_mask |= 1u << op; + if (!success) { + master->success = 0; + } + if (master->complete_mask == master->need_mask) { + for (i = 0; i < GRPC_IOREQ_OP_COUNT; i++) { + if (call->request_set[i] != master_set) { + continue; + } + call->request_set[i] = REQSET_DONE; + switch ((grpc_ioreq_op)i) { + case GRPC_IOREQ_RECV_MESSAGE: + case GRPC_IOREQ_SEND_MESSAGE: + call->request_set[i] = REQSET_EMPTY; + if (!master->success) { + call->write_state = WRITE_STATE_WRITE_CLOSED; + } + break; + case GRPC_IOREQ_SEND_STATUS: + if (call->request_data[GRPC_IOREQ_SEND_STATUS].send_status.details != + NULL) { + GRPC_MDSTR_UNREF( + call->request_data[GRPC_IOREQ_SEND_STATUS].send_status.details); + call->request_data[GRPC_IOREQ_SEND_STATUS].send_status.details = + NULL; + } + break; + case GRPC_IOREQ_RECV_CLOSE: + case GRPC_IOREQ_SEND_INITIAL_METADATA: + case GRPC_IOREQ_SEND_TRAILING_METADATA: + case GRPC_IOREQ_SEND_CLOSE: + break; + case GRPC_IOREQ_RECV_STATUS: + get_final_status(call, call->request_data[GRPC_IOREQ_RECV_STATUS]); + break; + case GRPC_IOREQ_RECV_STATUS_DETAILS: + get_final_details(call, + call->request_data[GRPC_IOREQ_RECV_STATUS_DETAILS]); + break; + case GRPC_IOREQ_RECV_INITIAL_METADATA: + GPR_SWAP(grpc_metadata_array, call->buffered_metadata[0], + *call->request_data[GRPC_IOREQ_RECV_INITIAL_METADATA] + .recv_metadata); + break; + case GRPC_IOREQ_RECV_TRAILING_METADATA: + GPR_SWAP(grpc_metadata_array, call->buffered_metadata[1], + *call->request_data[GRPC_IOREQ_RECV_TRAILING_METADATA] + .recv_metadata); + break; + case GRPC_IOREQ_OP_COUNT: + abort(); + break; + } + } + cr = &call->completed_requests[call->num_completed_requests++]; + cr->success = master->success; + cr->on_complete = master->on_complete; + cr->user_data = master->user_data; + } +} + +static void finish_ioreq_op(grpc_call *call, grpc_ioreq_op op, int success) { + if (is_op_live(call, op)) { + finish_live_ioreq_op(call, op, success); + } +} + +static void early_out_write_ops(grpc_call *call) { + switch (call->write_state) { + case WRITE_STATE_WRITE_CLOSED: + finish_ioreq_op(call, GRPC_IOREQ_SEND_MESSAGE, 0); + finish_ioreq_op(call, GRPC_IOREQ_SEND_STATUS, 0); + finish_ioreq_op(call, GRPC_IOREQ_SEND_TRAILING_METADATA, 0); + finish_ioreq_op(call, GRPC_IOREQ_SEND_CLOSE, 1); + /* fallthrough */ + case WRITE_STATE_STARTED: + finish_ioreq_op(call, GRPC_IOREQ_SEND_INITIAL_METADATA, 0); + /* fallthrough */ + case WRITE_STATE_INITIAL: + /* do nothing */ + break; + } +} + +static void call_on_done_send(void *pc, int success) { + grpc_call *call = pc; + lock(call); + if (call->last_send_contains & (1 << GRPC_IOREQ_SEND_INITIAL_METADATA)) { + finish_ioreq_op(call, GRPC_IOREQ_SEND_INITIAL_METADATA, success); + call->write_state = WRITE_STATE_STARTED; + } + if (call->last_send_contains & (1 << GRPC_IOREQ_SEND_MESSAGE)) { + finish_ioreq_op(call, GRPC_IOREQ_SEND_MESSAGE, success); + } + if (call->last_send_contains & (1 << GRPC_IOREQ_SEND_CLOSE)) { + finish_ioreq_op(call, GRPC_IOREQ_SEND_TRAILING_METADATA, success); + finish_ioreq_op(call, GRPC_IOREQ_SEND_STATUS, success); + finish_ioreq_op(call, GRPC_IOREQ_SEND_CLOSE, 1); + call->write_state = WRITE_STATE_WRITE_CLOSED; + } + if (!success) { + call->write_state = WRITE_STATE_WRITE_CLOSED; + early_out_write_ops(call); + } + call->send_ops.nops = 0; + call->last_send_contains = 0; + call->sending = 0; + unlock(call); + GRPC_CALL_INTERNAL_UNREF(call, "sending", 0); +} + +static void finish_message(grpc_call *call) { + if (call->error_status_set == 0) { + /* TODO(ctiller): this could be a lot faster if coded directly */ + grpc_byte_buffer *byte_buffer; + /* some aliases for readability */ + gpr_slice *slices = call->incoming_message.slices; + const size_t nslices = call->incoming_message.count; + + if ((call->incoming_message_flags & GRPC_WRITE_INTERNAL_COMPRESS) && + (call->compression_algorithm > GRPC_COMPRESS_NONE)) { + byte_buffer = grpc_raw_compressed_byte_buffer_create( + slices, nslices, call->compression_algorithm); + } else { + byte_buffer = grpc_raw_byte_buffer_create(slices, nslices); + } + grpc_bbq_push(&call->incoming_queue, byte_buffer); + } + gpr_slice_buffer_reset_and_unref(&call->incoming_message); + GPR_ASSERT(call->incoming_message.count == 0); + call->reading_message = 0; +} + +static int begin_message(grpc_call *call, grpc_begin_message msg) { + /* can't begin a message when we're still reading a message */ + if (call->reading_message) { + char *message = NULL; + gpr_asprintf( + &message, "Message terminated early; read %d bytes, expected %d", + (int)call->incoming_message.length, (int)call->incoming_message_length); + cancel_with_status(call, GRPC_STATUS_INVALID_ARGUMENT, message); + gpr_free(message); + return 0; + } + /* sanity check: if message flags indicate a compressed message, the + * compression level should already be present in the call, as parsed off its + * corresponding metadata. */ + if ((msg.flags & GRPC_WRITE_INTERNAL_COMPRESS) && + (call->compression_algorithm == GRPC_COMPRESS_NONE)) { + char *message = NULL; + char *alg_name; + if (!grpc_compression_algorithm_name(call->compression_algorithm, + &alg_name)) { + /* This shouldn't happen, other than due to data corruption */ + alg_name = ""; + } + gpr_asprintf(&message, + "Invalid compression algorithm (%s) for compressed message.", + alg_name); + cancel_with_status(call, GRPC_STATUS_INTERNAL, message); + gpr_free(message); + return 0; + } + /* stash away parameters, and prepare for incoming slices */ + if (msg.length > grpc_channel_get_max_message_length(call->channel)) { + char *message = NULL; + gpr_asprintf( + &message, + "Maximum message length of %d exceeded by a message of length %d", + grpc_channel_get_max_message_length(call->channel), msg.length); + cancel_with_status(call, GRPC_STATUS_INVALID_ARGUMENT, message); + gpr_free(message); + return 0; + } else if (msg.length > 0) { + call->reading_message = 1; + call->incoming_message_length = msg.length; + call->incoming_message_flags = msg.flags; + return 1; + } else { + finish_message(call); + return 1; + } +} + +static int add_slice_to_message(grpc_call *call, gpr_slice slice) { + if (GPR_SLICE_LENGTH(slice) == 0) { + gpr_slice_unref(slice); + return 1; + } + /* we have to be reading a message to know what to do here */ + if (!call->reading_message) { + cancel_with_status(call, GRPC_STATUS_INVALID_ARGUMENT, + "Received payload data while not reading a message"); + return 0; + } + /* append the slice to the incoming buffer */ + gpr_slice_buffer_add(&call->incoming_message, slice); + if (call->incoming_message.length > call->incoming_message_length) { + /* if we got too many bytes, complain */ + char *message = NULL; + gpr_asprintf( + &message, "Receiving message overflow; read %d bytes, expected %d", + (int)call->incoming_message.length, (int)call->incoming_message_length); + cancel_with_status(call, GRPC_STATUS_INVALID_ARGUMENT, message); + gpr_free(message); + return 0; + } else if (call->incoming_message.length == call->incoming_message_length) { + finish_message(call); + return 1; + } else { + return 1; + } +} + +static void call_on_done_recv(void *pc, int success) { + grpc_call *call = pc; + grpc_call *child_call; + grpc_call *next_child_call; + size_t i; + GRPC_TIMER_BEGIN(GRPC_PTAG_CALL_ON_DONE_RECV, 0); + lock(call); + call->receiving = 0; + if (success) { + for (i = 0; success && i < call->recv_ops.nops; i++) { + grpc_stream_op *op = &call->recv_ops.ops[i]; + switch (op->type) { + case GRPC_NO_OP: + break; + case GRPC_OP_METADATA: + recv_metadata(call, &op->data.metadata); + break; + case GRPC_OP_BEGIN_MESSAGE: + success = begin_message(call, op->data.begin_message); + break; + case GRPC_OP_SLICE: + success = add_slice_to_message(call, op->data.slice); + break; + } + } + if (!success) { + grpc_stream_ops_unref_owned_objects(&call->recv_ops.ops[i], + call->recv_ops.nops - i); + } + if (call->recv_state == GRPC_STREAM_RECV_CLOSED) { + GPR_ASSERT(call->read_state <= READ_STATE_READ_CLOSED); + call->read_state = READ_STATE_READ_CLOSED; + } + if (call->recv_state == GRPC_STREAM_CLOSED) { + GPR_ASSERT(call->read_state <= READ_STATE_STREAM_CLOSED); + call->read_state = READ_STATE_STREAM_CLOSED; + call->cancel_alarm |= call->have_alarm; + /* propagate cancellation to any interested children */ + child_call = call->first_child; + if (child_call != NULL) { + do { + next_child_call = child_call->sibling_next; + if (child_call->cancellation_is_inherited) { + GRPC_CALL_INTERNAL_REF(child_call, "propagate_cancel"); + grpc_call_cancel(child_call, NULL); + GRPC_CALL_INTERNAL_UNREF(child_call, "propagate_cancel", 0); + } + child_call = next_child_call; + } while (child_call != call->first_child); + } + GRPC_CALL_INTERNAL_UNREF(call, "closed", 0); + } + finish_read_ops(call); + } else { + finish_ioreq_op(call, GRPC_IOREQ_RECV_MESSAGE, 0); + finish_ioreq_op(call, GRPC_IOREQ_RECV_STATUS, 0); + finish_ioreq_op(call, GRPC_IOREQ_RECV_CLOSE, 0); + finish_ioreq_op(call, GRPC_IOREQ_RECV_TRAILING_METADATA, 0); + finish_ioreq_op(call, GRPC_IOREQ_RECV_INITIAL_METADATA, 0); + finish_ioreq_op(call, GRPC_IOREQ_RECV_STATUS_DETAILS, 0); + } + call->recv_ops.nops = 0; + unlock(call); + + GRPC_CALL_INTERNAL_UNREF(call, "receiving", 0); + GRPC_TIMER_END(GRPC_PTAG_CALL_ON_DONE_RECV, 0); +} + +static int prepare_application_metadata(grpc_call *call, size_t count, + grpc_metadata *metadata) { + size_t i; + for (i = 0; i < count; i++) { + grpc_metadata *md = &metadata[i]; + grpc_metadata *next_md = (i == count - 1) ? NULL : &metadata[i + 1]; + grpc_metadata *prev_md = (i == 0) ? NULL : &metadata[i - 1]; + grpc_linked_mdelem *l = (grpc_linked_mdelem *)&md->internal_data; + GPR_ASSERT(sizeof(grpc_linked_mdelem) == sizeof(md->internal_data)); + l->md = grpc_mdelem_from_string_and_buffer(call->metadata_context, md->key, + (const gpr_uint8 *)md->value, + md->value_length, 1); + if (!grpc_mdstr_is_legal_header(l->md->key)) { + gpr_log(GPR_ERROR, "attempt to send invalid metadata key: %s", + grpc_mdstr_as_c_string(l->md->key)); + return 0; + } else if (!grpc_mdstr_is_bin_suffixed(l->md->key) && + !grpc_mdstr_is_legal_nonbin_header(l->md->value)) { + gpr_log(GPR_ERROR, "attempt to send invalid metadata value"); + return 0; + } + l->next = next_md ? (grpc_linked_mdelem *)&next_md->internal_data : NULL; + l->prev = prev_md ? (grpc_linked_mdelem *)&prev_md->internal_data : NULL; + } + return 1; +} + +static grpc_mdelem_list chain_metadata_from_app(grpc_call *call, size_t count, + grpc_metadata *metadata) { + grpc_mdelem_list out; + if (count == 0) { + out.head = out.tail = NULL; + return out; + } + out.head = (grpc_linked_mdelem *)&(metadata[0].internal_data); + out.tail = (grpc_linked_mdelem *)&(metadata[count - 1].internal_data); + return out; +} + +/* Copy the contents of a byte buffer into stream ops */ +static void copy_byte_buffer_to_stream_ops(grpc_byte_buffer *byte_buffer, + grpc_stream_op_buffer *sopb) { + size_t i; + + switch (byte_buffer->type) { + case GRPC_BB_RAW: + for (i = 0; i < byte_buffer->data.raw.slice_buffer.count; i++) { + gpr_slice slice = byte_buffer->data.raw.slice_buffer.slices[i]; + gpr_slice_ref(slice); + grpc_sopb_add_slice(sopb, slice); + } + break; + } +} + +static int fill_send_ops(grpc_call *call, grpc_transport_stream_op *op) { + grpc_ioreq_data data; + gpr_uint32 flags; + grpc_metadata_batch mdb; + size_t i; + GPR_ASSERT(op->send_ops == NULL); + + switch (call->write_state) { + case WRITE_STATE_INITIAL: + if (!is_op_live(call, GRPC_IOREQ_SEND_INITIAL_METADATA)) { + break; + } + data = call->request_data[GRPC_IOREQ_SEND_INITIAL_METADATA]; + mdb.list = chain_metadata_from_app(call, data.send_metadata.count, + data.send_metadata.metadata); + mdb.garbage.head = mdb.garbage.tail = NULL; + mdb.deadline = call->send_deadline; + for (i = 0; i < call->send_initial_metadata_count; i++) { + grpc_metadata_batch_link_head(&mdb, &call->send_initial_metadata[i]); + } + grpc_sopb_add_metadata(&call->send_ops, mdb); + op->send_ops = &call->send_ops; + call->last_send_contains |= 1 << GRPC_IOREQ_SEND_INITIAL_METADATA; + call->send_initial_metadata_count = 0; + /* fall through intended */ + case WRITE_STATE_STARTED: + if (is_op_live(call, GRPC_IOREQ_SEND_MESSAGE)) { + data = call->request_data[GRPC_IOREQ_SEND_MESSAGE]; + flags = call->request_flags[GRPC_IOREQ_SEND_MESSAGE]; + grpc_sopb_add_begin_message( + &call->send_ops, grpc_byte_buffer_length(data.send_message), flags); + copy_byte_buffer_to_stream_ops(data.send_message, &call->send_ops); + op->send_ops = &call->send_ops; + call->last_send_contains |= 1 << GRPC_IOREQ_SEND_MESSAGE; + } + if (is_op_live(call, GRPC_IOREQ_SEND_CLOSE)) { + op->is_last_send = 1; + op->send_ops = &call->send_ops; + call->last_send_contains |= 1 << GRPC_IOREQ_SEND_CLOSE; + if (!call->is_client) { + /* send trailing metadata */ + data = call->request_data[GRPC_IOREQ_SEND_TRAILING_METADATA]; + mdb.list = chain_metadata_from_app(call, data.send_metadata.count, + data.send_metadata.metadata); + mdb.garbage.head = mdb.garbage.tail = NULL; + mdb.deadline = gpr_inf_future(GPR_CLOCK_REALTIME); + /* send status */ + /* TODO(ctiller): cache common status values */ + data = call->request_data[GRPC_IOREQ_SEND_STATUS]; + grpc_metadata_batch_add_tail( + &mdb, &call->status_link, + grpc_channel_get_reffed_status_elem(call->channel, + data.send_status.code)); + if (data.send_status.details) { + grpc_metadata_batch_add_tail( + &mdb, &call->details_link, + grpc_mdelem_from_metadata_strings( + call->metadata_context, + GRPC_MDSTR_REF( + grpc_channel_get_message_string(call->channel)), + data.send_status.details)); + call->request_data[GRPC_IOREQ_SEND_STATUS].send_status.details = + NULL; + } + grpc_sopb_add_metadata(&call->send_ops, mdb); + } + } + break; + case WRITE_STATE_WRITE_CLOSED: + break; + } + if (op->send_ops) { + op->on_done_send = &call->on_done_send; + } + return op->send_ops != NULL; +} + +static grpc_call_error start_ioreq_error(grpc_call *call, + gpr_uint32 mutated_ops, + grpc_call_error ret) { + size_t i; + for (i = 0; i < GRPC_IOREQ_OP_COUNT; i++) { + if (mutated_ops & (1u << i)) { + call->request_set[i] = REQSET_EMPTY; + } + } + return ret; +} + +static void finish_read_ops(grpc_call *call) { + int empty; + + if (is_op_live(call, GRPC_IOREQ_RECV_MESSAGE)) { + empty = + (NULL == (*call->request_data[GRPC_IOREQ_RECV_MESSAGE].recv_message = + grpc_bbq_pop(&call->incoming_queue))); + if (!empty) { + finish_live_ioreq_op(call, GRPC_IOREQ_RECV_MESSAGE, 1); + empty = grpc_bbq_empty(&call->incoming_queue); + } + } else { + empty = grpc_bbq_empty(&call->incoming_queue); + } + + switch (call->read_state) { + case READ_STATE_STREAM_CLOSED: + if (empty && !call->have_alarm) { + finish_ioreq_op(call, GRPC_IOREQ_RECV_CLOSE, 1); + } + /* fallthrough */ + case READ_STATE_READ_CLOSED: + if (empty) { + finish_ioreq_op(call, GRPC_IOREQ_RECV_MESSAGE, 1); + } + finish_ioreq_op(call, GRPC_IOREQ_RECV_STATUS, 1); + finish_ioreq_op(call, GRPC_IOREQ_RECV_STATUS_DETAILS, 1); + finish_ioreq_op(call, GRPC_IOREQ_RECV_TRAILING_METADATA, 1); + /* fallthrough */ + case READ_STATE_GOT_INITIAL_METADATA: + finish_ioreq_op(call, GRPC_IOREQ_RECV_INITIAL_METADATA, 1); + /* fallthrough */ + case READ_STATE_INITIAL: + /* do nothing */ + break; + } +} + +static grpc_call_error start_ioreq(grpc_call *call, const grpc_ioreq *reqs, + size_t nreqs, + grpc_ioreq_completion_func completion, + void *user_data) { + size_t i; + gpr_uint32 have_ops = 0; + grpc_ioreq_op op; + reqinfo_master *master; + grpc_ioreq_data data; + gpr_uint8 set; + + if (nreqs == 0) { + return GRPC_CALL_OK; + } + + set = reqs[0].op; + + for (i = 0; i < nreqs; i++) { + op = reqs[i].op; + if (call->request_set[op] < GRPC_IOREQ_OP_COUNT) { + return start_ioreq_error(call, have_ops, + GRPC_CALL_ERROR_TOO_MANY_OPERATIONS); + } else if (call->request_set[op] == REQSET_DONE) { + return start_ioreq_error(call, have_ops, GRPC_CALL_ERROR_ALREADY_INVOKED); + } + data = reqs[i].data; + if (op == GRPC_IOREQ_SEND_INITIAL_METADATA || + op == GRPC_IOREQ_SEND_TRAILING_METADATA) { + if (!prepare_application_metadata(call, data.send_metadata.count, + data.send_metadata.metadata)) { + return start_ioreq_error(call, have_ops, + GRPC_CALL_ERROR_INVALID_METADATA); + } + } + if (op == GRPC_IOREQ_SEND_STATUS) { + set_status_code(call, STATUS_FROM_SERVER_STATUS, + reqs[i].data.send_status.code); + if (reqs[i].data.send_status.details) { + set_status_details(call, STATUS_FROM_SERVER_STATUS, + GRPC_MDSTR_REF(reqs[i].data.send_status.details)); + } + } + have_ops |= 1u << op; + + call->request_data[op] = data; + call->request_flags[op] = reqs[i].flags; + call->request_set[op] = set; + } + + master = &call->masters[set]; + master->success = 1; + master->need_mask = have_ops; + master->complete_mask = 0; + master->on_complete = completion; + master->user_data = user_data; + + finish_read_ops(call); + early_out_write_ops(call); + + return GRPC_CALL_OK; +} + +grpc_call_error grpc_call_start_ioreq_and_call_back( + grpc_call *call, const grpc_ioreq *reqs, size_t nreqs, + grpc_ioreq_completion_func on_complete, void *user_data) { + grpc_call_error err; + lock(call); + err = start_ioreq(call, reqs, nreqs, on_complete, user_data); + unlock(call); + return err; +} + +void grpc_call_destroy(grpc_call *c) { + int cancel; + grpc_call *parent = c->parent; + + if (parent) { + gpr_mu_lock(&parent->mu); + if (c == parent->first_child) { + parent->first_child = c->sibling_next; + if (c == parent->first_child) { + parent->first_child = NULL; + } + c->sibling_prev->sibling_next = c->sibling_next; + c->sibling_next->sibling_prev = c->sibling_prev; + } + gpr_mu_unlock(&parent->mu); + GRPC_CALL_INTERNAL_UNREF(parent, "child", 1); + } + + lock(c); + GPR_ASSERT(!c->destroy_called); + c->destroy_called = 1; + c->cancel_alarm |= c->have_alarm; + cancel = c->read_state != READ_STATE_STREAM_CLOSED; + unlock(c); + if (cancel) grpc_call_cancel(c, NULL); + GRPC_CALL_INTERNAL_UNREF(c, "destroy", 1); +} + +grpc_call_error grpc_call_cancel(grpc_call *call, void *reserved) { + GPR_ASSERT(!reserved); + return grpc_call_cancel_with_status(call, GRPC_STATUS_CANCELLED, "Cancelled", + NULL); +} + +grpc_call_error grpc_call_cancel_with_status(grpc_call *c, + grpc_status_code status, + const char *description, + void *reserved) { + grpc_call_error r; + (void)reserved; + lock(c); + r = cancel_with_status(c, status, description); + unlock(c); + return r; +} + +static grpc_call_error cancel_with_status(grpc_call *c, grpc_status_code status, + const char *description) { + grpc_mdstr *details = + description ? grpc_mdstr_from_string(c->metadata_context, description, 0) + : NULL; + + GPR_ASSERT(status != GRPC_STATUS_OK); + + set_status_code(c, STATUS_FROM_API_OVERRIDE, status); + set_status_details(c, STATUS_FROM_API_OVERRIDE, details); + + c->cancel_with_status = status; + + return GRPC_CALL_OK; +} + +static void finished_loose_op(void *call, int success_ignored) { + GRPC_CALL_INTERNAL_UNREF(call, "loose-op", 0); +} + +typedef struct { + grpc_call *call; + grpc_iomgr_closure closure; +} finished_loose_op_allocated_args; + +static void finished_loose_op_allocated(void *alloc, int success) { + finished_loose_op_allocated_args *args = alloc; + finished_loose_op(args->call, success); + gpr_free(args); +} + +static void execute_op(grpc_call *call, grpc_transport_stream_op *op) { + grpc_call_element *elem; + + GPR_ASSERT(op->on_consumed == NULL); + if (op->cancel_with_status != GRPC_STATUS_OK || op->bind_pollset) { + GRPC_CALL_INTERNAL_REF(call, "loose-op"); + if (op->bind_pollset) { + op->on_consumed = &call->on_done_bind; + } else { + finished_loose_op_allocated_args *args = gpr_malloc(sizeof(*args)); + args->call = call; + grpc_iomgr_closure_init(&args->closure, finished_loose_op_allocated, + args); + op->on_consumed = &args->closure; + } + } + + elem = CALL_ELEM_FROM_CALL(call, 0); + op->context = call->context; + elem->filter->start_transport_stream_op(elem, op); +} + +char *grpc_call_get_peer(grpc_call *call) { + grpc_call_element *elem = CALL_ELEM_FROM_CALL(call, 0); + return elem->filter->get_peer(elem); +} + +grpc_call *grpc_call_from_top_element(grpc_call_element *elem) { + return CALL_FROM_TOP_ELEM(elem); +} + +static void call_alarm(void *arg, int success) { + grpc_call *call = arg; + lock(call); + call->have_alarm = 0; + if (success) { + cancel_with_status(call, GRPC_STATUS_DEADLINE_EXCEEDED, + "Deadline Exceeded"); + } + finish_read_ops(call); + unlock(call); + GRPC_CALL_INTERNAL_UNREF(call, "alarm", 1); +} + +static void set_deadline_alarm(grpc_call *call, gpr_timespec deadline) { + if (call->have_alarm) { + gpr_log(GPR_ERROR, "Attempt to set deadline alarm twice"); + assert(0); + return; + } + GRPC_CALL_INTERNAL_REF(call, "alarm"); + call->have_alarm = 1; + call->send_deadline = gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC); + grpc_alarm_init(&call->alarm, call->send_deadline, call_alarm, call, + gpr_now(GPR_CLOCK_MONOTONIC)); +} + +/* we offset status by a small amount when storing it into transport metadata + as metadata cannot store a 0 value (which is used as OK for grpc_status_codes + */ +#define STATUS_OFFSET 1 +static void destroy_status(void *ignored) {} + +static gpr_uint32 decode_status(grpc_mdelem *md) { + gpr_uint32 status; + void *user_data = grpc_mdelem_get_user_data(md, destroy_status); + if (user_data) { + status = ((gpr_uint32)(gpr_intptr)user_data) - STATUS_OFFSET; + } else { + if (!gpr_parse_bytes_to_uint32(grpc_mdstr_as_c_string(md->value), + GPR_SLICE_LENGTH(md->value->slice), + &status)) { + status = GRPC_STATUS_UNKNOWN; /* could not parse status code */ + } + grpc_mdelem_set_user_data(md, destroy_status, + (void *)(gpr_intptr)(status + STATUS_OFFSET)); + } + return status; +} + +/* just as for status above, we need to offset: metadata userdata can't hold a + * zero (null), which in this case is used to signal no compression */ +#define COMPRESS_OFFSET 1 +static void destroy_compression(void *ignored) {} + +static gpr_uint32 decode_compression(grpc_mdelem *md) { + grpc_compression_algorithm algorithm; + void *user_data = grpc_mdelem_get_user_data(md, destroy_compression); + if (user_data) { + algorithm = + ((grpc_compression_algorithm)(gpr_intptr)user_data) - COMPRESS_OFFSET; + } else { + const char *md_c_str = grpc_mdstr_as_c_string(md->value); + if (!grpc_compression_algorithm_parse(md_c_str, strlen(md_c_str), + &algorithm)) { + gpr_log(GPR_ERROR, "Invalid compression algorithm: '%s'", md_c_str); + assert(0); + } + grpc_mdelem_set_user_data( + md, destroy_compression, + (void *)(gpr_intptr)(algorithm + COMPRESS_OFFSET)); + } + return algorithm; +} + +static void recv_metadata(grpc_call *call, grpc_metadata_batch *md) { + grpc_linked_mdelem *l; + grpc_metadata_array *dest; + grpc_metadata *mdusr; + int is_trailing; + grpc_mdctx *mdctx = call->metadata_context; + + is_trailing = call->read_state >= READ_STATE_GOT_INITIAL_METADATA; + for (l = md->list.head; l != NULL; l = l->next) { + grpc_mdelem *md = l->md; + grpc_mdstr *key = md->key; + if (key == grpc_channel_get_status_string(call->channel)) { + set_status_code(call, STATUS_FROM_WIRE, decode_status(md)); + } else if (key == grpc_channel_get_message_string(call->channel)) { + set_status_details(call, STATUS_FROM_WIRE, GRPC_MDSTR_REF(md->value)); + } else if (key == + grpc_channel_get_compression_algorithm_string(call->channel)) { + set_compression_algorithm(call, decode_compression(md)); + } else if (key == grpc_channel_get_encodings_accepted_by_peer_string( + call->channel)) { + set_encodings_accepted_by_peer(call, md->value->slice); + } else { + dest = &call->buffered_metadata[is_trailing]; + if (dest->count == dest->capacity) { + dest->capacity = GPR_MAX(dest->capacity + 8, dest->capacity * 2); + dest->metadata = + gpr_realloc(dest->metadata, sizeof(grpc_metadata) * dest->capacity); + } + mdusr = &dest->metadata[dest->count++]; + mdusr->key = grpc_mdstr_as_c_string(md->key); + mdusr->value = grpc_mdstr_as_c_string(md->value); + mdusr->value_length = GPR_SLICE_LENGTH(md->value->slice); + if (call->owned_metadata_count == call->owned_metadata_capacity) { + call->owned_metadata_capacity = + GPR_MAX(call->owned_metadata_capacity + 8, + call->owned_metadata_capacity * 2); + call->owned_metadata = + gpr_realloc(call->owned_metadata, + sizeof(grpc_mdelem *) * call->owned_metadata_capacity); + } + call->owned_metadata[call->owned_metadata_count++] = md; + l->md = 0; + } + } + if (gpr_time_cmp(md->deadline, gpr_inf_future(md->deadline.clock_type)) != + 0 && + !call->is_client) { + set_deadline_alarm(call, md->deadline); + } + if (!is_trailing) { + call->read_state = READ_STATE_GOT_INITIAL_METADATA; + } + + grpc_mdctx_lock(mdctx); + for (l = md->list.head; l; l = l->next) { + if (l->md) GRPC_MDCTX_LOCKED_MDELEM_UNREF(mdctx, l->md); + } + for (l = md->garbage.head; l; l = l->next) { + GRPC_MDCTX_LOCKED_MDELEM_UNREF(mdctx, l->md); + } + grpc_mdctx_unlock(mdctx); +} + +grpc_call_stack *grpc_call_get_call_stack(grpc_call *call) { + return CALL_STACK_FROM_CALL(call); +} + +/* + * BATCH API IMPLEMENTATION + */ + +static void set_status_value_directly(grpc_status_code status, void *dest) { + *(grpc_status_code *)dest = status; +} + +static void set_cancelled_value(grpc_status_code status, void *dest) { + *(grpc_status_code *)dest = (status != GRPC_STATUS_OK); +} + +static void finish_batch(grpc_call *call, int success, void *tag) { + grpc_cq_end_op(call->cq, tag, success, done_completion, call, + allocate_completion(call)); +} + +static void finish_batch_with_close(grpc_call *call, int success, void *tag) { + grpc_cq_end_op(call->cq, tag, 1, done_completion, call, + allocate_completion(call)); +} + +static int are_write_flags_valid(gpr_uint32 flags) { + /* check that only bits in GRPC_WRITE_(INTERNAL?)_USED_MASK are set */ + const gpr_uint32 allowed_write_positions = + (GRPC_WRITE_USED_MASK | GRPC_WRITE_INTERNAL_USED_MASK); + const gpr_uint32 invalid_positions = ~allowed_write_positions; + return !(flags & invalid_positions); +} + +grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops, + size_t nops, void *tag, void *reserved) { + grpc_ioreq reqs[GRPC_IOREQ_OP_COUNT]; + size_t in; + size_t out; + const grpc_op *op; + grpc_ioreq *req; + void (*finish_func)(grpc_call *, int, void *) = finish_batch; + + if (reserved != NULL) return GRPC_CALL_ERROR; + + GRPC_CALL_LOG_BATCH(GPR_INFO, call, ops, nops, tag); + + if (nops == 0) { + grpc_cq_begin_op(call->cq); + GRPC_CALL_INTERNAL_REF(call, "completion"); + grpc_cq_end_op(call->cq, tag, 1, done_completion, call, + allocate_completion(call)); + return GRPC_CALL_OK; + } + + /* rewrite batch ops into ioreq ops */ + for (in = 0, out = 0; in < nops; in++) { + op = &ops[in]; + if (op->reserved != NULL) return GRPC_CALL_ERROR; + switch (op->op) { + case GRPC_OP_SEND_INITIAL_METADATA: + /* Flag validation: currently allow no flags */ + if (op->flags != 0) return GRPC_CALL_ERROR_INVALID_FLAGS; + req = &reqs[out++]; + if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG; + req->op = GRPC_IOREQ_SEND_INITIAL_METADATA; + req->data.send_metadata.count = op->data.send_initial_metadata.count; + req->data.send_metadata.metadata = + op->data.send_initial_metadata.metadata; + req->flags = op->flags; + break; + case GRPC_OP_SEND_MESSAGE: + if (!are_write_flags_valid(op->flags)) { + return GRPC_CALL_ERROR_INVALID_FLAGS; + } + if (op->data.send_message == NULL) { + return GRPC_CALL_ERROR_INVALID_MESSAGE; + } + req = &reqs[out++]; + if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG; + req->op = GRPC_IOREQ_SEND_MESSAGE; + req->data.send_message = op->data.send_message; + req->flags = op->flags; + break; + case GRPC_OP_SEND_CLOSE_FROM_CLIENT: + /* Flag validation: currently allow no flags */ + if (op->flags != 0) return GRPC_CALL_ERROR_INVALID_FLAGS; + if (!call->is_client) { + return GRPC_CALL_ERROR_NOT_ON_SERVER; + } + req = &reqs[out++]; + if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG; + req->op = GRPC_IOREQ_SEND_CLOSE; + req->flags = op->flags; + break; + case GRPC_OP_SEND_STATUS_FROM_SERVER: + /* Flag validation: currently allow no flags */ + if (op->flags != 0) return GRPC_CALL_ERROR_INVALID_FLAGS; + if (call->is_client) { + return GRPC_CALL_ERROR_NOT_ON_CLIENT; + } + req = &reqs[out++]; + if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG; + req->op = GRPC_IOREQ_SEND_TRAILING_METADATA; + req->flags = op->flags; + req->data.send_metadata.count = + op->data.send_status_from_server.trailing_metadata_count; + req->data.send_metadata.metadata = + op->data.send_status_from_server.trailing_metadata; + req = &reqs[out++]; + if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG; + req->op = GRPC_IOREQ_SEND_STATUS; + req->data.send_status.code = op->data.send_status_from_server.status; + req->data.send_status.details = + op->data.send_status_from_server.status_details != NULL + ? grpc_mdstr_from_string( + call->metadata_context, + op->data.send_status_from_server.status_details, 0) + : NULL; + req = &reqs[out++]; + if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG; + req->op = GRPC_IOREQ_SEND_CLOSE; + break; + case GRPC_OP_RECV_INITIAL_METADATA: + /* Flag validation: currently allow no flags */ + if (op->flags != 0) return GRPC_CALL_ERROR_INVALID_FLAGS; + if (!call->is_client) { + return GRPC_CALL_ERROR_NOT_ON_SERVER; + } + req = &reqs[out++]; + if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG; + req->op = GRPC_IOREQ_RECV_INITIAL_METADATA; + req->data.recv_metadata = op->data.recv_initial_metadata; + req->data.recv_metadata->count = 0; + req->flags = op->flags; + break; + case GRPC_OP_RECV_MESSAGE: + /* Flag validation: currently allow no flags */ + if (op->flags != 0) return GRPC_CALL_ERROR_INVALID_FLAGS; + req = &reqs[out++]; + if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG; + req->op = GRPC_IOREQ_RECV_MESSAGE; + req->data.recv_message = op->data.recv_message; + req->flags = op->flags; + break; + case GRPC_OP_RECV_STATUS_ON_CLIENT: + /* Flag validation: currently allow no flags */ + if (op->flags != 0) return GRPC_CALL_ERROR_INVALID_FLAGS; + if (!call->is_client) { + return GRPC_CALL_ERROR_NOT_ON_SERVER; + } + req = &reqs[out++]; + if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG; + req->op = GRPC_IOREQ_RECV_STATUS; + req->flags = op->flags; + req->data.recv_status.set_value = set_status_value_directly; + req->data.recv_status.user_data = op->data.recv_status_on_client.status; + req = &reqs[out++]; + if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG; + req->op = GRPC_IOREQ_RECV_STATUS_DETAILS; + req->data.recv_status_details.details = + op->data.recv_status_on_client.status_details; + req->data.recv_status_details.details_capacity = + op->data.recv_status_on_client.status_details_capacity; + req = &reqs[out++]; + if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG; + req->op = GRPC_IOREQ_RECV_TRAILING_METADATA; + req->data.recv_metadata = + op->data.recv_status_on_client.trailing_metadata; + req->data.recv_metadata->count = 0; + req = &reqs[out++]; + if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG; + req->op = GRPC_IOREQ_RECV_CLOSE; + finish_func = finish_batch_with_close; + break; + case GRPC_OP_RECV_CLOSE_ON_SERVER: + /* Flag validation: currently allow no flags */ + if (op->flags != 0) return GRPC_CALL_ERROR_INVALID_FLAGS; + req = &reqs[out++]; + if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG; + req->op = GRPC_IOREQ_RECV_STATUS; + req->flags = op->flags; + req->data.recv_status.set_value = set_cancelled_value; + req->data.recv_status.user_data = + op->data.recv_close_on_server.cancelled; + req = &reqs[out++]; + if (out > GRPC_IOREQ_OP_COUNT) return GRPC_CALL_ERROR_BATCH_TOO_BIG; + req->op = GRPC_IOREQ_RECV_CLOSE; + finish_func = finish_batch_with_close; + break; + } + } + + GRPC_CALL_INTERNAL_REF(call, "completion"); + grpc_cq_begin_op(call->cq); + + return grpc_call_start_ioreq_and_call_back(call, reqs, out, finish_func, tag); +} + +void grpc_call_context_set(grpc_call *call, grpc_context_index elem, + void *value, void (*destroy)(void *value)) { + if (call->context[elem].destroy) { + call->context[elem].destroy(call->context[elem].value); + } + call->context[elem].value = value; + call->context[elem].destroy = destroy; +} + +void *grpc_call_context_get(grpc_call *call, grpc_context_index elem) { + return call->context[elem].value; +} + +gpr_uint8 grpc_call_is_client(grpc_call *call) { return call->is_client; } diff --git a/src/core/surface/call.h b/src/core/surface/call.h new file mode 100644 index 00000000..00638e43 --- /dev/null +++ b/src/core/surface/call.h @@ -0,0 +1,184 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_SURFACE_CALL_H +#define GRPC_INTERNAL_CORE_SURFACE_CALL_H + +#include "src/core/channel/channel_stack.h" +#include "src/core/channel/context.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Primitive operation types - grpc_op's get rewritten into these */ +typedef enum { + GRPC_IOREQ_RECV_INITIAL_METADATA, + GRPC_IOREQ_RECV_MESSAGE, + GRPC_IOREQ_RECV_TRAILING_METADATA, + GRPC_IOREQ_RECV_STATUS, + GRPC_IOREQ_RECV_STATUS_DETAILS, + GRPC_IOREQ_RECV_CLOSE, + GRPC_IOREQ_SEND_INITIAL_METADATA, + GRPC_IOREQ_SEND_MESSAGE, + GRPC_IOREQ_SEND_TRAILING_METADATA, + GRPC_IOREQ_SEND_STATUS, + GRPC_IOREQ_SEND_CLOSE, + GRPC_IOREQ_OP_COUNT +} grpc_ioreq_op; + +typedef union { + grpc_metadata_array *recv_metadata; + grpc_byte_buffer **recv_message; + struct { + void (*set_value)(grpc_status_code status, void *user_data); + void *user_data; + } recv_status; + struct { + char **details; + size_t *details_capacity; + } recv_status_details; + struct { + size_t count; + grpc_metadata *metadata; + } send_metadata; + grpc_byte_buffer *send_message; + struct { + grpc_status_code code; + grpc_mdstr *details; + } send_status; +} grpc_ioreq_data; + +typedef struct { + grpc_ioreq_op op; + gpr_uint32 flags; /**< A copy of the write flags from grpc_op */ + grpc_ioreq_data data; +} grpc_ioreq; + +typedef void (*grpc_ioreq_completion_func)(grpc_call *call, int success, + void *user_data); + +grpc_call *grpc_call_create(grpc_channel *channel, grpc_call *parent_call, + gpr_uint32 propagation_mask, + grpc_completion_queue *cq, + const void *server_transport_data, + grpc_mdelem **add_initial_metadata, + size_t add_initial_metadata_count, + gpr_timespec send_deadline); + +void grpc_call_set_completion_queue(grpc_call *call, grpc_completion_queue *cq); +grpc_completion_queue *grpc_call_get_completion_queue(grpc_call *call); + +#ifdef GRPC_CALL_REF_COUNT_DEBUG +void grpc_call_internal_ref(grpc_call *call, const char *reason); +void grpc_call_internal_unref(grpc_call *call, const char *reason, + int allow_immediate_deletion); +#define GRPC_CALL_INTERNAL_REF(call, reason) \ + grpc_call_internal_ref(call, reason) +#define GRPC_CALL_INTERNAL_UNREF(call, reason, allow_immediate_deletion) \ + grpc_call_internal_unref(call, reason, allow_immediate_deletion) +#else +void grpc_call_internal_ref(grpc_call *call); +void grpc_call_internal_unref(grpc_call *call, int allow_immediate_deletion); +#define GRPC_CALL_INTERNAL_REF(call, reason) grpc_call_internal_ref(call) +#define GRPC_CALL_INTERNAL_UNREF(call, reason, allow_immediate_deletion) \ + grpc_call_internal_unref(call, allow_immediate_deletion) +#endif + +grpc_call_error grpc_call_start_ioreq_and_call_back( + grpc_call *call, const grpc_ioreq *reqs, size_t nreqs, + grpc_ioreq_completion_func on_complete, void *user_data); + +grpc_call_stack *grpc_call_get_call_stack(grpc_call *call); + +/* Given the top call_element, get the call object. */ +grpc_call *grpc_call_from_top_element(grpc_call_element *surface_element); + +extern int grpc_trace_batch; + +void grpc_call_log_batch(char *file, int line, gpr_log_severity severity, + grpc_call *call, const grpc_op *ops, size_t nops, + void *tag); + +void grpc_server_log_request_call(char *file, int line, + gpr_log_severity severity, + grpc_server *server, grpc_call **call, + grpc_call_details *details, + grpc_metadata_array *initial_metadata, + grpc_completion_queue *cq_bound_to_call, + grpc_completion_queue *cq_for_notification, + void *tag); + +void grpc_server_log_shutdown(char *file, int line, gpr_log_severity severity, + grpc_server *server, grpc_completion_queue *cq, + void *tag); + +/* Set a context pointer. + No thread safety guarantees are made wrt this value. */ +void grpc_call_context_set(grpc_call *call, grpc_context_index elem, + void *value, void (*destroy)(void *value)); +/* Get a context pointer. */ +void *grpc_call_context_get(grpc_call *call, grpc_context_index elem); + +#define GRPC_CALL_LOG_BATCH(sev, call, ops, nops, tag) \ + if (grpc_trace_batch) grpc_call_log_batch(sev, call, ops, nops, tag) + +#define GRPC_SERVER_LOG_REQUEST_CALL(sev, server, call, details, \ + initial_metadata, cq_bound_to_call, \ + cq_for_notifications, tag) \ + if (grpc_trace_batch) \ + grpc_server_log_request_call(sev, server, call, details, initial_metadata, \ + cq_bound_to_call, cq_for_notifications, tag) + +#define GRPC_SERVER_LOG_SHUTDOWN(sev, server, cq, tag) \ + if (grpc_trace_batch) grpc_server_log_shutdown(sev, server, cq, tag) + +gpr_uint8 grpc_call_is_client(grpc_call *call); + +grpc_compression_algorithm grpc_call_get_compression_algorithm( + const grpc_call *call); + +gpr_uint32 grpc_call_get_message_flags(const grpc_call *call); + +/** Returns a bitset for the encodings (compression algorithms) supported by \a + * call's peer. + * + * To be indexed by grpc_compression_algorithm enum values. */ +gpr_uint32 grpc_call_get_encodings_accepted_by_peer(grpc_call *call); + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_INTERNAL_CORE_SURFACE_CALL_H */ diff --git a/src/core/surface/call_details.c b/src/core/surface/call_details.c new file mode 100644 index 00000000..67862d7a --- /dev/null +++ b/src/core/surface/call_details.c @@ -0,0 +1,46 @@ +/* + * + * Copyright 2015, 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 +#include + +#include + +void grpc_call_details_init(grpc_call_details *cd) { + memset(cd, 0, sizeof(*cd)); +} + +void grpc_call_details_destroy(grpc_call_details *cd) { + gpr_free(cd->method); + gpr_free(cd->host); +} diff --git a/src/core/surface/call_log_batch.c b/src/core/surface/call_log_batch.c new file mode 100644 index 00000000..5a3ef1e5 --- /dev/null +++ b/src/core/surface/call_log_batch.c @@ -0,0 +1,147 @@ +/* + * + * Copyright 2015, 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 "src/core/surface/call.h" + +#include "src/core/support/string.h" +#include +#include + +int grpc_trace_batch = 0; + +static void add_metadata(gpr_strvec *b, const grpc_metadata *md, size_t count) { + size_t i; + for (i = 0; i < count; i++) { + gpr_strvec_add(b, gpr_strdup("\nkey=")); + gpr_strvec_add(b, gpr_strdup(md[i].key)); + + gpr_strvec_add(b, gpr_strdup(" value=")); + gpr_strvec_add(b, gpr_dump(md[i].value, md[i].value_length, + GPR_DUMP_HEX | GPR_DUMP_ASCII)); + } +} + +char *grpc_op_string(const grpc_op *op) { + char *tmp; + char *out; + + gpr_strvec b; + gpr_strvec_init(&b); + + switch (op->op) { + case GRPC_OP_SEND_INITIAL_METADATA: + gpr_strvec_add(&b, gpr_strdup("SEND_INITIAL_METADATA")); + add_metadata(&b, op->data.send_initial_metadata.metadata, + op->data.send_initial_metadata.count); + break; + case GRPC_OP_SEND_MESSAGE: + gpr_asprintf(&tmp, "SEND_MESSAGE ptr=%p", op->data.send_message); + gpr_strvec_add(&b, tmp); + break; + case GRPC_OP_SEND_CLOSE_FROM_CLIENT: + gpr_strvec_add(&b, gpr_strdup("SEND_CLOSE_FROM_CLIENT")); + break; + case GRPC_OP_SEND_STATUS_FROM_SERVER: + gpr_asprintf(&tmp, "SEND_STATUS_FROM_SERVER status=%d details=%s", + op->data.send_status_from_server.status, + op->data.send_status_from_server.status_details); + gpr_strvec_add(&b, tmp); + add_metadata(&b, op->data.send_status_from_server.trailing_metadata, + op->data.send_status_from_server.trailing_metadata_count); + break; + case GRPC_OP_RECV_INITIAL_METADATA: + gpr_asprintf(&tmp, "RECV_INITIAL_METADATA ptr=%p", + op->data.recv_initial_metadata); + gpr_strvec_add(&b, tmp); + break; + case GRPC_OP_RECV_MESSAGE: + gpr_asprintf(&tmp, "RECV_MESSAGE ptr=%p", op->data.recv_message); + gpr_strvec_add(&b, tmp); + break; + case GRPC_OP_RECV_STATUS_ON_CLIENT: + gpr_asprintf(&tmp, + "RECV_STATUS_ON_CLIENT metadata=%p status=%p details=%p", + op->data.recv_status_on_client.trailing_metadata, + op->data.recv_status_on_client.status, + op->data.recv_status_on_client.status_details); + gpr_strvec_add(&b, tmp); + break; + case GRPC_OP_RECV_CLOSE_ON_SERVER: + gpr_asprintf(&tmp, "RECV_CLOSE_ON_SERVER cancelled=%p", + op->data.recv_close_on_server.cancelled); + gpr_strvec_add(&b, tmp); + } + out = gpr_strvec_flatten(&b, NULL); + gpr_strvec_destroy(&b); + + return out; +} + +void grpc_call_log_batch(char *file, int line, gpr_log_severity severity, + grpc_call *call, const grpc_op *ops, size_t nops, + void *tag) { + char *tmp; + size_t i; + gpr_log(file, line, severity, + "grpc_call_start_batch(call=%p, ops=%p, nops=%d, tag=%p)", call, ops, + nops, tag); + for (i = 0; i < nops; i++) { + tmp = grpc_op_string(&ops[i]); + gpr_log(file, line, severity, "ops[%d]: %s", i, tmp); + gpr_free(tmp); + } +} + +void grpc_server_log_request_call(char *file, int line, + gpr_log_severity severity, + grpc_server *server, grpc_call **call, + grpc_call_details *details, + grpc_metadata_array *initial_metadata, + grpc_completion_queue *cq_bound_to_call, + grpc_completion_queue *cq_for_notification, + void *tag) { + gpr_log(file, line, severity, + "grpc_server_request_call(server=%p, call=%p, details=%p, " + "initial_metadata=%p, cq_bound_to_call=%p, cq_for_notification=%p, " + "tag=%p)", + server, call, details, initial_metadata, cq_bound_to_call, + cq_for_notification, tag); +} + +void grpc_server_log_shutdown(char *file, int line, gpr_log_severity severity, + grpc_server *server, grpc_completion_queue *cq, + void *tag) { + gpr_log(file, line, severity, + "grpc_server_shutdown_and_notify(server=%p, cq=%p, tag=%p)", server, + cq, tag); +} diff --git a/src/core/surface/channel.c b/src/core/surface/channel.c new file mode 100644 index 00000000..586402e2 --- /dev/null +++ b/src/core/surface/channel.c @@ -0,0 +1,369 @@ +/* + * + * Copyright 2015, 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 "src/core/surface/channel.h" + +#include +#include + +#include +#include +#include + +#include "src/core/client_config/resolver_registry.h" +#include "src/core/iomgr/iomgr.h" +#include "src/core/support/string.h" +#include "src/core/surface/call.h" +#include "src/core/surface/init.h" + +/** Cache grpc-status: X mdelems for X = 0..NUM_CACHED_STATUS_ELEMS. + * Avoids needing to take a metadata context lock for sending status + * if the status code is <= NUM_CACHED_STATUS_ELEMS. + * Sized to allow the most commonly used codes to fit in + * (OK, Cancelled, Unknown). */ +#define NUM_CACHED_STATUS_ELEMS 3 + +typedef struct registered_call { + grpc_mdelem *path; + grpc_mdelem *authority; + struct registered_call *next; +} registered_call; + +struct grpc_channel { + int is_client; + gpr_refcount refs; + gpr_uint32 max_message_length; + grpc_mdctx *metadata_context; + /** mdstr for the grpc-status key */ + grpc_mdstr *grpc_status_string; + grpc_mdstr *grpc_compression_algorithm_string; + grpc_mdstr *grpc_encodings_accepted_by_peer_string; + grpc_mdstr *grpc_message_string; + grpc_mdstr *path_string; + grpc_mdstr *authority_string; + grpc_mdelem *default_authority; + /** mdelem for grpc-status: 0 thru grpc-status: 2 */ + grpc_mdelem *grpc_status_elem[NUM_CACHED_STATUS_ELEMS]; + + gpr_mu registered_call_mu; + registered_call *registered_calls; + grpc_iomgr_closure destroy_closure; + char *target; +}; + +#define CHANNEL_STACK_FROM_CHANNEL(c) ((grpc_channel_stack *)((c) + 1)) +#define CHANNEL_FROM_CHANNEL_STACK(channel_stack) \ + (((grpc_channel *)(channel_stack)) - 1) +#define CHANNEL_FROM_TOP_ELEM(top_elem) \ + CHANNEL_FROM_CHANNEL_STACK(grpc_channel_stack_from_top_element(top_elem)) + +/* the protobuf library will (by default) start warning at 100megs */ +#define DEFAULT_MAX_MESSAGE_LENGTH (100 * 1024 * 1024) + +grpc_channel *grpc_channel_create_from_filters( + const char *target, const grpc_channel_filter **filters, size_t num_filters, + const grpc_channel_args *args, grpc_mdctx *mdctx, int is_client) { + size_t i; + size_t size = + sizeof(grpc_channel) + grpc_channel_stack_size(filters, num_filters); + grpc_channel *channel = gpr_malloc(size); + memset(channel, 0, sizeof(*channel)); + channel->target = gpr_strdup(target); + GPR_ASSERT(grpc_is_initialized() && "call grpc_init()"); + channel->is_client = is_client; + /* decremented by grpc_channel_destroy */ + gpr_ref_init(&channel->refs, 1); + channel->metadata_context = mdctx; + channel->grpc_status_string = grpc_mdstr_from_string(mdctx, "grpc-status", 0); + channel->grpc_compression_algorithm_string = + grpc_mdstr_from_string(mdctx, "grpc-encoding", 0); + channel->grpc_encodings_accepted_by_peer_string = + grpc_mdstr_from_string(mdctx, "grpc-accept-encoding", 0); + channel->grpc_message_string = + grpc_mdstr_from_string(mdctx, "grpc-message", 0); + for (i = 0; i < NUM_CACHED_STATUS_ELEMS; i++) { + char buf[GPR_LTOA_MIN_BUFSIZE]; + gpr_ltoa(i, buf); + channel->grpc_status_elem[i] = grpc_mdelem_from_metadata_strings( + mdctx, GRPC_MDSTR_REF(channel->grpc_status_string), + grpc_mdstr_from_string(mdctx, buf, 0)); + } + channel->path_string = grpc_mdstr_from_string(mdctx, ":path", 0); + channel->authority_string = grpc_mdstr_from_string(mdctx, ":authority", 0); + gpr_mu_init(&channel->registered_call_mu); + channel->registered_calls = NULL; + + channel->max_message_length = DEFAULT_MAX_MESSAGE_LENGTH; + if (args) { + for (i = 0; i < args->num_args; i++) { + if (0 == strcmp(args->args[i].key, GRPC_ARG_MAX_MESSAGE_LENGTH)) { + if (args->args[i].type != GRPC_ARG_INTEGER) { + gpr_log(GPR_ERROR, "%s ignored: it must be an integer", + GRPC_ARG_MAX_MESSAGE_LENGTH); + } else if (args->args[i].value.integer < 0) { + gpr_log(GPR_ERROR, "%s ignored: it must be >= 0", + GRPC_ARG_MAX_MESSAGE_LENGTH); + } else { + channel->max_message_length = args->args[i].value.integer; + } + } else if (0 == strcmp(args->args[i].key, GRPC_ARG_DEFAULT_AUTHORITY)) { + if (args->args[i].type != GRPC_ARG_STRING) { + gpr_log(GPR_ERROR, "%s: must be an string", + GRPC_ARG_DEFAULT_AUTHORITY); + } else { + if (channel->default_authority) { + /* setting this takes precedence over anything else */ + GRPC_MDELEM_UNREF(channel->default_authority); + } + channel->default_authority = grpc_mdelem_from_strings( + mdctx, ":authority", args->args[i].value.string); + } + } else if (0 == + strcmp(args->args[i].key, GRPC_SSL_TARGET_NAME_OVERRIDE_ARG)) { + if (args->args[i].type != GRPC_ARG_STRING) { + gpr_log(GPR_ERROR, "%s: must be an string", + GRPC_SSL_TARGET_NAME_OVERRIDE_ARG); + } else { + if (channel->default_authority) { + /* other ways of setting this (notably ssl) take precedence */ + gpr_log(GPR_ERROR, "%s: default host already set some other way", + GRPC_ARG_DEFAULT_AUTHORITY); + } else { + channel->default_authority = grpc_mdelem_from_strings( + mdctx, ":authority", args->args[i].value.string); + } + } + } + } + } + + if (channel->is_client && channel->default_authority == NULL && + target != NULL) { + char *default_authority = grpc_get_default_authority(target); + if (default_authority) { + channel->default_authority = grpc_mdelem_from_strings( + channel->metadata_context, ":authority", default_authority); + } + gpr_free(default_authority); + } + + grpc_channel_stack_init(filters, num_filters, channel, args, + channel->metadata_context, + CHANNEL_STACK_FROM_CHANNEL(channel)); + + return channel; +} + +char *grpc_channel_get_target(grpc_channel *channel) { + return gpr_strdup(channel->target); +} + +static grpc_call *grpc_channel_create_call_internal( + grpc_channel *channel, grpc_call *parent_call, gpr_uint32 propagation_mask, + grpc_completion_queue *cq, grpc_mdelem *path_mdelem, + grpc_mdelem *authority_mdelem, gpr_timespec deadline) { + grpc_mdelem *send_metadata[2]; + int num_metadata = 0; + + GPR_ASSERT(channel->is_client); + + send_metadata[num_metadata++] = path_mdelem; + if (authority_mdelem != NULL) { + send_metadata[num_metadata++] = authority_mdelem; + } else if (channel->default_authority != NULL) { + send_metadata[num_metadata++] = GRPC_MDELEM_REF(channel->default_authority); + } + + return grpc_call_create(channel, parent_call, propagation_mask, cq, NULL, + send_metadata, num_metadata, deadline); +} + +grpc_call *grpc_channel_create_call(grpc_channel *channel, + grpc_call *parent_call, + gpr_uint32 propagation_mask, + grpc_completion_queue *cq, + const char *method, const char *host, + gpr_timespec deadline, void *reserved) { + GPR_ASSERT(!reserved); + return grpc_channel_create_call_internal( + channel, parent_call, propagation_mask, cq, + grpc_mdelem_from_metadata_strings( + channel->metadata_context, GRPC_MDSTR_REF(channel->path_string), + grpc_mdstr_from_string(channel->metadata_context, method, 0)), + host ? grpc_mdelem_from_metadata_strings( + channel->metadata_context, + GRPC_MDSTR_REF(channel->authority_string), + grpc_mdstr_from_string(channel->metadata_context, host, 0)) + : NULL, + deadline); +} + +void *grpc_channel_register_call(grpc_channel *channel, const char *method, + const char *host, void *reserved) { + registered_call *rc = gpr_malloc(sizeof(registered_call)); + GPR_ASSERT(!reserved); + rc->path = grpc_mdelem_from_metadata_strings( + channel->metadata_context, GRPC_MDSTR_REF(channel->path_string), + grpc_mdstr_from_string(channel->metadata_context, method, 0)); + rc->authority = + host ? grpc_mdelem_from_metadata_strings( + channel->metadata_context, + GRPC_MDSTR_REF(channel->authority_string), + grpc_mdstr_from_string(channel->metadata_context, host, 0)) + : NULL; + gpr_mu_lock(&channel->registered_call_mu); + rc->next = channel->registered_calls; + channel->registered_calls = rc; + gpr_mu_unlock(&channel->registered_call_mu); + return rc; +} + +grpc_call *grpc_channel_create_registered_call( + grpc_channel *channel, grpc_call *parent_call, gpr_uint32 propagation_mask, + grpc_completion_queue *completion_queue, void *registered_call_handle, + gpr_timespec deadline, void *reserved) { + registered_call *rc = registered_call_handle; + GPR_ASSERT(!reserved); + return grpc_channel_create_call_internal( + channel, parent_call, propagation_mask, completion_queue, + GRPC_MDELEM_REF(rc->path), + rc->authority ? GRPC_MDELEM_REF(rc->authority) : NULL, deadline); +} + +#ifdef GRPC_CHANNEL_REF_COUNT_DEBUG +void grpc_channel_internal_ref(grpc_channel *c, const char *reason) { + gpr_log(GPR_DEBUG, "CHANNEL: ref %p %d -> %d [%s]", c, c->refs.count, + c->refs.count + 1, reason); +#else +void grpc_channel_internal_ref(grpc_channel *c) { +#endif + gpr_ref(&c->refs); +} + +static void destroy_channel(void *p, int ok) { + grpc_channel *channel = p; + size_t i; + grpc_channel_stack_destroy(CHANNEL_STACK_FROM_CHANNEL(channel)); + for (i = 0; i < NUM_CACHED_STATUS_ELEMS; i++) { + GRPC_MDELEM_UNREF(channel->grpc_status_elem[i]); + } + GRPC_MDSTR_UNREF(channel->grpc_status_string); + GRPC_MDSTR_UNREF(channel->grpc_compression_algorithm_string); + GRPC_MDSTR_UNREF(channel->grpc_encodings_accepted_by_peer_string); + GRPC_MDSTR_UNREF(channel->grpc_message_string); + GRPC_MDSTR_UNREF(channel->path_string); + GRPC_MDSTR_UNREF(channel->authority_string); + while (channel->registered_calls) { + registered_call *rc = channel->registered_calls; + channel->registered_calls = rc->next; + GRPC_MDELEM_UNREF(rc->path); + if (rc->authority) { + GRPC_MDELEM_UNREF(rc->authority); + } + gpr_free(rc); + } + if (channel->default_authority != NULL) { + GRPC_MDELEM_UNREF(channel->default_authority); + } + grpc_mdctx_unref(channel->metadata_context); + gpr_mu_destroy(&channel->registered_call_mu); + gpr_free(channel->target); + gpr_free(channel); +} + +#ifdef GRPC_CHANNEL_REF_COUNT_DEBUG +void grpc_channel_internal_unref(grpc_channel *channel, const char *reason) { + gpr_log(GPR_DEBUG, "CHANNEL: unref %p %d -> %d [%s]", channel, + channel->refs.count, channel->refs.count - 1, reason); +#else +void grpc_channel_internal_unref(grpc_channel *channel) { +#endif + if (gpr_unref(&channel->refs)) { + channel->destroy_closure.cb = destroy_channel; + channel->destroy_closure.cb_arg = channel; + grpc_iomgr_add_callback(&channel->destroy_closure); + } +} + +void grpc_channel_destroy(grpc_channel *channel) { + grpc_transport_op op; + grpc_channel_element *elem; + memset(&op, 0, sizeof(op)); + op.disconnect = 1; + elem = grpc_channel_stack_element(CHANNEL_STACK_FROM_CHANNEL(channel), 0); + elem->filter->start_transport_op(elem, &op); + + GRPC_CHANNEL_INTERNAL_UNREF(channel, "channel"); +} + +grpc_channel_stack *grpc_channel_get_channel_stack(grpc_channel *channel) { + return CHANNEL_STACK_FROM_CHANNEL(channel); +} + +grpc_mdctx *grpc_channel_get_metadata_context(grpc_channel *channel) { + return channel->metadata_context; +} + +grpc_mdstr *grpc_channel_get_status_string(grpc_channel *channel) { + return channel->grpc_status_string; +} + +grpc_mdstr *grpc_channel_get_compression_algorithm_string( + grpc_channel *channel) { + return channel->grpc_compression_algorithm_string; +} + +grpc_mdstr *grpc_channel_get_encodings_accepted_by_peer_string( + grpc_channel *channel) { + return channel->grpc_encodings_accepted_by_peer_string; +} + +grpc_mdelem *grpc_channel_get_reffed_status_elem(grpc_channel *channel, int i) { + if (i >= 0 && i < NUM_CACHED_STATUS_ELEMS) { + return GRPC_MDELEM_REF(channel->grpc_status_elem[i]); + } else { + char tmp[GPR_LTOA_MIN_BUFSIZE]; + gpr_ltoa(i, tmp); + return grpc_mdelem_from_metadata_strings( + channel->metadata_context, GRPC_MDSTR_REF(channel->grpc_status_string), + grpc_mdstr_from_string(channel->metadata_context, tmp, 0)); + } +} + +grpc_mdstr *grpc_channel_get_message_string(grpc_channel *channel) { + return channel->grpc_message_string; +} + +gpr_uint32 grpc_channel_get_max_message_length(grpc_channel *channel) { + return channel->max_message_length; +} diff --git a/src/core/surface/channel.h b/src/core/surface/channel.h new file mode 100644 index 00000000..f271616f --- /dev/null +++ b/src/core/surface/channel.h @@ -0,0 +1,80 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_SURFACE_CHANNEL_H +#define GRPC_INTERNAL_CORE_SURFACE_CHANNEL_H + +#include "src/core/channel/channel_stack.h" +#include "src/core/client_config/subchannel_factory.h" + +grpc_channel *grpc_channel_create_from_filters( + const char *target, const grpc_channel_filter **filters, size_t count, + const grpc_channel_args *args, grpc_mdctx *mdctx, int is_client); + +/** Get a (borrowed) pointer to this channels underlying channel stack */ +grpc_channel_stack *grpc_channel_get_channel_stack(grpc_channel *channel); + +/** Get a (borrowed) pointer to the channel wide metadata context */ +grpc_mdctx *grpc_channel_get_metadata_context(grpc_channel *channel); + +/** Get a grpc_mdelem of grpc-status: X where X is the numeric value of + status_code. + + The returned elem is owned by the caller. */ +grpc_mdelem *grpc_channel_get_reffed_status_elem(grpc_channel *channel, + int status_code); +grpc_mdstr *grpc_channel_get_status_string(grpc_channel *channel); +grpc_mdstr *grpc_channel_get_compression_algorithm_string( + grpc_channel *channel); +grpc_mdstr *grpc_channel_get_encodings_accepted_by_peer_string( + grpc_channel *channel); +grpc_mdstr *grpc_channel_get_message_string(grpc_channel *channel); +gpr_uint32 grpc_channel_get_max_message_length(grpc_channel *channel); + +#ifdef GRPC_CHANNEL_REF_COUNT_DEBUG +void grpc_channel_internal_ref(grpc_channel *channel, const char *reason); +void grpc_channel_internal_unref(grpc_channel *channel, const char *reason); +#define GRPC_CHANNEL_INTERNAL_REF(channel, reason) \ + grpc_channel_internal_ref(channel, reason) +#define GRPC_CHANNEL_INTERNAL_UNREF(channel, reason) \ + grpc_channel_internal_unref(channel, reason) +#else +void grpc_channel_internal_ref(grpc_channel *channel); +void grpc_channel_internal_unref(grpc_channel *channel); +#define GRPC_CHANNEL_INTERNAL_REF(channel, reason) \ + grpc_channel_internal_ref(channel) +#define GRPC_CHANNEL_INTERNAL_UNREF(channel, reason) \ + grpc_channel_internal_unref(channel) +#endif + +#endif /* GRPC_INTERNAL_CORE_SURFACE_CHANNEL_H */ diff --git a/src/core/surface/channel_connectivity.c b/src/core/surface/channel_connectivity.c new file mode 100644 index 00000000..88a7c165 --- /dev/null +++ b/src/core/surface/channel_connectivity.c @@ -0,0 +1,187 @@ +/* + * + * Copyright 2015, 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 "src/core/surface/channel.h" + +#include +#include + +#include "src/core/channel/client_channel.h" +#include "src/core/iomgr/alarm.h" +#include "src/core/surface/completion_queue.h" + +grpc_connectivity_state grpc_channel_check_connectivity_state( + grpc_channel *channel, int try_to_connect) { + /* forward through to the underlying client channel */ + grpc_channel_element *client_channel_elem = + grpc_channel_stack_last_element(grpc_channel_get_channel_stack(channel)); + if (client_channel_elem->filter != &grpc_client_channel_filter) { + gpr_log(GPR_ERROR, + "grpc_channel_check_connectivity_state called on something that is " + "not a client channel, but '%s'", + client_channel_elem->filter->name); + return GRPC_CHANNEL_FATAL_FAILURE; + } + return grpc_client_channel_check_connectivity_state(client_channel_elem, + try_to_connect); +} + +typedef enum { + WAITING, + CALLING_BACK, + CALLING_BACK_AND_FINISHED, + CALLED_BACK +} callback_phase; + +typedef struct { + gpr_mu mu; + callback_phase phase; + int success; + grpc_iomgr_closure on_complete; + grpc_alarm alarm; + grpc_connectivity_state state; + grpc_completion_queue *cq; + grpc_cq_completion completion_storage; + grpc_channel *channel; + void *tag; +} state_watcher; + +static void delete_state_watcher(state_watcher *w) { + grpc_channel_element *client_channel_elem = grpc_channel_stack_last_element( + grpc_channel_get_channel_stack(w->channel)); + grpc_client_channel_del_interested_party(client_channel_elem, + grpc_cq_pollset(w->cq)); + GRPC_CHANNEL_INTERNAL_UNREF(w->channel, "watch_connectivity"); + gpr_mu_destroy(&w->mu); + gpr_free(w); +} + +static void finished_completion(void *pw, grpc_cq_completion *ignored) { + int delete = 0; + state_watcher *w = pw; + gpr_mu_lock(&w->mu); + switch (w->phase) { + case WAITING: + case CALLED_BACK: + gpr_log(GPR_ERROR, "should never reach here"); + abort(); + break; + case CALLING_BACK: + w->phase = CALLED_BACK; + break; + case CALLING_BACK_AND_FINISHED: + delete = 1; + break; + } + gpr_mu_unlock(&w->mu); + + if (delete) { + delete_state_watcher(w); + } +} + +static void partly_done(state_watcher *w, int due_to_completion) { + int delete = 0; + + if (due_to_completion) { + gpr_mu_lock(&w->mu); + w->success = 1; + gpr_mu_unlock(&w->mu); + grpc_alarm_cancel(&w->alarm); + } + + gpr_mu_lock(&w->mu); + switch (w->phase) { + case WAITING: + w->phase = CALLING_BACK; + grpc_cq_end_op(w->cq, w->tag, w->success, finished_completion, w, + &w->completion_storage); + break; + case CALLING_BACK: + w->phase = CALLING_BACK_AND_FINISHED; + break; + case CALLING_BACK_AND_FINISHED: + gpr_log(GPR_ERROR, "should never reach here"); + abort(); + break; + case CALLED_BACK: + delete = 1; + break; + } + gpr_mu_unlock(&w->mu); + + if (delete) { + delete_state_watcher(w); + } +} + +static void watch_complete(void *pw, int success) { partly_done(pw, 1); } + +static void timeout_complete(void *pw, int success) { partly_done(pw, 0); } + +void grpc_channel_watch_connectivity_state( + grpc_channel *channel, grpc_connectivity_state last_observed_state, + gpr_timespec deadline, grpc_completion_queue *cq, void *tag) { + grpc_channel_element *client_channel_elem = + grpc_channel_stack_last_element(grpc_channel_get_channel_stack(channel)); + state_watcher *w = gpr_malloc(sizeof(*w)); + + grpc_cq_begin_op(cq); + + gpr_mu_init(&w->mu); + grpc_iomgr_closure_init(&w->on_complete, watch_complete, w); + w->phase = WAITING; + w->state = last_observed_state; + w->success = 0; + w->cq = cq; + w->tag = tag; + w->channel = channel; + + grpc_alarm_init(&w->alarm, + gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC), + timeout_complete, w, gpr_now(GPR_CLOCK_MONOTONIC)); + + if (client_channel_elem->filter != &grpc_client_channel_filter) { + gpr_log(GPR_ERROR, + "grpc_channel_watch_connectivity_state called on something that is " + "not a client channel, but '%s'", + client_channel_elem->filter->name); + grpc_iomgr_add_delayed_callback(&w->on_complete, 1); + } else { + GRPC_CHANNEL_INTERNAL_REF(channel, "watch_connectivity"); + grpc_client_channel_add_interested_party(client_channel_elem, + grpc_cq_pollset(cq)); + grpc_client_channel_watch_connectivity_state(client_channel_elem, &w->state, + &w->on_complete); + } +} diff --git a/src/core/surface/channel_create.c b/src/core/surface/channel_create.c new file mode 100644 index 00000000..707251da --- /dev/null +++ b/src/core/surface/channel_create.c @@ -0,0 +1,198 @@ +/* + * + * Copyright 2015, 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 + +#include +#include + +#include + +#include "src/core/census/grpc_filter.h" +#include "src/core/channel/channel_args.h" +#include "src/core/channel/client_channel.h" +#include "src/core/channel/compress_filter.h" +#include "src/core/channel/http_client_filter.h" +#include "src/core/client_config/resolver_registry.h" +#include "src/core/iomgr/tcp_client.h" +#include "src/core/surface/channel.h" +#include "src/core/transport/chttp2_transport.h" + +typedef struct { + grpc_connector base; + gpr_refcount refs; + + grpc_iomgr_closure *notify; + grpc_connect_in_args args; + grpc_connect_out_args *result; +} connector; + +static void connector_ref(grpc_connector *con) { + connector *c = (connector *)con; + gpr_ref(&c->refs); +} + +static void connector_unref(grpc_connector *con) { + connector *c = (connector *)con; + if (gpr_unref(&c->refs)) { + gpr_free(c); + } +} + +static void connected(void *arg, grpc_endpoint *tcp) { + connector *c = arg; + grpc_iomgr_closure *notify; + if (tcp != NULL) { + c->result->transport = grpc_create_chttp2_transport( + c->args.channel_args, tcp, c->args.metadata_context, 1); + grpc_chttp2_transport_start_reading(c->result->transport, NULL, 0); + GPR_ASSERT(c->result->transport); + c->result->filters = gpr_malloc(sizeof(grpc_channel_filter *)); + c->result->filters[0] = &grpc_http_client_filter; + c->result->num_filters = 1; + } else { + memset(c->result, 0, sizeof(*c->result)); + } + notify = c->notify; + c->notify = NULL; + grpc_iomgr_add_callback(notify); +} + +static void connector_connect(grpc_connector *con, + const grpc_connect_in_args *args, + grpc_connect_out_args *result, + grpc_iomgr_closure *notify) { + connector *c = (connector *)con; + GPR_ASSERT(c->notify == NULL); + GPR_ASSERT(notify->cb); + c->notify = notify; + c->args = *args; + c->result = result; + grpc_tcp_client_connect(connected, c, args->interested_parties, args->addr, + args->addr_len, args->deadline); +} + +static const grpc_connector_vtable connector_vtable = { + connector_ref, connector_unref, connector_connect}; + +typedef struct { + grpc_subchannel_factory base; + gpr_refcount refs; + grpc_mdctx *mdctx; + grpc_channel_args *merge_args; + grpc_channel *master; +} subchannel_factory; + +static void subchannel_factory_ref(grpc_subchannel_factory *scf) { + subchannel_factory *f = (subchannel_factory *)scf; + gpr_ref(&f->refs); +} + +static void subchannel_factory_unref(grpc_subchannel_factory *scf) { + subchannel_factory *f = (subchannel_factory *)scf; + if (gpr_unref(&f->refs)) { + GRPC_CHANNEL_INTERNAL_UNREF(f->master, "subchannel_factory"); + grpc_channel_args_destroy(f->merge_args); + grpc_mdctx_unref(f->mdctx); + gpr_free(f); + } +} + +static grpc_subchannel *subchannel_factory_create_subchannel( + grpc_subchannel_factory *scf, grpc_subchannel_args *args) { + subchannel_factory *f = (subchannel_factory *)scf; + connector *c = gpr_malloc(sizeof(*c)); + grpc_channel_args *final_args = + grpc_channel_args_merge(args->args, f->merge_args); + grpc_subchannel *s; + memset(c, 0, sizeof(*c)); + c->base.vtable = &connector_vtable; + gpr_ref_init(&c->refs, 1); + args->mdctx = f->mdctx; + args->args = final_args; + args->master = f->master; + s = grpc_subchannel_create(&c->base, args); + grpc_connector_unref(&c->base); + grpc_channel_args_destroy(final_args); + return s; +} + +static const grpc_subchannel_factory_vtable subchannel_factory_vtable = { + subchannel_factory_ref, subchannel_factory_unref, + subchannel_factory_create_subchannel}; + +/* Create a client channel: + Asynchronously: - resolve target + - connect to it (trying alternatives as presented) + - perform handshakes */ +grpc_channel *grpc_insecure_channel_create(const char *target, + const grpc_channel_args *args, + void *reserved) { + grpc_channel *channel = NULL; +#define MAX_FILTERS 3 + const grpc_channel_filter *filters[MAX_FILTERS]; + grpc_resolver *resolver; + subchannel_factory *f; + grpc_mdctx *mdctx = grpc_mdctx_create(); + int n = 0; + GPR_ASSERT(!reserved); + if (grpc_channel_args_is_census_enabled(args)) { + filters[n++] = &grpc_client_census_filter; + } + filters[n++] = &grpc_compress_filter; + filters[n++] = &grpc_client_channel_filter; + GPR_ASSERT(n <= MAX_FILTERS); + + channel = + grpc_channel_create_from_filters(target, filters, n, args, mdctx, 1); + + f = gpr_malloc(sizeof(*f)); + f->base.vtable = &subchannel_factory_vtable; + gpr_ref_init(&f->refs, 1); + grpc_mdctx_ref(mdctx); + f->mdctx = mdctx; + f->merge_args = grpc_channel_args_copy(args); + f->master = channel; + GRPC_CHANNEL_INTERNAL_REF(f->master, "subchannel_factory"); + resolver = grpc_resolver_create(target, &f->base); + if (!resolver) { + return NULL; + } + + grpc_client_channel_set_resolver(grpc_channel_get_channel_stack(channel), + resolver); + GRPC_RESOLVER_UNREF(resolver, "create"); + grpc_subchannel_factory_unref(&f->base); + + return channel; +} diff --git a/src/core/surface/completion_queue.c b/src/core/surface/completion_queue.c new file mode 100644 index 00000000..b58115a9 --- /dev/null +++ b/src/core/surface/completion_queue.c @@ -0,0 +1,342 @@ +/* + * + * Copyright 2015, 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 "src/core/surface/completion_queue.h" + +#include +#include + +#include "src/core/iomgr/pollset.h" +#include "src/core/support/string.h" +#include "src/core/surface/call.h" +#include "src/core/surface/event_string.h" +#include "src/core/surface/surface_trace.h" +#include +#include +#include + +typedef struct { + grpc_pollset_worker *worker; + void *tag; +} plucker; + +/* Completion queue structure */ +struct grpc_completion_queue { + /** completed events */ + grpc_cq_completion completed_head; + grpc_cq_completion *completed_tail; + /** Number of pending events (+1 if we're not shutdown) */ + gpr_refcount pending_events; + /** Once owning_refs drops to zero, we will destroy the cq */ + gpr_refcount owning_refs; + /** the set of low level i/o things that concern this cq */ + grpc_pollset pollset; + /** 0 initially, 1 once we've begun shutting down */ + int shutdown; + int shutdown_called; + int is_server_cq; + int num_pluckers; + plucker pluckers[GRPC_MAX_COMPLETION_QUEUE_PLUCKERS]; +}; + +grpc_completion_queue *grpc_completion_queue_create(void *reserved) { + grpc_completion_queue *cc = gpr_malloc(sizeof(grpc_completion_queue)); + GPR_ASSERT(!reserved); + memset(cc, 0, sizeof(*cc)); + /* Initial ref is dropped by grpc_completion_queue_shutdown */ + gpr_ref_init(&cc->pending_events, 1); + /* One for destroy(), one for pollset_shutdown */ + gpr_ref_init(&cc->owning_refs, 2); + grpc_pollset_init(&cc->pollset); + cc->completed_tail = &cc->completed_head; + cc->completed_head.next = (gpr_uintptr)cc->completed_tail; + return cc; +} + +#ifdef GRPC_CQ_REF_COUNT_DEBUG +void grpc_cq_internal_ref(grpc_completion_queue *cc, const char *reason, + const char *file, int line) { + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "CQ:%p ref %d -> %d %s", cc, + (int)cc->owning_refs.count, (int)cc->owning_refs.count + 1, reason); +#else +void grpc_cq_internal_ref(grpc_completion_queue *cc) { +#endif + gpr_ref(&cc->owning_refs); +} + +static void on_pollset_destroy_done(void *arg) { + grpc_completion_queue *cc = arg; + GRPC_CQ_INTERNAL_UNREF(cc, "pollset_destroy"); +} + +#ifdef GRPC_CQ_REF_COUNT_DEBUG +void grpc_cq_internal_unref(grpc_completion_queue *cc, const char *reason, + const char *file, int line) { + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "CQ:%p unref %d -> %d %s", cc, + (int)cc->owning_refs.count, (int)cc->owning_refs.count - 1, reason); +#else +void grpc_cq_internal_unref(grpc_completion_queue *cc) { +#endif + if (gpr_unref(&cc->owning_refs)) { + GPR_ASSERT(cc->completed_head.next == (gpr_uintptr)&cc->completed_head); + grpc_pollset_destroy(&cc->pollset); + gpr_free(cc); + } +} + +void grpc_cq_begin_op(grpc_completion_queue *cc) { +#ifndef NDEBUG + gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset)); + GPR_ASSERT(!cc->shutdown_called); + gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); +#endif + gpr_ref(&cc->pending_events); +} + +/* Signal the end of an operation - if this is the last waiting-to-be-queued + event, then enter shutdown mode */ +/* Queue a GRPC_OP_COMPLETED operation */ +void grpc_cq_end_op(grpc_completion_queue *cc, void *tag, int success, + void (*done)(void *done_arg, grpc_cq_completion *storage), + void *done_arg, grpc_cq_completion *storage) { + int shutdown; + int i; + grpc_pollset_worker *pluck_worker; + + storage->tag = tag; + storage->done = done; + storage->done_arg = done_arg; + storage->next = + ((gpr_uintptr)&cc->completed_head) | ((gpr_uintptr)(success != 0)); + + gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset)); + shutdown = gpr_unref(&cc->pending_events); + if (!shutdown) { + cc->completed_tail->next = + ((gpr_uintptr)storage) | (1u & (gpr_uintptr)cc->completed_tail->next); + cc->completed_tail = storage; + pluck_worker = NULL; + for (i = 0; i < cc->num_pluckers; i++) { + if (cc->pluckers[i].tag == tag) { + pluck_worker = cc->pluckers[i].worker; + break; + } + } + grpc_pollset_kick(&cc->pollset, pluck_worker); + gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); + } else { + cc->completed_tail->next = + ((gpr_uintptr)storage) | (1u & (gpr_uintptr)cc->completed_tail->next); + cc->completed_tail = storage; + GPR_ASSERT(!cc->shutdown); + GPR_ASSERT(cc->shutdown_called); + cc->shutdown = 1; + gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); + grpc_pollset_shutdown(&cc->pollset, on_pollset_destroy_done, cc); + } +} + +grpc_event grpc_completion_queue_next(grpc_completion_queue *cc, + gpr_timespec deadline, void *reserved) { + grpc_event ret; + grpc_pollset_worker worker; + int first_loop = 1; + gpr_timespec now; + + GPR_ASSERT(!reserved); + + deadline = gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC); + + GRPC_CQ_INTERNAL_REF(cc, "next"); + gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset)); + for (;;) { + if (cc->completed_tail != &cc->completed_head) { + grpc_cq_completion *c = (grpc_cq_completion *)cc->completed_head.next; + cc->completed_head.next = c->next & ~(gpr_uintptr)1; + if (c == cc->completed_tail) { + cc->completed_tail = &cc->completed_head; + } + gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); + ret.type = GRPC_OP_COMPLETE; + ret.success = c->next & 1u; + ret.tag = c->tag; + c->done(c->done_arg, c); + break; + } + if (cc->shutdown) { + gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); + memset(&ret, 0, sizeof(ret)); + ret.type = GRPC_QUEUE_SHUTDOWN; + break; + } + now = gpr_now(GPR_CLOCK_MONOTONIC); + if (!first_loop && gpr_time_cmp(now, deadline) >= 0) { + gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); + memset(&ret, 0, sizeof(ret)); + ret.type = GRPC_QUEUE_TIMEOUT; + break; + } + first_loop = 0; + grpc_pollset_work(&cc->pollset, &worker, now, deadline); + } + GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ret); + GRPC_CQ_INTERNAL_UNREF(cc, "next"); + return ret; +} + +static int add_plucker(grpc_completion_queue *cc, void *tag, + grpc_pollset_worker *worker) { + if (cc->num_pluckers == GRPC_MAX_COMPLETION_QUEUE_PLUCKERS) { + return 0; + } + cc->pluckers[cc->num_pluckers].tag = tag; + cc->pluckers[cc->num_pluckers].worker = worker; + cc->num_pluckers++; + return 1; +} + +static void del_plucker(grpc_completion_queue *cc, void *tag, + grpc_pollset_worker *worker) { + int i; + for (i = 0; i < cc->num_pluckers; i++) { + if (cc->pluckers[i].tag == tag && cc->pluckers[i].worker == worker) { + cc->num_pluckers--; + GPR_SWAP(plucker, cc->pluckers[i], cc->pluckers[cc->num_pluckers]); + return; + } + } + gpr_log(GPR_ERROR, "should never reach here"); + abort(); +} + +grpc_event grpc_completion_queue_pluck(grpc_completion_queue *cc, void *tag, + gpr_timespec deadline, void *reserved) { + grpc_event ret; + grpc_cq_completion *c; + grpc_cq_completion *prev; + grpc_pollset_worker worker; + gpr_timespec now; + int first_loop = 1; + + GPR_ASSERT(!reserved); + + deadline = gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC); + + GRPC_CQ_INTERNAL_REF(cc, "pluck"); + gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset)); + for (;;) { + prev = &cc->completed_head; + while ((c = (grpc_cq_completion *)(prev->next & ~(gpr_uintptr)1)) != + &cc->completed_head) { + if (c->tag == tag) { + prev->next = + (prev->next & (gpr_uintptr)1) | (c->next & ~(gpr_uintptr)1); + if (c == cc->completed_tail) { + cc->completed_tail = prev; + } + gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); + ret.type = GRPC_OP_COMPLETE; + ret.success = c->next & 1u; + ret.tag = c->tag; + c->done(c->done_arg, c); + goto done; + } + prev = c; + } + if (cc->shutdown) { + gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); + memset(&ret, 0, sizeof(ret)); + ret.type = GRPC_QUEUE_SHUTDOWN; + break; + } + if (!add_plucker(cc, tag, &worker)) { + gpr_log(GPR_DEBUG, + "Too many outstanding grpc_completion_queue_pluck calls: maximum " + "is %d", + GRPC_MAX_COMPLETION_QUEUE_PLUCKERS); + gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); + memset(&ret, 0, sizeof(ret)); + /* TODO(ctiller): should we use a different result here */ + ret.type = GRPC_QUEUE_TIMEOUT; + break; + } + now = gpr_now(GPR_CLOCK_MONOTONIC); + if (!first_loop && gpr_time_cmp(now, deadline) >= 0) { + del_plucker(cc, tag, &worker); + gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); + memset(&ret, 0, sizeof(ret)); + ret.type = GRPC_QUEUE_TIMEOUT; + break; + } + first_loop = 0; + grpc_pollset_work(&cc->pollset, &worker, now, deadline); + del_plucker(cc, tag, &worker); + } +done: + GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ret); + GRPC_CQ_INTERNAL_UNREF(cc, "pluck"); + return ret; +} + +/* Shutdown simply drops a ref that we reserved at creation time; if we drop + to zero here, then enter shutdown mode and wake up any waiters */ +void grpc_completion_queue_shutdown(grpc_completion_queue *cc) { + gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset)); + if (cc->shutdown_called) { + gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); + return; + } + cc->shutdown_called = 1; + gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); + + if (gpr_unref(&cc->pending_events)) { + gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset)); + GPR_ASSERT(!cc->shutdown); + cc->shutdown = 1; + gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); + grpc_pollset_shutdown(&cc->pollset, on_pollset_destroy_done, cc); + } +} + +void grpc_completion_queue_destroy(grpc_completion_queue *cc) { + grpc_completion_queue_shutdown(cc); + GRPC_CQ_INTERNAL_UNREF(cc, "destroy"); +} + +grpc_pollset *grpc_cq_pollset(grpc_completion_queue *cc) { + return &cc->pollset; +} + +void grpc_cq_mark_server_cq(grpc_completion_queue *cc) { cc->is_server_cq = 1; } + +int grpc_cq_is_server_cq(grpc_completion_queue *cc) { return cc->is_server_cq; } diff --git a/src/core/surface/completion_queue.h b/src/core/surface/completion_queue.h new file mode 100644 index 00000000..74dc09e3 --- /dev/null +++ b/src/core/surface/completion_queue.h @@ -0,0 +1,83 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_SURFACE_COMPLETION_QUEUE_H +#define GRPC_INTERNAL_CORE_SURFACE_COMPLETION_QUEUE_H + +/* Internal API for completion queues */ + +#include "src/core/iomgr/pollset.h" +#include + +typedef struct grpc_cq_completion { + /** user supplied tag */ + void *tag; + /** done callback - called when this queue element is no longer + needed by the completion queue */ + void (*done)(void *done_arg, struct grpc_cq_completion *c); + void *done_arg; + /** next pointer; low bit is used to indicate success or not */ + gpr_uintptr next; +} grpc_cq_completion; + +#ifdef GRPC_CQ_REF_COUNT_DEBUG +void grpc_cq_internal_ref(grpc_completion_queue *cc, const char *reason, + const char *file, int line); +void grpc_cq_internal_unref(grpc_completion_queue *cc, const char *reason, + const char *file, int line); +#define GRPC_CQ_INTERNAL_REF(cc, reason) \ + grpc_cq_internal_ref(cc, reason, __FILE__, __LINE__) +#define GRPC_CQ_INTERNAL_UNREF(cc, reason) \ + grpc_cq_internal_unref(cc, reason, __FILE__, __LINE__) +#else +void grpc_cq_internal_ref(grpc_completion_queue *cc); +void grpc_cq_internal_unref(grpc_completion_queue *cc); +#define GRPC_CQ_INTERNAL_REF(cc, reason) grpc_cq_internal_ref(cc) +#define GRPC_CQ_INTERNAL_UNREF(cc, reason) grpc_cq_internal_unref(cc) +#endif + +/* Flag that an operation is beginning: the completion channel will not finish + shutdown until a corrensponding grpc_cq_end_* call is made */ +void grpc_cq_begin_op(grpc_completion_queue *cc); + +/* Queue a GRPC_OP_COMPLETED operation */ +void grpc_cq_end_op(grpc_completion_queue *cc, void *tag, int success, + void (*done)(void *done_arg, grpc_cq_completion *storage), + void *done_arg, grpc_cq_completion *storage); + +grpc_pollset *grpc_cq_pollset(grpc_completion_queue *cc); + +void grpc_cq_mark_server_cq(grpc_completion_queue *cc); +int grpc_cq_is_server_cq(grpc_completion_queue *cc); + +#endif /* GRPC_INTERNAL_CORE_SURFACE_COMPLETION_QUEUE_H */ diff --git a/src/core/surface/event_string.c b/src/core/surface/event_string.c new file mode 100644 index 00000000..33cd4a43 --- /dev/null +++ b/src/core/surface/event_string.c @@ -0,0 +1,81 @@ +/* + * + * Copyright 2015, 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 "src/core/surface/event_string.h" + +#include + +#include "src/core/support/string.h" +#include +#include + +static void addhdr(gpr_strvec *buf, grpc_event *ev) { + char *tmp; + gpr_asprintf(&tmp, "tag:%p", ev->tag); + gpr_strvec_add(buf, tmp); +} + +static const char *errstr(int success) { return success ? "OK" : "ERROR"; } + +static void adderr(gpr_strvec *buf, int success) { + char *tmp; + gpr_asprintf(&tmp, " %s", errstr(success)); + gpr_strvec_add(buf, tmp); +} + +char *grpc_event_string(grpc_event *ev) { + char *out; + gpr_strvec buf; + + if (ev == NULL) return gpr_strdup("null"); + + gpr_strvec_init(&buf); + + switch (ev->type) { + case GRPC_QUEUE_TIMEOUT: + gpr_strvec_add(&buf, gpr_strdup("QUEUE_TIMEOUT")); + break; + case GRPC_QUEUE_SHUTDOWN: + gpr_strvec_add(&buf, gpr_strdup("QUEUE_SHUTDOWN")); + break; + case GRPC_OP_COMPLETE: + gpr_strvec_add(&buf, gpr_strdup("OP_COMPLETE: ")); + addhdr(&buf, ev); + adderr(&buf, ev->success); + break; + } + + out = gpr_strvec_flatten(&buf, NULL); + gpr_strvec_destroy(&buf); + return out; +} diff --git a/src/core/surface/event_string.h b/src/core/surface/event_string.h new file mode 100644 index 00000000..07c474e3 --- /dev/null +++ b/src/core/surface/event_string.h @@ -0,0 +1,42 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_SURFACE_EVENT_STRING_H +#define GRPC_INTERNAL_CORE_SURFACE_EVENT_STRING_H + +#include + +/* Returns a string describing an event. Must be later freed with gpr_free() */ +char *grpc_event_string(grpc_event *ev); + +#endif /* GRPC_INTERNAL_CORE_SURFACE_EVENT_STRING_H */ diff --git a/src/core/surface/init.c b/src/core/surface/init.c new file mode 100644 index 00000000..0d48cd42 --- /dev/null +++ b/src/core/surface/init.c @@ -0,0 +1,145 @@ +/* + * + * Copyright 2015, 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 + +#include + +#include +#include +#include +#include +#include "src/core/channel/channel_stack.h" +#include "src/core/client_config/resolver_registry.h" +#include "src/core/client_config/resolvers/dns_resolver.h" +#include "src/core/client_config/resolvers/sockaddr_resolver.h" +#include "src/core/debug/trace.h" +#include "src/core/iomgr/iomgr.h" +#include "src/core/profiling/timers.h" +#include "src/core/surface/call.h" +#include "src/core/surface/init.h" +#include "src/core/surface/surface_trace.h" +#include "src/core/transport/chttp2_transport.h" +#include "src/core/transport/connectivity_state.h" + +#define MAX_PLUGINS 128 + +static gpr_once g_basic_init = GPR_ONCE_INIT; +static gpr_mu g_init_mu; +static int g_initializations; + +static void do_basic_init(void) { + gpr_mu_init(&g_init_mu); + g_initializations = 0; +} + +typedef struct grpc_plugin { + void (*init)(); + void (*destroy)(); +} grpc_plugin; + +static grpc_plugin g_all_of_the_plugins[MAX_PLUGINS]; +static int g_number_of_plugins = 0; + +void grpc_register_plugin(void (*init)(void), void (*destroy)(void)) { + GPR_ASSERT(g_number_of_plugins != MAX_PLUGINS); + g_all_of_the_plugins[g_number_of_plugins].init = init; + g_all_of_the_plugins[g_number_of_plugins].destroy = destroy; + g_number_of_plugins++; +} + +void grpc_init(void) { + int i; + gpr_once_init(&g_basic_init, do_basic_init); + + gpr_mu_lock(&g_init_mu); + if (++g_initializations == 1) { + gpr_time_init(); + grpc_resolver_registry_init("dns:///"); + grpc_register_resolver_type(grpc_dns_resolver_factory_create()); + grpc_register_resolver_type(grpc_ipv4_resolver_factory_create()); + grpc_register_resolver_type(grpc_ipv6_resolver_factory_create()); +#ifdef GPR_POSIX_SOCKET + grpc_register_resolver_type(grpc_unix_resolver_factory_create()); +#endif + grpc_register_tracer("channel", &grpc_trace_channel); + grpc_register_tracer("surface", &grpc_surface_trace); + grpc_register_tracer("http", &grpc_http_trace); + grpc_register_tracer("flowctl", &grpc_flowctl_trace); + grpc_register_tracer("batch", &grpc_trace_batch); + grpc_register_tracer("connectivity_state", &grpc_connectivity_state_trace); + grpc_security_pre_init(); + grpc_iomgr_init(); + grpc_tracer_init("GRPC_TRACE"); + /* Only initialize census if noone else has. */ + if (census_enabled() == CENSUS_FEATURE_NONE) { + if (census_initialize(census_supported())) { /* enable all features. */ + gpr_log(GPR_ERROR, "Could not initialize census."); + } + } + grpc_timers_global_init(); + for (i = 0; i < g_number_of_plugins; i++) { + if (g_all_of_the_plugins[i].init != NULL) { + g_all_of_the_plugins[i].init(); + } + } + } + gpr_mu_unlock(&g_init_mu); +} + +void grpc_shutdown(void) { + int i; + gpr_mu_lock(&g_init_mu); + if (--g_initializations == 0) { + grpc_iomgr_shutdown(); + census_shutdown(); + grpc_timers_global_destroy(); + grpc_tracer_shutdown(); + grpc_resolver_registry_shutdown(); + for (i = 0; i < g_number_of_plugins; i++) { + if (g_all_of_the_plugins[i].destroy != NULL) { + g_all_of_the_plugins[i].destroy(); + } + } + } + gpr_mu_unlock(&g_init_mu); +} + +int grpc_is_initialized(void) { + int r; + gpr_once_init(&g_basic_init, do_basic_init); + gpr_mu_lock(&g_init_mu); + r = g_initializations > 0; + gpr_mu_unlock(&g_init_mu); + return r; +} diff --git a/src/core/surface/init.h b/src/core/surface/init.h new file mode 100644 index 00000000..771c30f4 --- /dev/null +++ b/src/core/surface/init.h @@ -0,0 +1,40 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_SURFACE_INIT_H +#define GRPC_INTERNAL_CORE_SURFACE_INIT_H + +void grpc_security_pre_init(void); +int grpc_is_initialized(void); + +#endif /* GRPC_INTERNAL_CORE_SURFACE_INIT_H */ diff --git a/src/core/surface/init_secure.c b/src/core/surface/init_secure.c new file mode 100644 index 00000000..fa20e915 --- /dev/null +++ b/src/core/surface/init_secure.c @@ -0,0 +1,42 @@ +/* + * + * Copyright 2015, 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 "src/core/surface/init.h" +#include "src/core/debug/trace.h" +#include "src/core/security/secure_endpoint.h" +#include "src/core/tsi/transport_security_interface.h" + +void grpc_security_pre_init(void) { + grpc_register_tracer("secure_endpoint", &grpc_trace_secure_endpoint); + grpc_register_tracer("transport_security", &tsi_tracing_enabled); +} diff --git a/src/core/surface/init_unsecure.c b/src/core/surface/init_unsecure.c new file mode 100644 index 00000000..630d564a --- /dev/null +++ b/src/core/surface/init_unsecure.c @@ -0,0 +1,36 @@ +/* + * + * Copyright 2015, 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 "src/core/surface/init.h" + +void grpc_security_pre_init(void) {} diff --git a/src/core/surface/lame_client.c b/src/core/surface/lame_client.c new file mode 100644 index 00000000..80704cbf --- /dev/null +++ b/src/core/surface/lame_client.c @@ -0,0 +1,160 @@ +/* + * + * Copyright 2015, 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 + +#include + +#include "src/core/channel/channel_stack.h" +#include "src/core/support/string.h" +#include "src/core/surface/channel.h" +#include "src/core/surface/call.h" +#include +#include + +typedef struct { + grpc_linked_mdelem status; + grpc_linked_mdelem details; +} call_data; + +typedef struct { + grpc_mdctx *mdctx; + grpc_channel *master; + grpc_status_code error_code; + const char *error_message; +} channel_data; + +static void lame_start_transport_stream_op(grpc_call_element *elem, + grpc_transport_stream_op *op) { + call_data *calld = elem->call_data; + channel_data *chand = elem->channel_data; + GRPC_CALL_LOG_OP(GPR_INFO, elem, op); + if (op->send_ops != NULL) { + grpc_stream_ops_unref_owned_objects(op->send_ops->ops, op->send_ops->nops); + op->on_done_send->cb(op->on_done_send->cb_arg, 0); + } + if (op->recv_ops != NULL) { + char tmp[GPR_LTOA_MIN_BUFSIZE]; + grpc_metadata_batch mdb; + gpr_ltoa(chand->error_code, tmp); + calld->status.md = + grpc_mdelem_from_strings(chand->mdctx, "grpc-status", tmp); + calld->details.md = grpc_mdelem_from_strings(chand->mdctx, "grpc-message", + chand->error_message); + calld->status.prev = calld->details.next = NULL; + calld->status.next = &calld->details; + calld->details.prev = &calld->status; + mdb.list.head = &calld->status; + mdb.list.tail = &calld->details; + mdb.garbage.head = mdb.garbage.tail = NULL; + mdb.deadline = gpr_inf_future(GPR_CLOCK_REALTIME); + grpc_sopb_add_metadata(op->recv_ops, mdb); + *op->recv_state = GRPC_STREAM_CLOSED; + op->on_done_recv->cb(op->on_done_recv->cb_arg, 1); + } + if (op->on_consumed != NULL) { + op->on_consumed->cb(op->on_consumed->cb_arg, 0); + } +} + +static char *lame_get_peer(grpc_call_element *elem) { + channel_data *chand = elem->channel_data; + return grpc_channel_get_target(chand->master); +} + +static void lame_start_transport_op(grpc_channel_element *elem, + grpc_transport_op *op) { + if (op->on_connectivity_state_change) { + GPR_ASSERT(*op->connectivity_state != GRPC_CHANNEL_FATAL_FAILURE); + *op->connectivity_state = GRPC_CHANNEL_FATAL_FAILURE; + op->on_connectivity_state_change->cb( + op->on_connectivity_state_change->cb_arg, 1); + } + if (op->on_consumed != NULL) { + op->on_consumed->cb(op->on_consumed->cb_arg, 1); + } +} + +static void init_call_elem(grpc_call_element *elem, + const void *transport_server_data, + grpc_transport_stream_op *initial_op) { + if (initial_op) { + grpc_transport_stream_op_finish_with_failure(initial_op); + } +} + +static void destroy_call_elem(grpc_call_element *elem) {} + +static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, + const grpc_channel_args *args, grpc_mdctx *mdctx, + int is_first, int is_last) { + channel_data *chand = elem->channel_data; + GPR_ASSERT(is_first); + GPR_ASSERT(is_last); + chand->mdctx = mdctx; + chand->master = master; +} + +static void destroy_channel_elem(grpc_channel_element *elem) {} + +static const grpc_channel_filter lame_filter = { + lame_start_transport_stream_op, + lame_start_transport_op, + sizeof(call_data), + init_call_elem, + destroy_call_elem, + sizeof(channel_data), + init_channel_elem, + destroy_channel_elem, + lame_get_peer, + "lame-client", +}; + +#define CHANNEL_STACK_FROM_CHANNEL(c) ((grpc_channel_stack *)((c) + 1)) + +grpc_channel *grpc_lame_client_channel_create(const char *target, + grpc_status_code error_code, + const char *error_message) { + grpc_channel *channel; + grpc_channel_element *elem; + channel_data *chand; + static const grpc_channel_filter *filters[] = {&lame_filter}; + channel = grpc_channel_create_from_filters(target, filters, 1, NULL, + grpc_mdctx_create(), 1); + elem = grpc_channel_stack_element(grpc_channel_get_channel_stack(channel), 0); + GPR_ASSERT(elem->filter == &lame_filter); + chand = (channel_data *)elem->channel_data; + chand->error_code = error_code; + chand->error_message = error_message; + return channel; +} diff --git a/src/core/surface/metadata_array.c b/src/core/surface/metadata_array.c new file mode 100644 index 00000000..40109774 --- /dev/null +++ b/src/core/surface/metadata_array.c @@ -0,0 +1,45 @@ +/* + * + * Copyright 2015, 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 +#include + +#include + +void grpc_metadata_array_init(grpc_metadata_array *array) { + memset(array, 0, sizeof(*array)); +} + +void grpc_metadata_array_destroy(grpc_metadata_array *array) { + gpr_free(array->metadata); +} diff --git a/src/core/surface/secure_channel_create.c b/src/core/surface/secure_channel_create.c new file mode 100644 index 00000000..35b60bdb --- /dev/null +++ b/src/core/surface/secure_channel_create.c @@ -0,0 +1,260 @@ +/* + * + * Copyright 2015, 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 + +#include +#include + +#include + +#include "src/core/census/grpc_filter.h" +#include "src/core/channel/channel_args.h" +#include "src/core/channel/client_channel.h" +#include "src/core/channel/compress_filter.h" +#include "src/core/channel/http_client_filter.h" +#include "src/core/client_config/resolver_registry.h" +#include "src/core/iomgr/tcp_client.h" +#include "src/core/security/auth_filters.h" +#include "src/core/security/credentials.h" +#include "src/core/security/secure_transport_setup.h" +#include "src/core/surface/channel.h" +#include "src/core/transport/chttp2_transport.h" +#include "src/core/tsi/transport_security_interface.h" + +typedef struct { + grpc_connector base; + gpr_refcount refs; + + grpc_channel_security_connector *security_connector; + + grpc_iomgr_closure *notify; + grpc_connect_in_args args; + grpc_connect_out_args *result; +} connector; + +static void connector_ref(grpc_connector *con) { + connector *c = (connector *)con; + gpr_ref(&c->refs); +} + +static void connector_unref(grpc_connector *con) { + connector *c = (connector *)con; + if (gpr_unref(&c->refs)) { + gpr_free(c); + } +} + +static void on_secure_transport_setup_done(void *arg, + grpc_security_status status, + grpc_endpoint *wrapped_endpoint, + grpc_endpoint *secure_endpoint) { + connector *c = arg; + grpc_iomgr_closure *notify; + if (status != GRPC_SECURITY_OK) { + gpr_log(GPR_ERROR, "Secure transport setup failed with error %d.", status); + memset(c->result, 0, sizeof(*c->result)); + } else { + c->result->transport = grpc_create_chttp2_transport( + c->args.channel_args, secure_endpoint, c->args.metadata_context, 1); + grpc_chttp2_transport_start_reading(c->result->transport, NULL, 0); + c->result->filters = gpr_malloc(sizeof(grpc_channel_filter *) * 2); + c->result->filters[0] = &grpc_http_client_filter; + c->result->filters[1] = &grpc_client_auth_filter; + c->result->num_filters = 2; + } + notify = c->notify; + c->notify = NULL; + grpc_iomgr_add_callback(notify); +} + +static void connected(void *arg, grpc_endpoint *tcp) { + connector *c = arg; + grpc_iomgr_closure *notify; + if (tcp != NULL) { + grpc_setup_secure_transport(&c->security_connector->base, tcp, + on_secure_transport_setup_done, c); + } else { + memset(c->result, 0, sizeof(*c->result)); + notify = c->notify; + c->notify = NULL; + grpc_iomgr_add_callback(notify); + } +} + +static void connector_connect(grpc_connector *con, + const grpc_connect_in_args *args, + grpc_connect_out_args *result, + grpc_iomgr_closure *notify) { + connector *c = (connector *)con; + GPR_ASSERT(c->notify == NULL); + GPR_ASSERT(notify->cb); + c->notify = notify; + c->args = *args; + c->result = result; + grpc_tcp_client_connect(connected, c, args->interested_parties, args->addr, + args->addr_len, args->deadline); +} + +static const grpc_connector_vtable connector_vtable = { + connector_ref, connector_unref, connector_connect}; + +typedef struct { + grpc_subchannel_factory base; + gpr_refcount refs; + grpc_mdctx *mdctx; + grpc_channel_args *merge_args; + grpc_channel_security_connector *security_connector; + grpc_channel *master; +} subchannel_factory; + +static void subchannel_factory_ref(grpc_subchannel_factory *scf) { + subchannel_factory *f = (subchannel_factory *)scf; + gpr_ref(&f->refs); +} + +static void subchannel_factory_unref(grpc_subchannel_factory *scf) { + subchannel_factory *f = (subchannel_factory *)scf; + if (gpr_unref(&f->refs)) { + GRPC_SECURITY_CONNECTOR_UNREF(&f->security_connector->base, + "subchannel_factory"); + GRPC_CHANNEL_INTERNAL_UNREF(f->master, "subchannel_factory"); + grpc_channel_args_destroy(f->merge_args); + grpc_mdctx_unref(f->mdctx); + gpr_free(f); + } +} + +static grpc_subchannel *subchannel_factory_create_subchannel( + grpc_subchannel_factory *scf, grpc_subchannel_args *args) { + subchannel_factory *f = (subchannel_factory *)scf; + connector *c = gpr_malloc(sizeof(*c)); + grpc_channel_args *final_args = + grpc_channel_args_merge(args->args, f->merge_args); + grpc_subchannel *s; + memset(c, 0, sizeof(*c)); + c->base.vtable = &connector_vtable; + c->security_connector = f->security_connector; + gpr_ref_init(&c->refs, 1); + args->mdctx = f->mdctx; + args->args = final_args; + args->master = f->master; + s = grpc_subchannel_create(&c->base, args); + grpc_connector_unref(&c->base); + grpc_channel_args_destroy(final_args); + return s; +} + +static const grpc_subchannel_factory_vtable subchannel_factory_vtable = { + subchannel_factory_ref, subchannel_factory_unref, + subchannel_factory_create_subchannel}; + +/* Create a secure client channel: + Asynchronously: - resolve target + - connect to it (trying alternatives as presented) + - perform handshakes */ +grpc_channel *grpc_secure_channel_create(grpc_credentials *creds, + const char *target, + const grpc_channel_args *args, + void *reserved) { + grpc_channel *channel; + grpc_arg connector_arg; + grpc_channel_args *args_copy; + grpc_channel_args *new_args_from_connector; + grpc_channel_security_connector *connector; + grpc_mdctx *mdctx; + grpc_resolver *resolver; + subchannel_factory *f; +#define MAX_FILTERS 3 + const grpc_channel_filter *filters[MAX_FILTERS]; + int n = 0; + + GPR_ASSERT(reserved == NULL); + if (grpc_find_security_connector_in_args(args) != NULL) { + gpr_log(GPR_ERROR, "Cannot set security context in channel args."); + return grpc_lame_client_channel_create( + target, GRPC_STATUS_INVALID_ARGUMENT, + "Security connector exists in channel args."); + } + + if (grpc_credentials_create_security_connector( + creds, target, args, NULL, &connector, &new_args_from_connector) != + GRPC_SECURITY_OK) { + return grpc_lame_client_channel_create( + target, GRPC_STATUS_INVALID_ARGUMENT, + "Failed to create security connector."); + } + mdctx = grpc_mdctx_create(); + + connector_arg = grpc_security_connector_to_arg(&connector->base); + args_copy = grpc_channel_args_copy_and_add( + new_args_from_connector != NULL ? new_args_from_connector : args, + &connector_arg, 1); + if (grpc_channel_args_is_census_enabled(args)) { + filters[n++] = &grpc_client_census_filter; + } + filters[n++] = &grpc_compress_filter; + filters[n++] = &grpc_client_channel_filter; + GPR_ASSERT(n <= MAX_FILTERS); + + channel = + grpc_channel_create_from_filters(target, filters, n, args_copy, mdctx, 1); + + f = gpr_malloc(sizeof(*f)); + f->base.vtable = &subchannel_factory_vtable; + gpr_ref_init(&f->refs, 1); + grpc_mdctx_ref(mdctx); + f->mdctx = mdctx; + GRPC_SECURITY_CONNECTOR_REF(&connector->base, "subchannel_factory"); + f->security_connector = connector; + f->merge_args = grpc_channel_args_copy(args_copy); + f->master = channel; + GRPC_CHANNEL_INTERNAL_REF(channel, "subchannel_factory"); + resolver = grpc_resolver_create(target, &f->base); + if (!resolver) { + return NULL; + } + + grpc_client_channel_set_resolver(grpc_channel_get_channel_stack(channel), + resolver); + GRPC_RESOLVER_UNREF(resolver, "create"); + grpc_subchannel_factory_unref(&f->base); + GRPC_SECURITY_CONNECTOR_UNREF(&connector->base, "channel_create"); + + grpc_channel_args_destroy(args_copy); + if (new_args_from_connector != NULL) { + grpc_channel_args_destroy(new_args_from_connector); + } + + return channel; +} diff --git a/src/core/surface/server.c b/src/core/surface/server.c new file mode 100644 index 00000000..292bf6fa --- /dev/null +++ b/src/core/surface/server.c @@ -0,0 +1,1305 @@ +/* + * + * Copyright 2015, 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 "src/core/surface/server.h" + +#include +#include + +#include +#include +#include +#include + +#include "src/core/census/grpc_filter.h" +#include "src/core/channel/channel_args.h" +#include "src/core/channel/connected_channel.h" +#include "src/core/iomgr/iomgr.h" +#include "src/core/support/stack_lockfree.h" +#include "src/core/support/string.h" +#include "src/core/surface/call.h" +#include "src/core/surface/channel.h" +#include "src/core/surface/completion_queue.h" +#include "src/core/surface/init.h" +#include "src/core/transport/metadata.h" + +typedef struct listener { + void *arg; + void (*start)(grpc_server *server, void *arg, grpc_pollset **pollsets, + size_t pollset_count); + void (*destroy)(grpc_server *server, void *arg); + struct listener *next; +} listener; + +typedef struct call_data call_data; +typedef struct channel_data channel_data; +typedef struct registered_method registered_method; + +typedef struct { + call_data *next; + call_data *prev; +} call_link; + +typedef enum { BATCH_CALL, REGISTERED_CALL } requested_call_type; + +typedef struct requested_call { + requested_call_type type; + void *tag; + grpc_server *server; + grpc_completion_queue *cq_bound_to_call; + grpc_completion_queue *cq_for_notification; + grpc_call **call; + grpc_cq_completion completion; + union { + struct { + grpc_call_details *details; + grpc_metadata_array *initial_metadata; + } batch; + struct { + registered_method *registered_method; + gpr_timespec *deadline; + grpc_metadata_array *initial_metadata; + grpc_byte_buffer **optional_payload; + } registered; + } data; +} requested_call; + +typedef struct channel_registered_method { + registered_method *server_registered_method; + grpc_mdstr *method; + grpc_mdstr *host; +} channel_registered_method; + +struct channel_data { + grpc_server *server; + grpc_connectivity_state connectivity_state; + grpc_channel *channel; + grpc_mdstr *path_key; + grpc_mdstr *authority_key; + /* linked list of all channels on a server */ + channel_data *next; + channel_data *prev; + channel_registered_method *registered_methods; + gpr_uint32 registered_method_slots; + gpr_uint32 registered_method_max_probes; + grpc_iomgr_closure finish_destroy_channel_closure; + grpc_iomgr_closure channel_connectivity_changed; +}; + +typedef struct shutdown_tag { + void *tag; + grpc_completion_queue *cq; + grpc_cq_completion completion; +} shutdown_tag; + +typedef enum { + /* waiting for metadata */ + NOT_STARTED, + /* inital metadata read, not flow controlled in yet */ + PENDING, + /* flow controlled in, on completion queue */ + ACTIVATED, + /* cancelled before being queued */ + ZOMBIED +} call_state; + +typedef struct request_matcher request_matcher; + +struct call_data { + grpc_call *call; + + /** protects state */ + gpr_mu mu_state; + /** the current state of a call - see call_state */ + call_state state; + + grpc_mdstr *path; + grpc_mdstr *host; + gpr_timespec deadline; + int got_initial_metadata; + + grpc_completion_queue *cq_new; + + grpc_stream_op_buffer *recv_ops; + grpc_stream_state *recv_state; + grpc_iomgr_closure *on_done_recv; + + grpc_iomgr_closure server_on_recv; + grpc_iomgr_closure kill_zombie_closure; + + call_data *pending_next; +}; + +struct request_matcher { + call_data *pending_head; + call_data *pending_tail; + gpr_stack_lockfree *requests; +}; + +struct registered_method { + char *method; + char *host; + request_matcher request_matcher; + registered_method *next; +}; + +typedef struct { + grpc_channel **channels; + size_t num_channels; +} channel_broadcaster; + +struct grpc_server { + size_t channel_filter_count; + const grpc_channel_filter **channel_filters; + grpc_channel_args *channel_args; + + grpc_completion_queue **cqs; + grpc_pollset **pollsets; + size_t cq_count; + + /* The two following mutexes control access to server-state + mu_global controls access to non-call-related state (e.g., channel state) + mu_call controls access to call-related state (e.g., the call lists) + + If they are ever required to be nested, you must lock mu_global + before mu_call. This is currently used in shutdown processing + (grpc_server_shutdown_and_notify and maybe_finish_shutdown) */ + gpr_mu mu_global; /* mutex for server and channel state */ + gpr_mu mu_call; /* mutex for call-specific state */ + + registered_method *registered_methods; + request_matcher unregistered_request_matcher; + /** free list of available requested_calls indices */ + gpr_stack_lockfree *request_freelist; + /** requested call backing data */ + requested_call *requested_calls; + int max_requested_calls; + + gpr_atm shutdown_flag; + gpr_uint8 shutdown_published; + size_t num_shutdown_tags; + shutdown_tag *shutdown_tags; + + channel_data root_channel_data; + + listener *listeners; + int listeners_destroyed; + gpr_refcount internal_refcount; + + /** when did we print the last shutdown progress message */ + gpr_timespec last_shutdown_message_time; +}; + +#define SERVER_FROM_CALL_ELEM(elem) \ + (((channel_data *)(elem)->channel_data)->server) + +static void begin_call(grpc_server *server, call_data *calld, + requested_call *rc); +static void fail_call(grpc_server *server, requested_call *rc); +/* Before calling maybe_finish_shutdown, we must hold mu_global and not + hold mu_call */ +static void maybe_finish_shutdown(grpc_server *server); + +/* + * channel broadcaster + */ + +/* assumes server locked */ +static void channel_broadcaster_init(grpc_server *s, channel_broadcaster *cb) { + channel_data *c; + size_t count = 0; + for (c = s->root_channel_data.next; c != &s->root_channel_data; c = c->next) { + count++; + } + cb->num_channels = count; + cb->channels = gpr_malloc(sizeof(*cb->channels) * cb->num_channels); + count = 0; + for (c = s->root_channel_data.next; c != &s->root_channel_data; c = c->next) { + cb->channels[count++] = c->channel; + GRPC_CHANNEL_INTERNAL_REF(c->channel, "broadcast"); + } +} + +struct shutdown_cleanup_args { + grpc_iomgr_closure closure; + gpr_slice slice; +}; + +static void shutdown_cleanup(void *arg, int iomgr_status_ignored) { + struct shutdown_cleanup_args *a = arg; + gpr_slice_unref(a->slice); + gpr_free(a); +} + +static void send_shutdown(grpc_channel *channel, int send_goaway, + int send_disconnect) { + grpc_transport_op op; + struct shutdown_cleanup_args *sc; + grpc_channel_element *elem; + + memset(&op, 0, sizeof(op)); + op.send_goaway = send_goaway; + sc = gpr_malloc(sizeof(*sc)); + sc->slice = gpr_slice_from_copied_string("Server shutdown"); + op.goaway_message = &sc->slice; + op.goaway_status = GRPC_STATUS_OK; + op.disconnect = send_disconnect; + grpc_iomgr_closure_init(&sc->closure, shutdown_cleanup, sc); + op.on_consumed = &sc->closure; + + elem = grpc_channel_stack_element(grpc_channel_get_channel_stack(channel), 0); + elem->filter->start_transport_op(elem, &op); +} + +static void channel_broadcaster_shutdown(channel_broadcaster *cb, + int send_goaway, + int force_disconnect) { + size_t i; + + for (i = 0; i < cb->num_channels; i++) { + send_shutdown(cb->channels[i], send_goaway, force_disconnect); + GRPC_CHANNEL_INTERNAL_UNREF(cb->channels[i], "broadcast"); + } + gpr_free(cb->channels); +} + +/* + * request_matcher + */ + +static void request_matcher_init(request_matcher *request_matcher, + int entries) { + memset(request_matcher, 0, sizeof(*request_matcher)); + request_matcher->requests = gpr_stack_lockfree_create(entries); +} + +static void request_matcher_destroy(request_matcher *request_matcher) { + GPR_ASSERT(gpr_stack_lockfree_pop(request_matcher->requests) == -1); + gpr_stack_lockfree_destroy(request_matcher->requests); +} + +static void kill_zombie(void *elem, int success) { + grpc_call_destroy(grpc_call_from_top_element(elem)); +} + +static void request_matcher_zombify_all_pending_calls( + request_matcher *request_matcher) { + while (request_matcher->pending_head) { + call_data *calld = request_matcher->pending_head; + request_matcher->pending_head = calld->pending_next; + gpr_mu_lock(&calld->mu_state); + calld->state = ZOMBIED; + gpr_mu_unlock(&calld->mu_state); + grpc_iomgr_closure_init( + &calld->kill_zombie_closure, kill_zombie, + grpc_call_stack_element(grpc_call_get_call_stack(calld->call), 0)); + grpc_iomgr_add_callback(&calld->kill_zombie_closure); + } +} + +static void request_matcher_kill_requests(grpc_server *server, + request_matcher *rm) { + int request_id; + while ((request_id = gpr_stack_lockfree_pop(rm->requests)) != -1) { + fail_call(server, &server->requested_calls[request_id]); + } +} + +/* + * server proper + */ + +static void server_ref(grpc_server *server) { + gpr_ref(&server->internal_refcount); +} + +static void server_delete(grpc_server *server) { + registered_method *rm; + size_t i; + grpc_channel_args_destroy(server->channel_args); + gpr_mu_destroy(&server->mu_global); + gpr_mu_destroy(&server->mu_call); + gpr_free(server->channel_filters); + while ((rm = server->registered_methods) != NULL) { + server->registered_methods = rm->next; + request_matcher_destroy(&rm->request_matcher); + gpr_free(rm->method); + gpr_free(rm->host); + gpr_free(rm); + } + for (i = 0; i < server->cq_count; i++) { + GRPC_CQ_INTERNAL_UNREF(server->cqs[i], "server"); + } + request_matcher_destroy(&server->unregistered_request_matcher); + gpr_stack_lockfree_destroy(server->request_freelist); + gpr_free(server->cqs); + gpr_free(server->pollsets); + gpr_free(server->shutdown_tags); + gpr_free(server->requested_calls); + gpr_free(server); +} + +static void server_unref(grpc_server *server) { + if (gpr_unref(&server->internal_refcount)) { + server_delete(server); + } +} + +static int is_channel_orphaned(channel_data *chand) { + return chand->next == chand; +} + +static void orphan_channel(channel_data *chand) { + chand->next->prev = chand->prev; + chand->prev->next = chand->next; + chand->next = chand->prev = chand; +} + +static void finish_destroy_channel(void *cd, int success) { + channel_data *chand = cd; + grpc_server *server = chand->server; + GRPC_CHANNEL_INTERNAL_UNREF(chand->channel, "server"); + server_unref(server); +} + +static void destroy_channel(channel_data *chand) { + if (is_channel_orphaned(chand)) return; + GPR_ASSERT(chand->server != NULL); + orphan_channel(chand); + server_ref(chand->server); + maybe_finish_shutdown(chand->server); + chand->finish_destroy_channel_closure.cb = finish_destroy_channel; + chand->finish_destroy_channel_closure.cb_arg = chand; + grpc_iomgr_add_callback(&chand->finish_destroy_channel_closure); +} + +static void finish_start_new_rpc(grpc_server *server, grpc_call_element *elem, + request_matcher *request_matcher) { + call_data *calld = elem->call_data; + int request_id; + + if (gpr_atm_acq_load(&server->shutdown_flag)) { + gpr_mu_lock(&calld->mu_state); + calld->state = ZOMBIED; + gpr_mu_unlock(&calld->mu_state); + grpc_iomgr_closure_init(&calld->kill_zombie_closure, kill_zombie, elem); + grpc_iomgr_add_callback(&calld->kill_zombie_closure); + return; + } + + request_id = gpr_stack_lockfree_pop(request_matcher->requests); + if (request_id == -1) { + gpr_mu_lock(&server->mu_call); + gpr_mu_lock(&calld->mu_state); + calld->state = PENDING; + gpr_mu_unlock(&calld->mu_state); + if (request_matcher->pending_head == NULL) { + request_matcher->pending_tail = request_matcher->pending_head = calld; + } else { + request_matcher->pending_tail->pending_next = calld; + request_matcher->pending_tail = calld; + } + calld->pending_next = NULL; + gpr_mu_unlock(&server->mu_call); + } else { + gpr_mu_lock(&calld->mu_state); + calld->state = ACTIVATED; + gpr_mu_unlock(&calld->mu_state); + begin_call(server, calld, &server->requested_calls[request_id]); + } +} + +static void start_new_rpc(grpc_call_element *elem) { + channel_data *chand = elem->channel_data; + call_data *calld = elem->call_data; + grpc_server *server = chand->server; + gpr_uint32 i; + gpr_uint32 hash; + channel_registered_method *rm; + + if (chand->registered_methods && calld->path && calld->host) { + /* TODO(ctiller): unify these two searches */ + /* check for an exact match with host */ + hash = GRPC_MDSTR_KV_HASH(calld->host->hash, calld->path->hash); + for (i = 0; i <= chand->registered_method_max_probes; i++) { + rm = &chand->registered_methods[(hash + i) % + chand->registered_method_slots]; + if (!rm) break; + if (rm->host != calld->host) continue; + if (rm->method != calld->path) continue; + finish_start_new_rpc(server, elem, + &rm->server_registered_method->request_matcher); + return; + } + /* check for a wildcard method definition (no host set) */ + hash = GRPC_MDSTR_KV_HASH(0, calld->path->hash); + for (i = 0; i <= chand->registered_method_max_probes; i++) { + rm = &chand->registered_methods[(hash + i) % + chand->registered_method_slots]; + if (!rm) break; + if (rm->host != NULL) continue; + if (rm->method != calld->path) continue; + finish_start_new_rpc(server, elem, + &rm->server_registered_method->request_matcher); + return; + } + } + finish_start_new_rpc(server, elem, &server->unregistered_request_matcher); +} + +static int num_listeners(grpc_server *server) { + listener *l; + int n = 0; + for (l = server->listeners; l; l = l->next) { + n++; + } + return n; +} + +static void done_shutdown_event(void *server, grpc_cq_completion *completion) { + server_unref(server); +} + +static int num_channels(grpc_server *server) { + channel_data *chand; + int n = 0; + for (chand = server->root_channel_data.next; + chand != &server->root_channel_data; chand = chand->next) { + n++; + } + return n; +} + +static void kill_pending_work_locked(grpc_server *server) { + registered_method *rm; + request_matcher_kill_requests(server, &server->unregistered_request_matcher); + request_matcher_zombify_all_pending_calls( + &server->unregistered_request_matcher); + for (rm = server->registered_methods; rm; rm = rm->next) { + request_matcher_kill_requests(server, &rm->request_matcher); + request_matcher_zombify_all_pending_calls(&rm->request_matcher); + } +} + +static void maybe_finish_shutdown(grpc_server *server) { + size_t i; + if (!gpr_atm_acq_load(&server->shutdown_flag) || server->shutdown_published) { + return; + } + + kill_pending_work_locked(server); + + if (server->root_channel_data.next != &server->root_channel_data || + server->listeners_destroyed < num_listeners(server)) { + if (gpr_time_cmp(gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME), + server->last_shutdown_message_time), + gpr_time_from_seconds(1, GPR_TIMESPAN)) >= 0) { + server->last_shutdown_message_time = gpr_now(GPR_CLOCK_REALTIME); + gpr_log(GPR_DEBUG, + "Waiting for %d channels and %d/%d listeners to be destroyed" + " before shutting down server", + num_channels(server), + num_listeners(server) - server->listeners_destroyed, + num_listeners(server)); + } + return; + } + server->shutdown_published = 1; + for (i = 0; i < server->num_shutdown_tags; i++) { + server_ref(server); + grpc_cq_end_op(server->shutdown_tags[i].cq, server->shutdown_tags[i].tag, 1, + done_shutdown_event, server, + &server->shutdown_tags[i].completion); + } +} + +static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) { + grpc_call_element *elem = user_data; + channel_data *chand = elem->channel_data; + call_data *calld = elem->call_data; + if (md->key == chand->path_key) { + calld->path = GRPC_MDSTR_REF(md->value); + return NULL; + } else if (md->key == chand->authority_key) { + calld->host = GRPC_MDSTR_REF(md->value); + return NULL; + } + return md; +} + +static void server_on_recv(void *ptr, int success) { + grpc_call_element *elem = ptr; + call_data *calld = elem->call_data; + gpr_timespec op_deadline; + + if (success && !calld->got_initial_metadata) { + size_t i; + size_t nops = calld->recv_ops->nops; + grpc_stream_op *ops = calld->recv_ops->ops; + for (i = 0; i < nops; i++) { + grpc_stream_op *op = &ops[i]; + if (op->type != GRPC_OP_METADATA) continue; + grpc_metadata_batch_filter(&op->data.metadata, server_filter, elem); + op_deadline = op->data.metadata.deadline; + if (0 != + gpr_time_cmp(op_deadline, gpr_inf_future(op_deadline.clock_type))) { + calld->deadline = op->data.metadata.deadline; + } + if (calld->host && calld->path) { + calld->got_initial_metadata = 1; + start_new_rpc(elem); + } + break; + } + } + + switch (*calld->recv_state) { + case GRPC_STREAM_OPEN: + break; + case GRPC_STREAM_SEND_CLOSED: + break; + case GRPC_STREAM_RECV_CLOSED: + gpr_mu_lock(&calld->mu_state); + if (calld->state == NOT_STARTED) { + calld->state = ZOMBIED; + gpr_mu_unlock(&calld->mu_state); + grpc_iomgr_closure_init(&calld->kill_zombie_closure, kill_zombie, elem); + grpc_iomgr_add_callback(&calld->kill_zombie_closure); + } else { + gpr_mu_unlock(&calld->mu_state); + } + break; + case GRPC_STREAM_CLOSED: + gpr_mu_lock(&calld->mu_state); + if (calld->state == NOT_STARTED) { + calld->state = ZOMBIED; + gpr_mu_unlock(&calld->mu_state); + grpc_iomgr_closure_init(&calld->kill_zombie_closure, kill_zombie, elem); + grpc_iomgr_add_callback(&calld->kill_zombie_closure); + } else if (calld->state == PENDING) { + calld->state = ZOMBIED; + gpr_mu_unlock(&calld->mu_state); + /* zombied call will be destroyed when it's removed from the pending + queue... later */ + } else { + gpr_mu_unlock(&calld->mu_state); + } + break; + } + + calld->on_done_recv->cb(calld->on_done_recv->cb_arg, success); +} + +static void server_mutate_op(grpc_call_element *elem, + grpc_transport_stream_op *op) { + call_data *calld = elem->call_data; + + if (op->recv_ops) { + /* substitute our callback for the higher callback */ + calld->recv_ops = op->recv_ops; + calld->recv_state = op->recv_state; + calld->on_done_recv = op->on_done_recv; + op->on_done_recv = &calld->server_on_recv; + } +} + +static void server_start_transport_stream_op(grpc_call_element *elem, + grpc_transport_stream_op *op) { + GRPC_CALL_LOG_OP(GPR_INFO, elem, op); + server_mutate_op(elem, op); + grpc_call_next_op(elem, op); +} + +static void accept_stream(void *cd, grpc_transport *transport, + const void *transport_server_data) { + channel_data *chand = cd; + /* create a call */ + grpc_call_create(chand->channel, NULL, 0, NULL, transport_server_data, NULL, + 0, gpr_inf_future(GPR_CLOCK_MONOTONIC)); +} + +static void channel_connectivity_changed(void *cd, int iomgr_status_ignored) { + channel_data *chand = cd; + grpc_server *server = chand->server; + if (chand->connectivity_state != GRPC_CHANNEL_FATAL_FAILURE) { + grpc_transport_op op; + memset(&op, 0, sizeof(op)); + op.on_connectivity_state_change = &chand->channel_connectivity_changed, + op.connectivity_state = &chand->connectivity_state; + grpc_channel_next_op(grpc_channel_stack_element( + grpc_channel_get_channel_stack(chand->channel), 0), + &op); + } else { + gpr_mu_lock(&server->mu_global); + destroy_channel(chand); + gpr_mu_unlock(&server->mu_global); + GRPC_CHANNEL_INTERNAL_UNREF(chand->channel, "connectivity"); + } +} + +static void init_call_elem(grpc_call_element *elem, + const void *server_transport_data, + grpc_transport_stream_op *initial_op) { + call_data *calld = elem->call_data; + channel_data *chand = elem->channel_data; + memset(calld, 0, sizeof(call_data)); + calld->deadline = gpr_inf_future(GPR_CLOCK_REALTIME); + calld->call = grpc_call_from_top_element(elem); + gpr_mu_init(&calld->mu_state); + + grpc_iomgr_closure_init(&calld->server_on_recv, server_on_recv, elem); + + server_ref(chand->server); + + if (initial_op) server_mutate_op(elem, initial_op); +} + +static void destroy_call_elem(grpc_call_element *elem) { + channel_data *chand = elem->channel_data; + call_data *calld = elem->call_data; + + GPR_ASSERT(calld->state != PENDING); + + if (calld->host) { + GRPC_MDSTR_UNREF(calld->host); + } + if (calld->path) { + GRPC_MDSTR_UNREF(calld->path); + } + + gpr_mu_destroy(&calld->mu_state); + + server_unref(chand->server); +} + +static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, + const grpc_channel_args *args, + grpc_mdctx *metadata_context, int is_first, + int is_last) { + channel_data *chand = elem->channel_data; + GPR_ASSERT(is_first); + GPR_ASSERT(!is_last); + chand->server = NULL; + chand->channel = NULL; + chand->path_key = grpc_mdstr_from_string(metadata_context, ":path", 0); + chand->authority_key = + grpc_mdstr_from_string(metadata_context, ":authority", 0); + chand->next = chand->prev = chand; + chand->registered_methods = NULL; + chand->connectivity_state = GRPC_CHANNEL_IDLE; + grpc_iomgr_closure_init(&chand->channel_connectivity_changed, + channel_connectivity_changed, chand); +} + +static void destroy_channel_elem(grpc_channel_element *elem) { + size_t i; + channel_data *chand = elem->channel_data; + if (chand->registered_methods) { + for (i = 0; i < chand->registered_method_slots; i++) { + if (chand->registered_methods[i].method) { + GRPC_MDSTR_UNREF(chand->registered_methods[i].method); + } + if (chand->registered_methods[i].host) { + GRPC_MDSTR_UNREF(chand->registered_methods[i].host); + } + } + gpr_free(chand->registered_methods); + } + if (chand->server) { + gpr_mu_lock(&chand->server->mu_global); + chand->next->prev = chand->prev; + chand->prev->next = chand->next; + chand->next = chand->prev = chand; + maybe_finish_shutdown(chand->server); + gpr_mu_unlock(&chand->server->mu_global); + GRPC_MDSTR_UNREF(chand->path_key); + GRPC_MDSTR_UNREF(chand->authority_key); + server_unref(chand->server); + } +} + +static const grpc_channel_filter server_surface_filter = { + server_start_transport_stream_op, + grpc_channel_next_op, + sizeof(call_data), + init_call_elem, + destroy_call_elem, + sizeof(channel_data), + init_channel_elem, + destroy_channel_elem, + grpc_call_next_get_peer, + "server", +}; + +void grpc_server_register_completion_queue(grpc_server *server, + grpc_completion_queue *cq, + void *reserved) { + size_t i, n; + GPR_ASSERT(!reserved); + for (i = 0; i < server->cq_count; i++) { + if (server->cqs[i] == cq) return; + } + GRPC_CQ_INTERNAL_REF(cq, "server"); + grpc_cq_mark_server_cq(cq); + n = server->cq_count++; + server->cqs = gpr_realloc(server->cqs, + server->cq_count * sizeof(grpc_completion_queue *)); + server->cqs[n] = cq; +} + +grpc_server *grpc_server_create_from_filters( + const grpc_channel_filter **filters, size_t filter_count, + const grpc_channel_args *args) { + size_t i; + /* TODO(census): restore this once we finalize census filter etc. + int census_enabled = grpc_channel_args_is_census_enabled(args); */ + int census_enabled = 0; + + grpc_server *server = gpr_malloc(sizeof(grpc_server)); + + GPR_ASSERT(grpc_is_initialized() && "call grpc_init()"); + + memset(server, 0, sizeof(grpc_server)); + + gpr_mu_init(&server->mu_global); + gpr_mu_init(&server->mu_call); + + /* decremented by grpc_server_destroy */ + gpr_ref_init(&server->internal_refcount, 1); + server->root_channel_data.next = server->root_channel_data.prev = + &server->root_channel_data; + + /* TODO(ctiller): expose a channel_arg for this */ + server->max_requested_calls = 32768; + server->request_freelist = + gpr_stack_lockfree_create(server->max_requested_calls); + for (i = 0; i < (size_t)server->max_requested_calls; i++) { + gpr_stack_lockfree_push(server->request_freelist, i); + } + request_matcher_init(&server->unregistered_request_matcher, + server->max_requested_calls); + server->requested_calls = gpr_malloc(server->max_requested_calls * + sizeof(*server->requested_calls)); + + /* Server filter stack is: + + server_surface_filter - for making surface API calls + grpc_server_census_filter (optional) - for stats collection and tracing + {passed in filter stack} + grpc_connected_channel_filter - for interfacing with transports */ + server->channel_filter_count = filter_count + 1 + census_enabled; + server->channel_filters = + gpr_malloc(server->channel_filter_count * sizeof(grpc_channel_filter *)); + server->channel_filters[0] = &server_surface_filter; + if (census_enabled) { + server->channel_filters[1] = &grpc_server_census_filter; + } + for (i = 0; i < filter_count; i++) { + server->channel_filters[i + 1 + census_enabled] = filters[i]; + } + + server->channel_args = grpc_channel_args_copy(args); + + return server; +} + +static int streq(const char *a, const char *b) { + if (a == NULL && b == NULL) return 1; + if (a == NULL) return 0; + if (b == NULL) return 0; + return 0 == strcmp(a, b); +} + +void *grpc_server_register_method(grpc_server *server, const char *method, + const char *host) { + registered_method *m; + if (!method) { + gpr_log(GPR_ERROR, + "grpc_server_register_method method string cannot be NULL"); + return NULL; + } + for (m = server->registered_methods; m; m = m->next) { + if (streq(m->method, method) && streq(m->host, host)) { + gpr_log(GPR_ERROR, "duplicate registration for %s@%s", method, + host ? host : "*"); + return NULL; + } + } + m = gpr_malloc(sizeof(registered_method)); + memset(m, 0, sizeof(*m)); + request_matcher_init(&m->request_matcher, server->max_requested_calls); + m->method = gpr_strdup(method); + m->host = gpr_strdup(host); + m->next = server->registered_methods; + server->registered_methods = m; + return m; +} + +void grpc_server_start(grpc_server *server) { + listener *l; + size_t i; + + server->pollsets = gpr_malloc(sizeof(grpc_pollset *) * server->cq_count); + for (i = 0; i < server->cq_count; i++) { + server->pollsets[i] = grpc_cq_pollset(server->cqs[i]); + } + + for (l = server->listeners; l; l = l->next) { + l->start(server, l->arg, server->pollsets, server->cq_count); + } +} + +void grpc_server_setup_transport(grpc_server *s, grpc_transport *transport, + grpc_channel_filter const **extra_filters, + size_t num_extra_filters, grpc_mdctx *mdctx, + const grpc_channel_args *args) { + size_t num_filters = s->channel_filter_count + num_extra_filters + 1; + grpc_channel_filter const **filters = + gpr_malloc(sizeof(grpc_channel_filter *) * num_filters); + size_t i; + size_t num_registered_methods; + size_t alloc; + registered_method *rm; + channel_registered_method *crm; + grpc_channel *channel; + channel_data *chand; + grpc_mdstr *host; + grpc_mdstr *method; + gpr_uint32 hash; + gpr_uint32 slots; + gpr_uint32 probes; + gpr_uint32 max_probes = 0; + grpc_transport_op op; + + for (i = 0; i < s->channel_filter_count; i++) { + filters[i] = s->channel_filters[i]; + } + for (; i < s->channel_filter_count + num_extra_filters; i++) { + filters[i] = extra_filters[i - s->channel_filter_count]; + } + filters[i] = &grpc_connected_channel_filter; + + for (i = 0; i < s->cq_count; i++) { + memset(&op, 0, sizeof(op)); + op.bind_pollset = grpc_cq_pollset(s->cqs[i]); + grpc_transport_perform_op(transport, &op); + } + + channel = grpc_channel_create_from_filters(NULL, filters, num_filters, args, + mdctx, 0); + chand = (channel_data *)grpc_channel_stack_element( + grpc_channel_get_channel_stack(channel), 0) + ->channel_data; + chand->server = s; + server_ref(s); + chand->channel = channel; + + num_registered_methods = 0; + for (rm = s->registered_methods; rm; rm = rm->next) { + num_registered_methods++; + } + /* build a lookup table phrased in terms of mdstr's in this channels context + to quickly find registered methods */ + if (num_registered_methods > 0) { + slots = 2 * num_registered_methods; + alloc = sizeof(channel_registered_method) * slots; + chand->registered_methods = gpr_malloc(alloc); + memset(chand->registered_methods, 0, alloc); + for (rm = s->registered_methods; rm; rm = rm->next) { + host = rm->host ? grpc_mdstr_from_string(mdctx, rm->host, 0) : NULL; + method = grpc_mdstr_from_string(mdctx, rm->method, 0); + hash = GRPC_MDSTR_KV_HASH(host ? host->hash : 0, method->hash); + for (probes = 0; chand->registered_methods[(hash + probes) % slots] + .server_registered_method != NULL; + probes++) + ; + if (probes > max_probes) max_probes = probes; + crm = &chand->registered_methods[(hash + probes) % slots]; + crm->server_registered_method = rm; + crm->host = host; + crm->method = method; + } + chand->registered_method_slots = slots; + chand->registered_method_max_probes = max_probes; + } + + grpc_connected_channel_bind_transport(grpc_channel_get_channel_stack(channel), + transport); + + gpr_mu_lock(&s->mu_global); + chand->next = &s->root_channel_data; + chand->prev = chand->next->prev; + chand->next->prev = chand->prev->next = chand; + gpr_mu_unlock(&s->mu_global); + + gpr_free(filters); + + GRPC_CHANNEL_INTERNAL_REF(channel, "connectivity"); + memset(&op, 0, sizeof(op)); + op.set_accept_stream = accept_stream; + op.set_accept_stream_user_data = chand; + op.on_connectivity_state_change = &chand->channel_connectivity_changed; + op.connectivity_state = &chand->connectivity_state; + op.disconnect = gpr_atm_acq_load(&s->shutdown_flag); + grpc_transport_perform_op(transport, &op); +} + +void done_published_shutdown(void *done_arg, grpc_cq_completion *storage) { + (void) done_arg; + gpr_free(storage); +} + +void grpc_server_shutdown_and_notify(grpc_server *server, + grpc_completion_queue *cq, void *tag) { + listener *l; + shutdown_tag *sdt; + channel_broadcaster broadcaster; + + GRPC_SERVER_LOG_SHUTDOWN(GPR_INFO, server, cq, tag); + + /* lock, and gather up some stuff to do */ + gpr_mu_lock(&server->mu_global); + grpc_cq_begin_op(cq); + if (server->shutdown_published) { + grpc_cq_end_op(cq, tag, 1, done_published_shutdown, NULL, + gpr_malloc(sizeof(grpc_cq_completion))); + gpr_mu_unlock(&server->mu_global); + return; + } + server->shutdown_tags = + gpr_realloc(server->shutdown_tags, + sizeof(shutdown_tag) * (server->num_shutdown_tags + 1)); + sdt = &server->shutdown_tags[server->num_shutdown_tags++]; + sdt->tag = tag; + sdt->cq = cq; + if (gpr_atm_acq_load(&server->shutdown_flag)) { + gpr_mu_unlock(&server->mu_global); + return; + } + + server->last_shutdown_message_time = gpr_now(GPR_CLOCK_REALTIME); + + channel_broadcaster_init(server, &broadcaster); + + /* collect all unregistered then registered calls */ + gpr_mu_lock(&server->mu_call); + kill_pending_work_locked(server); + gpr_mu_unlock(&server->mu_call); + + gpr_atm_rel_store(&server->shutdown_flag, 1); + maybe_finish_shutdown(server); + gpr_mu_unlock(&server->mu_global); + + /* Shutdown listeners */ + for (l = server->listeners; l; l = l->next) { + l->destroy(server, l->arg); + } + + channel_broadcaster_shutdown(&broadcaster, 1, 0); +} + +void grpc_server_listener_destroy_done(void *s) { + grpc_server *server = s; + gpr_mu_lock(&server->mu_global); + server->listeners_destroyed++; + maybe_finish_shutdown(server); + gpr_mu_unlock(&server->mu_global); +} + +void grpc_server_cancel_all_calls(grpc_server *server) { + channel_broadcaster broadcaster; + + gpr_mu_lock(&server->mu_global); + channel_broadcaster_init(server, &broadcaster); + gpr_mu_unlock(&server->mu_global); + + channel_broadcaster_shutdown(&broadcaster, 0, 1); +} + +void grpc_server_destroy(grpc_server *server) { + listener *l; + + gpr_mu_lock(&server->mu_global); + GPR_ASSERT(gpr_atm_acq_load(&server->shutdown_flag) || !server->listeners); + GPR_ASSERT(server->listeners_destroyed == num_listeners(server)); + + while (server->listeners) { + l = server->listeners; + server->listeners = l->next; + gpr_free(l); + } + + gpr_mu_unlock(&server->mu_global); + + server_unref(server); +} + +void grpc_server_add_listener(grpc_server *server, void *arg, + void (*start)(grpc_server *server, void *arg, + grpc_pollset **pollsets, + size_t pollset_count), + void (*destroy)(grpc_server *server, void *arg)) { + listener *l = gpr_malloc(sizeof(listener)); + l->arg = arg; + l->start = start; + l->destroy = destroy; + l->next = server->listeners; + server->listeners = l; +} + +static grpc_call_error queue_call_request(grpc_server *server, + requested_call *rc) { + call_data *calld = NULL; + request_matcher *request_matcher = NULL; + int request_id; + if (gpr_atm_acq_load(&server->shutdown_flag)) { + fail_call(server, rc); + return GRPC_CALL_OK; + } + request_id = gpr_stack_lockfree_pop(server->request_freelist); + if (request_id == -1) { + /* out of request ids: just fail this one */ + fail_call(server, rc); + return GRPC_CALL_OK; + } + switch (rc->type) { + case BATCH_CALL: + request_matcher = &server->unregistered_request_matcher; + break; + case REGISTERED_CALL: + request_matcher = &rc->data.registered.registered_method->request_matcher; + break; + } + server->requested_calls[request_id] = *rc; + gpr_free(rc); + if (gpr_stack_lockfree_push(request_matcher->requests, request_id)) { + /* this was the first queued request: we need to lock and start + matching calls */ + gpr_mu_lock(&server->mu_call); + while ((calld = request_matcher->pending_head) != NULL) { + request_id = gpr_stack_lockfree_pop(request_matcher->requests); + if (request_id == -1) break; + request_matcher->pending_head = calld->pending_next; + gpr_mu_unlock(&server->mu_call); + gpr_mu_lock(&calld->mu_state); + if (calld->state == ZOMBIED) { + gpr_mu_unlock(&calld->mu_state); + grpc_iomgr_closure_init( + &calld->kill_zombie_closure, kill_zombie, + grpc_call_stack_element(grpc_call_get_call_stack(calld->call), 0)); + grpc_iomgr_add_callback(&calld->kill_zombie_closure); + } else { + GPR_ASSERT(calld->state == PENDING); + calld->state = ACTIVATED; + gpr_mu_unlock(&calld->mu_state); + begin_call(server, calld, &server->requested_calls[request_id]); + } + gpr_mu_lock(&server->mu_call); + } + gpr_mu_unlock(&server->mu_call); + } + return GRPC_CALL_OK; +} + +grpc_call_error grpc_server_request_call( + grpc_server *server, grpc_call **call, grpc_call_details *details, + grpc_metadata_array *initial_metadata, + grpc_completion_queue *cq_bound_to_call, + grpc_completion_queue *cq_for_notification, void *tag) { + requested_call *rc = gpr_malloc(sizeof(*rc)); + GRPC_SERVER_LOG_REQUEST_CALL(GPR_INFO, server, call, details, + initial_metadata, cq_bound_to_call, + cq_for_notification, tag); + if (!grpc_cq_is_server_cq(cq_for_notification)) { + gpr_free(rc); + return GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE; + } + grpc_cq_begin_op(cq_for_notification); + details->reserved = NULL; + rc->type = BATCH_CALL; + rc->server = server; + rc->tag = tag; + rc->cq_bound_to_call = cq_bound_to_call; + rc->cq_for_notification = cq_for_notification; + rc->call = call; + rc->data.batch.details = details; + rc->data.batch.initial_metadata = initial_metadata; + return queue_call_request(server, rc); +} + +grpc_call_error grpc_server_request_registered_call( + grpc_server *server, void *rm, grpc_call **call, gpr_timespec *deadline, + grpc_metadata_array *initial_metadata, grpc_byte_buffer **optional_payload, + grpc_completion_queue *cq_bound_to_call, + grpc_completion_queue *cq_for_notification, void *tag) { + requested_call *rc = gpr_malloc(sizeof(*rc)); + registered_method *registered_method = rm; + if (!grpc_cq_is_server_cq(cq_for_notification)) { + gpr_free(rc); + return GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE; + } + grpc_cq_begin_op(cq_for_notification); + rc->type = REGISTERED_CALL; + rc->server = server; + rc->tag = tag; + rc->cq_bound_to_call = cq_bound_to_call; + rc->cq_for_notification = cq_for_notification; + rc->call = call; + rc->data.registered.registered_method = registered_method; + rc->data.registered.deadline = deadline; + rc->data.registered.initial_metadata = initial_metadata; + rc->data.registered.optional_payload = optional_payload; + return queue_call_request(server, rc); +} + +static void publish_registered_or_batch(grpc_call *call, int success, + void *tag); +static void publish_was_not_set(grpc_call *call, int success, void *tag) { + abort(); +} + +static void cpstr(char **dest, size_t *capacity, grpc_mdstr *value) { + gpr_slice slice = value->slice; + size_t len = GPR_SLICE_LENGTH(slice); + + if (len + 1 > *capacity) { + *capacity = GPR_MAX(len + 1, *capacity * 2); + *dest = gpr_realloc(*dest, *capacity); + } + memcpy(*dest, grpc_mdstr_as_c_string(value), len + 1); +} + +static void begin_call(grpc_server *server, call_data *calld, + requested_call *rc) { + grpc_ioreq_completion_func publish = publish_was_not_set; + grpc_ioreq req[2]; + grpc_ioreq *r = req; + + /* called once initial metadata has been read by the call, but BEFORE + the ioreq to fetch it out of the call has been executed. + This means metadata related fields can be relied on in calld, but to + fill in the metadata array passed by the client, we need to perform + an ioreq op, that should complete immediately. */ + + grpc_call_set_completion_queue(calld->call, rc->cq_bound_to_call); + *rc->call = calld->call; + calld->cq_new = rc->cq_for_notification; + switch (rc->type) { + case BATCH_CALL: + GPR_ASSERT(calld->host != NULL); + GPR_ASSERT(calld->path != NULL); + cpstr(&rc->data.batch.details->host, + &rc->data.batch.details->host_capacity, calld->host); + cpstr(&rc->data.batch.details->method, + &rc->data.batch.details->method_capacity, calld->path); + rc->data.batch.details->deadline = calld->deadline; + r->op = GRPC_IOREQ_RECV_INITIAL_METADATA; + r->data.recv_metadata = rc->data.batch.initial_metadata; + r->flags = 0; + r++; + publish = publish_registered_or_batch; + break; + case REGISTERED_CALL: + *rc->data.registered.deadline = calld->deadline; + r->op = GRPC_IOREQ_RECV_INITIAL_METADATA; + r->data.recv_metadata = rc->data.registered.initial_metadata; + r->flags = 0; + r++; + if (rc->data.registered.optional_payload) { + r->op = GRPC_IOREQ_RECV_MESSAGE; + r->data.recv_message = rc->data.registered.optional_payload; + r->flags = 0; + r++; + } + publish = publish_registered_or_batch; + break; + } + + GRPC_CALL_INTERNAL_REF(calld->call, "server"); + grpc_call_start_ioreq_and_call_back(calld->call, req, r - req, publish, rc); +} + +static void done_request_event(void *req, grpc_cq_completion *c) { + requested_call *rc = req; + grpc_server *server = rc->server; + + if (rc >= server->requested_calls && + rc < server->requested_calls + server->max_requested_calls) { + gpr_stack_lockfree_push(server->request_freelist, + rc - server->requested_calls); + } else { + gpr_free(req); + } + + server_unref(server); +} + +static void fail_call(grpc_server *server, requested_call *rc) { + *rc->call = NULL; + switch (rc->type) { + case BATCH_CALL: + rc->data.batch.initial_metadata->count = 0; + break; + case REGISTERED_CALL: + rc->data.registered.initial_metadata->count = 0; + break; + } + server_ref(server); + grpc_cq_end_op(rc->cq_for_notification, rc->tag, 0, done_request_event, rc, + &rc->completion); +} + +static void publish_registered_or_batch(grpc_call *call, int success, + void *prc) { + grpc_call_element *elem = + grpc_call_stack_element(grpc_call_get_call_stack(call), 0); + requested_call *rc = prc; + call_data *calld = elem->call_data; + channel_data *chand = elem->channel_data; + server_ref(chand->server); + grpc_cq_end_op(calld->cq_new, rc->tag, success, done_request_event, rc, + &rc->completion); + GRPC_CALL_INTERNAL_UNREF(call, "server", 0); +} + +const grpc_channel_args *grpc_server_get_channel_args(grpc_server *server) { + return server->channel_args; +} + +int grpc_server_has_open_connections(grpc_server *server) { + int r; + gpr_mu_lock(&server->mu_global); + r = server->root_channel_data.next != &server->root_channel_data; + gpr_mu_unlock(&server->mu_global); + return r; +} diff --git a/src/core/surface/server.h b/src/core/surface/server.h new file mode 100644 index 00000000..c638d682 --- /dev/null +++ b/src/core/surface/server.h @@ -0,0 +1,67 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_SURFACE_SERVER_H +#define GRPC_INTERNAL_CORE_SURFACE_SERVER_H + +#include "src/core/channel/channel_stack.h" +#include +#include "src/core/transport/transport.h" + +/* Create a server */ +grpc_server *grpc_server_create_from_filters( + const grpc_channel_filter **filters, size_t filter_count, + const grpc_channel_args *args); + +/* Add a listener to the server: when the server starts, it will call start, + and when it shuts down, it will call destroy */ +void grpc_server_add_listener(grpc_server *server, void *listener, + void (*start)(grpc_server *server, void *arg, + grpc_pollset **pollsets, + size_t npollsets), + void (*destroy)(grpc_server *server, void *arg)); + +void grpc_server_listener_destroy_done(void *server); + +/* Setup a transport - creates a channel stack, binds the transport to the + server */ +void grpc_server_setup_transport(grpc_server *server, grpc_transport *transport, + grpc_channel_filter const **extra_filters, + size_t num_extra_filters, grpc_mdctx *mdctx, + const grpc_channel_args *args); + +const grpc_channel_args *grpc_server_get_channel_args(grpc_server *server); + +int grpc_server_has_open_connections(grpc_server *server); + +#endif /* GRPC_INTERNAL_CORE_SURFACE_SERVER_H */ diff --git a/src/core/surface/server_chttp2.c b/src/core/surface/server_chttp2.c new file mode 100644 index 00000000..4ab845bc --- /dev/null +++ b/src/core/surface/server_chttp2.c @@ -0,0 +1,139 @@ +/* + * + * Copyright 2015, 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 + +#include "src/core/channel/http_server_filter.h" +#include "src/core/iomgr/resolve_address.h" +#include "src/core/iomgr/tcp_server.h" +#include "src/core/surface/server.h" +#include "src/core/transport/chttp2_transport.h" +#include +#include +#include + +static void setup_transport(void *server, grpc_transport *transport, + grpc_mdctx *mdctx) { + static grpc_channel_filter const *extra_filters[] = { + &grpc_http_server_filter}; + grpc_server_setup_transport(server, transport, extra_filters, + GPR_ARRAY_SIZE(extra_filters), mdctx, + grpc_server_get_channel_args(server)); +} + +static void new_transport(void *server, grpc_endpoint *tcp) { + /* + * Beware that the call to grpc_create_chttp2_transport() has to happen before + * grpc_tcp_server_destroy(). This is fine here, but similar code + * asynchronously doing a handshake instead of calling grpc_tcp_server_start() + * (as in server_secure_chttp2.c) needs to add synchronization to avoid this + * case. + */ + grpc_mdctx *mdctx = grpc_mdctx_create(); + grpc_transport *transport = grpc_create_chttp2_transport( + grpc_server_get_channel_args(server), tcp, mdctx, 0); + setup_transport(server, transport, mdctx); + grpc_chttp2_transport_start_reading(transport, NULL, 0); +} + +/* Server callback: start listening on our ports */ +static void start(grpc_server *server, void *tcpp, grpc_pollset **pollsets, + size_t pollset_count) { + grpc_tcp_server *tcp = tcpp; + grpc_tcp_server_start(tcp, pollsets, pollset_count, new_transport, server); +} + +/* Server callback: destroy the tcp listener (so we don't generate further + callbacks) */ +static void destroy(grpc_server *server, void *tcpp) { + grpc_tcp_server *tcp = tcpp; + grpc_tcp_server_destroy(tcp, grpc_server_listener_destroy_done, server); +} + +int grpc_server_add_insecure_http2_port(grpc_server *server, const char *addr) { + grpc_resolved_addresses *resolved = NULL; + grpc_tcp_server *tcp = NULL; + size_t i; + unsigned count = 0; + int port_num = -1; + int port_temp; + + resolved = grpc_blocking_resolve_address(addr, "http"); + if (!resolved) { + goto error; + } + + tcp = grpc_tcp_server_create(); + if (!tcp) { + goto error; + } + + for (i = 0; i < resolved->naddrs; i++) { + port_temp = grpc_tcp_server_add_port( + tcp, (struct sockaddr *)&resolved->addrs[i].addr, + resolved->addrs[i].len); + if (port_temp >= 0) { + if (port_num == -1) { + port_num = port_temp; + } else { + GPR_ASSERT(port_num == port_temp); + } + count++; + } + } + if (count == 0) { + gpr_log(GPR_ERROR, "No address added out of total %d resolved", + resolved->naddrs); + goto error; + } + if (count != resolved->naddrs) { + gpr_log(GPR_ERROR, "Only %d addresses added out of total %d resolved", + count, resolved->naddrs); + } + grpc_resolved_addresses_destroy(resolved); + + /* Register with the server only upon success */ + grpc_server_add_listener(server, tcp, start, destroy); + + return port_num; + +/* Error path: cleanup and return */ +error: + if (resolved) { + grpc_resolved_addresses_destroy(resolved); + } + if (tcp) { + grpc_tcp_server_destroy(tcp, NULL, NULL); + } + return 0; +} diff --git a/src/core/surface/server_create.c b/src/core/surface/server_create.c new file mode 100644 index 00000000..fc7ae820 --- /dev/null +++ b/src/core/surface/server_create.c @@ -0,0 +1,44 @@ +/* + * + * Copyright 2015, 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 +#include "src/core/surface/completion_queue.h" +#include "src/core/surface/server.h" +#include "src/core/channel/compress_filter.h" + +grpc_server *grpc_server_create(const grpc_channel_args *args, void *reserved) { + const grpc_channel_filter *filters[] = {&grpc_compress_filter}; + (void)reserved; + return grpc_server_create_from_filters(filters, GPR_ARRAY_SIZE(filters), + args); +} diff --git a/src/core/surface/surface_trace.c b/src/core/surface/surface_trace.c new file mode 100644 index 00000000..57a00531 --- /dev/null +++ b/src/core/surface/surface_trace.c @@ -0,0 +1,36 @@ +/* + * + * Copyright 2015, 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 "src/core/surface/surface_trace.h" + +int grpc_surface_trace = 0; diff --git a/src/core/surface/surface_trace.h b/src/core/surface/surface_trace.h new file mode 100644 index 00000000..2b4728e2 --- /dev/null +++ b/src/core/surface/surface_trace.h @@ -0,0 +1,49 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_SURFACE_SURFACE_TRACE_H +#define GRPC_INTERNAL_CORE_SURFACE_SURFACE_TRACE_H + +#include "src/core/debug/trace.h" +#include + +extern int grpc_surface_trace; + +#define GRPC_SURFACE_TRACE_RETURNED_EVENT(cq, event) \ + if (grpc_surface_trace) { \ + char *_ev = grpc_event_string(event); \ + gpr_log(GPR_INFO, "RETURN_EVENT[%p]: %s", cq, _ev); \ + gpr_free(_ev); \ + } + +#endif /* GRPC_INTERNAL_CORE_SURFACE_SURFACE_TRACE_H */ diff --git a/src/core/surface/version.c b/src/core/surface/version.c new file mode 100644 index 00000000..4b90e06a --- /dev/null +++ b/src/core/surface/version.c @@ -0,0 +1,41 @@ +/* + * + * Copyright 2015, 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. + * + */ + +/* This file is autogenerated from: + templates/src/core/surface/version.c.template */ + +#include + +const char *grpc_version_string(void) { + return "0.11.0.0"; +} diff --git a/src/core/transport/chttp2/alpn.c b/src/core/transport/chttp2/alpn.c new file mode 100644 index 00000000..69da4e67 --- /dev/null +++ b/src/core/transport/chttp2/alpn.c @@ -0,0 +1,56 @@ +/* + * + * Copyright 2015, 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 "src/core/transport/chttp2/alpn.h" +#include +#include + +/* in order of preference */ +static const char *const supported_versions[] = {"h2"}; + +int grpc_chttp2_is_alpn_version_supported(const char *version, size_t size) { + size_t i; + for (i = 0; i < GPR_ARRAY_SIZE(supported_versions); i++) { + if (!strncmp(version, supported_versions[i], size)) return 1; + } + return 0; +} + +size_t grpc_chttp2_num_alpn_versions(void) { + return GPR_ARRAY_SIZE(supported_versions); +} + +const char *grpc_chttp2_get_alpn_version_index(size_t i) { + GPR_ASSERT(i < GPR_ARRAY_SIZE(supported_versions)); + return supported_versions[i]; +} diff --git a/src/core/transport/chttp2/alpn.h b/src/core/transport/chttp2/alpn.h new file mode 100644 index 00000000..f38b4c31 --- /dev/null +++ b/src/core/transport/chttp2/alpn.h @@ -0,0 +1,49 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_TRANSPORT_CHTTP2_ALPN_H +#define GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_ALPN_H + +#include + +/* Retuns 1 if the version is supported, 0 otherwise. */ +int grpc_chttp2_is_alpn_version_supported(const char *version, size_t size); + +/* Returns the number of protocol versions to advertise */ +size_t grpc_chttp2_num_alpn_versions(void); + +/* Returns the protocol version at index i (0 <= i < + * grpc_chttp2_num_alpn_versions()) */ +const char *grpc_chttp2_get_alpn_version_index(size_t i); + +#endif /* GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_ALPN_H */ diff --git a/src/core/transport/chttp2/bin_encoder.c b/src/core/transport/chttp2/bin_encoder.c new file mode 100644 index 00000000..dee6dbec --- /dev/null +++ b/src/core/transport/chttp2/bin_encoder.c @@ -0,0 +1,227 @@ +/* + * + * Copyright 2015, 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 "src/core/transport/chttp2/bin_encoder.h" + +#include + +#include "src/core/transport/chttp2/huffsyms.h" +#include + +static const char alphabet[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +typedef struct { + gpr_uint16 bits; + gpr_uint8 length; +} b64_huff_sym; + +static const b64_huff_sym huff_alphabet[64] = { + {0x21, 6}, {0x5d, 7}, {0x5e, 7}, {0x5f, 7}, {0x60, 7}, {0x61, 7}, + {0x62, 7}, {0x63, 7}, {0x64, 7}, {0x65, 7}, {0x66, 7}, {0x67, 7}, + {0x68, 7}, {0x69, 7}, {0x6a, 7}, {0x6b, 7}, {0x6c, 7}, {0x6d, 7}, + {0x6e, 7}, {0x6f, 7}, {0x70, 7}, {0x71, 7}, {0x72, 7}, {0xfc, 8}, + {0x73, 7}, {0xfd, 8}, {0x3, 5}, {0x23, 6}, {0x4, 5}, {0x24, 6}, + {0x5, 5}, {0x25, 6}, {0x26, 6}, {0x27, 6}, {0x6, 5}, {0x74, 7}, + {0x75, 7}, {0x28, 6}, {0x29, 6}, {0x2a, 6}, {0x7, 5}, {0x2b, 6}, + {0x76, 7}, {0x2c, 6}, {0x8, 5}, {0x9, 5}, {0x2d, 6}, {0x77, 7}, + {0x78, 7}, {0x79, 7}, {0x7a, 7}, {0x7b, 7}, {0x0, 5}, {0x1, 5}, + {0x2, 5}, {0x19, 6}, {0x1a, 6}, {0x1b, 6}, {0x1c, 6}, {0x1d, 6}, + {0x1e, 6}, {0x1f, 6}, {0x7fb, 11}, {0x18, 6}}; + +static const gpr_uint8 tail_xtra[3] = {0, 2, 3}; + +gpr_slice grpc_chttp2_base64_encode(gpr_slice input) { + size_t input_length = GPR_SLICE_LENGTH(input); + size_t input_triplets = input_length / 3; + size_t tail_case = input_length % 3; + size_t output_length = input_triplets * 4 + tail_xtra[tail_case]; + gpr_slice output = gpr_slice_malloc(output_length); + gpr_uint8 *in = GPR_SLICE_START_PTR(input); + gpr_uint8 *out = GPR_SLICE_START_PTR(output); + size_t i; + + /* encode full triplets */ + for (i = 0; i < input_triplets; i++) { + out[0] = alphabet[in[0] >> 2]; + out[1] = alphabet[((in[0] & 0x3) << 4) | (in[1] >> 4)]; + out[2] = alphabet[((in[1] & 0xf) << 2) | (in[2] >> 6)]; + out[3] = alphabet[in[2] & 0x3f]; + out += 4; + in += 3; + } + + /* encode the remaining bytes */ + switch (tail_case) { + case 0: + break; + case 1: + out[0] = alphabet[in[0] >> 2]; + out[1] = alphabet[(in[0] & 0x3) << 4]; + out += 2; + in += 1; + break; + case 2: + out[0] = alphabet[in[0] >> 2]; + out[1] = alphabet[((in[0] & 0x3) << 4) | (in[1] >> 4)]; + out[2] = alphabet[(in[1] & 0xf) << 2]; + out += 3; + in += 2; + break; + } + + GPR_ASSERT(out == GPR_SLICE_END_PTR(output)); + GPR_ASSERT(in == GPR_SLICE_END_PTR(input)); + return output; +} + +gpr_slice grpc_chttp2_huffman_compress(gpr_slice input) { + size_t nbits; + gpr_uint8 *in; + gpr_uint8 *out; + gpr_slice output; + gpr_uint32 temp = 0; + gpr_uint32 temp_length = 0; + + nbits = 0; + for (in = GPR_SLICE_START_PTR(input); in != GPR_SLICE_END_PTR(input); ++in) { + nbits += grpc_chttp2_huffsyms[*in].length; + } + + output = gpr_slice_malloc(nbits / 8 + (nbits % 8 != 0)); + out = GPR_SLICE_START_PTR(output); + for (in = GPR_SLICE_START_PTR(input); in != GPR_SLICE_END_PTR(input); ++in) { + int sym = *in; + temp <<= grpc_chttp2_huffsyms[sym].length; + temp |= grpc_chttp2_huffsyms[sym].bits; + temp_length += grpc_chttp2_huffsyms[sym].length; + + while (temp_length > 8) { + temp_length -= 8; + *out++ = temp >> temp_length; + } + } + + if (temp_length) { + *out++ = (temp << (8 - temp_length)) | (0xff >> temp_length); + } + + GPR_ASSERT(out == GPR_SLICE_END_PTR(output)); + + return output; +} + +typedef struct { + gpr_uint32 temp; + gpr_uint32 temp_length; + gpr_uint8 *out; +} huff_out; + +static void enc_flush_some(huff_out *out) { + while (out->temp_length > 8) { + out->temp_length -= 8; + *out->out++ = out->temp >> out->temp_length; + } +} + +static void enc_add2(huff_out *out, gpr_uint8 a, gpr_uint8 b) { + b64_huff_sym sa = huff_alphabet[a]; + b64_huff_sym sb = huff_alphabet[b]; + out->temp = + (out->temp << (sa.length + sb.length)) | (sa.bits << sb.length) | sb.bits; + out->temp_length += sa.length + sb.length; + enc_flush_some(out); +} + +static void enc_add1(huff_out *out, gpr_uint8 a) { + b64_huff_sym sa = huff_alphabet[a]; + out->temp = (out->temp << sa.length) | sa.bits; + out->temp_length += sa.length; + enc_flush_some(out); +} + +gpr_slice grpc_chttp2_base64_encode_and_huffman_compress(gpr_slice input) { + size_t input_length = GPR_SLICE_LENGTH(input); + size_t input_triplets = input_length / 3; + size_t tail_case = input_length % 3; + size_t output_syms = input_triplets * 4 + tail_xtra[tail_case]; + size_t max_output_bits = 11 * output_syms; + size_t max_output_length = max_output_bits / 8 + (max_output_bits % 8 != 0); + gpr_slice output = gpr_slice_malloc(max_output_length); + gpr_uint8 *in = GPR_SLICE_START_PTR(input); + gpr_uint8 *start_out = GPR_SLICE_START_PTR(output); + huff_out out; + size_t i; + + out.temp = 0; + out.temp_length = 0; + out.out = start_out; + + /* encode full triplets */ + for (i = 0; i < input_triplets; i++) { + enc_add2(&out, in[0] >> 2, ((in[0] & 0x3) << 4) | (in[1] >> 4)); + enc_add2(&out, ((in[1] & 0xf) << 2) | (in[2] >> 6), in[2] & 0x3f); + in += 3; + } + + /* encode the remaining bytes */ + switch (tail_case) { + case 0: + break; + case 1: + enc_add2(&out, in[0] >> 2, (in[0] & 0x3) << 4); + in += 1; + break; + case 2: + enc_add2(&out, in[0] >> 2, ((in[0] & 0x3) << 4) | (in[1] >> 4)); + enc_add1(&out, (in[1] & 0xf) << 2); + in += 2; + break; + } + + if (out.temp_length) { + *out.out++ = + (out.temp << (8 - out.temp_length)) | (0xff >> out.temp_length); + } + + GPR_ASSERT(out.out <= GPR_SLICE_END_PTR(output)); + GPR_SLICE_SET_LENGTH(output, out.out - start_out); + + GPR_ASSERT(in == GPR_SLICE_END_PTR(input)); + return output; +} + +int grpc_is_binary_header(const char *key, size_t length) { + if (length < 5) return 0; + return 0 == memcmp(key + length - 4, "-bin", 4); +} diff --git a/src/core/transport/chttp2/bin_encoder.h b/src/core/transport/chttp2/bin_encoder.h new file mode 100644 index 00000000..d3e5a855 --- /dev/null +++ b/src/core/transport/chttp2/bin_encoder.h @@ -0,0 +1,56 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_TRANSPORT_CHTTP2_BIN_ENCODER_H +#define GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_BIN_ENCODER_H + +#include + +/* base64 encode a slice. Returns a new slice, does not take ownership of the + input */ +gpr_slice grpc_chttp2_base64_encode(gpr_slice input); + +/* Compress a slice with the static huffman encoder detailed in the hpack + standard. Returns a new slice, does not take ownership of the input */ +gpr_slice grpc_chttp2_huffman_compress(gpr_slice input); + +/* equivalent to: + gpr_slice x = grpc_chttp2_base64_encode(input); + gpr_slice y = grpc_chttp2_huffman_compress(x); + gpr_slice_unref(x); + return y; */ +gpr_slice grpc_chttp2_base64_encode_and_huffman_compress(gpr_slice input); + +int grpc_is_binary_header(const char *key, size_t length); + +#endif /* GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_BIN_ENCODER_H */ diff --git a/src/core/transport/chttp2/frame.h b/src/core/transport/chttp2/frame.h new file mode 100644 index 00000000..879ee036 --- /dev/null +++ b/src/core/transport/chttp2/frame.h @@ -0,0 +1,69 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_TRANSPORT_CHTTP2_FRAME_H +#define GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_FRAME_H + +#include +#include + +/* Common definitions for frame handling in the chttp2 transport */ + +typedef enum { + GRPC_CHTTP2_PARSE_OK, + GRPC_CHTTP2_STREAM_ERROR, + GRPC_CHTTP2_CONNECTION_ERROR +} grpc_chttp2_parse_error; + +/* defined in internal.h */ +typedef struct grpc_chttp2_stream_parsing grpc_chttp2_stream_parsing; +typedef struct grpc_chttp2_transport_parsing grpc_chttp2_transport_parsing; + +#define GRPC_CHTTP2_FRAME_DATA 0 +#define GRPC_CHTTP2_FRAME_HEADER 1 +#define GRPC_CHTTP2_FRAME_CONTINUATION 9 +#define GRPC_CHTTP2_FRAME_RST_STREAM 3 +#define GRPC_CHTTP2_FRAME_SETTINGS 4 +#define GRPC_CHTTP2_FRAME_PING 6 +#define GRPC_CHTTP2_FRAME_GOAWAY 7 +#define GRPC_CHTTP2_FRAME_WINDOW_UPDATE 8 + +#define GRPC_CHTTP2_MAX_PAYLOAD_LENGTH ((1 << 14) - 1) + +#define GRPC_CHTTP2_DATA_FLAG_END_STREAM 1 +#define GRPC_CHTTP2_FLAG_ACK 1 +#define GRPC_CHTTP2_DATA_FLAG_END_HEADERS 4 +#define GRPC_CHTTP2_DATA_FLAG_PADDED 8 +#define GRPC_CHTTP2_FLAG_HAS_PRIORITY 0x20 + +#endif /* GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_FRAME_H */ diff --git a/src/core/transport/chttp2/frame_data.c b/src/core/transport/chttp2/frame_data.c new file mode 100644 index 00000000..474c3d5e --- /dev/null +++ b/src/core/transport/chttp2/frame_data.c @@ -0,0 +1,170 @@ +/* + * + * Copyright 2015, 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 "src/core/transport/chttp2/frame_data.h" + +#include + +#include "src/core/transport/chttp2/internal.h" +#include "src/core/support/string.h" +#include +#include +#include +#include "src/core/transport/transport.h" + +grpc_chttp2_parse_error grpc_chttp2_data_parser_init( + grpc_chttp2_data_parser *parser) { + parser->state = GRPC_CHTTP2_DATA_FH_0; + grpc_sopb_init(&parser->incoming_sopb); + return GRPC_CHTTP2_PARSE_OK; +} + +void grpc_chttp2_data_parser_destroy(grpc_chttp2_data_parser *parser) { + grpc_sopb_destroy(&parser->incoming_sopb); +} + +grpc_chttp2_parse_error grpc_chttp2_data_parser_begin_frame( + grpc_chttp2_data_parser *parser, gpr_uint8 flags) { + if (flags & ~GRPC_CHTTP2_DATA_FLAG_END_STREAM) { + gpr_log(GPR_ERROR, "unsupported data flags: 0x%02x", flags); + return GRPC_CHTTP2_STREAM_ERROR; + } + + if (flags & GRPC_CHTTP2_DATA_FLAG_END_STREAM) { + parser->is_last_frame = 1; + } else { + parser->is_last_frame = 0; + } + + return GRPC_CHTTP2_PARSE_OK; +} + +grpc_chttp2_parse_error grpc_chttp2_data_parser_parse( + void *parser, grpc_chttp2_transport_parsing *transport_parsing, + grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last) { + gpr_uint8 *const beg = GPR_SLICE_START_PTR(slice); + gpr_uint8 *const end = GPR_SLICE_END_PTR(slice); + gpr_uint8 *cur = beg; + grpc_chttp2_data_parser *p = parser; + gpr_uint32 message_flags = 0; + + if (is_last && p->is_last_frame) { + stream_parsing->received_close = 1; + } + + if (cur == end) { + return GRPC_CHTTP2_PARSE_OK; + } + + switch (p->state) { + fh_0: + case GRPC_CHTTP2_DATA_FH_0: + p->frame_type = *cur; + switch (p->frame_type) { + case 0: + p->is_frame_compressed = 0; /* GPR_FALSE */ + break; + case 1: + p->is_frame_compressed = 1; /* GPR_TRUE */ + break; + default: + gpr_log(GPR_ERROR, "Bad GRPC frame type 0x%02x", p->frame_type); + return GRPC_CHTTP2_STREAM_ERROR; + } + if (++cur == end) { + p->state = GRPC_CHTTP2_DATA_FH_1; + return GRPC_CHTTP2_PARSE_OK; + } + /* fallthrough */ + case GRPC_CHTTP2_DATA_FH_1: + p->frame_size = ((gpr_uint32)*cur) << 24; + if (++cur == end) { + p->state = GRPC_CHTTP2_DATA_FH_2; + return GRPC_CHTTP2_PARSE_OK; + } + /* fallthrough */ + case GRPC_CHTTP2_DATA_FH_2: + p->frame_size |= ((gpr_uint32)*cur) << 16; + if (++cur == end) { + p->state = GRPC_CHTTP2_DATA_FH_3; + return GRPC_CHTTP2_PARSE_OK; + } + /* fallthrough */ + case GRPC_CHTTP2_DATA_FH_3: + p->frame_size |= ((gpr_uint32)*cur) << 8; + if (++cur == end) { + p->state = GRPC_CHTTP2_DATA_FH_4; + return GRPC_CHTTP2_PARSE_OK; + } + /* fallthrough */ + case GRPC_CHTTP2_DATA_FH_4: + p->frame_size |= ((gpr_uint32)*cur); + p->state = GRPC_CHTTP2_DATA_FRAME; + ++cur; + if (p->is_frame_compressed) { + message_flags |= GRPC_WRITE_INTERNAL_COMPRESS; + } + grpc_sopb_add_begin_message(&p->incoming_sopb, p->frame_size, + message_flags); + /* fallthrough */ + case GRPC_CHTTP2_DATA_FRAME: + if (cur == end) { + grpc_chttp2_list_add_parsing_seen_stream(transport_parsing, + stream_parsing); + return GRPC_CHTTP2_PARSE_OK; + } + grpc_chttp2_list_add_parsing_seen_stream(transport_parsing, + stream_parsing); + if ((gpr_uint32)(end - cur) == p->frame_size) { + grpc_sopb_add_slice(&p->incoming_sopb, + gpr_slice_sub(slice, cur - beg, end - beg)); + p->state = GRPC_CHTTP2_DATA_FH_0; + return GRPC_CHTTP2_PARSE_OK; + } else if ((gpr_uint32)(end - cur) > p->frame_size) { + grpc_sopb_add_slice( + &p->incoming_sopb, + gpr_slice_sub(slice, cur - beg, cur + p->frame_size - beg)); + cur += p->frame_size; + goto fh_0; /* loop */ + } else { + grpc_sopb_add_slice(&p->incoming_sopb, + gpr_slice_sub(slice, cur - beg, end - beg)); + p->frame_size -= (end - cur); + return GRPC_CHTTP2_PARSE_OK; + } + } + + gpr_log(GPR_ERROR, "should never reach here"); + abort(); + return GRPC_CHTTP2_CONNECTION_ERROR; +} diff --git a/src/core/transport/chttp2/frame_data.h b/src/core/transport/chttp2/frame_data.h new file mode 100644 index 00000000..23957b05 --- /dev/null +++ b/src/core/transport/chttp2/frame_data.h @@ -0,0 +1,82 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_TRANSPORT_CHTTP2_FRAME_DATA_H +#define GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_FRAME_DATA_H + +/* Parser for GRPC streams embedded in DATA frames */ + +#include +#include +#include "src/core/transport/stream_op.h" +#include "src/core/transport/chttp2/frame.h" + +typedef enum { + GRPC_CHTTP2_DATA_FH_0, + GRPC_CHTTP2_DATA_FH_1, + GRPC_CHTTP2_DATA_FH_2, + GRPC_CHTTP2_DATA_FH_3, + GRPC_CHTTP2_DATA_FH_4, + GRPC_CHTTP2_DATA_FRAME +} grpc_chttp2_stream_state; + +typedef struct { + grpc_chttp2_stream_state state; + gpr_uint8 is_last_frame; + gpr_uint8 frame_type; + gpr_uint32 frame_size; + + int is_frame_compressed; + grpc_stream_op_buffer incoming_sopb; +} grpc_chttp2_data_parser; + +/* initialize per-stream state for data frame parsing */ +grpc_chttp2_parse_error grpc_chttp2_data_parser_init( + grpc_chttp2_data_parser *parser); + +void grpc_chttp2_data_parser_destroy(grpc_chttp2_data_parser *parser); + +/* start processing a new data frame */ +grpc_chttp2_parse_error grpc_chttp2_data_parser_begin_frame( + grpc_chttp2_data_parser *parser, gpr_uint8 flags); + +/* handle a slice of a data frame - is_last indicates the last slice of a + frame */ +grpc_chttp2_parse_error grpc_chttp2_data_parser_parse( + void *parser, grpc_chttp2_transport_parsing *transport_parsing, + grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last); + +/* create a slice with an empty data frame and is_last set */ +gpr_slice grpc_chttp2_data_frame_create_empty_close(gpr_uint32 id); + +#endif /* GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_FRAME_DATA_H */ diff --git a/src/core/transport/chttp2/frame_goaway.c b/src/core/transport/chttp2/frame_goaway.c new file mode 100644 index 00000000..1ccbba84 --- /dev/null +++ b/src/core/transport/chttp2/frame_goaway.c @@ -0,0 +1,191 @@ +/* + * + * Copyright 2015, 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 "src/core/transport/chttp2/frame_goaway.h" +#include "src/core/transport/chttp2/internal.h" + +#include + +#include +#include + +void grpc_chttp2_goaway_parser_init(grpc_chttp2_goaway_parser *p) { + p->debug_data = NULL; +} + +void grpc_chttp2_goaway_parser_destroy(grpc_chttp2_goaway_parser *p) { + gpr_free(p->debug_data); +} + +grpc_chttp2_parse_error grpc_chttp2_goaway_parser_begin_frame( + grpc_chttp2_goaway_parser *p, gpr_uint32 length, gpr_uint8 flags) { + if (length < 8) { + gpr_log(GPR_ERROR, "goaway frame too short (%d bytes)", length); + return GRPC_CHTTP2_CONNECTION_ERROR; + } + + gpr_free(p->debug_data); + p->debug_length = length - 8; + p->debug_data = gpr_malloc(p->debug_length); + p->debug_pos = 0; + p->state = GRPC_CHTTP2_GOAWAY_LSI0; + return GRPC_CHTTP2_PARSE_OK; +} + +grpc_chttp2_parse_error grpc_chttp2_goaway_parser_parse( + void *parser, grpc_chttp2_transport_parsing *transport_parsing, + grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last) { + gpr_uint8 *const beg = GPR_SLICE_START_PTR(slice); + gpr_uint8 *const end = GPR_SLICE_END_PTR(slice); + gpr_uint8 *cur = beg; + grpc_chttp2_goaway_parser *p = parser; + + switch (p->state) { + case GRPC_CHTTP2_GOAWAY_LSI0: + if (cur == end) { + p->state = GRPC_CHTTP2_GOAWAY_LSI0; + return GRPC_CHTTP2_PARSE_OK; + } + p->last_stream_id = ((gpr_uint32)*cur) << 24; + ++cur; + /* fallthrough */ + case GRPC_CHTTP2_GOAWAY_LSI1: + if (cur == end) { + p->state = GRPC_CHTTP2_GOAWAY_LSI1; + return GRPC_CHTTP2_PARSE_OK; + } + p->last_stream_id |= ((gpr_uint32)*cur) << 16; + ++cur; + /* fallthrough */ + case GRPC_CHTTP2_GOAWAY_LSI2: + if (cur == end) { + p->state = GRPC_CHTTP2_GOAWAY_LSI2; + return GRPC_CHTTP2_PARSE_OK; + } + p->last_stream_id |= ((gpr_uint32)*cur) << 8; + ++cur; + /* fallthrough */ + case GRPC_CHTTP2_GOAWAY_LSI3: + if (cur == end) { + p->state = GRPC_CHTTP2_GOAWAY_LSI3; + return GRPC_CHTTP2_PARSE_OK; + } + p->last_stream_id |= ((gpr_uint32)*cur); + ++cur; + /* fallthrough */ + case GRPC_CHTTP2_GOAWAY_ERR0: + if (cur == end) { + p->state = GRPC_CHTTP2_GOAWAY_ERR0; + return GRPC_CHTTP2_PARSE_OK; + } + p->error_code = ((gpr_uint32)*cur) << 24; + ++cur; + /* fallthrough */ + case GRPC_CHTTP2_GOAWAY_ERR1: + if (cur == end) { + p->state = GRPC_CHTTP2_GOAWAY_ERR1; + return GRPC_CHTTP2_PARSE_OK; + } + p->error_code |= ((gpr_uint32)*cur) << 16; + ++cur; + /* fallthrough */ + case GRPC_CHTTP2_GOAWAY_ERR2: + if (cur == end) { + p->state = GRPC_CHTTP2_GOAWAY_ERR2; + return GRPC_CHTTP2_PARSE_OK; + } + p->error_code |= ((gpr_uint32)*cur) << 8; + ++cur; + /* fallthrough */ + case GRPC_CHTTP2_GOAWAY_ERR3: + if (cur == end) { + p->state = GRPC_CHTTP2_GOAWAY_ERR3; + return GRPC_CHTTP2_PARSE_OK; + } + p->error_code |= ((gpr_uint32)*cur); + ++cur; + /* fallthrough */ + case GRPC_CHTTP2_GOAWAY_DEBUG: + memcpy(p->debug_data + p->debug_pos, cur, end - cur); + p->debug_pos += end - cur; + p->state = GRPC_CHTTP2_GOAWAY_DEBUG; + if (is_last) { + transport_parsing->goaway_received = 1; + transport_parsing->goaway_last_stream_index = p->last_stream_id; + gpr_slice_unref(transport_parsing->goaway_text); + transport_parsing->goaway_error = p->error_code; + transport_parsing->goaway_text = + gpr_slice_new(p->debug_data, p->debug_length, gpr_free); + p->debug_data = NULL; + } + return GRPC_CHTTP2_PARSE_OK; + } + gpr_log(GPR_ERROR, "Should never end up here"); + abort(); + return GRPC_CHTTP2_CONNECTION_ERROR; +} + +void grpc_chttp2_goaway_append(gpr_uint32 last_stream_id, gpr_uint32 error_code, + gpr_slice debug_data, + gpr_slice_buffer *slice_buffer) { + gpr_slice header = gpr_slice_malloc(9 + 4 + 4); + gpr_uint8 *p = GPR_SLICE_START_PTR(header); + gpr_uint32 frame_length = 4 + 4 + GPR_SLICE_LENGTH(debug_data); + + /* frame header: length */ + *p++ = frame_length >> 16; + *p++ = frame_length >> 8; + *p++ = frame_length; + /* frame header: type */ + *p++ = GRPC_CHTTP2_FRAME_GOAWAY; + /* frame header: flags */ + *p++ = 0; + /* frame header: stream id */ + *p++ = 0; + *p++ = 0; + *p++ = 0; + *p++ = 0; + /* payload: last stream id */ + *p++ = last_stream_id >> 24; + *p++ = last_stream_id >> 16; + *p++ = last_stream_id >> 8; + *p++ = last_stream_id; + /* payload: error code */ + *p++ = error_code >> 24; + *p++ = error_code >> 16; + *p++ = error_code >> 8; + *p++ = error_code; + GPR_ASSERT(p == GPR_SLICE_END_PTR(header)); + gpr_slice_buffer_add(slice_buffer, header); + gpr_slice_buffer_add(slice_buffer, debug_data); +} diff --git a/src/core/transport/chttp2/frame_goaway.h b/src/core/transport/chttp2/frame_goaway.h new file mode 100644 index 00000000..9c5edfc8 --- /dev/null +++ b/src/core/transport/chttp2/frame_goaway.h @@ -0,0 +1,75 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_TRANSPORT_CHTTP2_FRAME_GOAWAY_H +#define GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_FRAME_GOAWAY_H + +#include "src/core/transport/chttp2/frame.h" +#include +#include +#include + +typedef enum { + GRPC_CHTTP2_GOAWAY_LSI0, + GRPC_CHTTP2_GOAWAY_LSI1, + GRPC_CHTTP2_GOAWAY_LSI2, + GRPC_CHTTP2_GOAWAY_LSI3, + GRPC_CHTTP2_GOAWAY_ERR0, + GRPC_CHTTP2_GOAWAY_ERR1, + GRPC_CHTTP2_GOAWAY_ERR2, + GRPC_CHTTP2_GOAWAY_ERR3, + GRPC_CHTTP2_GOAWAY_DEBUG +} grpc_chttp2_goaway_parse_state; + +typedef struct { + grpc_chttp2_goaway_parse_state state; + gpr_uint32 last_stream_id; + gpr_uint32 error_code; + char *debug_data; + gpr_uint32 debug_length; + gpr_uint32 debug_pos; +} grpc_chttp2_goaway_parser; + +void grpc_chttp2_goaway_parser_init(grpc_chttp2_goaway_parser *p); +void grpc_chttp2_goaway_parser_destroy(grpc_chttp2_goaway_parser *p); +grpc_chttp2_parse_error grpc_chttp2_goaway_parser_begin_frame( + grpc_chttp2_goaway_parser *parser, gpr_uint32 length, gpr_uint8 flags); +grpc_chttp2_parse_error grpc_chttp2_goaway_parser_parse( + void *parser, grpc_chttp2_transport_parsing *transport_parsing, + grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last); + +void grpc_chttp2_goaway_append(gpr_uint32 last_stream_id, gpr_uint32 error_code, + gpr_slice debug_data, + gpr_slice_buffer *slice_buffer); + +#endif /* GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_FRAME_GOAWAY_H */ diff --git a/src/core/transport/chttp2/frame_ping.c b/src/core/transport/chttp2/frame_ping.c new file mode 100644 index 00000000..05451c7a --- /dev/null +++ b/src/core/transport/chttp2/frame_ping.c @@ -0,0 +1,105 @@ +/* + * + * Copyright 2015, 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 "src/core/transport/chttp2/frame_ping.h" +#include "src/core/transport/chttp2/internal.h" + +#include + +#include +#include + +gpr_slice grpc_chttp2_ping_create(gpr_uint8 ack, gpr_uint8 *opaque_8bytes) { + gpr_slice slice = gpr_slice_malloc(9 + 8); + gpr_uint8 *p = GPR_SLICE_START_PTR(slice); + + *p++ = 0; + *p++ = 0; + *p++ = 8; + *p++ = GRPC_CHTTP2_FRAME_PING; + *p++ = ack ? 1 : 0; + *p++ = 0; + *p++ = 0; + *p++ = 0; + *p++ = 0; + memcpy(p, opaque_8bytes, 8); + + return slice; +} + +grpc_chttp2_parse_error grpc_chttp2_ping_parser_begin_frame( + grpc_chttp2_ping_parser *parser, gpr_uint32 length, gpr_uint8 flags) { + if (flags & 0xfe || length != 8) { + gpr_log(GPR_ERROR, "invalid ping: length=%d, flags=%02x", length, flags); + return GRPC_CHTTP2_CONNECTION_ERROR; + } + parser->byte = 0; + parser->is_ack = flags; + return GRPC_CHTTP2_PARSE_OK; +} + +grpc_chttp2_parse_error grpc_chttp2_ping_parser_parse( + void *parser, grpc_chttp2_transport_parsing *transport_parsing, + grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last) { + gpr_uint8 *const beg = GPR_SLICE_START_PTR(slice); + gpr_uint8 *const end = GPR_SLICE_END_PTR(slice); + gpr_uint8 *cur = beg; + grpc_chttp2_ping_parser *p = parser; + grpc_chttp2_outstanding_ping *ping; + + while (p->byte != 8 && cur != end) { + p->opaque_8bytes[p->byte] = *cur; + cur++; + p->byte++; + } + + if (p->byte == 8) { + GPR_ASSERT(is_last); + if (p->is_ack) { + for (ping = transport_parsing->pings.next; + ping != &transport_parsing->pings; ping = ping->next) { + if (0 == memcmp(p->opaque_8bytes, ping->id, 8)) { + grpc_iomgr_add_delayed_callback(ping->on_recv, 1); + } + ping->next->prev = ping->prev; + ping->prev->next = ping->next; + gpr_free(ping); + } + } else { + gpr_slice_buffer_add(&transport_parsing->qbuf, + grpc_chttp2_ping_create(1, p->opaque_8bytes)); + } + } + + return GRPC_CHTTP2_PARSE_OK; +} diff --git a/src/core/transport/chttp2/frame_ping.h b/src/core/transport/chttp2/frame_ping.h new file mode 100644 index 00000000..99197e83 --- /dev/null +++ b/src/core/transport/chttp2/frame_ping.h @@ -0,0 +1,54 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_TRANSPORT_CHTTP2_FRAME_PING_H +#define GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_FRAME_PING_H + +#include +#include "src/core/transport/chttp2/frame.h" + +typedef struct { + gpr_uint8 byte; + gpr_uint8 is_ack; + gpr_uint8 opaque_8bytes[8]; +} grpc_chttp2_ping_parser; + +gpr_slice grpc_chttp2_ping_create(gpr_uint8 ack, gpr_uint8 *opaque_8bytes); + +grpc_chttp2_parse_error grpc_chttp2_ping_parser_begin_frame( + grpc_chttp2_ping_parser *parser, gpr_uint32 length, gpr_uint8 flags); +grpc_chttp2_parse_error grpc_chttp2_ping_parser_parse( + void *parser, grpc_chttp2_transport_parsing *transport_parsing, + grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last); + +#endif /* GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_FRAME_PING_H */ diff --git a/src/core/transport/chttp2/frame_rst_stream.c b/src/core/transport/chttp2/frame_rst_stream.c new file mode 100644 index 00000000..a878d936 --- /dev/null +++ b/src/core/transport/chttp2/frame_rst_stream.c @@ -0,0 +1,99 @@ +/* + * + * Copyright 2015, 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 "src/core/transport/chttp2/frame_rst_stream.h" +#include "src/core/transport/chttp2/internal.h" + +#include + +#include "src/core/transport/chttp2/frame.h" + +gpr_slice grpc_chttp2_rst_stream_create(gpr_uint32 id, gpr_uint32 code) { + gpr_slice slice = gpr_slice_malloc(13); + gpr_uint8 *p = GPR_SLICE_START_PTR(slice); + + *p++ = 0; + *p++ = 0; + *p++ = 4; + *p++ = GRPC_CHTTP2_FRAME_RST_STREAM; + *p++ = 0; + *p++ = id >> 24; + *p++ = id >> 16; + *p++ = id >> 8; + *p++ = id; + *p++ = code >> 24; + *p++ = code >> 16; + *p++ = code >> 8; + *p++ = code; + + return slice; +} + +grpc_chttp2_parse_error grpc_chttp2_rst_stream_parser_begin_frame( + grpc_chttp2_rst_stream_parser *parser, gpr_uint32 length, gpr_uint8 flags) { + if (length != 4) { + gpr_log(GPR_ERROR, "invalid rst_stream: length=%d, flags=%02x", length, + flags); + return GRPC_CHTTP2_CONNECTION_ERROR; + } + parser->byte = 0; + return GRPC_CHTTP2_PARSE_OK; +} + +grpc_chttp2_parse_error grpc_chttp2_rst_stream_parser_parse( + void *parser, grpc_chttp2_transport_parsing *transport_parsing, + grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last) { + gpr_uint8 *const beg = GPR_SLICE_START_PTR(slice); + gpr_uint8 *const end = GPR_SLICE_END_PTR(slice); + gpr_uint8 *cur = beg; + grpc_chttp2_rst_stream_parser *p = parser; + + while (p->byte != 4 && cur != end) { + p->reason_bytes[p->byte] = *cur; + cur++; + p->byte++; + } + + if (p->byte == 4) { + GPR_ASSERT(is_last); + stream_parsing->received_close = 1; + stream_parsing->saw_rst_stream = 1; + stream_parsing->rst_stream_reason = + (((gpr_uint32)p->reason_bytes[0]) << 24) | + (((gpr_uint32)p->reason_bytes[1]) << 16) | + (((gpr_uint32)p->reason_bytes[2]) << 8) | + (((gpr_uint32)p->reason_bytes[3])); + } + + return GRPC_CHTTP2_PARSE_OK; +} diff --git a/src/core/transport/chttp2/frame_rst_stream.h b/src/core/transport/chttp2/frame_rst_stream.h new file mode 100644 index 00000000..ed69e588 --- /dev/null +++ b/src/core/transport/chttp2/frame_rst_stream.h @@ -0,0 +1,53 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_TRANSPORT_CHTTP2_FRAME_RST_STREAM_H +#define GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_FRAME_RST_STREAM_H + +#include +#include "src/core/transport/chttp2/frame.h" + +typedef struct { + gpr_uint8 byte; + gpr_uint8 reason_bytes[4]; +} grpc_chttp2_rst_stream_parser; + +gpr_slice grpc_chttp2_rst_stream_create(gpr_uint32 stream_id, gpr_uint32 code); + +grpc_chttp2_parse_error grpc_chttp2_rst_stream_parser_begin_frame( + grpc_chttp2_rst_stream_parser *parser, gpr_uint32 length, gpr_uint8 flags); +grpc_chttp2_parse_error grpc_chttp2_rst_stream_parser_parse( + void *parser, grpc_chttp2_transport_parsing *transport_parsing, + grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last); + +#endif /* GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_FRAME_RST_STREAM_H */ diff --git a/src/core/transport/chttp2/frame_settings.c b/src/core/transport/chttp2/frame_settings.c new file mode 100644 index 00000000..d42bc000 --- /dev/null +++ b/src/core/transport/chttp2/frame_settings.c @@ -0,0 +1,246 @@ +/* + * + * Copyright 2015, 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 "src/core/transport/chttp2/frame_settings.h" +#include "src/core/transport/chttp2/internal.h" + +#include + +#include "src/core/debug/trace.h" +#include "src/core/transport/chttp2/frame.h" +#include "src/core/transport/chttp2_transport.h" +#include +#include + +/* HTTP/2 mandated initial connection settings */ +const grpc_chttp2_setting_parameters + grpc_chttp2_settings_parameters[GRPC_CHTTP2_NUM_SETTINGS] = { + {NULL, 0, 0, 0, GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE}, + {"HEADER_TABLE_SIZE", 4096, 0, 0xffffffff, + GRPC_CHTTP2_CLAMP_INVALID_VALUE}, + {"ENABLE_PUSH", 1, 0, 1, GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE}, + {"MAX_CONCURRENT_STREAMS", 0xffffffffu, 0, 0xffffffffu, + GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE}, + {"INITIAL_WINDOW_SIZE", 65535, 0, 0xffffffffu, + GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE}, + {"MAX_FRAME_SIZE", 16384, 16384, 16777215, + GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE}, + {"MAX_HEADER_LIST_SIZE", 0xffffffffu, 0, 0xffffffffu, + GRPC_CHTTP2_CLAMP_INVALID_VALUE}, +}; + +static gpr_uint8 *fill_header(gpr_uint8 *out, gpr_uint32 length, + gpr_uint8 flags) { + *out++ = length >> 16; + *out++ = length >> 8; + *out++ = length; + *out++ = GRPC_CHTTP2_FRAME_SETTINGS; + *out++ = flags; + *out++ = 0; + *out++ = 0; + *out++ = 0; + *out++ = 0; + return out; +} + +gpr_slice grpc_chttp2_settings_create(gpr_uint32 *old, const gpr_uint32 *new, + gpr_uint32 force_mask, size_t count) { + size_t i; + size_t n = 0; + gpr_slice output; + gpr_uint8 *p; + + for (i = 0; i < count; i++) { + n += (new[i] != old[i] || (force_mask & (1 << i)) != 0); + } + + output = gpr_slice_malloc(9 + 6 * n); + p = fill_header(GPR_SLICE_START_PTR(output), 6 * n, 0); + + for (i = 0; i < count; i++) { + if (new[i] != old[i] || (force_mask & (1 << i)) != 0) { + GPR_ASSERT(i); + *p++ = i >> 8; + *p++ = i; + *p++ = new[i] >> 24; + *p++ = new[i] >> 16; + *p++ = new[i] >> 8; + *p++ = new[i]; + old[i] = new[i]; + } + } + + GPR_ASSERT(p == GPR_SLICE_END_PTR(output)); + + return output; +} + +gpr_slice grpc_chttp2_settings_ack_create(void) { + gpr_slice output = gpr_slice_malloc(9); + fill_header(GPR_SLICE_START_PTR(output), 0, GRPC_CHTTP2_FLAG_ACK); + return output; +} + +grpc_chttp2_parse_error grpc_chttp2_settings_parser_begin_frame( + grpc_chttp2_settings_parser *parser, gpr_uint32 length, gpr_uint8 flags, + gpr_uint32 *settings) { + parser->target_settings = settings; + memcpy(parser->incoming_settings, settings, + GRPC_CHTTP2_NUM_SETTINGS * sizeof(gpr_uint32)); + parser->is_ack = 0; + parser->state = GRPC_CHTTP2_SPS_ID0; + if (flags == GRPC_CHTTP2_FLAG_ACK) { + parser->is_ack = 1; + if (length != 0) { + gpr_log(GPR_ERROR, "non-empty settings ack frame received"); + return GRPC_CHTTP2_CONNECTION_ERROR; + } + return GRPC_CHTTP2_PARSE_OK; + } else if (flags != 0) { + gpr_log(GPR_ERROR, "invalid flags on settings frame"); + return GRPC_CHTTP2_CONNECTION_ERROR; + } else if (length % 6 != 0) { + gpr_log(GPR_ERROR, "settings frames must be a multiple of six bytes"); + return GRPC_CHTTP2_CONNECTION_ERROR; + } else { + return GRPC_CHTTP2_PARSE_OK; + } +} + +grpc_chttp2_parse_error grpc_chttp2_settings_parser_parse( + void *p, grpc_chttp2_transport_parsing *transport_parsing, + grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last) { + grpc_chttp2_settings_parser *parser = p; + const gpr_uint8 *cur = GPR_SLICE_START_PTR(slice); + const gpr_uint8 *end = GPR_SLICE_END_PTR(slice); + + if (parser->is_ack) { + return GRPC_CHTTP2_PARSE_OK; + } + + for (;;) { + switch (parser->state) { + case GRPC_CHTTP2_SPS_ID0: + if (cur == end) { + parser->state = GRPC_CHTTP2_SPS_ID0; + if (is_last) { + transport_parsing->settings_updated = 1; + memcpy(parser->target_settings, parser->incoming_settings, + GRPC_CHTTP2_NUM_SETTINGS * sizeof(gpr_uint32)); + gpr_slice_buffer_add(&transport_parsing->qbuf, + grpc_chttp2_settings_ack_create()); + } + return GRPC_CHTTP2_PARSE_OK; + } + parser->id = ((gpr_uint16)*cur) << 8; + cur++; + /* fallthrough */ + case GRPC_CHTTP2_SPS_ID1: + if (cur == end) { + parser->state = GRPC_CHTTP2_SPS_ID1; + return GRPC_CHTTP2_PARSE_OK; + } + parser->id |= (*cur); + cur++; + /* fallthrough */ + case GRPC_CHTTP2_SPS_VAL0: + if (cur == end) { + parser->state = GRPC_CHTTP2_SPS_VAL0; + return GRPC_CHTTP2_PARSE_OK; + } + parser->value = ((gpr_uint32)*cur) << 24; + cur++; + /* fallthrough */ + case GRPC_CHTTP2_SPS_VAL1: + if (cur == end) { + parser->state = GRPC_CHTTP2_SPS_VAL1; + return GRPC_CHTTP2_PARSE_OK; + } + parser->value |= ((gpr_uint32)*cur) << 16; + cur++; + /* fallthrough */ + case GRPC_CHTTP2_SPS_VAL2: + if (cur == end) { + parser->state = GRPC_CHTTP2_SPS_VAL2; + return GRPC_CHTTP2_PARSE_OK; + } + parser->value |= ((gpr_uint32)*cur) << 8; + cur++; + /* fallthrough */ + case GRPC_CHTTP2_SPS_VAL3: + if (cur == end) { + parser->state = GRPC_CHTTP2_SPS_VAL3; + return GRPC_CHTTP2_PARSE_OK; + } else { + parser->state = GRPC_CHTTP2_SPS_ID0; + } + parser->value |= *cur; + cur++; + + if (parser->id > 0 && parser->id < GRPC_CHTTP2_NUM_SETTINGS) { + const grpc_chttp2_setting_parameters *sp = + &grpc_chttp2_settings_parameters[parser->id]; + if (parser->value < sp->min_value || parser->value > sp->max_value) { + switch (sp->invalid_value_behavior) { + case GRPC_CHTTP2_CLAMP_INVALID_VALUE: + parser->value = + GPR_CLAMP(parser->value, sp->min_value, sp->max_value); + break; + case GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE: + gpr_log(GPR_ERROR, "invalid value %u passed for %s", + parser->value, sp->name); + return GRPC_CHTTP2_CONNECTION_ERROR; + } + } + if (parser->id == GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE && + parser->incoming_settings[parser->id] != parser->value) { + transport_parsing->initial_window_update = + (gpr_int64)parser->value - + parser->incoming_settings[parser->id]; + gpr_log(GPR_DEBUG, "adding %d for initial_window change", + (int)transport_parsing->initial_window_update); + } + parser->incoming_settings[parser->id] = parser->value; + if (grpc_http_trace) { + gpr_log(GPR_DEBUG, "CHTTP2:%s: got setting %d = %d", + transport_parsing->is_client ? "CLI" : "SVR", parser->id, + parser->value); + } + } else { + gpr_log(GPR_ERROR, "CHTTP2: Ignoring unknown setting %d (value %d)", + parser->id, parser->value); + } + break; + } + } +} diff --git a/src/core/transport/chttp2/frame_settings.h b/src/core/transport/chttp2/frame_settings.h new file mode 100644 index 00000000..0ac68a9f --- /dev/null +++ b/src/core/transport/chttp2/frame_settings.h @@ -0,0 +1,100 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_TRANSPORT_CHTTP2_FRAME_SETTINGS_H +#define GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_FRAME_SETTINGS_H + +#include +#include +#include "src/core/transport/chttp2/frame.h" + +typedef enum { + GRPC_CHTTP2_SPS_ID0, + GRPC_CHTTP2_SPS_ID1, + GRPC_CHTTP2_SPS_VAL0, + GRPC_CHTTP2_SPS_VAL1, + GRPC_CHTTP2_SPS_VAL2, + GRPC_CHTTP2_SPS_VAL3 +} grpc_chttp2_settings_parse_state; + +/* The things HTTP/2 defines as connection level settings */ +typedef enum { + GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE = 1, + GRPC_CHTTP2_SETTINGS_ENABLE_PUSH = 2, + GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS = 3, + GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE = 4, + GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE = 5, + GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE = 6, + GRPC_CHTTP2_NUM_SETTINGS +} grpc_chttp2_setting_id; + +typedef struct { + grpc_chttp2_settings_parse_state state; + gpr_uint32 *target_settings; + gpr_uint8 is_ack; + gpr_uint16 id; + gpr_uint32 value; + gpr_uint32 incoming_settings[GRPC_CHTTP2_NUM_SETTINGS]; +} grpc_chttp2_settings_parser; + +typedef enum { + GRPC_CHTTP2_CLAMP_INVALID_VALUE, + GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE +} grpc_chttp2_invalid_value_behavior; + +typedef struct { + const char *name; + gpr_uint32 default_value; + gpr_uint32 min_value; + gpr_uint32 max_value; + grpc_chttp2_invalid_value_behavior invalid_value_behavior; +} grpc_chttp2_setting_parameters; + +/* HTTP/2 mandated connection setting parameters */ +extern const grpc_chttp2_setting_parameters + grpc_chttp2_settings_parameters[GRPC_CHTTP2_NUM_SETTINGS]; + +/* Create a settings frame by diffing old & new, and updating old to be new */ +gpr_slice grpc_chttp2_settings_create(gpr_uint32 *old, const gpr_uint32 *new, + gpr_uint32 force_mask, size_t count); +/* Create an ack settings frame */ +gpr_slice grpc_chttp2_settings_ack_create(void); + +grpc_chttp2_parse_error grpc_chttp2_settings_parser_begin_frame( + grpc_chttp2_settings_parser *parser, gpr_uint32 length, gpr_uint8 flags, + gpr_uint32 *settings); +grpc_chttp2_parse_error grpc_chttp2_settings_parser_parse( + void *parser, grpc_chttp2_transport_parsing *transport_parsing, + grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last); + +#endif /* GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_FRAME_SETTINGS_H */ diff --git a/src/core/transport/chttp2/frame_window_update.c b/src/core/transport/chttp2/frame_window_update.c new file mode 100644 index 00000000..d624298a --- /dev/null +++ b/src/core/transport/chttp2/frame_window_update.c @@ -0,0 +1,114 @@ +/* + * + * Copyright 2015, 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 "src/core/transport/chttp2/frame_window_update.h" +#include "src/core/transport/chttp2/internal.h" + +#include + +gpr_slice grpc_chttp2_window_update_create(gpr_uint32 id, + gpr_uint32 window_update) { + gpr_slice slice = gpr_slice_malloc(13); + gpr_uint8 *p = GPR_SLICE_START_PTR(slice); + + GPR_ASSERT(window_update); + + *p++ = 0; + *p++ = 0; + *p++ = 4; + *p++ = GRPC_CHTTP2_FRAME_WINDOW_UPDATE; + *p++ = 0; + *p++ = id >> 24; + *p++ = id >> 16; + *p++ = id >> 8; + *p++ = id; + *p++ = window_update >> 24; + *p++ = window_update >> 16; + *p++ = window_update >> 8; + *p++ = window_update; + + return slice; +} + +grpc_chttp2_parse_error grpc_chttp2_window_update_parser_begin_frame( + grpc_chttp2_window_update_parser *parser, gpr_uint32 length, + gpr_uint8 flags) { + if (flags || length != 4) { + gpr_log(GPR_ERROR, "invalid window update: length=%d, flags=%02x", length, + flags); + return GRPC_CHTTP2_CONNECTION_ERROR; + } + parser->byte = 0; + parser->amount = 0; + return GRPC_CHTTP2_PARSE_OK; +} + +grpc_chttp2_parse_error grpc_chttp2_window_update_parser_parse( + void *parser, grpc_chttp2_transport_parsing *transport_parsing, + grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last) { + gpr_uint8 *const beg = GPR_SLICE_START_PTR(slice); + gpr_uint8 *const end = GPR_SLICE_END_PTR(slice); + gpr_uint8 *cur = beg; + grpc_chttp2_window_update_parser *p = parser; + + while (p->byte != 4 && cur != end) { + p->amount |= ((gpr_uint32)*cur) << (8 * (3 - p->byte)); + cur++; + p->byte++; + } + + if (p->byte == 4) { + if (p->amount == 0 || (p->amount & 0x80000000u)) { + gpr_log(GPR_ERROR, "invalid window update bytes: %d", p->amount); + return GRPC_CHTTP2_CONNECTION_ERROR; + } + GPR_ASSERT(is_last); + + if (transport_parsing->incoming_stream_id != 0) { + if (stream_parsing != NULL) { + GRPC_CHTTP2_FLOWCTL_TRACE_STREAM("update", transport_parsing, + stream_parsing, outgoing_window_update, + p->amount); + stream_parsing->outgoing_window_update += p->amount; + grpc_chttp2_list_add_parsing_seen_stream(transport_parsing, + stream_parsing); + } + } else { + GRPC_CHTTP2_FLOWCTL_TRACE_TRANSPORT("update", transport_parsing, + outgoing_window_update, p->amount); + transport_parsing->outgoing_window_update += p->amount; + } + } + + return GRPC_CHTTP2_PARSE_OK; +} diff --git a/src/core/transport/chttp2/frame_window_update.h b/src/core/transport/chttp2/frame_window_update.h new file mode 100644 index 00000000..deba801d --- /dev/null +++ b/src/core/transport/chttp2/frame_window_update.h @@ -0,0 +1,56 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_TRANSPORT_CHTTP2_FRAME_WINDOW_UPDATE_H +#define GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_FRAME_WINDOW_UPDATE_H + +#include +#include "src/core/transport/chttp2/frame.h" + +typedef struct { + gpr_uint8 byte; + gpr_uint8 is_connection_update; + gpr_uint32 amount; +} grpc_chttp2_window_update_parser; + +gpr_slice grpc_chttp2_window_update_create(gpr_uint32 id, + gpr_uint32 window_delta); + +grpc_chttp2_parse_error grpc_chttp2_window_update_parser_begin_frame( + grpc_chttp2_window_update_parser *parser, gpr_uint32 length, + gpr_uint8 flags); +grpc_chttp2_parse_error grpc_chttp2_window_update_parser_parse( + void *parser, grpc_chttp2_transport_parsing *transport_parsing, + grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last); + +#endif /* GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_FRAME_WINDOW_UPDATE_H */ diff --git a/src/core/transport/chttp2/hpack_parser.c b/src/core/transport/chttp2/hpack_parser.c new file mode 100644 index 00000000..f8bff42e --- /dev/null +++ b/src/core/transport/chttp2/hpack_parser.c @@ -0,0 +1,1407 @@ +/* + * + * Copyright 2015, 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 "src/core/transport/chttp2/hpack_parser.h" +#include "src/core/transport/chttp2/internal.h" + +#include +#include +#include + +#include "src/core/transport/chttp2/bin_encoder.h" +#include "src/core/support/string.h" +#include +#include +#include +#include + +typedef enum { + NOT_BINARY, + B64_BYTE0, + B64_BYTE1, + B64_BYTE2, + B64_BYTE3 +} binary_state; + +/* How parsing works: + + The parser object keeps track of a function pointer which represents the + current parse state. + + Each time new bytes are presented, we call into the current state, which + recursively parses until all bytes in the given chunk are exhausted. + + The parse state that terminates then saves its function pointer to be the + current state so that it can resume when more bytes are available. + + It's expected that most optimizing compilers will turn this code into + a set of indirect jumps, and so not waste stack space. */ + +/* forward declarations for parsing states */ +static int parse_begin(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end); +static int parse_error(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end); + +static int parse_string_prefix(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end); +static int parse_key_string(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end); +static int parse_value_string_with_indexed_key(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, + const gpr_uint8 *end); +static int parse_value_string_with_literal_key(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, + const gpr_uint8 *end); + +static int parse_value0(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end); +static int parse_value1(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end); +static int parse_value2(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end); +static int parse_value3(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end); +static int parse_value4(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end); +static int parse_value5up(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end); + +static int parse_indexed_field(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end); +static int parse_indexed_field_x(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end); +static int parse_lithdr_incidx(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end); +static int parse_lithdr_incidx_x(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end); +static int parse_lithdr_incidx_v(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end); +static int parse_lithdr_notidx(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end); +static int parse_lithdr_notidx_x(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end); +static int parse_lithdr_notidx_v(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end); +static int parse_lithdr_nvridx(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end); +static int parse_lithdr_nvridx_x(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end); +static int parse_lithdr_nvridx_v(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end); +static int parse_max_tbl_size(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end); +static int parse_max_tbl_size_x(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end); + +/* we translate the first byte of a hpack field into one of these decoding + cases, then use a lookup table to jump directly to the appropriate parser. + + _X => the integer index is all ones, meaning we need to do varint decoding + _V => the integer index is all zeros, meaning we need to decode an additional + string value */ +typedef enum { + INDEXED_FIELD, + INDEXED_FIELD_X, + LITHDR_INCIDX, + LITHDR_INCIDX_X, + LITHDR_INCIDX_V, + LITHDR_NOTIDX, + LITHDR_NOTIDX_X, + LITHDR_NOTIDX_V, + LITHDR_NVRIDX, + LITHDR_NVRIDX_X, + LITHDR_NVRIDX_V, + MAX_TBL_SIZE, + MAX_TBL_SIZE_X, + ILLEGAL +} first_byte_type; + +/* jump table of parse state functions -- order must match first_byte_type + above */ +static const grpc_chttp2_hpack_parser_state first_byte_action[] = { + parse_indexed_field, parse_indexed_field_x, + parse_lithdr_incidx, parse_lithdr_incidx_x, + parse_lithdr_incidx_v, parse_lithdr_notidx, + parse_lithdr_notidx_x, parse_lithdr_notidx_v, + parse_lithdr_nvridx, parse_lithdr_nvridx_x, + parse_lithdr_nvridx_v, parse_max_tbl_size, + parse_max_tbl_size_x, parse_error}; + +/* indexes the first byte to a parse state function - generated by + gen_hpack_tables.c */ +static const gpr_uint8 first_byte_lut[256] = { + LITHDR_NOTIDX_V, LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX, + LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX, + LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX, + LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX_X, + LITHDR_NVRIDX_V, LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, + LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, + LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, + LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX_X, + ILLEGAL, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, + MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, + MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, + MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, + MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, + MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, + MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, + MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE_X, + LITHDR_INCIDX_V, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX_X, + ILLEGAL, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD_X, +}; + +/* state table for huffman decoding: given a state, gives an index/16 into + next_sub_tbl. Taking that index and adding the value of the nibble being + considered returns the next state. + + generated by gen_hpack_tables.c */ +static const gpr_uint8 next_tbl[256] = { + 0, 1, 2, 3, 4, 1, 2, 5, 6, 1, 7, 8, 1, 3, 3, 9, 10, 11, 1, 1, + 1, 12, 1, 2, 13, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, + 14, 1, 15, 16, 1, 17, 1, 15, 2, 7, 3, 18, 19, 1, 1, 1, 1, 20, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 15, 2, 2, 7, 21, 1, 22, 1, 1, 1, 1, 1, + 1, 1, 1, 15, 2, 2, 2, 2, 2, 2, 23, 24, 25, 1, 1, 1, 1, 2, 2, 2, + 26, 3, 3, 27, 10, 28, 1, 1, 1, 1, 1, 1, 2, 3, 29, 10, 30, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 31, 1, 1, 1, 1, 1, 1, 1, 2, + 2, 2, 2, 2, 2, 2, 2, 32, 1, 1, 15, 33, 1, 34, 35, 9, 36, 1, 1, 1, + 1, 1, 1, 1, 37, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 26, 9, + 38, 1, 1, 1, 1, 1, 1, 1, 15, 2, 2, 2, 2, 26, 3, 3, 39, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 7, 3, 3, 3, 40, 2, + 41, 1, 1, 1, 42, 43, 1, 1, 44, 1, 1, 1, 1, 15, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 45, 46, 1, 1, 2, 2, 2, 35, 3, 3, 18, 47, 2, +}; +/* next state, based upon current state and the current nibble: see above. + generated by gen_hpack_tables.c */ +static const gpr_int16 next_sub_tbl[48 * 16] = { + 1, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, + 218, 2, 6, 10, 13, 14, 15, 16, 17, 2, 6, 10, 13, 14, 15, + 16, 17, 3, 7, 11, 24, 3, 7, 11, 24, 3, 7, 11, 24, 3, + 7, 11, 24, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, + 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, + 199, 200, 201, 202, 203, 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 9, 133, 134, 135, 136, 137, 138, 139, 140, + 141, 142, 143, 144, 145, 146, 147, 3, 7, 11, 24, 3, 7, 11, 24, + 4, 8, 4, 8, 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 12, 132, 4, 8, 4, 8, 4, 8, + 4, 8, 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 18, 19, 20, 21, 4, 8, 4, + 8, 4, 8, 4, 8, 4, 8, 0, 0, 0, 22, 23, 91, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 3, + 7, 11, 24, 3, 7, 11, 24, 0, 0, 0, 0, 0, 41, 42, 43, + 2, 6, 10, 13, 14, 15, 16, 17, 3, 7, 11, 24, 3, 7, 11, + 24, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 0, 0, + 44, 45, 2, 6, 10, 13, 14, 15, 16, 17, 46, 47, 48, 49, 50, + 51, 52, 57, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 53, 54, 55, 56, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 73, 75, 76, 77, 78, 79, 80, 81, 82, + 83, 84, 85, 86, 87, 88, 89, 90, 3, 7, 11, 24, 3, 7, 11, + 24, 3, 7, 11, 24, 0, 0, 0, 0, 3, 7, 11, 24, 3, 7, + 11, 24, 4, 8, 4, 8, 0, 0, 0, 92, 0, 0, 0, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 3, 7, 11, 24, + 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, + 8, 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 4, + 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 0, 0, + 0, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, + 131, 2, 6, 10, 13, 14, 15, 16, 17, 4, 8, 4, 8, 4, 8, + 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 148, + 149, 150, 151, 3, 7, 11, 24, 4, 8, 4, 8, 0, 0, 0, 0, + 0, 0, 152, 153, 3, 7, 11, 24, 3, 7, 11, 24, 3, 7, 11, + 24, 154, 155, 156, 164, 3, 7, 11, 24, 3, 7, 11, 24, 3, 7, + 11, 24, 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 157, 158, 159, 160, 161, 162, 163, 165, 166, 167, 168, 169, 170, 171, 172, + 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 4, 8, 4, 8, 4, 8, + 4, 8, 4, 8, 4, 8, 4, 8, 197, 198, 4, 8, 4, 8, 4, + 8, 4, 8, 0, 0, 0, 0, 0, 0, 219, 220, 3, 7, 11, 24, + 4, 8, 4, 8, 4, 8, 0, 0, 221, 222, 223, 224, 3, 7, 11, + 24, 3, 7, 11, 24, 4, 8, 4, 8, 4, 8, 225, 228, 4, 8, + 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 226, 227, 229, + 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 245, 246, 247, 248, 249, 250, 251, 252, + 253, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, +}; +/* emission table: indexed like next_tbl, ultimately gives the byte to be + emitted, or -1 for no byte, or 256 for end of stream + + generated by gen_hpack_tables.c */ +static const gpr_uint16 emit_tbl[256] = { + 0, 1, 2, 3, 4, 5, 6, 7, 0, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 0, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 0, 55, 56, + 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 0, + 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, + 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, + 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, + 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 0, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, + 0, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, + 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, + 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, + 219, 220, 221, 0, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, + 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, + 248, +}; +/* generated by gen_hpack_tables.c */ +static const gpr_int16 emit_sub_tbl[249 * 16] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 48, 48, 48, 48, 48, 48, 48, 48, 49, 49, 49, 49, 49, 49, + 49, 49, 48, 48, 48, 48, 49, 49, 49, 49, 50, 50, 50, 50, 97, + 97, 97, 97, 48, 48, 49, 49, 50, 50, 97, 97, 99, 99, 101, 101, + 105, 105, 111, 111, 48, 49, 50, 97, 99, 101, 105, 111, 115, 116, -1, + -1, -1, -1, -1, -1, 32, 32, 32, 32, 32, 32, 32, 32, 37, 37, + 37, 37, 37, 37, 37, 37, 99, 99, 99, 99, 101, 101, 101, 101, 105, + 105, 105, 105, 111, 111, 111, 111, 115, 115, 116, 116, 32, 37, 45, 46, + 47, 51, 52, 53, 54, 55, 56, 57, 61, 61, 61, 61, 61, 61, 61, + 61, 65, 65, 65, 65, 65, 65, 65, 65, 115, 115, 115, 115, 116, 116, + 116, 116, 32, 32, 37, 37, 45, 45, 46, 46, 61, 65, 95, 98, 100, + 102, 103, 104, 108, 109, 110, 112, 114, 117, -1, -1, 58, 58, 58, 58, + 58, 58, 58, 58, 66, 66, 66, 66, 66, 66, 66, 66, 47, 47, 51, + 51, 52, 52, 53, 53, 54, 54, 55, 55, 56, 56, 57, 57, 61, 61, + 65, 65, 95, 95, 98, 98, 100, 100, 102, 102, 103, 103, 104, 104, 108, + 108, 109, 109, 110, 110, 112, 112, 114, 114, 117, 117, 58, 66, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 89, 106, 107, 113, 118, 119, 120, 121, 122, -1, -1, + -1, -1, 38, 38, 38, 38, 38, 38, 38, 38, 42, 42, 42, 42, 42, + 42, 42, 42, 44, 44, 44, 44, 44, 44, 44, 44, 59, 59, 59, 59, + 59, 59, 59, 59, 88, 88, 88, 88, 88, 88, 88, 88, 90, 90, 90, + 90, 90, 90, 90, 90, 33, 33, 34, 34, 40, 40, 41, 41, 63, 63, + 39, 43, 124, -1, -1, -1, 35, 35, 35, 35, 35, 35, 35, 35, 62, + 62, 62, 62, 62, 62, 62, 62, 0, 0, 0, 0, 36, 36, 36, 36, + 64, 64, 64, 64, 91, 91, 91, 91, 69, 69, 69, 69, 69, 69, 69, + 69, 70, 70, 70, 70, 70, 70, 70, 70, 71, 71, 71, 71, 71, 71, + 71, 71, 72, 72, 72, 72, 72, 72, 72, 72, 73, 73, 73, 73, 73, + 73, 73, 73, 74, 74, 74, 74, 74, 74, 74, 74, 75, 75, 75, 75, + 75, 75, 75, 75, 76, 76, 76, 76, 76, 76, 76, 76, 77, 77, 77, + 77, 77, 77, 77, 77, 78, 78, 78, 78, 78, 78, 78, 78, 79, 79, + 79, 79, 79, 79, 79, 79, 80, 80, 80, 80, 80, 80, 80, 80, 81, + 81, 81, 81, 81, 81, 81, 81, 82, 82, 82, 82, 82, 82, 82, 82, + 83, 83, 83, 83, 83, 83, 83, 83, 84, 84, 84, 84, 84, 84, 84, + 84, 85, 85, 85, 85, 85, 85, 85, 85, 86, 86, 86, 86, 86, 86, + 86, 86, 87, 87, 87, 87, 87, 87, 87, 87, 89, 89, 89, 89, 89, + 89, 89, 89, 106, 106, 106, 106, 106, 106, 106, 106, 107, 107, 107, 107, + 107, 107, 107, 107, 113, 113, 113, 113, 113, 113, 113, 113, 118, 118, 118, + 118, 118, 118, 118, 118, 119, 119, 119, 119, 119, 119, 119, 119, 120, 120, + 120, 120, 120, 120, 120, 120, 121, 121, 121, 121, 121, 121, 121, 121, 122, + 122, 122, 122, 122, 122, 122, 122, 38, 38, 38, 38, 42, 42, 42, 42, + 44, 44, 44, 44, 59, 59, 59, 59, 88, 88, 88, 88, 90, 90, 90, + 90, 33, 34, 40, 41, 63, -1, -1, -1, 39, 39, 39, 39, 39, 39, + 39, 39, 43, 43, 43, 43, 43, 43, 43, 43, 124, 124, 124, 124, 124, + 124, 124, 124, 35, 35, 35, 35, 62, 62, 62, 62, 0, 0, 36, 36, + 64, 64, 91, 91, 93, 93, 126, 126, 94, 125, -1, -1, 60, 60, 60, + 60, 60, 60, 60, 60, 96, 96, 96, 96, 96, 96, 96, 96, 123, 123, + 123, 123, 123, 123, 123, 123, -1, -1, -1, -1, -1, -1, -1, -1, 92, + 92, 92, 92, 92, 92, 92, 92, 195, 195, 195, 195, 195, 195, 195, 195, + 208, 208, 208, 208, 208, 208, 208, 208, 128, 128, 128, 128, 130, 130, 130, + 130, 131, 131, 131, 131, 162, 162, 162, 162, 184, 184, 184, 184, 194, 194, + 194, 194, 224, 224, 224, 224, 226, 226, 226, 226, 153, 153, 161, 161, 167, + 167, 172, 172, 176, 176, 177, 177, 179, 179, 209, 209, 216, 216, 217, 217, + 227, 227, 229, 229, 230, 230, 129, 132, 133, 134, 136, 146, 154, 156, 160, + 163, 164, 169, 170, 173, 178, 181, 185, 186, 187, 189, 190, 196, 198, 228, + 232, 233, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 135, + 135, 135, 135, 135, 135, 135, 135, 137, 137, 137, 137, 137, 137, 137, 137, + 138, 138, 138, 138, 138, 138, 138, 138, 139, 139, 139, 139, 139, 139, 139, + 139, 140, 140, 140, 140, 140, 140, 140, 140, 141, 141, 141, 141, 141, 141, + 141, 141, 143, 143, 143, 143, 143, 143, 143, 143, 147, 147, 147, 147, 147, + 147, 147, 147, 149, 149, 149, 149, 149, 149, 149, 149, 150, 150, 150, 150, + 150, 150, 150, 150, 151, 151, 151, 151, 151, 151, 151, 151, 152, 152, 152, + 152, 152, 152, 152, 152, 155, 155, 155, 155, 155, 155, 155, 155, 157, 157, + 157, 157, 157, 157, 157, 157, 158, 158, 158, 158, 158, 158, 158, 158, 165, + 165, 165, 165, 165, 165, 165, 165, 166, 166, 166, 166, 166, 166, 166, 166, + 168, 168, 168, 168, 168, 168, 168, 168, 174, 174, 174, 174, 174, 174, 174, + 174, 175, 175, 175, 175, 175, 175, 175, 175, 180, 180, 180, 180, 180, 180, + 180, 180, 182, 182, 182, 182, 182, 182, 182, 182, 183, 183, 183, 183, 183, + 183, 183, 183, 188, 188, 188, 188, 188, 188, 188, 188, 191, 191, 191, 191, + 191, 191, 191, 191, 197, 197, 197, 197, 197, 197, 197, 197, 231, 231, 231, + 231, 231, 231, 231, 231, 239, 239, 239, 239, 239, 239, 239, 239, 9, 9, + 9, 9, 142, 142, 142, 142, 144, 144, 144, 144, 145, 145, 145, 145, 148, + 148, 148, 148, 159, 159, 159, 159, 171, 171, 171, 171, 206, 206, 206, 206, + 215, 215, 215, 215, 225, 225, 225, 225, 236, 236, 236, 236, 237, 237, 237, + 237, 199, 199, 207, 207, 234, 234, 235, 235, 192, 193, 200, 201, 202, 205, + 210, 213, 218, 219, 238, 240, 242, 243, 255, -1, 203, 203, 203, 203, 203, + 203, 203, 203, 204, 204, 204, 204, 204, 204, 204, 204, 211, 211, 211, 211, + 211, 211, 211, 211, 212, 212, 212, 212, 212, 212, 212, 212, 214, 214, 214, + 214, 214, 214, 214, 214, 221, 221, 221, 221, 221, 221, 221, 221, 222, 222, + 222, 222, 222, 222, 222, 222, 223, 223, 223, 223, 223, 223, 223, 223, 241, + 241, 241, 241, 241, 241, 241, 241, 244, 244, 244, 244, 244, 244, 244, 244, + 245, 245, 245, 245, 245, 245, 245, 245, 246, 246, 246, 246, 246, 246, 246, + 246, 247, 247, 247, 247, 247, 247, 247, 247, 248, 248, 248, 248, 248, 248, + 248, 248, 250, 250, 250, 250, 250, 250, 250, 250, 251, 251, 251, 251, 251, + 251, 251, 251, 252, 252, 252, 252, 252, 252, 252, 252, 253, 253, 253, 253, + 253, 253, 253, 253, 254, 254, 254, 254, 254, 254, 254, 254, 2, 2, 2, + 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, + 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 11, 11, 11, 11, 12, + 12, 12, 12, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, + 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, + 20, 21, 21, 21, 21, 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, + 25, 25, 26, 26, 26, 26, 27, 27, 27, 27, 28, 28, 28, 28, 29, + 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 31, 127, 127, 127, 127, + 220, 220, 220, 220, 249, 249, 249, 249, 10, 13, 22, 256, 93, 93, 93, + 93, 126, 126, 126, 126, 94, 94, 125, 125, 60, 96, 123, -1, 92, 195, + 208, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 128, + 128, 128, 128, 128, 128, 128, 128, 130, 130, 130, 130, 130, 130, 130, 130, + 131, 131, 131, 131, 131, 131, 131, 131, 162, 162, 162, 162, 162, 162, 162, + 162, 184, 184, 184, 184, 184, 184, 184, 184, 194, 194, 194, 194, 194, 194, + 194, 194, 224, 224, 224, 224, 224, 224, 224, 224, 226, 226, 226, 226, 226, + 226, 226, 226, 153, 153, 153, 153, 161, 161, 161, 161, 167, 167, 167, 167, + 172, 172, 172, 172, 176, 176, 176, 176, 177, 177, 177, 177, 179, 179, 179, + 179, 209, 209, 209, 209, 216, 216, 216, 216, 217, 217, 217, 217, 227, 227, + 227, 227, 229, 229, 229, 229, 230, 230, 230, 230, 129, 129, 132, 132, 133, + 133, 134, 134, 136, 136, 146, 146, 154, 154, 156, 156, 160, 160, 163, 163, + 164, 164, 169, 169, 170, 170, 173, 173, 178, 178, 181, 181, 185, 185, 186, + 186, 187, 187, 189, 189, 190, 190, 196, 196, 198, 198, 228, 228, 232, 232, + 233, 233, 1, 135, 137, 138, 139, 140, 141, 143, 147, 149, 150, 151, 152, + 155, 157, 158, 165, 166, 168, 174, 175, 180, 182, 183, 188, 191, 197, 231, + 239, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 9, 9, 9, + 9, 9, 9, 9, 9, 142, 142, 142, 142, 142, 142, 142, 142, 144, 144, + 144, 144, 144, 144, 144, 144, 145, 145, 145, 145, 145, 145, 145, 145, 148, + 148, 148, 148, 148, 148, 148, 148, 159, 159, 159, 159, 159, 159, 159, 159, + 171, 171, 171, 171, 171, 171, 171, 171, 206, 206, 206, 206, 206, 206, 206, + 206, 215, 215, 215, 215, 215, 215, 215, 215, 225, 225, 225, 225, 225, 225, + 225, 225, 236, 236, 236, 236, 236, 236, 236, 236, 237, 237, 237, 237, 237, + 237, 237, 237, 199, 199, 199, 199, 207, 207, 207, 207, 234, 234, 234, 234, + 235, 235, 235, 235, 192, 192, 193, 193, 200, 200, 201, 201, 202, 202, 205, + 205, 210, 210, 213, 213, 218, 218, 219, 219, 238, 238, 240, 240, 242, 242, + 243, 243, 255, 255, 203, 204, 211, 212, 214, 221, 222, 223, 241, 244, 245, + 246, 247, 248, 250, 251, 252, 253, 254, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 2, 2, 2, 2, 2, 2, 2, + 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, + 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, + 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, + 12, 12, 12, 12, 12, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, + 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 17, + 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, + 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, + 20, 21, 21, 21, 21, 21, 21, 21, 21, 23, 23, 23, 23, 23, 23, + 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, + 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, + 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, + 29, 29, 29, 29, 29, 30, 30, 30, 30, 30, 30, 30, 30, 31, 31, + 31, 31, 31, 31, 31, 31, 127, 127, 127, 127, 127, 127, 127, 127, 220, + 220, 220, 220, 220, 220, 220, 220, 249, 249, 249, 249, 249, 249, 249, 249, + 10, 10, 13, 13, 22, 22, 256, 256, 67, 67, 67, 67, 67, 67, 67, + 67, 68, 68, 68, 68, 68, 68, 68, 68, 95, 95, 95, 95, 95, 95, + 95, 95, 98, 98, 98, 98, 98, 98, 98, 98, 100, 100, 100, 100, 100, + 100, 100, 100, 102, 102, 102, 102, 102, 102, 102, 102, 103, 103, 103, 103, + 103, 103, 103, 103, 104, 104, 104, 104, 104, 104, 104, 104, 108, 108, 108, + 108, 108, 108, 108, 108, 109, 109, 109, 109, 109, 109, 109, 109, 110, 110, + 110, 110, 110, 110, 110, 110, 112, 112, 112, 112, 112, 112, 112, 112, 114, + 114, 114, 114, 114, 114, 114, 114, 117, 117, 117, 117, 117, 117, 117, 117, + 58, 58, 58, 58, 66, 66, 66, 66, 67, 67, 67, 67, 68, 68, 68, + 68, 69, 69, 69, 69, 70, 70, 70, 70, 71, 71, 71, 71, 72, 72, + 72, 72, 73, 73, 73, 73, 74, 74, 74, 74, 75, 75, 75, 75, 76, + 76, 76, 76, 77, 77, 77, 77, 78, 78, 78, 78, 79, 79, 79, 79, + 80, 80, 80, 80, 81, 81, 81, 81, 82, 82, 82, 82, 83, 83, 83, + 83, 84, 84, 84, 84, 85, 85, 85, 85, 86, 86, 86, 86, 87, 87, + 87, 87, 89, 89, 89, 89, 106, 106, 106, 106, 107, 107, 107, 107, 113, + 113, 113, 113, 118, 118, 118, 118, 119, 119, 119, 119, 120, 120, 120, 120, + 121, 121, 121, 121, 122, 122, 122, 122, 38, 38, 42, 42, 44, 44, 59, + 59, 88, 88, 90, 90, -1, -1, -1, -1, 33, 33, 33, 33, 33, 33, + 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 40, 40, 40, 40, 40, + 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, 41, 63, 63, 63, 63, + 63, 63, 63, 63, 39, 39, 39, 39, 43, 43, 43, 43, 124, 124, 124, + 124, 35, 35, 62, 62, 0, 36, 64, 91, 93, 126, -1, -1, 94, 94, + 94, 94, 94, 94, 94, 94, 125, 125, 125, 125, 125, 125, 125, 125, 60, + 60, 60, 60, 96, 96, 96, 96, 123, 123, 123, 123, -1, -1, -1, -1, + 92, 92, 92, 92, 195, 195, 195, 195, 208, 208, 208, 208, 128, 128, 130, + 130, 131, 131, 162, 162, 184, 184, 194, 194, 224, 224, 226, 226, 153, 161, + 167, 172, 176, 177, 179, 209, 216, 217, 227, 229, 230, -1, -1, -1, -1, + -1, -1, -1, 129, 129, 129, 129, 129, 129, 129, 129, 132, 132, 132, 132, + 132, 132, 132, 132, 133, 133, 133, 133, 133, 133, 133, 133, 134, 134, 134, + 134, 134, 134, 134, 134, 136, 136, 136, 136, 136, 136, 136, 136, 146, 146, + 146, 146, 146, 146, 146, 146, 154, 154, 154, 154, 154, 154, 154, 154, 156, + 156, 156, 156, 156, 156, 156, 156, 160, 160, 160, 160, 160, 160, 160, 160, + 163, 163, 163, 163, 163, 163, 163, 163, 164, 164, 164, 164, 164, 164, 164, + 164, 169, 169, 169, 169, 169, 169, 169, 169, 170, 170, 170, 170, 170, 170, + 170, 170, 173, 173, 173, 173, 173, 173, 173, 173, 178, 178, 178, 178, 178, + 178, 178, 178, 181, 181, 181, 181, 181, 181, 181, 181, 185, 185, 185, 185, + 185, 185, 185, 185, 186, 186, 186, 186, 186, 186, 186, 186, 187, 187, 187, + 187, 187, 187, 187, 187, 189, 189, 189, 189, 189, 189, 189, 189, 190, 190, + 190, 190, 190, 190, 190, 190, 196, 196, 196, 196, 196, 196, 196, 196, 198, + 198, 198, 198, 198, 198, 198, 198, 228, 228, 228, 228, 228, 228, 228, 228, + 232, 232, 232, 232, 232, 232, 232, 232, 233, 233, 233, 233, 233, 233, 233, + 233, 1, 1, 1, 1, 135, 135, 135, 135, 137, 137, 137, 137, 138, 138, + 138, 138, 139, 139, 139, 139, 140, 140, 140, 140, 141, 141, 141, 141, 143, + 143, 143, 143, 147, 147, 147, 147, 149, 149, 149, 149, 150, 150, 150, 150, + 151, 151, 151, 151, 152, 152, 152, 152, 155, 155, 155, 155, 157, 157, 157, + 157, 158, 158, 158, 158, 165, 165, 165, 165, 166, 166, 166, 166, 168, 168, + 168, 168, 174, 174, 174, 174, 175, 175, 175, 175, 180, 180, 180, 180, 182, + 182, 182, 182, 183, 183, 183, 183, 188, 188, 188, 188, 191, 191, 191, 191, + 197, 197, 197, 197, 231, 231, 231, 231, 239, 239, 239, 239, 9, 9, 142, + 142, 144, 144, 145, 145, 148, 148, 159, 159, 171, 171, 206, 206, 215, 215, + 225, 225, 236, 236, 237, 237, 199, 207, 234, 235, 192, 192, 192, 192, 192, + 192, 192, 192, 193, 193, 193, 193, 193, 193, 193, 193, 200, 200, 200, 200, + 200, 200, 200, 200, 201, 201, 201, 201, 201, 201, 201, 201, 202, 202, 202, + 202, 202, 202, 202, 202, 205, 205, 205, 205, 205, 205, 205, 205, 210, 210, + 210, 210, 210, 210, 210, 210, 213, 213, 213, 213, 213, 213, 213, 213, 218, + 218, 218, 218, 218, 218, 218, 218, 219, 219, 219, 219, 219, 219, 219, 219, + 238, 238, 238, 238, 238, 238, 238, 238, 240, 240, 240, 240, 240, 240, 240, + 240, 242, 242, 242, 242, 242, 242, 242, 242, 243, 243, 243, 243, 243, 243, + 243, 243, 255, 255, 255, 255, 255, 255, 255, 255, 203, 203, 203, 203, 204, + 204, 204, 204, 211, 211, 211, 211, 212, 212, 212, 212, 214, 214, 214, 214, + 221, 221, 221, 221, 222, 222, 222, 222, 223, 223, 223, 223, 241, 241, 241, + 241, 244, 244, 244, 244, 245, 245, 245, 245, 246, 246, 246, 246, 247, 247, + 247, 247, 248, 248, 248, 248, 250, 250, 250, 250, 251, 251, 251, 251, 252, + 252, 252, 252, 253, 253, 253, 253, 254, 254, 254, 254, 2, 2, 3, 3, + 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 11, 11, 12, 12, 14, + 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, + 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, + 30, 31, 31, 127, 127, 220, 220, 249, 249, -1, -1, 10, 10, 10, 10, + 10, 10, 10, 10, 13, 13, 13, 13, 13, 13, 13, 13, 22, 22, 22, + 22, 22, 22, 22, 22, 256, 256, 256, 256, 256, 256, 256, 256, 45, 45, + 45, 45, 45, 45, 45, 45, 46, 46, 46, 46, 46, 46, 46, 46, 47, + 47, 47, 47, 47, 47, 47, 47, 51, 51, 51, 51, 51, 51, 51, 51, + 52, 52, 52, 52, 52, 52, 52, 52, 53, 53, 53, 53, 53, 53, 53, + 53, 54, 54, 54, 54, 54, 54, 54, 54, 55, 55, 55, 55, 55, 55, + 55, 55, 56, 56, 56, 56, 56, 56, 56, 56, 57, 57, 57, 57, 57, + 57, 57, 57, 50, 50, 50, 50, 50, 50, 50, 50, 97, 97, 97, 97, + 97, 97, 97, 97, 99, 99, 99, 99, 99, 99, 99, 99, 101, 101, 101, + 101, 101, 101, 101, 101, 105, 105, 105, 105, 105, 105, 105, 105, 111, 111, + 111, 111, 111, 111, 111, 111, 115, 115, 115, 115, 115, 115, 115, 115, 116, + 116, 116, 116, 116, 116, 116, 116, 32, 32, 32, 32, 37, 37, 37, 37, + 45, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47, 51, 51, 51, + 51, 52, 52, 52, 52, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, + 55, 55, 56, 56, 56, 56, 57, 57, 57, 57, 61, 61, 61, 61, 65, + 65, 65, 65, 95, 95, 95, 95, 98, 98, 98, 98, 100, 100, 100, 100, + 102, 102, 102, 102, 103, 103, 103, 103, 104, 104, 104, 104, 108, 108, 108, + 108, 109, 109, 109, 109, 110, 110, 110, 110, 112, 112, 112, 112, 114, 114, + 114, 114, 117, 117, 117, 117, 58, 58, 66, 66, 67, 67, 68, 68, 69, + 69, 70, 70, 71, 71, 72, 72, 73, 73, 74, 74, 75, 75, 76, 76, + 77, 77, 78, 78, 79, 79, 80, 80, 81, 81, 82, 82, 83, 83, 84, + 84, 85, 85, 86, 86, 87, 87, 89, 89, 106, 106, 107, 107, 113, 113, + 118, 118, 119, 119, 120, 120, 121, 121, 122, 122, 38, 42, 44, 59, 88, + 90, -1, -1, 33, 33, 33, 33, 34, 34, 34, 34, 40, 40, 40, 40, + 41, 41, 41, 41, 63, 63, 63, 63, 39, 39, 43, 43, 124, 124, 35, + 62, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 36, 36, + 36, 36, 36, 36, 36, 36, 64, 64, 64, 64, 64, 64, 64, 64, 91, + 91, 91, 91, 91, 91, 91, 91, 93, 93, 93, 93, 93, 93, 93, 93, + 126, 126, 126, 126, 126, 126, 126, 126, 94, 94, 94, 94, 125, 125, 125, + 125, 60, 60, 96, 96, 123, 123, -1, -1, 92, 92, 195, 195, 208, 208, + 128, 130, 131, 162, 184, 194, 224, 226, -1, -1, 153, 153, 153, 153, 153, + 153, 153, 153, 161, 161, 161, 161, 161, 161, 161, 161, 167, 167, 167, 167, + 167, 167, 167, 167, 172, 172, 172, 172, 172, 172, 172, 172, 176, 176, 176, + 176, 176, 176, 176, 176, 177, 177, 177, 177, 177, 177, 177, 177, 179, 179, + 179, 179, 179, 179, 179, 179, 209, 209, 209, 209, 209, 209, 209, 209, 216, + 216, 216, 216, 216, 216, 216, 216, 217, 217, 217, 217, 217, 217, 217, 217, + 227, 227, 227, 227, 227, 227, 227, 227, 229, 229, 229, 229, 229, 229, 229, + 229, 230, 230, 230, 230, 230, 230, 230, 230, 129, 129, 129, 129, 132, 132, + 132, 132, 133, 133, 133, 133, 134, 134, 134, 134, 136, 136, 136, 136, 146, + 146, 146, 146, 154, 154, 154, 154, 156, 156, 156, 156, 160, 160, 160, 160, + 163, 163, 163, 163, 164, 164, 164, 164, 169, 169, 169, 169, 170, 170, 170, + 170, 173, 173, 173, 173, 178, 178, 178, 178, 181, 181, 181, 181, 185, 185, + 185, 185, 186, 186, 186, 186, 187, 187, 187, 187, 189, 189, 189, 189, 190, + 190, 190, 190, 196, 196, 196, 196, 198, 198, 198, 198, 228, 228, 228, 228, + 232, 232, 232, 232, 233, 233, 233, 233, 1, 1, 135, 135, 137, 137, 138, + 138, 139, 139, 140, 140, 141, 141, 143, 143, 147, 147, 149, 149, 150, 150, + 151, 151, 152, 152, 155, 155, 157, 157, 158, 158, 165, 165, 166, 166, 168, + 168, 174, 174, 175, 175, 180, 180, 182, 182, 183, 183, 188, 188, 191, 191, + 197, 197, 231, 231, 239, 239, 9, 142, 144, 145, 148, 159, 171, 206, 215, + 225, 236, 237, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 199, 199, + 199, 199, 199, 199, 199, 199, 207, 207, 207, 207, 207, 207, 207, 207, 234, + 234, 234, 234, 234, 234, 234, 234, 235, 235, 235, 235, 235, 235, 235, 235, + 192, 192, 192, 192, 193, 193, 193, 193, 200, 200, 200, 200, 201, 201, 201, + 201, 202, 202, 202, 202, 205, 205, 205, 205, 210, 210, 210, 210, 213, 213, + 213, 213, 218, 218, 218, 218, 219, 219, 219, 219, 238, 238, 238, 238, 240, + 240, 240, 240, 242, 242, 242, 242, 243, 243, 243, 243, 255, 255, 255, 255, + 203, 203, 204, 204, 211, 211, 212, 212, 214, 214, 221, 221, 222, 222, 223, + 223, 241, 241, 244, 244, 245, 245, 246, 246, 247, 247, 248, 248, 250, 250, + 251, 251, 252, 252, 253, 253, 254, 254, 2, 3, 4, 5, 6, 7, 8, + 11, 12, 14, 15, 16, 17, 18, 19, 20, 21, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 127, 220, 249, -1, 10, 10, 10, 10, 13, 13, 13, + 13, 22, 22, 22, 22, 256, 256, 256, 256, +}; + +static const gpr_uint8 inverse_base64[256] = { + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, + 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, + 255, 64, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 255, 255, 255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, +}; + +/* emission helpers */ +static void on_hdr(grpc_chttp2_hpack_parser *p, grpc_mdelem *md, + int add_to_table) { + if (add_to_table) { + GRPC_MDELEM_REF(md); + grpc_chttp2_hptbl_add(&p->table, md); + } + p->on_header(p->on_header_user_data, md); +} + +static grpc_mdstr *take_string(grpc_chttp2_hpack_parser *p, + grpc_chttp2_hpack_parser_string *str) { + grpc_mdstr *s = grpc_mdstr_from_buffer(p->table.mdctx, (gpr_uint8 *)str->str, + str->length); + str->length = 0; + return s; +} + +/* jump to the next state */ +static int parse_next(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + p->state = *p->next_state++; + return p->state(p, cur, end); +} + +/* begin parsing a header: all functionality is encoded into lookup tables + above */ +static int parse_begin(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + if (cur == end) { + p->state = parse_begin; + return 1; + } + + return first_byte_action[first_byte_lut[*cur]](p, cur, end); +} + +/* stream dependency and prioritization data: we just skip it */ +static int parse_stream_weight(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + if (cur == end) { + p->state = parse_stream_weight; + return 1; + } + + return p->after_prioritization(p, cur + 1, end); +} + +static int parse_stream_dep3(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + if (cur == end) { + p->state = parse_stream_dep3; + return 1; + } + + return parse_stream_weight(p, cur + 1, end); +} + +static int parse_stream_dep2(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + if (cur == end) { + p->state = parse_stream_dep2; + return 1; + } + + return parse_stream_dep3(p, cur + 1, end); +} + +static int parse_stream_dep1(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + if (cur == end) { + p->state = parse_stream_dep1; + return 1; + } + + return parse_stream_dep2(p, cur + 1, end); +} + +static int parse_stream_dep0(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + if (cur == end) { + p->state = parse_stream_dep0; + return 1; + } + + return parse_stream_dep1(p, cur + 1, end); +} + +/* emit an indexed field; for now just logs it to console; jumps to + begin the next field on completion */ +static int finish_indexed_field(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index); + GRPC_MDELEM_REF(md); + on_hdr(p, md, 0); + return parse_begin(p, cur, end); +} + +/* parse an indexed field with index < 127 */ +static int parse_indexed_field(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + p->index = (*cur) & 0x7f; + return finish_indexed_field(p, cur + 1, end); +} + +/* parse an indexed field with index >= 127 */ +static int parse_indexed_field_x(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + static const grpc_chttp2_hpack_parser_state and_then[] = { + finish_indexed_field}; + p->next_state = and_then; + p->index = 0x7f; + p->parsing.value = &p->index; + return parse_value0(p, cur + 1, end); +} + +/* finish a literal header with incremental indexing: just log, and jump to ' + begin */ +static int finish_lithdr_incidx(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index); + on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx, + GRPC_MDSTR_REF(md->key), + take_string(p, &p->value)), + 1); + return parse_begin(p, cur, end); +} + +/* finish a literal header with incremental indexing with no index */ +static int finish_lithdr_incidx_v(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx, + take_string(p, &p->key), + take_string(p, &p->value)), + 1); + return parse_begin(p, cur, end); +} + +/* parse a literal header with incremental indexing; index < 63 */ +static int parse_lithdr_incidx(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + static const grpc_chttp2_hpack_parser_state and_then[] = { + parse_value_string_with_indexed_key, finish_lithdr_incidx}; + p->next_state = and_then; + p->index = (*cur) & 0x3f; + return parse_string_prefix(p, cur + 1, end); +} + +/* parse a literal header with incremental indexing; index >= 63 */ +static int parse_lithdr_incidx_x(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + static const grpc_chttp2_hpack_parser_state and_then[] = { + parse_string_prefix, parse_value_string_with_indexed_key, + finish_lithdr_incidx}; + p->next_state = and_then; + p->index = 0x3f; + p->parsing.value = &p->index; + return parse_value0(p, cur + 1, end); +} + +/* parse a literal header with incremental indexing; index = 0 */ +static int parse_lithdr_incidx_v(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + static const grpc_chttp2_hpack_parser_state and_then[] = { + parse_key_string, parse_string_prefix, + parse_value_string_with_literal_key, finish_lithdr_incidx_v}; + p->next_state = and_then; + return parse_string_prefix(p, cur + 1, end); +} + +/* finish a literal header without incremental indexing */ +static int finish_lithdr_notidx(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index); + on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx, + GRPC_MDSTR_REF(md->key), + take_string(p, &p->value)), + 0); + return parse_begin(p, cur, end); +} + +/* finish a literal header without incremental indexing with index = 0 */ +static int finish_lithdr_notidx_v(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx, + take_string(p, &p->key), + take_string(p, &p->value)), + 0); + return parse_begin(p, cur, end); +} + +/* parse a literal header without incremental indexing; index < 15 */ +static int parse_lithdr_notidx(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + static const grpc_chttp2_hpack_parser_state and_then[] = { + parse_value_string_with_indexed_key, finish_lithdr_notidx}; + p->next_state = and_then; + p->index = (*cur) & 0xf; + return parse_string_prefix(p, cur + 1, end); +} + +/* parse a literal header without incremental indexing; index >= 15 */ +static int parse_lithdr_notidx_x(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + static const grpc_chttp2_hpack_parser_state and_then[] = { + parse_string_prefix, parse_value_string_with_indexed_key, + finish_lithdr_notidx}; + p->next_state = and_then; + p->index = 0xf; + p->parsing.value = &p->index; + return parse_value0(p, cur + 1, end); +} + +/* parse a literal header without incremental indexing; index == 0 */ +static int parse_lithdr_notidx_v(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + static const grpc_chttp2_hpack_parser_state and_then[] = { + parse_key_string, parse_string_prefix, + parse_value_string_with_literal_key, finish_lithdr_notidx_v}; + p->next_state = and_then; + return parse_string_prefix(p, cur + 1, end); +} + +/* finish a literal header that is never indexed */ +static int finish_lithdr_nvridx(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index); + on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx, + GRPC_MDSTR_REF(md->key), + take_string(p, &p->value)), + 0); + return parse_begin(p, cur, end); +} + +/* finish a literal header that is never indexed with an extra value */ +static int finish_lithdr_nvridx_v(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx, + take_string(p, &p->key), + take_string(p, &p->value)), + 0); + return parse_begin(p, cur, end); +} + +/* parse a literal header that is never indexed; index < 15 */ +static int parse_lithdr_nvridx(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + static const grpc_chttp2_hpack_parser_state and_then[] = { + parse_value_string_with_indexed_key, finish_lithdr_nvridx}; + p->next_state = and_then; + p->index = (*cur) & 0xf; + return parse_string_prefix(p, cur + 1, end); +} + +/* parse a literal header that is never indexed; index >= 15 */ +static int parse_lithdr_nvridx_x(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + static const grpc_chttp2_hpack_parser_state and_then[] = { + parse_string_prefix, parse_value_string_with_indexed_key, + finish_lithdr_nvridx}; + p->next_state = and_then; + p->index = 0xf; + p->parsing.value = &p->index; + return parse_value0(p, cur + 1, end); +} + +/* parse a literal header that is never indexed; index == 0 */ +static int parse_lithdr_nvridx_v(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + static const grpc_chttp2_hpack_parser_state and_then[] = { + parse_key_string, parse_string_prefix, + parse_value_string_with_literal_key, finish_lithdr_nvridx_v}; + p->next_state = and_then; + return parse_string_prefix(p, cur + 1, end); +} + +/* finish parsing a max table size change */ +static int finish_max_tbl_size(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + gpr_log(GPR_INFO, "MAX TABLE SIZE: %d", p->index); + abort(); /* not implemented */ + return parse_begin(p, cur, end); +} + +/* parse a max table size change, max size < 15 */ +static int parse_max_tbl_size(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + p->index = (*cur) & 0xf; + return finish_max_tbl_size(p, cur + 1, end); +} + +/* parse a max table size change, max size >= 15 */ +static int parse_max_tbl_size_x(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + static const grpc_chttp2_hpack_parser_state and_then[] = { + finish_max_tbl_size}; + p->next_state = and_then; + p->index = 0xf; + p->parsing.value = &p->index; + return parse_value0(p, cur + 1, end); +} + +/* a parse error: jam the parse state into parse_error, and return error */ +static int parse_error(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + p->state = parse_error; + return 0; +} + +/* parse the 1st byte of a varint into p->parsing.value + no overflow is possible */ +static int parse_value0(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + if (cur == end) { + p->state = parse_value0; + return 1; + } + + *p->parsing.value += (*cur) & 0x7f; + + if ((*cur) & 0x80) { + return parse_value1(p, cur + 1, end); + } else { + return parse_next(p, cur + 1, end); + } +} + +/* parse the 2nd byte of a varint into p->parsing.value + no overflow is possible */ +static int parse_value1(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + if (cur == end) { + p->state = parse_value1; + return 1; + } + + *p->parsing.value += (((gpr_uint32)*cur) & 0x7f) << 7; + + if ((*cur) & 0x80) { + return parse_value2(p, cur + 1, end); + } else { + return parse_next(p, cur + 1, end); + } +} + +/* parse the 3rd byte of a varint into p->parsing.value + no overflow is possible */ +static int parse_value2(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + if (cur == end) { + p->state = parse_value2; + return 1; + } + + *p->parsing.value += (((gpr_uint32)*cur) & 0x7f) << 14; + + if ((*cur) & 0x80) { + return parse_value3(p, cur + 1, end); + } else { + return parse_next(p, cur + 1, end); + } +} + +/* parse the 4th byte of a varint into p->parsing.value + no overflow is possible */ +static int parse_value3(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + if (cur == end) { + p->state = parse_value3; + return 1; + } + + *p->parsing.value += (((gpr_uint32)*cur) & 0x7f) << 21; + + if ((*cur) & 0x80) { + return parse_value4(p, cur + 1, end); + } else { + return parse_next(p, cur + 1, end); + } +} + +/* parse the 5th byte of a varint into p->parsing.value + depending on the byte, we may overflow, and care must be taken */ +static int parse_value4(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + gpr_uint8 c; + gpr_uint32 cur_value; + gpr_uint32 add_value; + + if (cur == end) { + p->state = parse_value4; + return 1; + } + + c = (*cur) & 0x7f; + if (c > 0xf) { + goto error; + } + + cur_value = *p->parsing.value; + add_value = ((gpr_uint32)c) << 28; + if (add_value > 0xffffffffu - cur_value) { + goto error; + } + + *p->parsing.value = cur_value + add_value; + + if ((*cur) & 0x80) { + return parse_value5up(p, cur + 1, end); + } else { + return parse_next(p, cur + 1, end); + } + +error: + gpr_log(GPR_ERROR, + "integer overflow in hpack integer decoding: have 0x%08x, " + "got byte 0x%02x", + *p->parsing.value, *cur); + return parse_error(p, cur, end); +} + +/* parse any trailing bytes in a varint: it's possible to append an arbitrary + number of 0x80's and not affect the value - a zero will terminate - and + anything else will overflow */ +static int parse_value5up(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + while (cur != end && *cur == 0x80) { + ++cur; + } + + if (cur == end) { + p->state = parse_value5up; + return 1; + } + + if (*cur == 0) { + return parse_next(p, cur + 1, end); + } + + gpr_log(GPR_ERROR, + "integer overflow in hpack integer decoding: have 0x%08x, " + "got byte 0x%02x sometime after byte 4"); + return parse_error(p, cur, end); +} + +/* parse a string prefix */ +static int parse_string_prefix(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, const gpr_uint8 *end) { + if (cur == end) { + p->state = parse_string_prefix; + return 1; + } + + p->strlen = (*cur) & 0x7f; + p->huff = (*cur) >> 7; + if (p->strlen == 0x7f) { + p->parsing.value = &p->strlen; + return parse_value0(p, cur + 1, end); + } else { + return parse_next(p, cur + 1, end); + } +} + +/* append some bytes to a string */ +static void append_bytes(grpc_chttp2_hpack_parser_string *str, + const gpr_uint8 *data, size_t length) { + if (length + str->length > str->capacity) { + str->capacity = str->length + length; + str->str = gpr_realloc(str->str, str->capacity); + } + memcpy(str->str + str->length, data, length); + str->length += length; +} + +static int append_string(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + grpc_chttp2_hpack_parser_string *str = p->parsing.str; + gpr_uint32 bits; + gpr_uint8 decoded[3]; + switch ((binary_state)p->binary) { + case NOT_BINARY: + append_bytes(str, cur, end - cur); + return 1; + b64_byte0: + case B64_BYTE0: + if (cur == end) { + p->binary = B64_BYTE0; + return 1; + } + bits = inverse_base64[*cur]; + ++cur; + if (bits == 255) + return 0; + else if (bits == 64) + goto b64_byte0; + p->base64_buffer = bits << 18; + /* fallthrough */ + b64_byte1: + case B64_BYTE1: + if (cur == end) { + p->binary = B64_BYTE1; + return 1; + } + bits = inverse_base64[*cur]; + ++cur; + if (bits == 255) + return 0; + else if (bits == 64) + goto b64_byte1; + p->base64_buffer |= bits << 12; + /* fallthrough */ + b64_byte2: + case B64_BYTE2: + if (cur == end) { + p->binary = B64_BYTE2; + return 1; + } + bits = inverse_base64[*cur]; + ++cur; + if (bits == 255) + return 0; + else if (bits == 64) + goto b64_byte2; + p->base64_buffer |= bits << 6; + /* fallthrough */ + b64_byte3: + case B64_BYTE3: + if (cur == end) { + p->binary = B64_BYTE3; + return 1; + } + bits = inverse_base64[*cur]; + ++cur; + if (bits == 255) + return 0; + else if (bits == 64) + goto b64_byte3; + p->base64_buffer |= bits; + bits = p->base64_buffer; + decoded[0] = bits >> 16; + decoded[1] = bits >> 8; + decoded[2] = bits; + append_bytes(str, decoded, 3); + goto b64_byte0; + } + gpr_log(GPR_ERROR, "should never reach here"); + abort(); + return 1; +} + +/* append a null terminator to a string */ +static int finish_str(grpc_chttp2_hpack_parser *p) { + gpr_uint8 terminator = 0; + gpr_uint8 decoded[2]; + gpr_uint32 bits; + grpc_chttp2_hpack_parser_string *str = p->parsing.str; + switch ((binary_state)p->binary) { + case NOT_BINARY: + break; + case B64_BYTE0: + break; + case B64_BYTE1: + gpr_log(GPR_ERROR, "illegal base64 encoding"); + return 0; /* illegal encoding */ + case B64_BYTE2: + bits = p->base64_buffer; + if (bits & 0xffff) { + gpr_log(GPR_ERROR, "trailing bits in base64 encoding: 0x%04x", + bits & 0xffff); + return 0; + } + decoded[0] = bits >> 16; + append_bytes(str, decoded, 1); + break; + case B64_BYTE3: + bits = p->base64_buffer; + if (bits & 0xff) { + gpr_log(GPR_ERROR, "trailing bits in base64 encoding: 0x%02x", + bits & 0xff); + return 0; + } + decoded[0] = bits >> 16; + decoded[1] = bits >> 8; + append_bytes(str, decoded, 2); + break; + } + append_bytes(str, &terminator, 1); + p->parsing.str->length--; /* don't actually count the null terminator */ + return 1; +} + +/* decode a nibble from a huffman encoded stream */ +static int huff_nibble(grpc_chttp2_hpack_parser *p, gpr_uint8 nibble) { + gpr_int16 emit = emit_sub_tbl[16 * emit_tbl[p->huff_state] + nibble]; + gpr_int16 next = next_sub_tbl[16 * next_tbl[p->huff_state] + nibble]; + if (emit != -1) { + if (emit >= 0 && emit < 256) { + gpr_uint8 c = (gpr_uint8)emit; + if (!append_string(p, &c, (&c) + 1)) return 0; + } else { + assert(emit == 256); + } + } + p->huff_state = next; + return 1; +} + +/* decode full bytes from a huffman encoded stream */ +static int add_huff_bytes(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + for (; cur != end; ++cur) { + if (!huff_nibble(p, *cur >> 4) || !huff_nibble(p, *cur & 0xf)) return 0; + } + return 1; +} + +/* decode some string bytes based on the current decoding mode + (huffman or not) */ +static int add_str_bytes(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + if (p->huff) { + return add_huff_bytes(p, cur, end); + } else { + return append_string(p, cur, end); + } +} + +/* parse a string - tries to do large chunks at a time */ +static int parse_string(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + size_t remaining = p->strlen - p->strgot; + size_t given = end - cur; + if (remaining <= given) { + return add_str_bytes(p, cur, cur + remaining) && finish_str(p) && + parse_next(p, cur + remaining, end); + } else { + if (!add_str_bytes(p, cur, cur + given)) return 0; + p->strgot += given; + p->state = parse_string; + return 1; + } +} + +/* begin parsing a string - performs setup, calls parse_string */ +static int begin_parse_string(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end, gpr_uint8 binary, + grpc_chttp2_hpack_parser_string *str) { + p->strgot = 0; + str->length = 0; + p->parsing.str = str; + p->huff_state = 0; + p->binary = binary; + return parse_string(p, cur, end); +} + +/* parse the key string */ +static int parse_key_string(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + return begin_parse_string(p, cur, end, NOT_BINARY, &p->key); +} + +/* check if a key represents a binary header or not */ +typedef enum { BINARY_HEADER, PLAINTEXT_HEADER, ERROR_HEADER } is_binary_header; + +static is_binary_header is_binary_literal_header(grpc_chttp2_hpack_parser *p) { + return grpc_is_binary_header(p->key.str, p->key.length) ? BINARY_HEADER + : PLAINTEXT_HEADER; +} + +static is_binary_header is_binary_indexed_header(grpc_chttp2_hpack_parser *p) { + grpc_mdelem *elem = grpc_chttp2_hptbl_lookup(&p->table, p->index); + if (!elem) return ERROR_HEADER; + return grpc_is_binary_header( + (const char *)GPR_SLICE_START_PTR(elem->key->slice), + GPR_SLICE_LENGTH(elem->key->slice)) + ? BINARY_HEADER + : PLAINTEXT_HEADER; +} + +/* parse the value string */ +static int parse_value_string(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end, is_binary_header type) { + switch (type) { + case BINARY_HEADER: + return begin_parse_string(p, cur, end, B64_BYTE0, &p->value); + case PLAINTEXT_HEADER: + return begin_parse_string(p, cur, end, NOT_BINARY, &p->value); + case ERROR_HEADER: + return 0; + } + /* Add code to prevent return without value error */ + gpr_log(GPR_ERROR, "Should never reach beyond switch in parse_value_string"); + abort(); + return 0; +} + +static int parse_value_string_with_indexed_key(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, + const gpr_uint8 *end) { + return parse_value_string(p, cur, end, is_binary_indexed_header(p)); +} + +static int parse_value_string_with_literal_key(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, + const gpr_uint8 *end) { + return parse_value_string(p, cur, end, is_binary_literal_header(p)); +} + +/* PUBLIC INTERFACE */ + +static void on_header_not_set(void *user_data, grpc_mdelem *md) { + char *keyhex = gpr_dump_slice(md->key->slice, GPR_DUMP_HEX | GPR_DUMP_ASCII); + char *valuehex = + gpr_dump_slice(md->value->slice, GPR_DUMP_HEX | GPR_DUMP_ASCII); + gpr_log(GPR_ERROR, "on_header callback not set; key=%s value=%s", keyhex, + valuehex); + gpr_free(keyhex); + gpr_free(valuehex); + GRPC_MDELEM_UNREF(md); + abort(); +} + +void grpc_chttp2_hpack_parser_init(grpc_chttp2_hpack_parser *p, + grpc_mdctx *mdctx) { + p->on_header = on_header_not_set; + p->on_header_user_data = NULL; + p->state = parse_begin; + p->key.str = NULL; + p->key.capacity = 0; + p->key.length = 0; + p->value.str = NULL; + p->value.capacity = 0; + p->value.length = 0; + grpc_chttp2_hptbl_init(&p->table, mdctx); +} + +void grpc_chttp2_hpack_parser_set_has_priority(grpc_chttp2_hpack_parser *p) { + p->after_prioritization = p->state; + p->state = parse_stream_dep0; +} + +void grpc_chttp2_hpack_parser_destroy(grpc_chttp2_hpack_parser *p) { + grpc_chttp2_hptbl_destroy(&p->table); + gpr_free(p->key.str); + gpr_free(p->value.str); +} + +int grpc_chttp2_hpack_parser_parse(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *beg, const gpr_uint8 *end) { + /* TODO(ctiller): limit the distance of end from beg, and perform multiple + steps in the event of a large chunk of data to limit + stack space usage when no tail call optimization is + available */ + return p->state(p, beg, end); +} + +grpc_chttp2_parse_error grpc_chttp2_header_parser_parse( + void *hpack_parser, grpc_chttp2_transport_parsing *transport_parsing, + grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last) { + grpc_chttp2_hpack_parser *parser = hpack_parser; + if (!grpc_chttp2_hpack_parser_parse(parser, GPR_SLICE_START_PTR(slice), + GPR_SLICE_END_PTR(slice))) { + return GRPC_CHTTP2_CONNECTION_ERROR; + } + if (is_last) { + if (parser->is_boundary && parser->state != parse_begin) { + gpr_log(GPR_ERROR, + "end of header frame not aligned with a hpack record boundary"); + return GRPC_CHTTP2_CONNECTION_ERROR; + } + if (parser->is_boundary) { + grpc_chttp2_incoming_metadata_buffer_place_metadata_batch_into( + &stream_parsing->incoming_metadata, + &stream_parsing->data_parser.incoming_sopb); + grpc_chttp2_list_add_parsing_seen_stream(transport_parsing, + stream_parsing); + } + if (parser->is_eof) { + stream_parsing->received_close = 1; + } + parser->on_header = on_header_not_set; + parser->on_header_user_data = NULL; + parser->is_boundary = 0xde; + parser->is_eof = 0xde; + } + return GRPC_CHTTP2_PARSE_OK; +} diff --git a/src/core/transport/chttp2/hpack_parser.h b/src/core/transport/chttp2/hpack_parser.h new file mode 100644 index 00000000..c1768d9d --- /dev/null +++ b/src/core/transport/chttp2/hpack_parser.h @@ -0,0 +1,113 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_TRANSPORT_CHTTP2_HPACK_PARSER_H +#define GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_HPACK_PARSER_H + +#include + +#include +#include "src/core/transport/chttp2/frame.h" +#include "src/core/transport/chttp2/hpack_table.h" +#include "src/core/transport/metadata.h" + +typedef struct grpc_chttp2_hpack_parser grpc_chttp2_hpack_parser; + +typedef int (*grpc_chttp2_hpack_parser_state)(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *beg, + const gpr_uint8 *end); + +typedef struct { + char *str; + gpr_uint32 length; + gpr_uint32 capacity; +} grpc_chttp2_hpack_parser_string; + +struct grpc_chttp2_hpack_parser { + /* user specified callback for each header output */ + void (*on_header)(void *user_data, grpc_mdelem *md); + void *on_header_user_data; + + /* current parse state - or a function that implements it */ + grpc_chttp2_hpack_parser_state state; + /* future states dependent on the opening op code */ + const grpc_chttp2_hpack_parser_state *next_state; + /* what to do after skipping prioritization data */ + grpc_chttp2_hpack_parser_state after_prioritization; + /* the value we're currently parsing */ + union { + gpr_uint32 *value; + grpc_chttp2_hpack_parser_string *str; + } parsing; + /* string parameters for each chunk */ + grpc_chttp2_hpack_parser_string key; + grpc_chttp2_hpack_parser_string value; + /* parsed index */ + gpr_uint32 index; + /* length of source bytes for the currently parsing string */ + gpr_uint32 strlen; + /* number of source bytes read for the currently parsing string */ + gpr_uint32 strgot; + /* huffman decoding state */ + gpr_uint16 huff_state; + /* is the string being decoded binary? */ + gpr_uint8 binary; + /* is the current string huffman encoded? */ + gpr_uint8 huff; + /* set by higher layers, used by grpc_chttp2_header_parser_parse to signal + it should append a metadata boundary at the end of frame */ + gpr_uint8 is_boundary; + gpr_uint8 is_eof; + gpr_uint32 base64_buffer; + + /* hpack table */ + grpc_chttp2_hptbl table; +}; + +void grpc_chttp2_hpack_parser_init(grpc_chttp2_hpack_parser *p, + grpc_mdctx *mdctx); +void grpc_chttp2_hpack_parser_destroy(grpc_chttp2_hpack_parser *p); + +void grpc_chttp2_hpack_parser_set_has_priority(grpc_chttp2_hpack_parser *p); + +/* returns 1 on success, 0 on error */ +int grpc_chttp2_hpack_parser_parse(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *beg, const gpr_uint8 *end); + +/* wraps grpc_chttp2_hpack_parser_parse to provide a frame level parser for + the transport */ +grpc_chttp2_parse_error grpc_chttp2_header_parser_parse( + void *hpack_parser, grpc_chttp2_transport_parsing *transport_parsing, + grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last); + +#endif /* GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_HPACK_PARSER_H */ diff --git a/src/core/transport/chttp2/hpack_table.c b/src/core/transport/chttp2/hpack_table.c new file mode 100644 index 00000000..4fc15438 --- /dev/null +++ b/src/core/transport/chttp2/hpack_table.c @@ -0,0 +1,224 @@ +/* + * + * Copyright 2015, 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 "src/core/transport/chttp2/hpack_table.h" + +#include +#include + +#include +#include "src/core/support/murmur_hash.h" + +static struct { + const char *key; + const char *value; +} static_table[] = { + /* 0: */ {NULL, NULL}, + /* 1: */ {":authority", ""}, + /* 2: */ {":method", "GET"}, + /* 3: */ {":method", "POST"}, + /* 4: */ {":path", "/"}, + /* 5: */ {":path", "/index.html"}, + /* 6: */ {":scheme", "http"}, + /* 7: */ {":scheme", "https"}, + /* 8: */ {":status", "200"}, + /* 9: */ {":status", "204"}, + /* 10: */ {":status", "206"}, + /* 11: */ {":status", "304"}, + /* 12: */ {":status", "400"}, + /* 13: */ {":status", "404"}, + /* 14: */ {":status", "500"}, + /* 15: */ {"accept-charset", ""}, + /* 16: */ {"accept-encoding", "gzip, deflate"}, + /* 17: */ {"accept-language", ""}, + /* 18: */ {"accept-ranges", ""}, + /* 19: */ {"accept", ""}, + /* 20: */ {"access-control-allow-origin", ""}, + /* 21: */ {"age", ""}, + /* 22: */ {"allow", ""}, + /* 23: */ {"authorization", ""}, + /* 24: */ {"cache-control", ""}, + /* 25: */ {"content-disposition", ""}, + /* 26: */ {"content-encoding", ""}, + /* 27: */ {"content-language", ""}, + /* 28: */ {"content-length", ""}, + /* 29: */ {"content-location", ""}, + /* 30: */ {"content-range", ""}, + /* 31: */ {"content-type", ""}, + /* 32: */ {"cookie", ""}, + /* 33: */ {"date", ""}, + /* 34: */ {"etag", ""}, + /* 35: */ {"expect", ""}, + /* 36: */ {"expires", ""}, + /* 37: */ {"from", ""}, + /* 38: */ {"host", ""}, + /* 39: */ {"if-match", ""}, + /* 40: */ {"if-modified-since", ""}, + /* 41: */ {"if-none-match", ""}, + /* 42: */ {"if-range", ""}, + /* 43: */ {"if-unmodified-since", ""}, + /* 44: */ {"last-modified", ""}, + /* 45: */ {"link", ""}, + /* 46: */ {"location", ""}, + /* 47: */ {"max-forwards", ""}, + /* 48: */ {"proxy-authenticate", ""}, + /* 49: */ {"proxy-authorization", ""}, + /* 50: */ {"range", ""}, + /* 51: */ {"referer", ""}, + /* 52: */ {"refresh", ""}, + /* 53: */ {"retry-after", ""}, + /* 54: */ {"server", ""}, + /* 55: */ {"set-cookie", ""}, + /* 56: */ {"strict-transport-security", ""}, + /* 57: */ {"transfer-encoding", ""}, + /* 58: */ {"user-agent", ""}, + /* 59: */ {"vary", ""}, + /* 60: */ {"via", ""}, + /* 61: */ {"www-authenticate", ""}, +}; + +void grpc_chttp2_hptbl_init(grpc_chttp2_hptbl *tbl, grpc_mdctx *mdctx) { + size_t i; + + memset(tbl, 0, sizeof(*tbl)); + tbl->mdctx = mdctx; + tbl->max_bytes = GRPC_CHTTP2_INITIAL_HPACK_TABLE_SIZE; + for (i = 1; i <= GRPC_CHTTP2_LAST_STATIC_ENTRY; i++) { + tbl->static_ents[i - 1] = grpc_mdelem_from_strings( + mdctx, static_table[i].key, static_table[i].value); + } +} + +void grpc_chttp2_hptbl_destroy(grpc_chttp2_hptbl *tbl) { + size_t i; + for (i = 0; i < GRPC_CHTTP2_LAST_STATIC_ENTRY; i++) { + GRPC_MDELEM_UNREF(tbl->static_ents[i]); + } + for (i = 0; i < tbl->num_ents; i++) { + GRPC_MDELEM_UNREF( + tbl->ents[(tbl->first_ent + i) % GRPC_CHTTP2_MAX_TABLE_COUNT]); + } +} + +grpc_mdelem *grpc_chttp2_hptbl_lookup(const grpc_chttp2_hptbl *tbl, + gpr_uint32 index) { + /* Static table comes first, just return an entry from it */ + if (index <= GRPC_CHTTP2_LAST_STATIC_ENTRY) { + return tbl->static_ents[index - 1]; + } + /* Otherwise, find the value in the list of valid entries */ + index -= (GRPC_CHTTP2_LAST_STATIC_ENTRY + 1); + if (index < tbl->num_ents) { + gpr_uint32 offset = (tbl->num_ents - 1 - index + tbl->first_ent) % + GRPC_CHTTP2_MAX_TABLE_COUNT; + return tbl->ents[offset]; + } + /* Invalid entry: return error */ + return NULL; +} + +/* Evict one element from the table */ +static void evict1(grpc_chttp2_hptbl *tbl) { + grpc_mdelem *first_ent = tbl->ents[tbl->first_ent]; + tbl->mem_used -= GPR_SLICE_LENGTH(first_ent->key->slice) + + GPR_SLICE_LENGTH(first_ent->value->slice) + + GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD; + tbl->first_ent = (tbl->first_ent + 1) % GRPC_CHTTP2_MAX_TABLE_COUNT; + tbl->num_ents--; + GRPC_MDELEM_UNREF(first_ent); +} + +void grpc_chttp2_hptbl_add(grpc_chttp2_hptbl *tbl, grpc_mdelem *md) { + /* determine how many bytes of buffer this entry represents */ + gpr_uint16 elem_bytes = GPR_SLICE_LENGTH(md->key->slice) + + GPR_SLICE_LENGTH(md->value->slice) + + GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD; + + /* we can't add elements bigger than the max table size */ + if (elem_bytes > tbl->max_bytes) { + /* HPACK draft 10 section 4.4 states: + * If the size of the new entry is less than or equal to the maximum + * size, that entry is added to the table. It is not an error to + * attempt to add an entry that is larger than the maximum size; an + * attempt to add an entry larger than the entire table causes + * the table + * to be emptied of all existing entries, and results in an + * empty table. + */ + while (tbl->num_ents) { + evict1(tbl); + } + return; + } + + /* evict entries to ensure no overflow */ + while (elem_bytes > tbl->max_bytes - tbl->mem_used) { + evict1(tbl); + } + + /* copy the finalized entry in */ + tbl->ents[tbl->last_ent] = md; + + /* update accounting values */ + tbl->last_ent = (tbl->last_ent + 1) % GRPC_CHTTP2_MAX_TABLE_COUNT; + tbl->num_ents++; + tbl->mem_used += elem_bytes; +} + +grpc_chttp2_hptbl_find_result grpc_chttp2_hptbl_find( + const grpc_chttp2_hptbl *tbl, grpc_mdelem *md) { + grpc_chttp2_hptbl_find_result r = {0, 0}; + int i; + + /* See if the string is in the static table */ + for (i = 0; i < GRPC_CHTTP2_LAST_STATIC_ENTRY; i++) { + grpc_mdelem *ent = tbl->static_ents[i]; + if (md->key != ent->key) continue; + r.index = i + 1; + r.has_value = md->value == ent->value; + if (r.has_value) return r; + } + + /* Scan the dynamic table */ + for (i = 0; i < tbl->num_ents; i++) { + int idx = tbl->num_ents - i + GRPC_CHTTP2_LAST_STATIC_ENTRY; + grpc_mdelem *ent = + tbl->ents[(tbl->first_ent + i) % GRPC_CHTTP2_MAX_TABLE_COUNT]; + if (md->key != ent->key) continue; + r.index = idx; + r.has_value = md->value == ent->value; + if (r.has_value) return r; + } + + return r; +} diff --git a/src/core/transport/chttp2/hpack_table.h b/src/core/transport/chttp2/hpack_table.h new file mode 100644 index 00000000..4f882e2e --- /dev/null +++ b/src/core/transport/chttp2/hpack_table.h @@ -0,0 +1,97 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_TRANSPORT_CHTTP2_HPACK_TABLE_H +#define GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_HPACK_TABLE_H + +#include "src/core/transport/metadata.h" +#include +#include + +/* HPACK header table */ + +/* last index in the static table */ +#define GRPC_CHTTP2_LAST_STATIC_ENTRY 61 + +/* Initial table size as per the spec */ +#define GRPC_CHTTP2_INITIAL_HPACK_TABLE_SIZE 4096 +/* Maximum table size that we'll use */ +#define GRPC_CHTTP2_MAX_HPACK_TABLE_SIZE GRPC_CHTTP2_INITIAL_HPACK_TABLE_SIZE +/* Per entry overhead bytes as per the spec */ +#define GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD 32 +/* Maximum number of entries we could possibly fit in the table, given defined + overheads */ +#define GRPC_CHTTP2_MAX_TABLE_COUNT \ + ((GRPC_CHTTP2_MAX_HPACK_TABLE_SIZE + GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD - 1) / \ + GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD) + +/* hpack decoder table */ +typedef struct { + grpc_mdctx *mdctx; + /* the first used entry in ents */ + gpr_uint16 first_ent; + /* the last used entry in ents */ + gpr_uint16 last_ent; + /* how many entries are in the table */ + gpr_uint16 num_ents; + /* the amount of memory used by the table, according to the hpack algorithm */ + gpr_uint16 mem_used; + /* the max memory allowed to be used by the table, according to the hpack + algorithm */ + gpr_uint16 max_bytes; + /* a circular buffer of headers - this is stored in the opposite order to + what hpack specifies, in order to simplify table management a little... + meaning lookups need to SUBTRACT from the end position */ + grpc_mdelem *ents[GRPC_CHTTP2_MAX_TABLE_COUNT]; + grpc_mdelem *static_ents[GRPC_CHTTP2_LAST_STATIC_ENTRY]; +} grpc_chttp2_hptbl; + +/* initialize a hpack table */ +void grpc_chttp2_hptbl_init(grpc_chttp2_hptbl *tbl, grpc_mdctx *mdctx); +void grpc_chttp2_hptbl_destroy(grpc_chttp2_hptbl *tbl); + +/* lookup a table entry based on its hpack index */ +grpc_mdelem *grpc_chttp2_hptbl_lookup(const grpc_chttp2_hptbl *tbl, + gpr_uint32 index); +/* add a table entry to the index */ +void grpc_chttp2_hptbl_add(grpc_chttp2_hptbl *tbl, grpc_mdelem *md); +/* Find a key/value pair in the table... returns the index in the table of the + most similar entry, or 0 if the value was not found */ +typedef struct { + gpr_uint16 index; + gpr_uint8 has_value; +} grpc_chttp2_hptbl_find_result; +grpc_chttp2_hptbl_find_result grpc_chttp2_hptbl_find( + const grpc_chttp2_hptbl *tbl, grpc_mdelem *md); + +#endif /* GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_HPACK_TABLE_H */ diff --git a/src/core/transport/chttp2/hpack_tables.txt b/src/core/transport/chttp2/hpack_tables.txt new file mode 100644 index 00000000..08842a02 --- /dev/null +++ b/src/core/transport/chttp2/hpack_tables.txt @@ -0,0 +1,66 @@ +Static table, from the spec: + +-------+-----------------------------+---------------+ + | Index | Header Name | Header Value | + +-------+-----------------------------+---------------+ + | 1 | :authority | | + | 2 | :method | GET | + | 3 | :method | POST | + | 4 | :path | / | + | 5 | :path | /index.html | + | 6 | :scheme | http | + | 7 | :scheme | https | + | 8 | :status | 200 | + | 9 | :status | 204 | + | 10 | :status | 206 | + | 11 | :status | 304 | + | 12 | :status | 400 | + | 13 | :status | 404 | + | 14 | :status | 500 | + | 15 | accept-charset | | + | 16 | accept-encoding | gzip, deflate | + | 17 | accept-language | | + | 18 | accept-ranges | | + | 19 | accept | | + | 20 | access-control-allow-origin | | + | 21 | age | | + | 22 | allow | | + | 23 | authorization | | + | 24 | cache-control | | + | 25 | content-disposition | | + | 26 | content-encoding | | + | 27 | content-language | | + | 28 | content-length | | + | 29 | content-location | | + | 30 | content-range | | + | 31 | content-type | | + | 32 | cookie | | + | 33 | date | | + | 34 | etag | | + | 35 | expect | | + | 36 | expires | | + | 37 | from | | + | 38 | host | | + | 39 | if-match | | + | 40 | if-modified-since | | + | 41 | if-none-match | | + | 42 | if-range | | + | 43 | if-unmodified-since | | + | 44 | last-modified | | + | 45 | link | | + | 46 | location | | + | 47 | max-forwards | | + | 48 | proxy-authenticate | | + | 49 | proxy-authorization | | + | 50 | range | | + | 51 | referer | | + | 52 | refresh | | + | 53 | retry-after | | + | 54 | server | | + | 55 | set-cookie | | + | 56 | strict-transport-security | | + | 57 | transfer-encoding | | + | 58 | user-agent | | + | 59 | vary | | + | 60 | via | | + | 61 | www-authenticate | | + +-------+-----------------------------+---------------+ diff --git a/src/core/transport/chttp2/http2_errors.h b/src/core/transport/chttp2/http2_errors.h new file mode 100644 index 00000000..a4f309e0 --- /dev/null +++ b/src/core/transport/chttp2/http2_errors.h @@ -0,0 +1,56 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_TRANSPORT_CHTTP2_HTTP2_ERRORS_H +#define GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_HTTP2_ERRORS_H + +/* error codes for RST_STREAM from http2 draft 14 section 7 */ +typedef enum { + GRPC_CHTTP2_NO_ERROR = 0x0, + GRPC_CHTTP2_PROTOCOL_ERROR = 0x1, + GRPC_CHTTP2_INTERNAL_ERROR = 0x2, + GRPC_CHTTP2_FLOW_CONTROL_ERROR = 0x3, + GRPC_CHTTP2_SETTINGS_TIMEOUT = 0x4, + GRPC_CHTTP2_STREAM_CLOSED = 0x5, + GRPC_CHTTP2_FRAME_SIZE_ERROR = 0x6, + GRPC_CHTTP2_REFUSED_STREAM = 0x7, + GRPC_CHTTP2_CANCEL = 0x8, + GRPC_CHTTP2_COMPRESSION_ERROR = 0x9, + GRPC_CHTTP2_CONNECT_ERROR = 0xa, + GRPC_CHTTP2_ENHANCE_YOUR_CALM = 0xb, + GRPC_CHTTP2_INADEQUATE_SECURITY = 0xc, + /* force use of a default clause */ + GRPC_CHTTP2__ERROR_DO_NOT_USE = -1 +} grpc_chttp2_error_code; + +#endif /* GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_HTTP2_ERRORS_H */ diff --git a/src/core/transport/chttp2/huffsyms.c b/src/core/transport/chttp2/huffsyms.c new file mode 100644 index 00000000..6f5cf6a2 --- /dev/null +++ b/src/core/transport/chttp2/huffsyms.c @@ -0,0 +1,105 @@ +/* + * + * Copyright 2015, 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 "src/core/transport/chttp2/huffsyms.h" + +/* Constants pulled from the HPACK spec, and converted to C using the vim + command: + :%s/.* \([0-9a-f]\+\) \[ *\([0-9]\+\)\]/{0x\1, \2},/g */ +const grpc_chttp2_huffsym grpc_chttp2_huffsyms[GRPC_CHTTP2_NUM_HUFFSYMS] = { + {0x1ff8, 13}, {0x7fffd8, 23}, {0xfffffe2, 28}, {0xfffffe3, 28}, + {0xfffffe4, 28}, {0xfffffe5, 28}, {0xfffffe6, 28}, {0xfffffe7, 28}, + {0xfffffe8, 28}, {0xffffea, 24}, {0x3ffffffc, 30}, {0xfffffe9, 28}, + {0xfffffea, 28}, {0x3ffffffd, 30}, {0xfffffeb, 28}, {0xfffffec, 28}, + {0xfffffed, 28}, {0xfffffee, 28}, {0xfffffef, 28}, {0xffffff0, 28}, + {0xffffff1, 28}, {0xffffff2, 28}, {0x3ffffffe, 30}, {0xffffff3, 28}, + {0xffffff4, 28}, {0xffffff5, 28}, {0xffffff6, 28}, {0xffffff7, 28}, + {0xffffff8, 28}, {0xffffff9, 28}, {0xffffffa, 28}, {0xffffffb, 28}, + {0x14, 6}, {0x3f8, 10}, {0x3f9, 10}, {0xffa, 12}, + {0x1ff9, 13}, {0x15, 6}, {0xf8, 8}, {0x7fa, 11}, + {0x3fa, 10}, {0x3fb, 10}, {0xf9, 8}, {0x7fb, 11}, + {0xfa, 8}, {0x16, 6}, {0x17, 6}, {0x18, 6}, + {0x0, 5}, {0x1, 5}, {0x2, 5}, {0x19, 6}, + {0x1a, 6}, {0x1b, 6}, {0x1c, 6}, {0x1d, 6}, + {0x1e, 6}, {0x1f, 6}, {0x5c, 7}, {0xfb, 8}, + {0x7ffc, 15}, {0x20, 6}, {0xffb, 12}, {0x3fc, 10}, + {0x1ffa, 13}, {0x21, 6}, {0x5d, 7}, {0x5e, 7}, + {0x5f, 7}, {0x60, 7}, {0x61, 7}, {0x62, 7}, + {0x63, 7}, {0x64, 7}, {0x65, 7}, {0x66, 7}, + {0x67, 7}, {0x68, 7}, {0x69, 7}, {0x6a, 7}, + {0x6b, 7}, {0x6c, 7}, {0x6d, 7}, {0x6e, 7}, + {0x6f, 7}, {0x70, 7}, {0x71, 7}, {0x72, 7}, + {0xfc, 8}, {0x73, 7}, {0xfd, 8}, {0x1ffb, 13}, + {0x7fff0, 19}, {0x1ffc, 13}, {0x3ffc, 14}, {0x22, 6}, + {0x7ffd, 15}, {0x3, 5}, {0x23, 6}, {0x4, 5}, + {0x24, 6}, {0x5, 5}, {0x25, 6}, {0x26, 6}, + {0x27, 6}, {0x6, 5}, {0x74, 7}, {0x75, 7}, + {0x28, 6}, {0x29, 6}, {0x2a, 6}, {0x7, 5}, + {0x2b, 6}, {0x76, 7}, {0x2c, 6}, {0x8, 5}, + {0x9, 5}, {0x2d, 6}, {0x77, 7}, {0x78, 7}, + {0x79, 7}, {0x7a, 7}, {0x7b, 7}, {0x7ffe, 15}, + {0x7fc, 11}, {0x3ffd, 14}, {0x1ffd, 13}, {0xffffffc, 28}, + {0xfffe6, 20}, {0x3fffd2, 22}, {0xfffe7, 20}, {0xfffe8, 20}, + {0x3fffd3, 22}, {0x3fffd4, 22}, {0x3fffd5, 22}, {0x7fffd9, 23}, + {0x3fffd6, 22}, {0x7fffda, 23}, {0x7fffdb, 23}, {0x7fffdc, 23}, + {0x7fffdd, 23}, {0x7fffde, 23}, {0xffffeb, 24}, {0x7fffdf, 23}, + {0xffffec, 24}, {0xffffed, 24}, {0x3fffd7, 22}, {0x7fffe0, 23}, + {0xffffee, 24}, {0x7fffe1, 23}, {0x7fffe2, 23}, {0x7fffe3, 23}, + {0x7fffe4, 23}, {0x1fffdc, 21}, {0x3fffd8, 22}, {0x7fffe5, 23}, + {0x3fffd9, 22}, {0x7fffe6, 23}, {0x7fffe7, 23}, {0xffffef, 24}, + {0x3fffda, 22}, {0x1fffdd, 21}, {0xfffe9, 20}, {0x3fffdb, 22}, + {0x3fffdc, 22}, {0x7fffe8, 23}, {0x7fffe9, 23}, {0x1fffde, 21}, + {0x7fffea, 23}, {0x3fffdd, 22}, {0x3fffde, 22}, {0xfffff0, 24}, + {0x1fffdf, 21}, {0x3fffdf, 22}, {0x7fffeb, 23}, {0x7fffec, 23}, + {0x1fffe0, 21}, {0x1fffe1, 21}, {0x3fffe0, 22}, {0x1fffe2, 21}, + {0x7fffed, 23}, {0x3fffe1, 22}, {0x7fffee, 23}, {0x7fffef, 23}, + {0xfffea, 20}, {0x3fffe2, 22}, {0x3fffe3, 22}, {0x3fffe4, 22}, + {0x7ffff0, 23}, {0x3fffe5, 22}, {0x3fffe6, 22}, {0x7ffff1, 23}, + {0x3ffffe0, 26}, {0x3ffffe1, 26}, {0xfffeb, 20}, {0x7fff1, 19}, + {0x3fffe7, 22}, {0x7ffff2, 23}, {0x3fffe8, 22}, {0x1ffffec, 25}, + {0x3ffffe2, 26}, {0x3ffffe3, 26}, {0x3ffffe4, 26}, {0x7ffffde, 27}, + {0x7ffffdf, 27}, {0x3ffffe5, 26}, {0xfffff1, 24}, {0x1ffffed, 25}, + {0x7fff2, 19}, {0x1fffe3, 21}, {0x3ffffe6, 26}, {0x7ffffe0, 27}, + {0x7ffffe1, 27}, {0x3ffffe7, 26}, {0x7ffffe2, 27}, {0xfffff2, 24}, + {0x1fffe4, 21}, {0x1fffe5, 21}, {0x3ffffe8, 26}, {0x3ffffe9, 26}, + {0xffffffd, 28}, {0x7ffffe3, 27}, {0x7ffffe4, 27}, {0x7ffffe5, 27}, + {0xfffec, 20}, {0xfffff3, 24}, {0xfffed, 20}, {0x1fffe6, 21}, + {0x3fffe9, 22}, {0x1fffe7, 21}, {0x1fffe8, 21}, {0x7ffff3, 23}, + {0x3fffea, 22}, {0x3fffeb, 22}, {0x1ffffee, 25}, {0x1ffffef, 25}, + {0xfffff4, 24}, {0xfffff5, 24}, {0x3ffffea, 26}, {0x7ffff4, 23}, + {0x3ffffeb, 26}, {0x7ffffe6, 27}, {0x3ffffec, 26}, {0x3ffffed, 26}, + {0x7ffffe7, 27}, {0x7ffffe8, 27}, {0x7ffffe9, 27}, {0x7ffffea, 27}, + {0x7ffffeb, 27}, {0xffffffe, 28}, {0x7ffffec, 27}, {0x7ffffed, 27}, + {0x7ffffee, 27}, {0x7ffffef, 27}, {0x7fffff0, 27}, {0x3ffffee, 26}, + {0x3fffffff, 30}, +}; diff --git a/src/core/transport/chttp2/huffsyms.h b/src/core/transport/chttp2/huffsyms.h new file mode 100644 index 00000000..a3cdba82 --- /dev/null +++ b/src/core/transport/chttp2/huffsyms.h @@ -0,0 +1,48 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_TRANSPORT_CHTTP2_HUFFSYMS_H +#define GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_HUFFSYMS_H + +/* HPACK static huffman table */ + +#define GRPC_CHTTP2_NUM_HUFFSYMS 257 + +typedef struct { + unsigned bits; + unsigned length; +} grpc_chttp2_huffsym; + +extern const grpc_chttp2_huffsym grpc_chttp2_huffsyms[GRPC_CHTTP2_NUM_HUFFSYMS]; + +#endif /* GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_HUFFSYMS_H */ diff --git a/src/core/transport/chttp2/incoming_metadata.c b/src/core/transport/chttp2/incoming_metadata.c new file mode 100644 index 00000000..974b864f --- /dev/null +++ b/src/core/transport/chttp2/incoming_metadata.c @@ -0,0 +1,182 @@ +/* + * + * Copyright 2015, 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 "src/core/transport/chttp2/incoming_metadata.h" + +#include + +#include "src/core/transport/chttp2/internal.h" + +#include +#include + +void grpc_chttp2_incoming_metadata_buffer_init( + grpc_chttp2_incoming_metadata_buffer *buffer) { + buffer->deadline = gpr_inf_future(GPR_CLOCK_REALTIME); +} + +void grpc_chttp2_incoming_metadata_buffer_destroy( + grpc_chttp2_incoming_metadata_buffer *buffer) { + size_t i; + for (i = 0; i < buffer->count; i++) { + GRPC_MDELEM_UNREF(buffer->elems[i].md); + } + gpr_free(buffer->elems); +} + +void grpc_chttp2_incoming_metadata_buffer_add( + grpc_chttp2_incoming_metadata_buffer *buffer, grpc_mdelem *elem) { + if (buffer->capacity == buffer->count) { + buffer->capacity = GPR_MAX(8, 2 * buffer->capacity); + buffer->elems = + gpr_realloc(buffer->elems, sizeof(*buffer->elems) * buffer->capacity); + } + buffer->elems[buffer->count++].md = elem; +} + +void grpc_chttp2_incoming_metadata_buffer_set_deadline( + grpc_chttp2_incoming_metadata_buffer *buffer, gpr_timespec deadline) { + buffer->deadline = deadline; +} + +void grpc_chttp2_incoming_metadata_live_op_buffer_end( + grpc_chttp2_incoming_metadata_live_op_buffer *buffer) { + gpr_free(buffer->elems); + buffer->elems = NULL; +} + +void grpc_chttp2_incoming_metadata_buffer_place_metadata_batch_into( + grpc_chttp2_incoming_metadata_buffer *buffer, grpc_stream_op_buffer *sopb) { + grpc_metadata_batch b; + + b.list.head = NULL; + /* Store away the last element of the list, so that in patch_metadata_ops + we can reconstitute the list. + We can't do list building here as later incoming metadata may reallocate + the underlying array. */ + b.list.tail = (void *)(gpr_intptr)buffer->count; + b.garbage.head = b.garbage.tail = NULL; + b.deadline = buffer->deadline; + buffer->deadline = gpr_inf_future(GPR_CLOCK_REALTIME); + + grpc_sopb_add_metadata(sopb, b); +} + +void grpc_chttp2_incoming_metadata_buffer_swap( + grpc_chttp2_incoming_metadata_buffer *a, + grpc_chttp2_incoming_metadata_buffer *b) { + GPR_SWAP(grpc_chttp2_incoming_metadata_buffer, *a, *b); +} + +void grpc_incoming_metadata_buffer_move_to_referencing_sopb( + grpc_chttp2_incoming_metadata_buffer *src, + grpc_chttp2_incoming_metadata_buffer *dst, grpc_stream_op_buffer *sopb) { + size_t delta; + size_t i; + dst->deadline = gpr_time_min(src->deadline, dst->deadline); + + if (src->count == 0) { + return; + } + if (dst->count == 0) { + grpc_chttp2_incoming_metadata_buffer_swap(src, dst); + return; + } + delta = dst->count; + if (dst->capacity < src->count + dst->count) { + dst->capacity = GPR_MAX(dst->capacity * 2, src->count + dst->count); + dst->elems = gpr_realloc(dst->elems, dst->capacity * sizeof(*dst->elems)); + } + memcpy(dst->elems + dst->count, src->elems, src->count * sizeof(*src->elems)); + dst->count += src->count; + for (i = 0; i < sopb->nops; i++) { + if (sopb->ops[i].type != GRPC_OP_METADATA) continue; + sopb->ops[i].data.metadata.list.tail = + (void *)(delta + (gpr_intptr)sopb->ops[i].data.metadata.list.tail); + } + src->count = 0; +} + +void grpc_chttp2_incoming_metadata_buffer_postprocess_sopb_and_begin_live_op( + grpc_chttp2_incoming_metadata_buffer *buffer, grpc_stream_op_buffer *sopb, + grpc_chttp2_incoming_metadata_live_op_buffer *live_op_buffer) { + grpc_stream_op *ops = sopb->ops; + size_t nops = sopb->nops; + size_t i; + size_t j; + size_t mdidx = 0; + size_t last_mdidx; + int found_metadata = 0; + + /* rework the array of metadata into a linked list, making use + of the breadcrumbs we left in metadata batches during + add_metadata_batch */ + for (i = 0; i < nops; i++) { + grpc_stream_op *op = &ops[i]; + if (op->type != GRPC_OP_METADATA) continue; + found_metadata = 1; + /* we left a breadcrumb indicating where the end of this list is, + and since we add sequentially, we know from the end of the last + segment where this segment begins */ + last_mdidx = (size_t)(gpr_intptr)(op->data.metadata.list.tail); + GPR_ASSERT(last_mdidx > mdidx); + GPR_ASSERT(last_mdidx <= buffer->count); + /* turn the array into a doubly linked list */ + op->data.metadata.list.head = &buffer->elems[mdidx]; + op->data.metadata.list.tail = &buffer->elems[last_mdidx - 1]; + for (j = mdidx + 1; j < last_mdidx; j++) { + buffer->elems[j].prev = &buffer->elems[j - 1]; + buffer->elems[j - 1].next = &buffer->elems[j]; + } + buffer->elems[mdidx].prev = NULL; + buffer->elems[last_mdidx - 1].next = NULL; + /* track where we're up to */ + mdidx = last_mdidx; + } + if (found_metadata) { + live_op_buffer->elems = buffer->elems; + if (mdidx != buffer->count) { + /* we have a partially read metadata batch still in incoming_metadata */ + size_t new_count = buffer->count - mdidx; + size_t copy_bytes = sizeof(*buffer->elems) * new_count; + GPR_ASSERT(mdidx < buffer->count); + buffer->elems = gpr_malloc(copy_bytes); + memcpy(live_op_buffer->elems + mdidx, buffer->elems, copy_bytes); + buffer->count = buffer->capacity = new_count; + } else { + buffer->elems = NULL; + buffer->count = 0; + buffer->capacity = 0; + } + } +} diff --git a/src/core/transport/chttp2/incoming_metadata.h b/src/core/transport/chttp2/incoming_metadata.h new file mode 100644 index 00000000..2f1de411 --- /dev/null +++ b/src/core/transport/chttp2/incoming_metadata.h @@ -0,0 +1,80 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_CHTTP2_INCOMING_METADATA_H +#define GRPC_INTERNAL_CORE_CHTTP2_INCOMING_METADATA_H + +#include "src/core/transport/transport.h" + +typedef struct { + grpc_linked_mdelem *elems; + size_t count; + size_t capacity; + gpr_timespec deadline; +} grpc_chttp2_incoming_metadata_buffer; + +typedef struct { + grpc_linked_mdelem *elems; +} grpc_chttp2_incoming_metadata_live_op_buffer; + +/** assumes everything initially zeroed */ +void grpc_chttp2_incoming_metadata_buffer_init( + grpc_chttp2_incoming_metadata_buffer *buffer); +void grpc_chttp2_incoming_metadata_buffer_destroy( + grpc_chttp2_incoming_metadata_buffer *buffer); +void grpc_chttp2_incoming_metadata_buffer_reset( + grpc_chttp2_incoming_metadata_buffer *buffer); + +void grpc_chttp2_incoming_metadata_buffer_add( + grpc_chttp2_incoming_metadata_buffer *buffer, grpc_mdelem *elem); +void grpc_chttp2_incoming_metadata_buffer_set_deadline( + grpc_chttp2_incoming_metadata_buffer *buffer, gpr_timespec deadline); + +/** extend sopb with a metadata batch; this must be post-processed by + grpc_chttp2_incoming_metadata_buffer_postprocess_sopb before being handed + out of the transport */ +void grpc_chttp2_incoming_metadata_buffer_place_metadata_batch_into( + grpc_chttp2_incoming_metadata_buffer *buffer, grpc_stream_op_buffer *sopb); + +void grpc_incoming_metadata_buffer_move_to_referencing_sopb( + grpc_chttp2_incoming_metadata_buffer *src, + grpc_chttp2_incoming_metadata_buffer *dst, grpc_stream_op_buffer *sopb); + +void grpc_chttp2_incoming_metadata_buffer_postprocess_sopb_and_begin_live_op( + grpc_chttp2_incoming_metadata_buffer *buffer, grpc_stream_op_buffer *sopb, + grpc_chttp2_incoming_metadata_live_op_buffer *live_op_buffer); + +void grpc_chttp2_incoming_metadata_live_op_buffer_end( + grpc_chttp2_incoming_metadata_live_op_buffer *live_op_buffer); + +#endif /* GRPC_INTERNAL_CORE_CHTTP2_INCOMING_METADATA_H */ diff --git a/src/core/transport/chttp2/internal.h b/src/core/transport/chttp2/internal.h new file mode 100644 index 00000000..7a42de92 --- /dev/null +++ b/src/core/transport/chttp2/internal.h @@ -0,0 +1,633 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_CHTTP2_INTERNAL_H +#define GRPC_INTERNAL_CORE_CHTTP2_INTERNAL_H + +#include "src/core/iomgr/endpoint.h" +#include "src/core/transport/chttp2/frame.h" +#include "src/core/transport/chttp2/frame_data.h" +#include "src/core/transport/chttp2/frame_goaway.h" +#include "src/core/transport/chttp2/frame_ping.h" +#include "src/core/transport/chttp2/frame_rst_stream.h" +#include "src/core/transport/chttp2/frame_settings.h" +#include "src/core/transport/chttp2/frame_window_update.h" +#include "src/core/transport/chttp2/hpack_parser.h" +#include "src/core/transport/chttp2/incoming_metadata.h" +#include "src/core/transport/chttp2/stream_encoder.h" +#include "src/core/transport/chttp2/stream_map.h" +#include "src/core/transport/connectivity_state.h" +#include "src/core/transport/transport_impl.h" + +typedef struct grpc_chttp2_transport grpc_chttp2_transport; +typedef struct grpc_chttp2_stream grpc_chttp2_stream; + +/* streams are kept in various linked lists depending on what things need to + happen to them... this enum labels each list */ +typedef enum { + GRPC_CHTTP2_LIST_ALL_STREAMS, + GRPC_CHTTP2_LIST_READ_WRITE_STATE_CHANGED, + GRPC_CHTTP2_LIST_WRITABLE, + GRPC_CHTTP2_LIST_WRITING, + GRPC_CHTTP2_LIST_WRITTEN, + GRPC_CHTTP2_LIST_PARSING_SEEN, + GRPC_CHTTP2_LIST_CLOSED_WAITING_FOR_PARSING, + GRPC_CHTTP2_LIST_CANCELLED_WAITING_FOR_WRITING, + GRPC_CHTTP2_LIST_INCOMING_WINDOW_UPDATED, + /** streams that are waiting to start because there are too many concurrent + streams on the connection */ + GRPC_CHTTP2_LIST_WAITING_FOR_CONCURRENCY, + STREAM_LIST_COUNT /* must be last */ +} grpc_chttp2_stream_list_id; + +/* deframer state for the overall http2 stream of bytes */ +typedef enum { + /* prefix: one entry per http2 connection prefix byte */ + GRPC_DTS_CLIENT_PREFIX_0 = 0, + GRPC_DTS_CLIENT_PREFIX_1, + GRPC_DTS_CLIENT_PREFIX_2, + GRPC_DTS_CLIENT_PREFIX_3, + GRPC_DTS_CLIENT_PREFIX_4, + GRPC_DTS_CLIENT_PREFIX_5, + GRPC_DTS_CLIENT_PREFIX_6, + GRPC_DTS_CLIENT_PREFIX_7, + GRPC_DTS_CLIENT_PREFIX_8, + GRPC_DTS_CLIENT_PREFIX_9, + GRPC_DTS_CLIENT_PREFIX_10, + GRPC_DTS_CLIENT_PREFIX_11, + GRPC_DTS_CLIENT_PREFIX_12, + GRPC_DTS_CLIENT_PREFIX_13, + GRPC_DTS_CLIENT_PREFIX_14, + GRPC_DTS_CLIENT_PREFIX_15, + GRPC_DTS_CLIENT_PREFIX_16, + GRPC_DTS_CLIENT_PREFIX_17, + GRPC_DTS_CLIENT_PREFIX_18, + GRPC_DTS_CLIENT_PREFIX_19, + GRPC_DTS_CLIENT_PREFIX_20, + GRPC_DTS_CLIENT_PREFIX_21, + GRPC_DTS_CLIENT_PREFIX_22, + GRPC_DTS_CLIENT_PREFIX_23, + /* frame header byte 0... */ + /* must follow from the prefix states */ + GRPC_DTS_FH_0, + GRPC_DTS_FH_1, + GRPC_DTS_FH_2, + GRPC_DTS_FH_3, + GRPC_DTS_FH_4, + GRPC_DTS_FH_5, + GRPC_DTS_FH_6, + GRPC_DTS_FH_7, + /* ... frame header byte 8 */ + GRPC_DTS_FH_8, + /* inside a http2 frame */ + GRPC_DTS_FRAME +} grpc_chttp2_deframe_transport_state; + +typedef enum { + GRPC_WRITE_STATE_OPEN, + GRPC_WRITE_STATE_QUEUED_CLOSE, + GRPC_WRITE_STATE_SENT_CLOSE +} grpc_chttp2_write_state; + +/* flags that can be or'd into stream_global::writing_now */ +#define GRPC_CHTTP2_WRITING_DATA 1 +#define GRPC_CHTTP2_WRITING_WINDOW 2 + +typedef enum { + GRPC_DONT_SEND_CLOSED = 0, + GRPC_SEND_CLOSED, + GRPC_SEND_CLOSED_WITH_RST_STREAM +} grpc_chttp2_send_closed; + +typedef struct { + grpc_chttp2_stream *head; + grpc_chttp2_stream *tail; +} grpc_chttp2_stream_list; + +typedef struct { + grpc_chttp2_stream *next; + grpc_chttp2_stream *prev; +} grpc_chttp2_stream_link; + +/* We keep several sets of connection wide parameters */ +typedef enum { + /* The settings our peer has asked for (and we have acked) */ + GRPC_PEER_SETTINGS = 0, + /* The settings we'd like to have */ + GRPC_LOCAL_SETTINGS, + /* The settings we've published to our peer */ + GRPC_SENT_SETTINGS, + /* The settings the peer has acked */ + GRPC_ACKED_SETTINGS, + GRPC_NUM_SETTING_SETS +} grpc_chttp2_setting_set; + +/* Outstanding ping request data */ +typedef struct grpc_chttp2_outstanding_ping { + gpr_uint8 id[8]; + grpc_iomgr_closure *on_recv; + struct grpc_chttp2_outstanding_ping *next; + struct grpc_chttp2_outstanding_ping *prev; +} grpc_chttp2_outstanding_ping; + +typedef struct { + /** data to write next write */ + gpr_slice_buffer qbuf; + /** queued callbacks */ + grpc_iomgr_closure *pending_closures_head; + grpc_iomgr_closure *pending_closures_tail; + + /** window available for us to send to peer */ + gpr_uint32 outgoing_window; + /** window available for peer to send to us - updated after parse */ + gpr_uint32 incoming_window; + /** how much window would we like to have for incoming_window */ + gpr_uint32 connection_window_target; + + /** have we seen a goaway */ + gpr_uint8 seen_goaway; + /** have we sent a goaway */ + gpr_uint8 sent_goaway; + + /** is this transport a client? */ + gpr_uint8 is_client; + /** are the local settings dirty and need to be sent? */ + gpr_uint8 dirtied_local_settings; + /** have local settings been sent? */ + gpr_uint8 sent_local_settings; + /** bitmask of setting indexes to send out */ + gpr_uint32 force_send_settings; + /** settings values */ + gpr_uint32 settings[GRPC_NUM_SETTING_SETS][GRPC_CHTTP2_NUM_SETTINGS]; + + /** what is the next stream id to be allocated by this peer? + copied to next_stream_id in parsing when parsing commences */ + gpr_uint32 next_stream_id; + + /** last received stream id */ + gpr_uint32 last_incoming_stream_id; + + /** pings awaiting responses */ + grpc_chttp2_outstanding_ping pings; + /** next payload for an outgoing ping */ + gpr_uint64 ping_counter; + + /** concurrent stream count: updated when not parsing, + so this is a strict over-estimation on the client */ + gpr_uint32 concurrent_stream_count; +} grpc_chttp2_transport_global; + +typedef struct { + /** data to write now */ + gpr_slice_buffer outbuf; + /** hpack encoding */ + grpc_chttp2_hpack_compressor hpack_compressor; + /** is this a client? */ + gpr_uint8 is_client; + /** callback for when writing is done */ + grpc_iomgr_closure done_cb; +} grpc_chttp2_transport_writing; + +struct grpc_chttp2_transport_parsing { + /** is this transport a client? (boolean) */ + gpr_uint8 is_client; + + /** were settings updated? */ + gpr_uint8 settings_updated; + /** was a settings ack received? */ + gpr_uint8 settings_ack_received; + /** was a goaway frame received? */ + gpr_uint8 goaway_received; + + /** initial window change */ + gpr_int64 initial_window_update; + + /** data to write later - after parsing */ + gpr_slice_buffer qbuf; + /* metadata object cache */ + grpc_mdstr *str_grpc_timeout; + /** parser for headers */ + grpc_chttp2_hpack_parser hpack_parser; + /** simple one shot parsers */ + union { + grpc_chttp2_window_update_parser window_update; + grpc_chttp2_settings_parser settings; + grpc_chttp2_ping_parser ping; + grpc_chttp2_rst_stream_parser rst_stream; + } simple; + /** parser for goaway frames */ + grpc_chttp2_goaway_parser goaway_parser; + + /** window available for peer to send to us */ + gpr_uint32 incoming_window; + gpr_uint32 incoming_window_delta; + + /** next stream id available at the time of beginning parsing */ + gpr_uint32 next_stream_id; + gpr_uint32 last_incoming_stream_id; + + /* deframing */ + grpc_chttp2_deframe_transport_state deframe_state; + gpr_uint8 incoming_frame_type; + gpr_uint8 incoming_frame_flags; + gpr_uint8 header_eof; + gpr_uint32 expect_continuation_stream_id; + gpr_uint32 incoming_frame_size; + gpr_uint32 incoming_stream_id; + + /* active parser */ + void *parser_data; + grpc_chttp2_stream_parsing *incoming_stream; + grpc_chttp2_parse_error (*parser)( + void *parser_user_data, grpc_chttp2_transport_parsing *transport_parsing, + grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last); + + /* received settings */ + gpr_uint32 settings[GRPC_CHTTP2_NUM_SETTINGS]; + + /* goaway data */ + grpc_status_code goaway_error; + gpr_uint32 goaway_last_stream_index; + gpr_slice goaway_text; + + gpr_uint64 outgoing_window_update; + + /** pings awaiting responses */ + grpc_chttp2_outstanding_ping pings; +}; + +struct grpc_chttp2_transport { + grpc_transport base; /* must be first */ + grpc_endpoint *ep; + grpc_mdctx *metadata_context; + gpr_refcount refs; + char *peer_string; + + /** when this drops to zero it's safe to shutdown the endpoint */ + gpr_refcount shutdown_ep_refs; + + gpr_mu mu; + + /** is the transport destroying itself? */ + gpr_uint8 destroying; + /** has the upper layer closed the transport? */ + gpr_uint8 closed; + + /** is a thread currently writing */ + gpr_uint8 writing_active; + /** is a thread currently parsing */ + gpr_uint8 parsing_active; + + /** is there a read request to the endpoint outstanding? */ + gpr_uint8 endpoint_reading; + + /** various lists of streams */ + grpc_chttp2_stream_list lists[STREAM_LIST_COUNT]; + + /** global state for reading/writing */ + grpc_chttp2_transport_global global; + /** state only accessible by the chain of execution that + set writing_active=1 */ + grpc_chttp2_transport_writing writing; + /** state only accessible by the chain of execution that + set parsing_active=1 */ + grpc_chttp2_transport_parsing parsing; + + /** maps stream id to grpc_chttp2_stream objects; + owned by the parsing thread when parsing */ + grpc_chttp2_stream_map parsing_stream_map; + + /** streams created by the client (possibly during parsing); + merged with parsing_stream_map during unlock when no + parsing is occurring */ + grpc_chttp2_stream_map new_stream_map; + + /** closure to execute writing */ + grpc_iomgr_closure writing_action; + /** closure to finish reading from the endpoint */ + grpc_iomgr_closure recv_data; + + /** incoming read bytes */ + gpr_slice_buffer read_buffer; + + /** address to place a newly accepted stream - set and unset by + grpc_chttp2_parsing_accept_stream; used by init_stream to + publish the accepted server stream */ + grpc_chttp2_stream **accepting_stream; + + struct { + /* accept stream callback */ + void (*accept_stream)(void *user_data, grpc_transport *transport, + const void *server_data); + void *accept_stream_user_data; + + /** connectivity tracking */ + grpc_connectivity_state_tracker state_tracker; + } channel_callback; +}; + +typedef struct { + /** HTTP2 stream id for this stream, or zero if one has not been assigned */ + gpr_uint32 id; + + grpc_iomgr_closure *send_done_closure; + grpc_iomgr_closure *recv_done_closure; + + /** window available for us to send to peer */ + gpr_int64 outgoing_window; + /** The number of bytes the upper layers have offered to receive. + As the upper layer offers more bytes, this value increases. + As bytes are read, this value decreases. */ + gpr_uint32 max_recv_bytes; + /** The number of bytes the upper layer has offered to read but we have + not yet announced to HTTP2 flow control. + As the upper layers offer to read more bytes, this value increases. + As we advertise incoming flow control window, this value decreases. */ + gpr_uint32 unannounced_incoming_window; + /** The number of bytes of HTTP2 flow control we have advertised. + As we advertise incoming flow control window, this value increases. + As bytes are read, this value decreases. + Updated after parse. */ + gpr_uint32 incoming_window; + /** stream ops the transport user would like to send */ + grpc_stream_op_buffer *outgoing_sopb; + /** when the application requests writes be closed, the write_closed is + 'queued'; when the close is flow controlled into the send path, we are + 'sending' it; when the write has been performed it is 'sent' */ + grpc_chttp2_write_state write_state; + /** is this stream closed (boolean) */ + gpr_uint8 read_closed; + /** has this stream been cancelled? (boolean) */ + gpr_uint8 cancelled; + grpc_status_code cancelled_status; + /** have we told the upper layer that this stream is cancelled? */ + gpr_uint8 published_cancelled; + /** is this stream in the stream map? (boolean) */ + gpr_uint8 in_stream_map; + /** bitmask of GRPC_CHTTP2_WRITING_xxx above */ + gpr_uint8 writing_now; + /** has anything been written to this stream? */ + gpr_uint8 written_anything; + + /** stream state already published to the upper layer */ + grpc_stream_state published_state; + /** address to publish next stream state to */ + grpc_stream_state *publish_state; + /** pointer to sop buffer to fill in with new stream ops */ + grpc_stream_op_buffer *publish_sopb; + grpc_stream_op_buffer incoming_sopb; + + /** incoming metadata */ + grpc_chttp2_incoming_metadata_buffer incoming_metadata; + grpc_chttp2_incoming_metadata_live_op_buffer outstanding_metadata; +} grpc_chttp2_stream_global; + +typedef struct { + /** HTTP2 stream id for this stream, or zero if one has not been assigned */ + gpr_uint32 id; + /** sops that have passed flow control to be written */ + grpc_stream_op_buffer sopb; + /** how strongly should we indicate closure with the next write */ + grpc_chttp2_send_closed send_closed; + /** how much window should we announce? */ + gpr_uint32 announce_window; +} grpc_chttp2_stream_writing; + +struct grpc_chttp2_stream_parsing { + /** HTTP2 stream id for this stream, or zero if one has not been assigned */ + gpr_uint32 id; + /** has this stream received a close */ + gpr_uint8 received_close; + /** saw a rst_stream */ + gpr_uint8 saw_rst_stream; + /** incoming_window has been reduced by this much during parsing */ + gpr_uint32 incoming_window_delta; + /** window available for peer to send to us */ + gpr_uint32 incoming_window; + /** parsing state for data frames */ + grpc_chttp2_data_parser data_parser; + /** reason give to rst_stream */ + gpr_uint32 rst_stream_reason; + /* amount of window given */ + gpr_uint64 outgoing_window_update; + + /** incoming metadata */ + grpc_chttp2_incoming_metadata_buffer incoming_metadata; +}; + +struct grpc_chttp2_stream { + grpc_chttp2_stream_global global; + grpc_chttp2_stream_writing writing; + grpc_chttp2_stream_parsing parsing; + + grpc_chttp2_stream_link links[STREAM_LIST_COUNT]; + gpr_uint8 included[STREAM_LIST_COUNT]; +}; + +/** Transport writing call flow: + chttp2_transport.c calls grpc_chttp2_unlocking_check_writes to see if writes + are required; + if they are, chttp2_transport.c calls grpc_chttp2_perform_writes to do the + writes. + Once writes have been completed (meaning another write could potentially be + started), + grpc_chttp2_terminate_writing is called. This will call + grpc_chttp2_cleanup_writing, at which + point the write phase is complete. */ + +/** Someone is unlocking the transport mutex: check to see if writes + are required, and schedule them if so */ +int grpc_chttp2_unlocking_check_writes(grpc_chttp2_transport_global *global, + grpc_chttp2_transport_writing *writing); +void grpc_chttp2_perform_writes( + grpc_chttp2_transport_writing *transport_writing, grpc_endpoint *endpoint); +void grpc_chttp2_terminate_writing(void *transport_writing, int success); +void grpc_chttp2_cleanup_writing(grpc_chttp2_transport_global *global, + grpc_chttp2_transport_writing *writing); + +void grpc_chttp2_prepare_to_read(grpc_chttp2_transport_global *global, + grpc_chttp2_transport_parsing *parsing); +/** Process one slice of incoming data; return 1 if the connection is still + viable after reading, or 0 if the connection should be torn down */ +int grpc_chttp2_perform_read(grpc_chttp2_transport_parsing *transport_parsing, + gpr_slice slice); +void grpc_chttp2_publish_reads(grpc_chttp2_transport_global *global, + grpc_chttp2_transport_parsing *parsing); + +/** Get a writable stream + returns non-zero if there was a stream available */ +void grpc_chttp2_list_add_writable_stream( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global *stream_global); +void grpc_chttp2_list_add_first_writable_stream( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global *stream_global); +int grpc_chttp2_list_pop_writable_stream( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_transport_writing *transport_writing, + grpc_chttp2_stream_global **stream_global, + grpc_chttp2_stream_writing **stream_writing); +void grpc_chttp2_list_remove_writable_stream( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global *stream_global); + +void grpc_chttp2_list_add_incoming_window_updated( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global *stream_global); +int grpc_chttp2_list_pop_incoming_window_updated( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_transport_parsing *transport_parsing, + grpc_chttp2_stream_global **stream_global, + grpc_chttp2_stream_parsing **stream_parsing); +void grpc_chttp2_list_remove_incoming_window_updated( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global *stream_global); + +void grpc_chttp2_list_add_writing_stream( + grpc_chttp2_transport_writing *transport_writing, + grpc_chttp2_stream_writing *stream_writing); +int grpc_chttp2_list_have_writing_streams( + grpc_chttp2_transport_writing *transport_writing); +int grpc_chttp2_list_pop_writing_stream( + grpc_chttp2_transport_writing *transport_writing, + grpc_chttp2_stream_writing **stream_writing); + +void grpc_chttp2_list_add_written_stream( + grpc_chttp2_transport_writing *transport_writing, + grpc_chttp2_stream_writing *stream_writing); +int grpc_chttp2_list_pop_written_stream( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_transport_writing *transport_writing, + grpc_chttp2_stream_global **stream_global, + grpc_chttp2_stream_writing **stream_writing); + +void grpc_chttp2_list_add_parsing_seen_stream( + grpc_chttp2_transport_parsing *transport_parsing, + grpc_chttp2_stream_parsing *stream_parsing); +int grpc_chttp2_list_pop_parsing_seen_stream( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_transport_parsing *transport_parsing, + grpc_chttp2_stream_global **stream_global, + grpc_chttp2_stream_parsing **stream_parsing); + +void grpc_chttp2_list_add_waiting_for_concurrency( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global *stream_global); +int grpc_chttp2_list_pop_waiting_for_concurrency( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global **stream_global); + +void grpc_chttp2_list_add_closed_waiting_for_parsing( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global *stream_global); +int grpc_chttp2_list_pop_closed_waiting_for_parsing( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global **stream_global); + +void grpc_chttp2_list_add_cancelled_waiting_for_writing( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global *stream_global); +int grpc_chttp2_list_pop_cancelled_waiting_for_writing( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global **stream_global); + +void grpc_chttp2_list_add_read_write_state_changed( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global *stream_global); +int grpc_chttp2_list_pop_read_write_state_changed( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global **stream_global); + +/** schedule a closure to run without the transport lock taken */ +void grpc_chttp2_schedule_closure( + grpc_chttp2_transport_global *transport_global, grpc_iomgr_closure *closure, + int success); + +grpc_chttp2_stream_parsing *grpc_chttp2_parsing_lookup_stream( + grpc_chttp2_transport_parsing *transport_parsing, gpr_uint32 id); +grpc_chttp2_stream_parsing *grpc_chttp2_parsing_accept_stream( + grpc_chttp2_transport_parsing *transport_parsing, gpr_uint32 id); + +void grpc_chttp2_add_incoming_goaway( + grpc_chttp2_transport_global *transport_global, gpr_uint32 goaway_error, + gpr_slice goaway_text); + +void grpc_chttp2_register_stream(grpc_chttp2_transport *t, + grpc_chttp2_stream *s); +/* returns 1 if this is the last stream, 0 otherwise */ +int grpc_chttp2_unregister_stream(grpc_chttp2_transport *t, + grpc_chttp2_stream *s) GRPC_MUST_USE_RESULT; +int grpc_chttp2_has_streams(grpc_chttp2_transport *t); +void grpc_chttp2_for_all_streams( + grpc_chttp2_transport_global *transport_global, void *user_data, + void (*cb)(grpc_chttp2_transport_global *transport_global, void *user_data, + grpc_chttp2_stream_global *stream_global)); + +void grpc_chttp2_parsing_become_skip_parser( + grpc_chttp2_transport_parsing *transport_parsing); + +#define GRPC_CHTTP2_CLIENT_CONNECT_STRING "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" +#define GRPC_CHTTP2_CLIENT_CONNECT_STRLEN \ + (sizeof(GRPC_CHTTP2_CLIENT_CONNECT_STRING) - 1) + +extern int grpc_http_trace; +extern int grpc_flowctl_trace; + +#define GRPC_CHTTP2_IF_TRACING(stmt) \ + if (!(grpc_http_trace)) \ + ; \ + else \ + stmt + +#define GRPC_CHTTP2_FLOWCTL_TRACE_STREAM(reason, transport, context, var, \ + delta) \ + if (!(grpc_flowctl_trace)) { \ + } else { \ + grpc_chttp2_flowctl_trace(__FILE__, __LINE__, reason, #context, #var, \ + transport->is_client, context->id, context->var, \ + delta); \ + } + +#define GRPC_CHTTP2_FLOWCTL_TRACE_TRANSPORT(reason, context, var, delta) \ + if (!(grpc_flowctl_trace)) { \ + } else { \ + grpc_chttp2_flowctl_trace(__FILE__, __LINE__, reason, #context, #var, \ + context->is_client, 0, context->var, delta); \ + } + +void grpc_chttp2_flowctl_trace(const char *file, int line, const char *reason, + const char *context, const char *var, + int is_client, gpr_uint32 stream_id, + gpr_int64 current_value, gpr_int64 delta); + +#endif diff --git a/src/core/transport/chttp2/parsing.c b/src/core/transport/chttp2/parsing.c new file mode 100644 index 00000000..dc5eb18e --- /dev/null +++ b/src/core/transport/chttp2/parsing.c @@ -0,0 +1,816 @@ +/* + * + * Copyright 2015, 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 "src/core/transport/chttp2/internal.h" + +#include + +#include "src/core/transport/chttp2/http2_errors.h" +#include "src/core/transport/chttp2/status_conversion.h" +#include "src/core/transport/chttp2/timeout_encoding.h" + +#include +#include + +static int init_frame_parser(grpc_chttp2_transport_parsing *transport_parsing); +static int init_header_frame_parser( + grpc_chttp2_transport_parsing *transport_parsing, int is_continuation); +static int init_data_frame_parser( + grpc_chttp2_transport_parsing *transport_parsing); +static int init_rst_stream_parser( + grpc_chttp2_transport_parsing *transport_parsing); +static int init_settings_frame_parser( + grpc_chttp2_transport_parsing *transport_parsing); +static int init_window_update_frame_parser( + grpc_chttp2_transport_parsing *transport_parsing); +static int init_ping_parser(grpc_chttp2_transport_parsing *transport_parsing); +static int init_goaway_parser(grpc_chttp2_transport_parsing *transport_parsing); +static int init_skip_frame_parser( + grpc_chttp2_transport_parsing *transport_parsing, int is_header); + +static int parse_frame_slice(grpc_chttp2_transport_parsing *transport_parsing, + gpr_slice slice, int is_last); + +void grpc_chttp2_prepare_to_read( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_transport_parsing *transport_parsing) { + grpc_chttp2_stream_global *stream_global; + grpc_chttp2_stream_parsing *stream_parsing; + + transport_parsing->next_stream_id = transport_global->next_stream_id; + + /* update the parsing view of incoming window */ + if (transport_parsing->incoming_window != transport_global->incoming_window) { + GRPC_CHTTP2_FLOWCTL_TRACE_TRANSPORT( + "parse", transport_parsing, incoming_window, + (gpr_int64)transport_global->incoming_window - + (gpr_int64)transport_parsing->incoming_window); + transport_parsing->incoming_window = transport_global->incoming_window; + } + while (grpc_chttp2_list_pop_incoming_window_updated( + transport_global, transport_parsing, &stream_global, &stream_parsing)) { + stream_parsing->id = stream_global->id; + if (stream_parsing->incoming_window != stream_global->incoming_window) { + GRPC_CHTTP2_FLOWCTL_TRACE_STREAM( + "parse", transport_parsing, stream_parsing, incoming_window, + (gpr_int64)stream_global->incoming_window - + (gpr_int64)stream_parsing->incoming_window); + stream_parsing->incoming_window = stream_global->incoming_window; + } + } +} + +void grpc_chttp2_publish_reads( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_transport_parsing *transport_parsing) { + grpc_chttp2_stream_global *stream_global; + grpc_chttp2_stream_parsing *stream_parsing; + + /* transport_parsing->last_incoming_stream_id is used as + last-grpc_chttp2_stream-id when + sending GOAWAY frame. + https://tools.ietf.org/html/draft-ietf-httpbis-http2-17#section-6.8 + says that last-grpc_chttp2_stream-id is peer-initiated grpc_chttp2_stream + ID. So, + since we don't have server pushed streams, client should send + GOAWAY last-grpc_chttp2_stream-id=0 in this case. */ + if (!transport_parsing->is_client) { + transport_global->last_incoming_stream_id = + transport_parsing->incoming_stream_id; + } + + /* copy parsing qbuf to global qbuf */ + gpr_slice_buffer_move_into(&transport_parsing->qbuf, &transport_global->qbuf); + + /* update global settings */ + if (transport_parsing->settings_updated) { + memcpy(transport_global->settings[GRPC_PEER_SETTINGS], + transport_parsing->settings, sizeof(transport_parsing->settings)); + transport_parsing->settings_updated = 0; + } + + /* update settings based on ack if received */ + if (transport_parsing->settings_ack_received) { + memcpy(transport_global->settings[GRPC_ACKED_SETTINGS], + transport_global->settings[GRPC_SENT_SETTINGS], + GRPC_CHTTP2_NUM_SETTINGS * sizeof(gpr_uint32)); + transport_parsing->settings_ack_received = 0; + } + + /* move goaway to the global state if we received one (it will be + published later */ + if (transport_parsing->goaway_received) { + grpc_chttp2_add_incoming_goaway(transport_global, + transport_parsing->goaway_error, + transport_parsing->goaway_text); + transport_parsing->goaway_text = gpr_empty_slice(); + transport_parsing->goaway_received = 0; + } + + /* propagate flow control tokens to global state */ + if (transport_parsing->outgoing_window_update) { + GRPC_CHTTP2_FLOWCTL_TRACE_TRANSPORT( + "parsed", transport_global, outgoing_window, + transport_parsing->outgoing_window_update); + GRPC_CHTTP2_FLOWCTL_TRACE_TRANSPORT( + "parsed", transport_parsing, outgoing_window_update, + -(gpr_int64)transport_parsing->outgoing_window_update); + transport_global->outgoing_window += + transport_parsing->outgoing_window_update; + transport_parsing->outgoing_window_update = 0; + } + + if (transport_parsing->incoming_window_delta) { + GRPC_CHTTP2_FLOWCTL_TRACE_TRANSPORT( + "parsed", transport_global, incoming_window, + -(gpr_int64)transport_parsing->incoming_window_delta); + GRPC_CHTTP2_FLOWCTL_TRACE_TRANSPORT( + "parsed", transport_parsing, incoming_window_delta, + -(gpr_int64)transport_parsing->incoming_window_delta); + transport_global->incoming_window -= + transport_parsing->incoming_window_delta; + transport_parsing->incoming_window_delta = 0; + } + + /* for each stream that saw an update, fixup global state */ + while (grpc_chttp2_list_pop_parsing_seen_stream( + transport_global, transport_parsing, &stream_global, &stream_parsing)) { + /* update incoming flow control window */ + if (stream_parsing->incoming_window_delta) { + GRPC_CHTTP2_FLOWCTL_TRACE_STREAM( + "parsed", transport_parsing, stream_global, incoming_window, + -(gpr_int64)stream_parsing->incoming_window_delta); + GRPC_CHTTP2_FLOWCTL_TRACE_STREAM( + "parsed", transport_parsing, stream_parsing, incoming_window_delta, + -(gpr_int64)stream_parsing->incoming_window_delta); + GRPC_CHTTP2_FLOWCTL_TRACE_STREAM( + "parsed", transport_parsing, stream_global, max_recv_bytes, + -(gpr_int64)stream_parsing->incoming_window_delta); + stream_global->incoming_window -= stream_parsing->incoming_window_delta; + GPR_ASSERT(stream_global->max_recv_bytes >= + stream_parsing->incoming_window_delta); + stream_global->max_recv_bytes -= stream_parsing->incoming_window_delta; + stream_parsing->incoming_window_delta = 0; + grpc_chttp2_list_add_writable_stream(transport_global, stream_global); + } + + /* update outgoing flow control window */ + if (stream_parsing->outgoing_window_update) { + int was_zero = stream_global->outgoing_window <= 0; + int is_zero; + GRPC_CHTTP2_FLOWCTL_TRACE_STREAM("parsed", transport_parsing, + stream_global, outgoing_window, + stream_parsing->outgoing_window_update); + GRPC_CHTTP2_FLOWCTL_TRACE_STREAM( + "parsed", transport_parsing, stream_parsing, outgoing_window_update, + -(gpr_int64)stream_parsing->outgoing_window_update); + stream_global->outgoing_window += stream_parsing->outgoing_window_update; + stream_parsing->outgoing_window_update = 0; + is_zero = stream_global->outgoing_window <= 0; + if (was_zero && !is_zero) { + grpc_chttp2_list_add_writable_stream(transport_global, stream_global); + } + } + + /* updating closed status */ + if (stream_parsing->received_close) { + stream_global->read_closed = 1; + grpc_chttp2_list_add_read_write_state_changed(transport_global, + stream_global); + } + if (stream_parsing->saw_rst_stream) { + stream_global->cancelled = 1; + stream_global->cancelled_status = grpc_chttp2_http2_error_to_grpc_status( + stream_parsing->rst_stream_reason); + if (stream_parsing->rst_stream_reason == GRPC_CHTTP2_NO_ERROR) { + stream_global->published_cancelled = 1; + } + grpc_chttp2_list_add_read_write_state_changed(transport_global, + stream_global); + } + + /* publish incoming stream ops */ + if (stream_parsing->data_parser.incoming_sopb.nops > 0) { + grpc_incoming_metadata_buffer_move_to_referencing_sopb( + &stream_parsing->incoming_metadata, &stream_global->incoming_metadata, + &stream_parsing->data_parser.incoming_sopb); + grpc_sopb_move_to(&stream_parsing->data_parser.incoming_sopb, + &stream_global->incoming_sopb); + grpc_chttp2_list_add_read_write_state_changed(transport_global, + stream_global); + } + } +} + +int grpc_chttp2_perform_read(grpc_chttp2_transport_parsing *transport_parsing, + gpr_slice slice) { + gpr_uint8 *beg = GPR_SLICE_START_PTR(slice); + gpr_uint8 *end = GPR_SLICE_END_PTR(slice); + gpr_uint8 *cur = beg; + + if (cur == end) return 1; + + switch (transport_parsing->deframe_state) { + case GRPC_DTS_CLIENT_PREFIX_0: + case GRPC_DTS_CLIENT_PREFIX_1: + case GRPC_DTS_CLIENT_PREFIX_2: + case GRPC_DTS_CLIENT_PREFIX_3: + case GRPC_DTS_CLIENT_PREFIX_4: + case GRPC_DTS_CLIENT_PREFIX_5: + case GRPC_DTS_CLIENT_PREFIX_6: + case GRPC_DTS_CLIENT_PREFIX_7: + case GRPC_DTS_CLIENT_PREFIX_8: + case GRPC_DTS_CLIENT_PREFIX_9: + case GRPC_DTS_CLIENT_PREFIX_10: + case GRPC_DTS_CLIENT_PREFIX_11: + case GRPC_DTS_CLIENT_PREFIX_12: + case GRPC_DTS_CLIENT_PREFIX_13: + case GRPC_DTS_CLIENT_PREFIX_14: + case GRPC_DTS_CLIENT_PREFIX_15: + case GRPC_DTS_CLIENT_PREFIX_16: + case GRPC_DTS_CLIENT_PREFIX_17: + case GRPC_DTS_CLIENT_PREFIX_18: + case GRPC_DTS_CLIENT_PREFIX_19: + case GRPC_DTS_CLIENT_PREFIX_20: + case GRPC_DTS_CLIENT_PREFIX_21: + case GRPC_DTS_CLIENT_PREFIX_22: + case GRPC_DTS_CLIENT_PREFIX_23: + while (cur != end && transport_parsing->deframe_state != GRPC_DTS_FH_0) { + if (*cur != GRPC_CHTTP2_CLIENT_CONNECT_STRING[transport_parsing + ->deframe_state]) { + gpr_log(GPR_INFO, + "Connect string mismatch: expected '%c' (%d) got '%c' (%d) " + "at byte %d", + GRPC_CHTTP2_CLIENT_CONNECT_STRING[transport_parsing + ->deframe_state], + (int)(gpr_uint8)GRPC_CHTTP2_CLIENT_CONNECT_STRING + [transport_parsing->deframe_state], + *cur, (int)*cur, transport_parsing->deframe_state); + return 0; + } + ++cur; + ++transport_parsing->deframe_state; + } + if (cur == end) { + return 1; + } + /* fallthrough */ + dts_fh_0: + case GRPC_DTS_FH_0: + GPR_ASSERT(cur < end); + transport_parsing->incoming_frame_size = ((gpr_uint32)*cur) << 16; + if (++cur == end) { + transport_parsing->deframe_state = GRPC_DTS_FH_1; + return 1; + } + /* fallthrough */ + case GRPC_DTS_FH_1: + GPR_ASSERT(cur < end); + transport_parsing->incoming_frame_size |= ((gpr_uint32)*cur) << 8; + if (++cur == end) { + transport_parsing->deframe_state = GRPC_DTS_FH_2; + return 1; + } + /* fallthrough */ + case GRPC_DTS_FH_2: + GPR_ASSERT(cur < end); + transport_parsing->incoming_frame_size |= *cur; + if (++cur == end) { + transport_parsing->deframe_state = GRPC_DTS_FH_3; + return 1; + } + /* fallthrough */ + case GRPC_DTS_FH_3: + GPR_ASSERT(cur < end); + transport_parsing->incoming_frame_type = *cur; + if (++cur == end) { + transport_parsing->deframe_state = GRPC_DTS_FH_4; + return 1; + } + /* fallthrough */ + case GRPC_DTS_FH_4: + GPR_ASSERT(cur < end); + transport_parsing->incoming_frame_flags = *cur; + if (++cur == end) { + transport_parsing->deframe_state = GRPC_DTS_FH_5; + return 1; + } + /* fallthrough */ + case GRPC_DTS_FH_5: + GPR_ASSERT(cur < end); + transport_parsing->incoming_stream_id = (((gpr_uint32)*cur) & 0x7f) << 24; + if (++cur == end) { + transport_parsing->deframe_state = GRPC_DTS_FH_6; + return 1; + } + /* fallthrough */ + case GRPC_DTS_FH_6: + GPR_ASSERT(cur < end); + transport_parsing->incoming_stream_id |= ((gpr_uint32)*cur) << 16; + if (++cur == end) { + transport_parsing->deframe_state = GRPC_DTS_FH_7; + return 1; + } + /* fallthrough */ + case GRPC_DTS_FH_7: + GPR_ASSERT(cur < end); + transport_parsing->incoming_stream_id |= ((gpr_uint32)*cur) << 8; + if (++cur == end) { + transport_parsing->deframe_state = GRPC_DTS_FH_8; + return 1; + } + /* fallthrough */ + case GRPC_DTS_FH_8: + GPR_ASSERT(cur < end); + transport_parsing->incoming_stream_id |= ((gpr_uint32)*cur); + transport_parsing->deframe_state = GRPC_DTS_FRAME; + if (!init_frame_parser(transport_parsing)) { + return 0; + } + if (transport_parsing->incoming_stream_id) { + transport_parsing->last_incoming_stream_id = + transport_parsing->incoming_stream_id; + } + if (transport_parsing->incoming_frame_size == 0) { + if (!parse_frame_slice(transport_parsing, gpr_empty_slice(), 1)) { + return 0; + } + transport_parsing->incoming_stream = NULL; + if (++cur == end) { + transport_parsing->deframe_state = GRPC_DTS_FH_0; + return 1; + } + goto dts_fh_0; /* loop */ + } + if (++cur == end) { + return 1; + } + /* fallthrough */ + case GRPC_DTS_FRAME: + GPR_ASSERT(cur < end); + if ((gpr_uint32)(end - cur) == transport_parsing->incoming_frame_size) { + if (!parse_frame_slice( + transport_parsing, + gpr_slice_sub_no_ref(slice, cur - beg, end - beg), 1)) { + return 0; + } + transport_parsing->deframe_state = GRPC_DTS_FH_0; + transport_parsing->incoming_stream = NULL; + return 1; + } else if ((gpr_uint32)(end - cur) > + transport_parsing->incoming_frame_size) { + if (!parse_frame_slice( + transport_parsing, + gpr_slice_sub_no_ref( + slice, cur - beg, + cur + transport_parsing->incoming_frame_size - beg), + 1)) { + return 0; + } + cur += transport_parsing->incoming_frame_size; + transport_parsing->incoming_stream = NULL; + goto dts_fh_0; /* loop */ + } else { + if (!parse_frame_slice( + transport_parsing, + gpr_slice_sub_no_ref(slice, cur - beg, end - beg), 0)) { + return 0; + } + transport_parsing->incoming_frame_size -= (end - cur); + return 1; + } + gpr_log(GPR_ERROR, "should never reach here"); + abort(); + } + + gpr_log(GPR_ERROR, "should never reach here"); + abort(); + + return 0; +} + +static int init_frame_parser(grpc_chttp2_transport_parsing *transport_parsing) { + if (transport_parsing->expect_continuation_stream_id != 0) { + if (transport_parsing->incoming_frame_type != + GRPC_CHTTP2_FRAME_CONTINUATION) { + gpr_log(GPR_ERROR, "Expected CONTINUATION frame, got frame type %02x", + transport_parsing->incoming_frame_type); + return 0; + } + if (transport_parsing->expect_continuation_stream_id != + transport_parsing->incoming_stream_id) { + gpr_log(GPR_ERROR, + "Expected CONTINUATION frame for grpc_chttp2_stream %08x, got " + "grpc_chttp2_stream %08x", + transport_parsing->expect_continuation_stream_id, + transport_parsing->incoming_stream_id); + return 0; + } + return init_header_frame_parser(transport_parsing, 1); + } + switch (transport_parsing->incoming_frame_type) { + case GRPC_CHTTP2_FRAME_DATA: + return init_data_frame_parser(transport_parsing); + case GRPC_CHTTP2_FRAME_HEADER: + return init_header_frame_parser(transport_parsing, 0); + case GRPC_CHTTP2_FRAME_CONTINUATION: + gpr_log(GPR_ERROR, "Unexpected CONTINUATION frame"); + return 0; + case GRPC_CHTTP2_FRAME_RST_STREAM: + return init_rst_stream_parser(transport_parsing); + case GRPC_CHTTP2_FRAME_SETTINGS: + return init_settings_frame_parser(transport_parsing); + case GRPC_CHTTP2_FRAME_WINDOW_UPDATE: + return init_window_update_frame_parser(transport_parsing); + case GRPC_CHTTP2_FRAME_PING: + return init_ping_parser(transport_parsing); + case GRPC_CHTTP2_FRAME_GOAWAY: + return init_goaway_parser(transport_parsing); + default: + gpr_log(GPR_ERROR, "Unknown frame type %02x", + transport_parsing->incoming_frame_type); + return init_skip_frame_parser(transport_parsing, 0); + } +} + +static grpc_chttp2_parse_error skip_parser( + void *parser, grpc_chttp2_transport_parsing *transport_parsing, + grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last) { + return GRPC_CHTTP2_PARSE_OK; +} + +static void skip_header(void *tp, grpc_mdelem *md) { GRPC_MDELEM_UNREF(md); } + +static int init_skip_frame_parser( + grpc_chttp2_transport_parsing *transport_parsing, int is_header) { + if (is_header) { + int is_eoh = transport_parsing->expect_continuation_stream_id != 0; + transport_parsing->parser = grpc_chttp2_header_parser_parse; + transport_parsing->parser_data = &transport_parsing->hpack_parser; + transport_parsing->hpack_parser.on_header = skip_header; + transport_parsing->hpack_parser.on_header_user_data = NULL; + transport_parsing->hpack_parser.is_boundary = is_eoh; + transport_parsing->hpack_parser.is_eof = + is_eoh ? transport_parsing->header_eof : 0; + } else { + transport_parsing->parser = skip_parser; + } + return 1; +} + +void grpc_chttp2_parsing_become_skip_parser( + grpc_chttp2_transport_parsing *transport_parsing) { + init_skip_frame_parser( + transport_parsing, + transport_parsing->parser == grpc_chttp2_header_parser_parse); +} + +static grpc_chttp2_parse_error update_incoming_window( + grpc_chttp2_transport_parsing *transport_parsing, + grpc_chttp2_stream_parsing *stream_parsing) { + if (transport_parsing->incoming_frame_size > + transport_parsing->incoming_window) { + gpr_log(GPR_ERROR, "frame of size %d overflows incoming window of %d", + transport_parsing->incoming_frame_size, + transport_parsing->incoming_window); + return GRPC_CHTTP2_CONNECTION_ERROR; + } + + if (transport_parsing->incoming_frame_size > + stream_parsing->incoming_window) { + gpr_log(GPR_ERROR, "frame of size %d overflows incoming window of %d", + transport_parsing->incoming_frame_size, + stream_parsing->incoming_window); + return GRPC_CHTTP2_CONNECTION_ERROR; + } + + GRPC_CHTTP2_FLOWCTL_TRACE_TRANSPORT( + "data", transport_parsing, incoming_window, + -(gpr_int64)transport_parsing->incoming_frame_size); + GRPC_CHTTP2_FLOWCTL_TRACE_TRANSPORT("data", transport_parsing, + incoming_window_delta, + transport_parsing->incoming_frame_size); + GRPC_CHTTP2_FLOWCTL_TRACE_STREAM( + "data", transport_parsing, stream_parsing, incoming_window, + -(gpr_int64)transport_parsing->incoming_frame_size); + GRPC_CHTTP2_FLOWCTL_TRACE_STREAM("data", transport_parsing, stream_parsing, + incoming_window_delta, + transport_parsing->incoming_frame_size); + + transport_parsing->incoming_window -= transport_parsing->incoming_frame_size; + transport_parsing->incoming_window_delta += + transport_parsing->incoming_frame_size; + stream_parsing->incoming_window -= transport_parsing->incoming_frame_size; + stream_parsing->incoming_window_delta += + transport_parsing->incoming_frame_size; + grpc_chttp2_list_add_parsing_seen_stream(transport_parsing, stream_parsing); + + return GRPC_CHTTP2_PARSE_OK; +} + +static int init_data_frame_parser( + grpc_chttp2_transport_parsing *transport_parsing) { + grpc_chttp2_stream_parsing *stream_parsing = + grpc_chttp2_parsing_lookup_stream(transport_parsing, + transport_parsing->incoming_stream_id); + grpc_chttp2_parse_error err = GRPC_CHTTP2_PARSE_OK; + if (!stream_parsing || stream_parsing->received_close) + return init_skip_frame_parser(transport_parsing, 0); + if (err == GRPC_CHTTP2_PARSE_OK) { + err = update_incoming_window(transport_parsing, stream_parsing); + } + if (err == GRPC_CHTTP2_PARSE_OK) { + err = grpc_chttp2_data_parser_begin_frame( + &stream_parsing->data_parser, transport_parsing->incoming_frame_flags); + } + switch (err) { + case GRPC_CHTTP2_PARSE_OK: + transport_parsing->incoming_stream = stream_parsing; + transport_parsing->parser = grpc_chttp2_data_parser_parse; + transport_parsing->parser_data = &stream_parsing->data_parser; + return 1; + case GRPC_CHTTP2_STREAM_ERROR: + stream_parsing->received_close = 1; + stream_parsing->saw_rst_stream = 1; + stream_parsing->rst_stream_reason = GRPC_CHTTP2_PROTOCOL_ERROR; + gpr_slice_buffer_add( + &transport_parsing->qbuf, + grpc_chttp2_rst_stream_create(transport_parsing->incoming_stream_id, + GRPC_CHTTP2_PROTOCOL_ERROR)); + return init_skip_frame_parser(transport_parsing, 0); + case GRPC_CHTTP2_CONNECTION_ERROR: + return 0; + } + gpr_log(GPR_ERROR, "should never reach here"); + abort(); + return 0; +} + +static void free_timeout(void *p) { gpr_free(p); } + +static void on_header(void *tp, grpc_mdelem *md) { + grpc_chttp2_transport_parsing *transport_parsing = tp; + grpc_chttp2_stream_parsing *stream_parsing = + transport_parsing->incoming_stream; + + GPR_ASSERT(stream_parsing); + + GRPC_CHTTP2_IF_TRACING(gpr_log( + GPR_INFO, "HTTP:%d:HDR:%s: %s: %s", stream_parsing->id, + transport_parsing->is_client ? "CLI" : "SVR", + grpc_mdstr_as_c_string(md->key), grpc_mdstr_as_c_string(md->value))); + + if (md->key == transport_parsing->str_grpc_timeout) { + gpr_timespec *cached_timeout = grpc_mdelem_get_user_data(md, free_timeout); + if (!cached_timeout) { + /* not already parsed: parse it now, and store the result away */ + cached_timeout = gpr_malloc(sizeof(gpr_timespec)); + if (!grpc_chttp2_decode_timeout(grpc_mdstr_as_c_string(md->value), + cached_timeout)) { + gpr_log(GPR_ERROR, "Ignoring bad timeout value '%s'", + grpc_mdstr_as_c_string(md->value)); + *cached_timeout = gpr_inf_future(GPR_CLOCK_REALTIME); + } + grpc_mdelem_set_user_data(md, free_timeout, cached_timeout); + } + grpc_chttp2_incoming_metadata_buffer_set_deadline( + &stream_parsing->incoming_metadata, + gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), *cached_timeout)); + GRPC_MDELEM_UNREF(md); + } else { + grpc_chttp2_incoming_metadata_buffer_add(&stream_parsing->incoming_metadata, + md); + } + + grpc_chttp2_list_add_parsing_seen_stream(transport_parsing, stream_parsing); +} + +static int init_header_frame_parser( + grpc_chttp2_transport_parsing *transport_parsing, int is_continuation) { + int is_eoh = (transport_parsing->incoming_frame_flags & + GRPC_CHTTP2_DATA_FLAG_END_HEADERS) != 0; + int via_accept = 0; + grpc_chttp2_stream_parsing *stream_parsing; + + if (is_eoh) { + transport_parsing->expect_continuation_stream_id = 0; + } else { + transport_parsing->expect_continuation_stream_id = + transport_parsing->incoming_stream_id; + } + + if (!is_continuation) { + transport_parsing->header_eof = (transport_parsing->incoming_frame_flags & + GRPC_CHTTP2_DATA_FLAG_END_STREAM) != 0; + } + + /* could be a new grpc_chttp2_stream or an existing grpc_chttp2_stream */ + stream_parsing = grpc_chttp2_parsing_lookup_stream( + transport_parsing, transport_parsing->incoming_stream_id); + if (stream_parsing == NULL) { + if (is_continuation) { + gpr_log(GPR_ERROR, + "grpc_chttp2_stream disbanded before CONTINUATION received"); + return init_skip_frame_parser(transport_parsing, 1); + } + if (transport_parsing->is_client) { + if ((transport_parsing->incoming_stream_id & 1) && + transport_parsing->incoming_stream_id < + transport_parsing->next_stream_id) { + /* this is an old (probably cancelled) grpc_chttp2_stream */ + } else { + gpr_log(GPR_ERROR, + "ignoring new grpc_chttp2_stream creation on client"); + } + return init_skip_frame_parser(transport_parsing, 1); + } else if (transport_parsing->last_incoming_stream_id > + transport_parsing->incoming_stream_id) { + gpr_log(GPR_ERROR, + "ignoring out of order new grpc_chttp2_stream request on server; " + "last grpc_chttp2_stream " + "id=%d, new grpc_chttp2_stream id=%d", + transport_parsing->last_incoming_stream_id, + transport_parsing->incoming_stream_id); + return init_skip_frame_parser(transport_parsing, 1); + } else if ((transport_parsing->incoming_stream_id & 1) == 0) { + gpr_log(GPR_ERROR, + "ignoring grpc_chttp2_stream with non-client generated index %d", + transport_parsing->incoming_stream_id); + return init_skip_frame_parser(transport_parsing, 1); + } + stream_parsing = transport_parsing->incoming_stream = + grpc_chttp2_parsing_accept_stream( + transport_parsing, transport_parsing->incoming_stream_id); + if (stream_parsing == NULL) { + gpr_log(GPR_ERROR, "grpc_chttp2_stream not accepted"); + return init_skip_frame_parser(transport_parsing, 1); + } + via_accept = 1; + } else { + transport_parsing->incoming_stream = stream_parsing; + } + GPR_ASSERT(stream_parsing != NULL && (via_accept == 0 || via_accept == 1)); + if (stream_parsing->received_close) { + gpr_log(GPR_ERROR, "skipping already closed grpc_chttp2_stream header"); + transport_parsing->incoming_stream = NULL; + return init_skip_frame_parser(transport_parsing, 1); + } + transport_parsing->parser = grpc_chttp2_header_parser_parse; + transport_parsing->parser_data = &transport_parsing->hpack_parser; + transport_parsing->hpack_parser.on_header = on_header; + transport_parsing->hpack_parser.on_header_user_data = transport_parsing; + transport_parsing->hpack_parser.is_boundary = is_eoh; + transport_parsing->hpack_parser.is_eof = + is_eoh ? transport_parsing->header_eof : 0; + if (!is_continuation && (transport_parsing->incoming_frame_flags & + GRPC_CHTTP2_FLAG_HAS_PRIORITY)) { + grpc_chttp2_hpack_parser_set_has_priority(&transport_parsing->hpack_parser); + } + return 1; +} + +static int init_window_update_frame_parser( + grpc_chttp2_transport_parsing *transport_parsing) { + int ok = GRPC_CHTTP2_PARSE_OK == grpc_chttp2_window_update_parser_begin_frame( + &transport_parsing->simple.window_update, + transport_parsing->incoming_frame_size, + transport_parsing->incoming_frame_flags); + if (transport_parsing->incoming_stream_id) { + transport_parsing->incoming_stream = grpc_chttp2_parsing_lookup_stream( + transport_parsing, transport_parsing->incoming_stream_id); + } + transport_parsing->parser = grpc_chttp2_window_update_parser_parse; + transport_parsing->parser_data = &transport_parsing->simple.window_update; + return ok; +} + +static int init_ping_parser(grpc_chttp2_transport_parsing *transport_parsing) { + int ok = GRPC_CHTTP2_PARSE_OK == grpc_chttp2_ping_parser_begin_frame( + &transport_parsing->simple.ping, + transport_parsing->incoming_frame_size, + transport_parsing->incoming_frame_flags); + transport_parsing->parser = grpc_chttp2_ping_parser_parse; + transport_parsing->parser_data = &transport_parsing->simple.ping; + return ok; +} + +static int init_rst_stream_parser( + grpc_chttp2_transport_parsing *transport_parsing) { + int ok = GRPC_CHTTP2_PARSE_OK == grpc_chttp2_rst_stream_parser_begin_frame( + &transport_parsing->simple.rst_stream, + transport_parsing->incoming_frame_size, + transport_parsing->incoming_frame_flags); + transport_parsing->incoming_stream = grpc_chttp2_parsing_lookup_stream( + transport_parsing, transport_parsing->incoming_stream_id); + if (!transport_parsing->incoming_stream) { + return init_skip_frame_parser(transport_parsing, 0); + } + transport_parsing->parser = grpc_chttp2_rst_stream_parser_parse; + transport_parsing->parser_data = &transport_parsing->simple.rst_stream; + return ok; +} + +static int init_goaway_parser( + grpc_chttp2_transport_parsing *transport_parsing) { + int ok = GRPC_CHTTP2_PARSE_OK == grpc_chttp2_goaway_parser_begin_frame( + &transport_parsing->goaway_parser, + transport_parsing->incoming_frame_size, + transport_parsing->incoming_frame_flags); + transport_parsing->parser = grpc_chttp2_goaway_parser_parse; + transport_parsing->parser_data = &transport_parsing->goaway_parser; + return ok; +} + +static int init_settings_frame_parser( + grpc_chttp2_transport_parsing *transport_parsing) { + int ok; + + if (transport_parsing->incoming_stream_id != 0) { + gpr_log(GPR_ERROR, "settings frame received for grpc_chttp2_stream %d", + transport_parsing->incoming_stream_id); + return 0; + } + + ok = GRPC_CHTTP2_PARSE_OK == grpc_chttp2_settings_parser_begin_frame( + &transport_parsing->simple.settings, + transport_parsing->incoming_frame_size, + transport_parsing->incoming_frame_flags, + transport_parsing->settings); + if (!ok) { + return 0; + } + if (transport_parsing->incoming_frame_flags & GRPC_CHTTP2_FLAG_ACK) { + transport_parsing->settings_ack_received = 1; + } + transport_parsing->parser = grpc_chttp2_settings_parser_parse; + transport_parsing->parser_data = &transport_parsing->simple.settings; + return ok; +} + +/* +static int is_window_update_legal(gpr_int64 window_update, gpr_int64 window) { + return window + window_update < MAX_WINDOW; +} +*/ + +static int parse_frame_slice(grpc_chttp2_transport_parsing *transport_parsing, + gpr_slice slice, int is_last) { + grpc_chttp2_stream_parsing *stream_parsing = + transport_parsing->incoming_stream; + switch (transport_parsing->parser(transport_parsing->parser_data, + transport_parsing, stream_parsing, slice, + is_last)) { + case GRPC_CHTTP2_PARSE_OK: + if (stream_parsing) { + grpc_chttp2_list_add_parsing_seen_stream(transport_parsing, + stream_parsing); + } + return 1; + case GRPC_CHTTP2_STREAM_ERROR: + grpc_chttp2_parsing_become_skip_parser(transport_parsing); + if (stream_parsing) { + stream_parsing->saw_rst_stream = 1; + stream_parsing->rst_stream_reason = GRPC_CHTTP2_PROTOCOL_ERROR; + gpr_slice_buffer_add( + &transport_parsing->qbuf, + grpc_chttp2_rst_stream_create(transport_parsing->incoming_stream_id, + GRPC_CHTTP2_PROTOCOL_ERROR)); + } + return 1; + case GRPC_CHTTP2_CONNECTION_ERROR: + return 0; + } + gpr_log(GPR_ERROR, "should never reach here"); + abort(); + return 0; +} diff --git a/src/core/transport/chttp2/status_conversion.c b/src/core/transport/chttp2/status_conversion.c new file mode 100644 index 00000000..bf214b01 --- /dev/null +++ b/src/core/transport/chttp2/status_conversion.c @@ -0,0 +1,109 @@ +/* + * + * Copyright 2015, 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 "src/core/transport/chttp2/status_conversion.h" + +int grpc_chttp2_grpc_status_to_http2_error(grpc_status_code status) { + switch (status) { + case GRPC_STATUS_OK: + return GRPC_CHTTP2_NO_ERROR; + case GRPC_STATUS_CANCELLED: + return GRPC_CHTTP2_CANCEL; + case GRPC_STATUS_RESOURCE_EXHAUSTED: + return GRPC_CHTTP2_ENHANCE_YOUR_CALM; + case GRPC_STATUS_PERMISSION_DENIED: + return GRPC_CHTTP2_INADEQUATE_SECURITY; + case GRPC_STATUS_UNAVAILABLE: + return GRPC_CHTTP2_REFUSED_STREAM; + default: + return GRPC_CHTTP2_INTERNAL_ERROR; + } +} + +grpc_status_code grpc_chttp2_http2_error_to_grpc_status( + grpc_chttp2_error_code error) { + switch (error) { + case GRPC_CHTTP2_NO_ERROR: + /* should never be received */ + return GRPC_STATUS_INTERNAL; + case GRPC_CHTTP2_CANCEL: + return GRPC_STATUS_CANCELLED; + case GRPC_CHTTP2_ENHANCE_YOUR_CALM: + return GRPC_STATUS_RESOURCE_EXHAUSTED; + case GRPC_CHTTP2_INADEQUATE_SECURITY: + return GRPC_STATUS_PERMISSION_DENIED; + case GRPC_CHTTP2_REFUSED_STREAM: + return GRPC_STATUS_UNAVAILABLE; + default: + return GRPC_STATUS_INTERNAL; + } +} + +grpc_status_code grpc_chttp2_http2_status_to_grpc_status(int status) { + switch (status) { + /* these HTTP2 status codes are called out explicitly in status.proto */ + case 200: + return GRPC_STATUS_OK; + case 400: + return GRPC_STATUS_INVALID_ARGUMENT; + case 401: + return GRPC_STATUS_UNAUTHENTICATED; + case 403: + return GRPC_STATUS_PERMISSION_DENIED; + case 404: + return GRPC_STATUS_NOT_FOUND; + case 409: + return GRPC_STATUS_ABORTED; + case 412: + return GRPC_STATUS_FAILED_PRECONDITION; + case 429: + return GRPC_STATUS_RESOURCE_EXHAUSTED; + case 499: + return GRPC_STATUS_CANCELLED; + case 500: + return GRPC_STATUS_UNKNOWN; + case 501: + return GRPC_STATUS_UNIMPLEMENTED; + case 503: + return GRPC_STATUS_UNAVAILABLE; + case 504: + return GRPC_STATUS_DEADLINE_EXCEEDED; + /* everything else is unknown */ + default: + return GRPC_STATUS_UNKNOWN; + } +} + +int grpc_chttp2_grpc_status_to_http2_status(grpc_status_code status) { + return 200; +} diff --git a/src/core/transport/chttp2/status_conversion.h b/src/core/transport/chttp2/status_conversion.h new file mode 100644 index 00000000..0ec5b560 --- /dev/null +++ b/src/core/transport/chttp2/status_conversion.h @@ -0,0 +1,50 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_TRANSPORT_CHTTP2_STATUS_CONVERSION_H +#define GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_STATUS_CONVERSION_H + +#include +#include "src/core/transport/chttp2/http2_errors.h" + +/* Conversion of grpc status codes to http2 error codes (for RST_STREAM) */ +grpc_chttp2_error_code grpc_chttp2_grpc_status_to_http2_error( + grpc_status_code status); +grpc_status_code grpc_chttp2_http2_error_to_grpc_status( + grpc_chttp2_error_code error); + +/* Conversion of HTTP status codes (:status) to grpc status codes */ +grpc_status_code grpc_chttp2_http2_status_to_grpc_status(int status); +int grpc_chttp2_grpc_status_to_http2_status(grpc_status_code status); + +#endif /* GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_STATUS_CONVERSION_H */ diff --git a/src/core/transport/chttp2/stream_encoder.c b/src/core/transport/chttp2/stream_encoder.c new file mode 100644 index 00000000..1ea697f7 --- /dev/null +++ b/src/core/transport/chttp2/stream_encoder.c @@ -0,0 +1,650 @@ +/* + * + * Copyright 2015, 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 "src/core/transport/chttp2/stream_encoder.h" + +#include +#include + +#include +#include +#include "src/core/transport/chttp2/bin_encoder.h" +#include "src/core/transport/chttp2/hpack_table.h" +#include "src/core/transport/chttp2/timeout_encoding.h" +#include "src/core/transport/chttp2/varint.h" + +#define HASH_FRAGMENT_1(x) ((x)&255) +#define HASH_FRAGMENT_2(x) ((x >> 8) & 255) +#define HASH_FRAGMENT_3(x) ((x >> 16) & 255) +#define HASH_FRAGMENT_4(x) ((x >> 24) & 255) + +/* if the probability of this item being seen again is < 1/x then don't add + it to the table */ +#define ONE_ON_ADD_PROBABILITY 128 +/* don't consider adding anything bigger than this to the hpack table */ +#define MAX_DECODER_SPACE_USAGE 512 + +/* what kind of frame our we encoding? */ +typedef enum { HEADER, DATA, NONE } frame_type; + +typedef struct { + frame_type cur_frame_type; + /* number of bytes in 'output' when we started the frame - used to calculate + frame length */ + size_t output_length_at_start_of_frame; + /* index (in output) of the header for the current frame */ + size_t header_idx; + /* was the last frame emitted a header? (if yes, we'll need a CONTINUATION */ + gpr_uint8 last_was_header; + /* have we seen a regular (non-colon-prefixed) header yet? */ + gpr_uint8 seen_regular_header; + /* output stream id */ + gpr_uint32 stream_id; + gpr_slice_buffer *output; +} framer_state; + +/* fills p (which is expected to be 9 bytes long) with a data frame header */ +static void fill_header(gpr_uint8 *p, gpr_uint8 type, gpr_uint32 id, + gpr_uint32 len, gpr_uint8 flags) { + *p++ = len >> 16; + *p++ = len >> 8; + *p++ = len; + *p++ = type; + *p++ = flags; + *p++ = id >> 24; + *p++ = id >> 16; + *p++ = id >> 8; + *p++ = id; +} + +/* finish a frame - fill in the previously reserved header */ +static void finish_frame(framer_state *st, int is_header_boundary, + int is_last_in_stream) { + gpr_uint8 type = 0xff; + switch (st->cur_frame_type) { + case HEADER: + type = st->last_was_header ? GRPC_CHTTP2_FRAME_CONTINUATION + : GRPC_CHTTP2_FRAME_HEADER; + st->last_was_header = 1; + break; + case DATA: + type = GRPC_CHTTP2_FRAME_DATA; + st->last_was_header = 0; + is_header_boundary = 0; + break; + case NONE: + return; + } + fill_header(GPR_SLICE_START_PTR(st->output->slices[st->header_idx]), type, + st->stream_id, + st->output->length - st->output_length_at_start_of_frame, + (is_last_in_stream ? GRPC_CHTTP2_DATA_FLAG_END_STREAM : 0) | + (is_header_boundary ? GRPC_CHTTP2_DATA_FLAG_END_HEADERS : 0)); + st->cur_frame_type = NONE; +} + +/* begin a new frame: reserve off header space, remember how many bytes we'd + output before beginning */ +static void begin_frame(framer_state *st, frame_type type) { + GPR_ASSERT(type != NONE); + GPR_ASSERT(st->cur_frame_type == NONE); + st->cur_frame_type = type; + st->header_idx = + gpr_slice_buffer_add_indexed(st->output, gpr_slice_malloc(9)); + st->output_length_at_start_of_frame = st->output->length; +} + +static void begin_new_frame(framer_state *st, frame_type type) { + finish_frame(st, 1, 0); + st->last_was_header = 0; + begin_frame(st, type); +} + +/* make sure that the current frame is of the type desired, and has sufficient + space to add at least about_to_add bytes -- finishes the current frame if + needed */ +static void ensure_frame_type(framer_state *st, frame_type type, + int need_bytes) { + if (st->cur_frame_type == type && + st->output->length - st->output_length_at_start_of_frame + need_bytes <= + GRPC_CHTTP2_MAX_PAYLOAD_LENGTH) { + return; + } + finish_frame(st, type != HEADER, 0); + begin_frame(st, type); +} + +/* increment a filter count, halve all counts if one element reaches max */ +static void inc_filter(gpr_uint8 idx, gpr_uint32 *sum, gpr_uint8 *elems) { + elems[idx]++; + if (elems[idx] < 255) { + (*sum)++; + } else { + int i; + *sum = 0; + for (i = 0; i < GRPC_CHTTP2_HPACKC_NUM_FILTERS; i++) { + elems[i] /= 2; + (*sum) += elems[i]; + } + } +} + +static void add_header_data(framer_state *st, gpr_slice slice) { + size_t len = GPR_SLICE_LENGTH(slice); + size_t remaining; + if (len == 0) return; + ensure_frame_type(st, HEADER, 1); + remaining = GRPC_CHTTP2_MAX_PAYLOAD_LENGTH + + st->output_length_at_start_of_frame - st->output->length; + if (len <= remaining) { + gpr_slice_buffer_add(st->output, slice); + } else { + gpr_slice_buffer_add(st->output, gpr_slice_split_head(&slice, remaining)); + add_header_data(st, slice); + } +} + +static gpr_uint8 *add_tiny_header_data(framer_state *st, int len) { + ensure_frame_type(st, HEADER, len); + return gpr_slice_buffer_tiny_add(st->output, len); +} + +/* add an element to the decoder table: returns metadata element to unref */ +static grpc_mdelem *add_elem(grpc_chttp2_hpack_compressor *c, + grpc_mdelem *elem) { + gpr_uint32 key_hash = elem->key->hash; + gpr_uint32 elem_hash = GRPC_MDSTR_KV_HASH(key_hash, elem->value->hash); + gpr_uint32 new_index = c->tail_remote_index + c->table_elems + 1; + gpr_uint32 elem_size = 32 + GPR_SLICE_LENGTH(elem->key->slice) + + GPR_SLICE_LENGTH(elem->value->slice); + grpc_mdelem *elem_to_unref; + + /* Reserve space for this element in the remote table: if this overflows + the current table, drop elements until it fits, matching the decompressor + algorithm */ + /* TODO(ctiller): constant */ + while (c->table_size + elem_size > 4096) { + c->tail_remote_index++; + GPR_ASSERT(c->tail_remote_index > 0); + GPR_ASSERT(c->table_size >= + c->table_elem_size[c->tail_remote_index % + GRPC_CHTTP2_HPACKC_MAX_TABLE_ELEMS]); + GPR_ASSERT(c->table_elems > 0); + c->table_size -= c->table_elem_size[c->tail_remote_index % + GRPC_CHTTP2_HPACKC_MAX_TABLE_ELEMS]; + c->table_elems--; + } + GPR_ASSERT(c->table_elems < GRPC_CHTTP2_HPACKC_MAX_TABLE_ELEMS); + c->table_elem_size[new_index % GRPC_CHTTP2_HPACKC_MAX_TABLE_ELEMS] = + elem_size; + c->table_size += elem_size; + c->table_elems++; + + /* Store this element into {entries,indices}_elem */ + if (c->entries_elems[HASH_FRAGMENT_2(elem_hash)] == elem) { + /* already there: update with new index */ + c->indices_elems[HASH_FRAGMENT_2(elem_hash)] = new_index; + elem_to_unref = elem; + } else if (c->entries_elems[HASH_FRAGMENT_3(elem_hash)] == elem) { + /* already there (cuckoo): update with new index */ + c->indices_elems[HASH_FRAGMENT_3(elem_hash)] = new_index; + elem_to_unref = elem; + } else if (c->entries_elems[HASH_FRAGMENT_2(elem_hash)] == NULL) { + /* not there, but a free element: add */ + c->entries_elems[HASH_FRAGMENT_2(elem_hash)] = elem; + c->indices_elems[HASH_FRAGMENT_2(elem_hash)] = new_index; + elem_to_unref = NULL; + } else if (c->entries_elems[HASH_FRAGMENT_3(elem_hash)] == NULL) { + /* not there (cuckoo), but a free element: add */ + c->entries_elems[HASH_FRAGMENT_3(elem_hash)] = elem; + c->indices_elems[HASH_FRAGMENT_3(elem_hash)] = new_index; + elem_to_unref = NULL; + } else if (c->indices_elems[HASH_FRAGMENT_2(elem_hash)] < + c->indices_elems[HASH_FRAGMENT_3(elem_hash)]) { + /* not there: replace oldest */ + elem_to_unref = c->entries_elems[HASH_FRAGMENT_2(elem_hash)]; + c->entries_elems[HASH_FRAGMENT_2(elem_hash)] = elem; + c->indices_elems[HASH_FRAGMENT_2(elem_hash)] = new_index; + } else { + /* not there: replace oldest */ + elem_to_unref = c->entries_elems[HASH_FRAGMENT_3(elem_hash)]; + c->entries_elems[HASH_FRAGMENT_3(elem_hash)] = elem; + c->indices_elems[HASH_FRAGMENT_3(elem_hash)] = new_index; + } + + /* do exactly the same for the key (so we can find by that again too) */ + + if (c->entries_keys[HASH_FRAGMENT_2(key_hash)] == elem->key) { + c->indices_keys[HASH_FRAGMENT_2(key_hash)] = new_index; + } else if (c->entries_keys[HASH_FRAGMENT_3(key_hash)] == elem->key) { + c->indices_keys[HASH_FRAGMENT_3(key_hash)] = new_index; + } else if (c->entries_keys[HASH_FRAGMENT_2(key_hash)] == NULL) { + c->entries_keys[HASH_FRAGMENT_2(key_hash)] = GRPC_MDSTR_REF(elem->key); + c->indices_keys[HASH_FRAGMENT_2(key_hash)] = new_index; + } else if (c->entries_keys[HASH_FRAGMENT_3(key_hash)] == NULL) { + c->entries_keys[HASH_FRAGMENT_3(key_hash)] = GRPC_MDSTR_REF(elem->key); + c->indices_keys[HASH_FRAGMENT_3(key_hash)] = new_index; + } else if (c->indices_keys[HASH_FRAGMENT_2(key_hash)] < + c->indices_keys[HASH_FRAGMENT_3(key_hash)]) { + GRPC_MDSTR_UNREF(c->entries_keys[HASH_FRAGMENT_2(key_hash)]); + c->entries_keys[HASH_FRAGMENT_2(key_hash)] = GRPC_MDSTR_REF(elem->key); + c->indices_keys[HASH_FRAGMENT_2(key_hash)] = new_index; + } else { + GRPC_MDSTR_UNREF(c->entries_keys[HASH_FRAGMENT_3(key_hash)]); + c->entries_keys[HASH_FRAGMENT_3(key_hash)] = GRPC_MDSTR_REF(elem->key); + c->indices_keys[HASH_FRAGMENT_3(key_hash)] = new_index; + } + + return elem_to_unref; +} + +static void emit_indexed(grpc_chttp2_hpack_compressor *c, gpr_uint32 index, + framer_state *st) { + int len = GRPC_CHTTP2_VARINT_LENGTH(index, 1); + GRPC_CHTTP2_WRITE_VARINT(index, 1, 0x80, add_tiny_header_data(st, len), len); +} + +static gpr_slice get_wire_value(grpc_mdelem *elem, gpr_uint8 *huffman_prefix) { + if (grpc_is_binary_header((const char *)GPR_SLICE_START_PTR(elem->key->slice), + GPR_SLICE_LENGTH(elem->key->slice))) { + *huffman_prefix = 0x80; + return grpc_mdstr_as_base64_encoded_and_huffman_compressed(elem->value); + } + /* TODO(ctiller): opportunistically compress non-binary headers */ + *huffman_prefix = 0x00; + return elem->value->slice; +} + +static void emit_lithdr_incidx(grpc_chttp2_hpack_compressor *c, + gpr_uint32 key_index, grpc_mdelem *elem, + framer_state *st) { + int len_pfx = GRPC_CHTTP2_VARINT_LENGTH(key_index, 2); + gpr_uint8 huffman_prefix; + gpr_slice value_slice = get_wire_value(elem, &huffman_prefix); + int len_val = GPR_SLICE_LENGTH(value_slice); + int len_val_len = GRPC_CHTTP2_VARINT_LENGTH(len_val, 1); + GRPC_CHTTP2_WRITE_VARINT(key_index, 2, 0x40, + add_tiny_header_data(st, len_pfx), len_pfx); + GRPC_CHTTP2_WRITE_VARINT(len_val, 1, 0x00, + add_tiny_header_data(st, len_val_len), len_val_len); + add_header_data(st, gpr_slice_ref(value_slice)); +} + +static void emit_lithdr_noidx(grpc_chttp2_hpack_compressor *c, + gpr_uint32 key_index, grpc_mdelem *elem, + framer_state *st) { + int len_pfx = GRPC_CHTTP2_VARINT_LENGTH(key_index, 4); + gpr_uint8 huffman_prefix; + gpr_slice value_slice = get_wire_value(elem, &huffman_prefix); + int len_val = GPR_SLICE_LENGTH(value_slice); + int len_val_len = GRPC_CHTTP2_VARINT_LENGTH(len_val, 1); + GRPC_CHTTP2_WRITE_VARINT(key_index, 4, 0x00, + add_tiny_header_data(st, len_pfx), len_pfx); + GRPC_CHTTP2_WRITE_VARINT(len_val, 1, 0x00, + add_tiny_header_data(st, len_val_len), len_val_len); + add_header_data(st, gpr_slice_ref(value_slice)); +} + +static void emit_lithdr_incidx_v(grpc_chttp2_hpack_compressor *c, + grpc_mdelem *elem, framer_state *st) { + int len_key = GPR_SLICE_LENGTH(elem->key->slice); + gpr_uint8 huffman_prefix; + gpr_slice value_slice = get_wire_value(elem, &huffman_prefix); + int len_val = GPR_SLICE_LENGTH(value_slice); + int len_key_len = GRPC_CHTTP2_VARINT_LENGTH(len_key, 1); + int len_val_len = GRPC_CHTTP2_VARINT_LENGTH(len_val, 1); + *add_tiny_header_data(st, 1) = 0x40; + GRPC_CHTTP2_WRITE_VARINT(len_key, 1, 0x00, + add_tiny_header_data(st, len_key_len), len_key_len); + add_header_data(st, gpr_slice_ref(elem->key->slice)); + GRPC_CHTTP2_WRITE_VARINT(len_val, 1, huffman_prefix, + add_tiny_header_data(st, len_val_len), len_val_len); + add_header_data(st, gpr_slice_ref(value_slice)); +} + +static void emit_lithdr_noidx_v(grpc_chttp2_hpack_compressor *c, + grpc_mdelem *elem, framer_state *st) { + int len_key = GPR_SLICE_LENGTH(elem->key->slice); + gpr_uint8 huffman_prefix; + gpr_slice value_slice = get_wire_value(elem, &huffman_prefix); + int len_val = GPR_SLICE_LENGTH(value_slice); + int len_key_len = GRPC_CHTTP2_VARINT_LENGTH(len_key, 1); + int len_val_len = GRPC_CHTTP2_VARINT_LENGTH(len_val, 1); + *add_tiny_header_data(st, 1) = 0x00; + GRPC_CHTTP2_WRITE_VARINT(len_key, 1, 0x00, + add_tiny_header_data(st, len_key_len), len_key_len); + add_header_data(st, gpr_slice_ref(elem->key->slice)); + GRPC_CHTTP2_WRITE_VARINT(len_val, 1, huffman_prefix, + add_tiny_header_data(st, len_val_len), len_val_len); + add_header_data(st, gpr_slice_ref(value_slice)); +} + +static gpr_uint32 dynidx(grpc_chttp2_hpack_compressor *c, gpr_uint32 index) { + return 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY + c->tail_remote_index + + c->table_elems - index; +} + +/* encode an mdelem; returns metadata element to unref */ +static grpc_mdelem *hpack_enc(grpc_chttp2_hpack_compressor *c, + grpc_mdelem *elem, framer_state *st) { + gpr_uint32 key_hash = elem->key->hash; + gpr_uint32 elem_hash = GRPC_MDSTR_KV_HASH(key_hash, elem->value->hash); + size_t decoder_space_usage; + gpr_uint32 indices_key; + int should_add_elem; + + GPR_ASSERT (GPR_SLICE_LENGTH(elem->key->slice) > 0); + if (GPR_SLICE_START_PTR(elem->key->slice)[0] != ':') { /* regular header */ + st->seen_regular_header = 1; + } else if (st->seen_regular_header != 0) { /* reserved header */ + gpr_log(GPR_ERROR, + "Reserved header (colon-prefixed) happening after regular ones."); + abort(); + } + + inc_filter(HASH_FRAGMENT_1(elem_hash), &c->filter_elems_sum, c->filter_elems); + + /* is this elem currently in the decoders table? */ + + if (c->entries_elems[HASH_FRAGMENT_2(elem_hash)] == elem && + c->indices_elems[HASH_FRAGMENT_2(elem_hash)] > c->tail_remote_index) { + /* HIT: complete element (first cuckoo hash) */ + emit_indexed(c, dynidx(c, c->indices_elems[HASH_FRAGMENT_2(elem_hash)]), + st); + return elem; + } + + if (c->entries_elems[HASH_FRAGMENT_3(elem_hash)] == elem && + c->indices_elems[HASH_FRAGMENT_3(elem_hash)] > c->tail_remote_index) { + /* HIT: complete element (second cuckoo hash) */ + emit_indexed(c, dynidx(c, c->indices_elems[HASH_FRAGMENT_3(elem_hash)]), + st); + return elem; + } + + /* should this elem be in the table? */ + decoder_space_usage = 32 + GPR_SLICE_LENGTH(elem->key->slice) + + GPR_SLICE_LENGTH(elem->value->slice); + should_add_elem = decoder_space_usage < MAX_DECODER_SPACE_USAGE && + c->filter_elems[HASH_FRAGMENT_1(elem_hash)] >= + c->filter_elems_sum / ONE_ON_ADD_PROBABILITY; + + /* no hits for the elem... maybe there's a key? */ + + indices_key = c->indices_keys[HASH_FRAGMENT_2(key_hash)]; + if (c->entries_keys[HASH_FRAGMENT_2(key_hash)] == elem->key && + indices_key > c->tail_remote_index) { + /* HIT: key (first cuckoo hash) */ + if (should_add_elem) { + emit_lithdr_incidx(c, dynidx(c, indices_key), elem, st); + return add_elem(c, elem); + } else { + emit_lithdr_noidx(c, dynidx(c, indices_key), elem, st); + return elem; + } + abort(); + } + + indices_key = c->indices_keys[HASH_FRAGMENT_3(key_hash)]; + if (c->entries_keys[HASH_FRAGMENT_3(key_hash)] == elem->key && + indices_key > c->tail_remote_index) { + /* HIT: key (first cuckoo hash) */ + if (should_add_elem) { + emit_lithdr_incidx(c, dynidx(c, indices_key), elem, st); + return add_elem(c, elem); + } else { + emit_lithdr_noidx(c, dynidx(c, indices_key), elem, st); + return elem; + } + abort(); + } + + /* no elem, key in the table... fall back to literal emission */ + + if (should_add_elem) { + emit_lithdr_incidx_v(c, elem, st); + return add_elem(c, elem); + } else { + emit_lithdr_noidx_v(c, elem, st); + return elem; + } + abort(); +} + +#define STRLEN_LIT(x) (sizeof(x) - 1) +#define TIMEOUT_KEY "grpc-timeout" + +static void deadline_enc(grpc_chttp2_hpack_compressor *c, gpr_timespec deadline, + framer_state *st) { + char timeout_str[GRPC_CHTTP2_TIMEOUT_ENCODE_MIN_BUFSIZE]; + grpc_mdelem *mdelem; + grpc_chttp2_encode_timeout( + gpr_time_sub(deadline, gpr_now(deadline.clock_type)), timeout_str); + mdelem = grpc_mdelem_from_metadata_strings( + c->mdctx, GRPC_MDSTR_REF(c->timeout_key_str), + grpc_mdstr_from_string(c->mdctx, timeout_str, 0)); + mdelem = hpack_enc(c, mdelem, st); + if (mdelem) GRPC_MDELEM_UNREF(mdelem); +} + +gpr_slice grpc_chttp2_data_frame_create_empty_close(gpr_uint32 id) { + gpr_slice slice = gpr_slice_malloc(9); + fill_header(GPR_SLICE_START_PTR(slice), GRPC_CHTTP2_FRAME_DATA, id, 0, 1); + return slice; +} + +void grpc_chttp2_hpack_compressor_init(grpc_chttp2_hpack_compressor *c, + grpc_mdctx *ctx) { + memset(c, 0, sizeof(*c)); + c->mdctx = ctx; + c->timeout_key_str = grpc_mdstr_from_string(ctx, "grpc-timeout", 0); +} + +void grpc_chttp2_hpack_compressor_destroy(grpc_chttp2_hpack_compressor *c) { + int i; + for (i = 0; i < GRPC_CHTTP2_HPACKC_NUM_VALUES; i++) { + if (c->entries_keys[i]) GRPC_MDSTR_UNREF(c->entries_keys[i]); + if (c->entries_elems[i]) GRPC_MDELEM_UNREF(c->entries_elems[i]); + } + GRPC_MDSTR_UNREF(c->timeout_key_str); +} + +gpr_uint32 grpc_chttp2_preencode(grpc_stream_op *inops, size_t *inops_count, + gpr_uint32 max_flow_controlled_bytes, + grpc_stream_op_buffer *outops) { + gpr_slice slice; + grpc_stream_op *op; + gpr_uint32 max_take_size; + gpr_uint32 flow_controlled_bytes_taken = 0; + gpr_uint32 curop = 0; + gpr_uint8 *p; + int compressed_flag_set = 0; + + while (curop < *inops_count) { + GPR_ASSERT(flow_controlled_bytes_taken <= max_flow_controlled_bytes); + op = &inops[curop]; + switch (op->type) { + case GRPC_NO_OP: + /* skip */ + curop++; + break; + case GRPC_OP_METADATA: + grpc_metadata_batch_assert_ok(&op->data.metadata); + /* these just get copied as they don't impact the number of flow + controlled bytes */ + grpc_sopb_append(outops, op, 1); + curop++; + break; + case GRPC_OP_BEGIN_MESSAGE: + /* begin op: for now we just convert the op to a slice and fall + through - this lets us reuse the slice framing code below */ + compressed_flag_set = + (op->data.begin_message.flags & GRPC_WRITE_INTERNAL_COMPRESS) != 0; + slice = gpr_slice_malloc(5); + + p = GPR_SLICE_START_PTR(slice); + p[0] = compressed_flag_set; + p[1] = op->data.begin_message.length >> 24; + p[2] = op->data.begin_message.length >> 16; + p[3] = op->data.begin_message.length >> 8; + p[4] = op->data.begin_message.length; + op->type = GRPC_OP_SLICE; + op->data.slice = slice; + /* fallthrough */ + case GRPC_OP_SLICE: + slice = op->data.slice; + if (!GPR_SLICE_LENGTH(slice)) { + /* skip zero length slices */ + gpr_slice_unref(slice); + curop++; + break; + } + max_take_size = max_flow_controlled_bytes - flow_controlled_bytes_taken; + if (max_take_size == 0) { + goto exit_loop; + } + if (GPR_SLICE_LENGTH(slice) > max_take_size) { + slice = gpr_slice_split_head(&op->data.slice, max_take_size); + grpc_sopb_add_slice(outops, slice); + } else { + /* consume this op immediately */ + grpc_sopb_append(outops, op, 1); + curop++; + } + flow_controlled_bytes_taken += GPR_SLICE_LENGTH(slice); + break; + } + } +exit_loop: + *inops_count -= curop; + memmove(inops, inops + curop, *inops_count * sizeof(grpc_stream_op)); + + for (curop = 0; curop < *inops_count; curop++) { + if (inops[curop].type == GRPC_OP_METADATA) { + grpc_metadata_batch_assert_ok(&inops[curop].data.metadata); + } + } + + return flow_controlled_bytes_taken; +} + +void grpc_chttp2_encode(grpc_stream_op *ops, size_t ops_count, int eof, + gpr_uint32 stream_id, + grpc_chttp2_hpack_compressor *compressor, + gpr_slice_buffer *output) { + framer_state st; + gpr_slice slice; + grpc_stream_op *op; + gpr_uint32 max_take_size; + gpr_uint32 curop = 0; + gpr_uint32 unref_op; + grpc_mdctx *mdctx = compressor->mdctx; + grpc_linked_mdelem *l; + int need_unref = 0; + gpr_timespec deadline; + + GPR_ASSERT(stream_id != 0); + + st.cur_frame_type = NONE; + st.last_was_header = 0; + st.seen_regular_header = 0; + st.stream_id = stream_id; + st.output = output; + + while (curop < ops_count) { + op = &ops[curop]; + switch (op->type) { + case GRPC_NO_OP: + case GRPC_OP_BEGIN_MESSAGE: + gpr_log( + GPR_ERROR, + "These stream ops should be filtered out by grpc_chttp2_preencode"); + abort(); + case GRPC_OP_METADATA: + /* Encode a metadata batch; store the returned values, representing + a metadata element that needs to be unreffed back into the metadata + slot. THIS MAY NOT BE THE SAME ELEMENT (if a decoder table slot got + updated). After this loop, we'll do a batch unref of elements. */ + begin_new_frame(&st, HEADER); + need_unref |= op->data.metadata.garbage.head != NULL; + grpc_metadata_batch_assert_ok(&op->data.metadata); + for (l = op->data.metadata.list.head; l; l = l->next) { + l->md = hpack_enc(compressor, l->md, &st); + need_unref |= l->md != NULL; + } + deadline = op->data.metadata.deadline; + if (gpr_time_cmp(deadline, gpr_inf_future(deadline.clock_type)) != 0) { + deadline_enc(compressor, deadline, &st); + } + curop++; + break; + case GRPC_OP_SLICE: + slice = op->data.slice; + if (st.cur_frame_type == DATA && + st.output->length - st.output_length_at_start_of_frame == + GRPC_CHTTP2_MAX_PAYLOAD_LENGTH) { + finish_frame(&st, 0, 0); + } + ensure_frame_type(&st, DATA, 1); + max_take_size = GRPC_CHTTP2_MAX_PAYLOAD_LENGTH + + st.output_length_at_start_of_frame - st.output->length; + if (GPR_SLICE_LENGTH(slice) > max_take_size) { + slice = gpr_slice_split_head(&op->data.slice, max_take_size); + } else { + /* consume this op immediately */ + curop++; + } + gpr_slice_buffer_add(output, slice); + break; + } + } + if (eof && st.cur_frame_type == NONE) { + begin_frame(&st, DATA); + } + finish_frame(&st, 1, eof); + + if (need_unref) { + grpc_mdctx_lock(mdctx); + for (unref_op = 0; unref_op < curop; unref_op++) { + op = &ops[unref_op]; + if (op->type != GRPC_OP_METADATA) continue; + for (l = op->data.metadata.list.head; l; l = l->next) { + if (l->md) GRPC_MDCTX_LOCKED_MDELEM_UNREF(mdctx, l->md); + } + for (l = op->data.metadata.garbage.head; l; l = l->next) { + GRPC_MDCTX_LOCKED_MDELEM_UNREF(mdctx, l->md); + } + } + grpc_mdctx_unlock(mdctx); + } +} diff --git a/src/core/transport/chttp2/stream_encoder.h b/src/core/transport/chttp2/stream_encoder.h new file mode 100644 index 00000000..db52f2a0 --- /dev/null +++ b/src/core/transport/chttp2/stream_encoder.h @@ -0,0 +1,93 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_TRANSPORT_CHTTP2_STREAM_ENCODER_H +#define GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_STREAM_ENCODER_H + +#include "src/core/transport/chttp2/frame.h" +#include "src/core/transport/metadata.h" +#include "src/core/transport/stream_op.h" +#include +#include +#include + +#define GRPC_CHTTP2_HPACKC_NUM_FILTERS 256 +#define GRPC_CHTTP2_HPACKC_NUM_VALUES 256 +#define GRPC_CHTTP2_HPACKC_MAX_TABLE_ELEMS (4096 / 32) + +typedef struct { + gpr_uint32 filter_elems_sum; + /* one before the lowest usable table index */ + gpr_uint32 tail_remote_index; + gpr_uint16 table_size; + gpr_uint16 table_elems; + + /* filter tables for elems: this tables provides an approximate + popularity count for particular hashes, and are used to determine whether + a new literal should be added to the compression table or not. + They track a single integer that counts how often a particular value has + been seen. When that count reaches max (255), all values are halved. */ + gpr_uint8 filter_elems[GRPC_CHTTP2_HPACKC_NUM_FILTERS]; + + /* metadata context */ + grpc_mdctx *mdctx; + /* the string 'grpc-timeout' */ + grpc_mdstr *timeout_key_str; + + /* entry tables for keys & elems: these tables track values that have been + seen and *may* be in the decompressor table */ + grpc_mdstr *entries_keys[GRPC_CHTTP2_HPACKC_NUM_VALUES]; + grpc_mdelem *entries_elems[GRPC_CHTTP2_HPACKC_NUM_VALUES]; + gpr_uint32 indices_keys[GRPC_CHTTP2_HPACKC_NUM_VALUES]; + gpr_uint32 indices_elems[GRPC_CHTTP2_HPACKC_NUM_VALUES]; + + gpr_uint16 table_elem_size[GRPC_CHTTP2_HPACKC_MAX_TABLE_ELEMS]; +} grpc_chttp2_hpack_compressor; + +void grpc_chttp2_hpack_compressor_init(grpc_chttp2_hpack_compressor *c, + grpc_mdctx *mdctx); +void grpc_chttp2_hpack_compressor_destroy(grpc_chttp2_hpack_compressor *c); + +/* select stream ops to be encoded, moving them from inops to outops, and + moving subsequent ops in inops forward in the queue */ +gpr_uint32 grpc_chttp2_preencode(grpc_stream_op *inops, size_t *inops_count, + gpr_uint32 max_flow_controlled_bytes, + grpc_stream_op_buffer *outops); + +/* encode stream ops to output */ +void grpc_chttp2_encode(grpc_stream_op *ops, size_t ops_count, int eof, + gpr_uint32 stream_id, + grpc_chttp2_hpack_compressor *compressor, + gpr_slice_buffer *output); + +#endif /* GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_STREAM_ENCODER_H */ diff --git a/src/core/transport/chttp2/stream_lists.c b/src/core/transport/chttp2/stream_lists.c new file mode 100644 index 00000000..781db7b0 --- /dev/null +++ b/src/core/transport/chttp2/stream_lists.c @@ -0,0 +1,403 @@ +/* + * + * Copyright 2015, 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 "src/core/transport/chttp2/internal.h" + +#include + +#define TRANSPORT_FROM_GLOBAL(tg) \ + ((grpc_chttp2_transport *)((char *)(tg)-offsetof(grpc_chttp2_transport, \ + global))) + +#define STREAM_FROM_GLOBAL(sg) \ + ((grpc_chttp2_stream *)((char *)(sg)-offsetof(grpc_chttp2_stream, global))) + +#define TRANSPORT_FROM_WRITING(tw) \ + ((grpc_chttp2_transport *)((char *)(tw)-offsetof(grpc_chttp2_transport, \ + writing))) + +#define STREAM_FROM_WRITING(sw) \ + ((grpc_chttp2_stream *)((char *)(sw)-offsetof(grpc_chttp2_stream, writing))) + +#define TRANSPORT_FROM_PARSING(tp) \ + ((grpc_chttp2_transport *)((char *)(tp)-offsetof(grpc_chttp2_transport, \ + parsing))) + +#define STREAM_FROM_PARSING(sp) \ + ((grpc_chttp2_stream *)((char *)(sp)-offsetof(grpc_chttp2_stream, parsing))) + +/* core list management */ + +static int stream_list_empty(grpc_chttp2_transport *t, + grpc_chttp2_stream_list_id id) { + return t->lists[id].head == NULL; +} + +static int stream_list_pop(grpc_chttp2_transport *t, + grpc_chttp2_stream **stream, + grpc_chttp2_stream_list_id id) { + grpc_chttp2_stream *s = t->lists[id].head; + if (s) { + grpc_chttp2_stream *new_head = s->links[id].next; + GPR_ASSERT(s->included[id]); + if (new_head) { + t->lists[id].head = new_head; + new_head->links[id].prev = NULL; + } else { + t->lists[id].head = NULL; + t->lists[id].tail = NULL; + } + s->included[id] = 0; + } + *stream = s; + return s != 0; +} + +static void stream_list_remove(grpc_chttp2_transport *t, grpc_chttp2_stream *s, + grpc_chttp2_stream_list_id id) { + GPR_ASSERT(s->included[id]); + s->included[id] = 0; + if (s->links[id].prev) { + s->links[id].prev->links[id].next = s->links[id].next; + } else { + GPR_ASSERT(t->lists[id].head == s); + t->lists[id].head = s->links[id].next; + } + if (s->links[id].next) { + s->links[id].next->links[id].prev = s->links[id].prev; + } else { + t->lists[id].tail = s->links[id].prev; + } +} + +static void stream_list_maybe_remove(grpc_chttp2_transport *t, + grpc_chttp2_stream *s, + grpc_chttp2_stream_list_id id) { + if (s->included[id]) { + stream_list_remove(t, s, id); + } +} + +static void stream_list_add_head(grpc_chttp2_transport *t, + grpc_chttp2_stream *s, + grpc_chttp2_stream_list_id id) { + grpc_chttp2_stream *old_head; + GPR_ASSERT(!s->included[id]); + old_head = t->lists[id].head; + s->links[id].next = old_head; + s->links[id].prev = NULL; + if (old_head) { + old_head->links[id].prev = s; + } else { + t->lists[id].tail = s; + } + t->lists[id].head = s; + s->included[id] = 1; +} + +static void stream_list_add_tail(grpc_chttp2_transport *t, + grpc_chttp2_stream *s, + grpc_chttp2_stream_list_id id) { + grpc_chttp2_stream *old_tail; + GPR_ASSERT(!s->included[id]); + old_tail = t->lists[id].tail; + s->links[id].next = NULL; + s->links[id].prev = old_tail; + if (old_tail) { + old_tail->links[id].next = s; + } else { + t->lists[id].head = s; + } + t->lists[id].tail = s; + s->included[id] = 1; +} + +static void stream_list_add(grpc_chttp2_transport *t, grpc_chttp2_stream *s, + grpc_chttp2_stream_list_id id) { + if (s->included[id]) { + return; + } + stream_list_add_tail(t, s, id); +} + +/* wrappers for specializations */ + +void grpc_chttp2_list_add_writable_stream( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global *stream_global) { + GPR_ASSERT(stream_global->id != 0); + stream_list_add(TRANSPORT_FROM_GLOBAL(transport_global), + STREAM_FROM_GLOBAL(stream_global), GRPC_CHTTP2_LIST_WRITABLE); +} + +void grpc_chttp2_list_add_first_writable_stream( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global *stream_global) { + GPR_ASSERT(stream_global->id != 0); + stream_list_add_head(TRANSPORT_FROM_GLOBAL(transport_global), + STREAM_FROM_GLOBAL(stream_global), + GRPC_CHTTP2_LIST_WRITABLE); +} + +int grpc_chttp2_list_pop_writable_stream( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_transport_writing *transport_writing, + grpc_chttp2_stream_global **stream_global, + grpc_chttp2_stream_writing **stream_writing) { + grpc_chttp2_stream *stream; + int r = stream_list_pop(TRANSPORT_FROM_GLOBAL(transport_global), &stream, + GRPC_CHTTP2_LIST_WRITABLE); + if (r != 0) { + *stream_global = &stream->global; + *stream_writing = &stream->writing; + } + return r; +} + +void grpc_chttp2_list_remove_writable_stream( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global *stream_global) { + stream_list_maybe_remove(TRANSPORT_FROM_GLOBAL(transport_global), + STREAM_FROM_GLOBAL(stream_global), + GRPC_CHTTP2_LIST_WRITABLE); +} + +void grpc_chttp2_list_add_writing_stream( + grpc_chttp2_transport_writing *transport_writing, + grpc_chttp2_stream_writing *stream_writing) { + stream_list_add(TRANSPORT_FROM_WRITING(transport_writing), + STREAM_FROM_WRITING(stream_writing), + GRPC_CHTTP2_LIST_WRITING); +} + +int grpc_chttp2_list_have_writing_streams( + grpc_chttp2_transport_writing *transport_writing) { + return !stream_list_empty(TRANSPORT_FROM_WRITING(transport_writing), + GRPC_CHTTP2_LIST_WRITING); +} + +int grpc_chttp2_list_pop_writing_stream( + grpc_chttp2_transport_writing *transport_writing, + grpc_chttp2_stream_writing **stream_writing) { + grpc_chttp2_stream *stream; + int r = stream_list_pop(TRANSPORT_FROM_WRITING(transport_writing), &stream, + GRPC_CHTTP2_LIST_WRITING); + if (r != 0) { + *stream_writing = &stream->writing; + } + return r; +} + +void grpc_chttp2_list_add_written_stream( + grpc_chttp2_transport_writing *transport_writing, + grpc_chttp2_stream_writing *stream_writing) { + stream_list_add(TRANSPORT_FROM_WRITING(transport_writing), + STREAM_FROM_WRITING(stream_writing), + GRPC_CHTTP2_LIST_WRITTEN); +} + +int grpc_chttp2_list_pop_written_stream( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_transport_writing *transport_writing, + grpc_chttp2_stream_global **stream_global, + grpc_chttp2_stream_writing **stream_writing) { + grpc_chttp2_stream *stream; + int r = stream_list_pop(TRANSPORT_FROM_WRITING(transport_writing), &stream, + GRPC_CHTTP2_LIST_WRITTEN); + if (r != 0) { + *stream_global = &stream->global; + *stream_writing = &stream->writing; + } + return r; +} + +void grpc_chttp2_list_add_parsing_seen_stream( + grpc_chttp2_transport_parsing *transport_parsing, + grpc_chttp2_stream_parsing *stream_parsing) { + stream_list_add(TRANSPORT_FROM_PARSING(transport_parsing), + STREAM_FROM_PARSING(stream_parsing), + GRPC_CHTTP2_LIST_PARSING_SEEN); +} + +int grpc_chttp2_list_pop_parsing_seen_stream( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_transport_parsing *transport_parsing, + grpc_chttp2_stream_global **stream_global, + grpc_chttp2_stream_parsing **stream_parsing) { + grpc_chttp2_stream *stream; + int r = stream_list_pop(TRANSPORT_FROM_PARSING(transport_parsing), &stream, + GRPC_CHTTP2_LIST_PARSING_SEEN); + if (r != 0) { + *stream_global = &stream->global; + *stream_parsing = &stream->parsing; + } + return r; +} + +void grpc_chttp2_list_add_waiting_for_concurrency( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global *stream_global) { + stream_list_add(TRANSPORT_FROM_GLOBAL(transport_global), + STREAM_FROM_GLOBAL(stream_global), + GRPC_CHTTP2_LIST_WAITING_FOR_CONCURRENCY); +} + +int grpc_chttp2_list_pop_waiting_for_concurrency( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global **stream_global) { + grpc_chttp2_stream *stream; + int r = stream_list_pop(TRANSPORT_FROM_GLOBAL(transport_global), &stream, + GRPC_CHTTP2_LIST_WAITING_FOR_CONCURRENCY); + if (r != 0) { + *stream_global = &stream->global; + } + return r; +} + +void grpc_chttp2_list_add_closed_waiting_for_parsing( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global *stream_global) { + stream_list_add(TRANSPORT_FROM_GLOBAL(transport_global), + STREAM_FROM_GLOBAL(stream_global), + GRPC_CHTTP2_LIST_CLOSED_WAITING_FOR_PARSING); +} + +int grpc_chttp2_list_pop_closed_waiting_for_parsing( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global **stream_global) { + grpc_chttp2_stream *stream; + int r = stream_list_pop(TRANSPORT_FROM_GLOBAL(transport_global), &stream, + GRPC_CHTTP2_LIST_CLOSED_WAITING_FOR_PARSING); + if (r != 0) { + *stream_global = &stream->global; + } + return r; +} + +void grpc_chttp2_list_add_cancelled_waiting_for_writing( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global *stream_global) { + stream_list_add(TRANSPORT_FROM_GLOBAL(transport_global), + STREAM_FROM_GLOBAL(stream_global), + GRPC_CHTTP2_LIST_CANCELLED_WAITING_FOR_WRITING); +} + +int grpc_chttp2_list_pop_cancelled_waiting_for_writing( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global **stream_global) { + grpc_chttp2_stream *stream; + int r = stream_list_pop(TRANSPORT_FROM_GLOBAL(transport_global), &stream, + GRPC_CHTTP2_LIST_CANCELLED_WAITING_FOR_WRITING); + if (r != 0) { + *stream_global = &stream->global; + } + return r; +} + +void grpc_chttp2_list_add_incoming_window_updated( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global *stream_global) { + stream_list_add(TRANSPORT_FROM_GLOBAL(transport_global), + STREAM_FROM_GLOBAL(stream_global), + GRPC_CHTTP2_LIST_INCOMING_WINDOW_UPDATED); +} + +int grpc_chttp2_list_pop_incoming_window_updated( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_transport_parsing *transport_parsing, + grpc_chttp2_stream_global **stream_global, + grpc_chttp2_stream_parsing **stream_parsing) { + grpc_chttp2_stream *stream; + int r = stream_list_pop(TRANSPORT_FROM_GLOBAL(transport_global), &stream, + GRPC_CHTTP2_LIST_INCOMING_WINDOW_UPDATED); + if (r != 0) { + *stream_global = &stream->global; + *stream_parsing = &stream->parsing; + } + return r; +} + +void grpc_chttp2_list_remove_incoming_window_updated( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global *stream_global) { + stream_list_maybe_remove(TRANSPORT_FROM_GLOBAL(transport_global), + STREAM_FROM_GLOBAL(stream_global), + GRPC_CHTTP2_LIST_INCOMING_WINDOW_UPDATED); +} + +void grpc_chttp2_list_add_read_write_state_changed( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global *stream_global) { + stream_list_add(TRANSPORT_FROM_GLOBAL(transport_global), + STREAM_FROM_GLOBAL(stream_global), + GRPC_CHTTP2_LIST_READ_WRITE_STATE_CHANGED); +} + +int grpc_chttp2_list_pop_read_write_state_changed( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global **stream_global) { + grpc_chttp2_stream *stream; + int r = stream_list_pop(TRANSPORT_FROM_GLOBAL(transport_global), &stream, + GRPC_CHTTP2_LIST_READ_WRITE_STATE_CHANGED); + if (r != 0) { + *stream_global = &stream->global; + } + return r; +} + +void grpc_chttp2_register_stream(grpc_chttp2_transport *t, + grpc_chttp2_stream *s) { + stream_list_add_tail(t, s, GRPC_CHTTP2_LIST_ALL_STREAMS); +} + +int grpc_chttp2_unregister_stream(grpc_chttp2_transport *t, + grpc_chttp2_stream *s) { + stream_list_maybe_remove(t, s, GRPC_CHTTP2_LIST_ALL_STREAMS); + return stream_list_empty(t, GRPC_CHTTP2_LIST_ALL_STREAMS); +} + +int grpc_chttp2_has_streams(grpc_chttp2_transport *t) { + return !stream_list_empty(t, GRPC_CHTTP2_LIST_ALL_STREAMS); +} + +void grpc_chttp2_for_all_streams( + grpc_chttp2_transport_global *transport_global, void *user_data, + void (*cb)(grpc_chttp2_transport_global *transport_global, void *user_data, + grpc_chttp2_stream_global *stream_global)) { + grpc_chttp2_stream *s; + grpc_chttp2_transport *t = TRANSPORT_FROM_GLOBAL(transport_global); + for (s = t->lists[GRPC_CHTTP2_LIST_ALL_STREAMS].head; s != NULL; + s = s->links[GRPC_CHTTP2_LIST_ALL_STREAMS].next) { + cb(transport_global, user_data, &s->global); + } +} diff --git a/src/core/transport/chttp2/stream_map.c b/src/core/transport/chttp2/stream_map.c new file mode 100644 index 00000000..bd16153e --- /dev/null +++ b/src/core/transport/chttp2/stream_map.c @@ -0,0 +1,197 @@ +/* + * + * Copyright 2015, 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 "src/core/transport/chttp2/stream_map.h" + +#include + +#include +#include +#include + +void grpc_chttp2_stream_map_init(grpc_chttp2_stream_map *map, + size_t initial_capacity) { + GPR_ASSERT(initial_capacity > 1); + map->keys = gpr_malloc(sizeof(gpr_uint32) * initial_capacity); + map->values = gpr_malloc(sizeof(void *) * initial_capacity); + map->count = 0; + map->free = 0; + map->capacity = initial_capacity; +} + +void grpc_chttp2_stream_map_destroy(grpc_chttp2_stream_map *map) { + gpr_free(map->keys); + gpr_free(map->values); +} + +static size_t compact(gpr_uint32 *keys, void **values, size_t count) { + size_t i, out; + + for (i = 0, out = 0; i < count; i++) { + if (values[i]) { + keys[out] = keys[i]; + values[out] = values[i]; + out++; + } + } + + return out; +} + +void grpc_chttp2_stream_map_add(grpc_chttp2_stream_map *map, gpr_uint32 key, + void *value) { + size_t count = map->count; + size_t capacity = map->capacity; + gpr_uint32 *keys = map->keys; + void **values = map->values; + + GPR_ASSERT(count == 0 || keys[count - 1] < key); + GPR_ASSERT(value); + + if (count == capacity) { + if (map->free > capacity / 4) { + count = compact(keys, values, count); + map->free = 0; + } else { + /* resize when less than 25% of the table is free, because compaction + won't help much */ + map->capacity = capacity = 3 * capacity / 2; + map->keys = keys = gpr_realloc(keys, capacity * sizeof(gpr_uint32)); + map->values = values = gpr_realloc(values, capacity * sizeof(void *)); + } + } + + keys[count] = key; + values[count] = value; + map->count = count + 1; +} + +void grpc_chttp2_stream_map_move_into(grpc_chttp2_stream_map *src, + grpc_chttp2_stream_map *dst) { + /* if src is empty we dont need to do anything */ + if (src->count == src->free) { + return; + } + /* if dst is empty we simply need to swap */ + if (dst->count == dst->free) { + GPR_SWAP(grpc_chttp2_stream_map, *src, *dst); + return; + } + /* the first element of src must be greater than the last of dst... + * however the maps may need compacting for this property to hold */ + if (src->keys[0] <= dst->keys[dst->count - 1]) { + src->count = compact(src->keys, src->values, src->count); + src->free = 0; + dst->count = compact(dst->keys, dst->values, dst->count); + dst->free = 0; + } + GPR_ASSERT(src->keys[0] > dst->keys[dst->count - 1]); + /* if dst doesn't have capacity, resize */ + if (dst->count + src->count > dst->capacity) { + dst->capacity = GPR_MAX(dst->capacity * 3 / 2, dst->count + src->count); + dst->keys = gpr_realloc(dst->keys, dst->capacity * sizeof(gpr_uint32)); + dst->values = gpr_realloc(dst->values, dst->capacity * sizeof(void *)); + } + memcpy(dst->keys + dst->count, src->keys, src->count * sizeof(gpr_uint32)); + memcpy(dst->values + dst->count, src->values, src->count * sizeof(void *)); + dst->count += src->count; + dst->free += src->free; + src->count = 0; + src->free = 0; +} + +static void **find(grpc_chttp2_stream_map *map, gpr_uint32 key) { + size_t min_idx = 0; + size_t max_idx = map->count; + size_t mid_idx; + gpr_uint32 *keys = map->keys; + void **values = map->values; + gpr_uint32 mid_key; + + if (max_idx == 0) return NULL; + + while (min_idx < max_idx) { + /* find the midpoint, avoiding overflow */ + mid_idx = min_idx + ((max_idx - min_idx) / 2); + mid_key = keys[mid_idx]; + + if (mid_key < key) { + min_idx = mid_idx + 1; + } else if (mid_key > key) { + max_idx = mid_idx; + } else /* mid_key == key */ { + return &values[mid_idx]; + } + } + + return NULL; +} + +void *grpc_chttp2_stream_map_delete(grpc_chttp2_stream_map *map, + gpr_uint32 key) { + void **pvalue = find(map, key); + void *out = NULL; + if (pvalue != NULL) { + out = *pvalue; + *pvalue = NULL; + map->free += (out != NULL); + /* recognize complete emptyness and ensure we can skip + * defragmentation later */ + if (map->free == map->count) { + map->free = map->count = 0; + } + } + return out; +} + +void *grpc_chttp2_stream_map_find(grpc_chttp2_stream_map *map, gpr_uint32 key) { + void **pvalue = find(map, key); + return pvalue != NULL ? *pvalue : NULL; +} + +size_t grpc_chttp2_stream_map_size(grpc_chttp2_stream_map *map) { + return map->count - map->free; +} + +void grpc_chttp2_stream_map_for_each(grpc_chttp2_stream_map *map, + void (*f)(void *user_data, gpr_uint32 key, + void *value), + void *user_data) { + size_t i; + + for (i = 0; i < map->count; i++) { + if (map->values[i]) { + f(user_data, map->keys[i], map->values[i]); + } + } +} diff --git a/src/core/transport/chttp2/stream_map.h b/src/core/transport/chttp2/stream_map.h new file mode 100644 index 00000000..71b05820 --- /dev/null +++ b/src/core/transport/chttp2/stream_map.h @@ -0,0 +1,85 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_TRANSPORT_CHTTP2_STREAM_MAP_H +#define GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_STREAM_MAP_H + +#include + +#include + +/* Data structure to map a gpr_uint32 to a data object (represented by a void*) + + Represented as a sorted array of keys, and a corresponding array of values. + Lookups are performed with binary search. + Adds are restricted to strictly higher keys than previously seen (this is + guaranteed by http2). */ +typedef struct { + gpr_uint32 *keys; + void **values; + size_t count; + size_t free; + size_t capacity; +} grpc_chttp2_stream_map; + +void grpc_chttp2_stream_map_init(grpc_chttp2_stream_map *map, + size_t initial_capacity); +void grpc_chttp2_stream_map_destroy(grpc_chttp2_stream_map *map); + +/* Add a new key: given http2 semantics, new keys must always be greater than + existing keys - this is asserted */ +void grpc_chttp2_stream_map_add(grpc_chttp2_stream_map *map, gpr_uint32 key, + void *value); + +/* Delete an existing key - returns the previous value of the key if it existed, + or NULL otherwise */ +void *grpc_chttp2_stream_map_delete(grpc_chttp2_stream_map *map, + gpr_uint32 key); + +/* Move all elements of src into dst */ +void grpc_chttp2_stream_map_move_into(grpc_chttp2_stream_map *src, + grpc_chttp2_stream_map *dst); + +/* Return an existing key, or NULL if it does not exist */ +void *grpc_chttp2_stream_map_find(grpc_chttp2_stream_map *map, gpr_uint32 key); + +/* How many (populated) entries are in the stream map? */ +size_t grpc_chttp2_stream_map_size(grpc_chttp2_stream_map *map); + +/* Callback on each stream */ +void grpc_chttp2_stream_map_for_each(grpc_chttp2_stream_map *map, + void (*f)(void *user_data, gpr_uint32 key, + void *value), + void *user_data); + +#endif /* GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_STREAM_MAP_H */ diff --git a/src/core/transport/chttp2/timeout_encoding.c b/src/core/transport/chttp2/timeout_encoding.c new file mode 100644 index 00000000..1dd768ad --- /dev/null +++ b/src/core/transport/chttp2/timeout_encoding.c @@ -0,0 +1,184 @@ +/* + * + * Copyright 2015, 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 "src/core/transport/chttp2/timeout_encoding.h" + +#include +#include + +#include "src/core/support/string.h" + +static int round_up(int x, int divisor) { + return (x / divisor + (x % divisor != 0)) * divisor; +} + +/* round an integer up to the next value with three significant figures */ +static int round_up_to_three_sig_figs(int x) { + if (x < 1000) return x; + if (x < 10000) return round_up(x, 10); + if (x < 100000) return round_up(x, 100); + if (x < 1000000) return round_up(x, 1000); + if (x < 10000000) return round_up(x, 10000); + if (x < 100000000) return round_up(x, 100000); + if (x < 1000000000) return round_up(x, 1000000); + return round_up(x, 10000000); +} + +/* encode our minimum viable timeout value */ +static void enc_tiny(char *buffer) { memcpy(buffer, "1n", 3); } + +static void enc_ext(char *buffer, long value, char ext) { + int n = gpr_ltoa(value, buffer); + buffer[n] = ext; + buffer[n + 1] = 0; +} + +static void enc_seconds(char *buffer, long sec) { + if (sec % 3600 == 0) { + enc_ext(buffer, sec / 3600, 'H'); + } else if (sec % 60 == 0) { + enc_ext(buffer, sec / 60, 'M'); + } else { + enc_ext(buffer, sec, 'S'); + } +} + +static void enc_nanos(char *buffer, int x) { + x = round_up_to_three_sig_figs(x); + if (x < 100000) { + if (x % 1000 == 0) { + enc_ext(buffer, x / 1000, 'u'); + } else { + enc_ext(buffer, x, 'n'); + } + } else if (x < 100000000) { + if (x % 1000000 == 0) { + enc_ext(buffer, x / 1000000, 'm'); + } else { + enc_ext(buffer, x / 1000, 'u'); + } + } else if (x < 1000000000) { + enc_ext(buffer, x / 1000000, 'm'); + } else { + /* note that this is only ever called with times of less than one second, + so if we reach here the time must have been rounded up to a whole second + (and no more) */ + memcpy(buffer, "1S", 3); + } +} + +static void enc_micros(char *buffer, int x) { + x = round_up_to_three_sig_figs(x); + if (x < 100000) { + if (x % 1000 == 0) { + enc_ext(buffer, x / 1000, 'm'); + } else { + enc_ext(buffer, x, 'u'); + } + } else if (x < 100000000) { + if (x % 1000000 == 0) { + enc_ext(buffer, x / 1000000, 'S'); + } else { + enc_ext(buffer, x / 1000, 'm'); + } + } else { + enc_ext(buffer, x / 1000000, 'S'); + } +} + +void grpc_chttp2_encode_timeout(gpr_timespec timeout, char *buffer) { + if (timeout.tv_sec < 0) { + enc_tiny(buffer); + } else if (timeout.tv_sec == 0) { + enc_nanos(buffer, timeout.tv_nsec); + } else if (timeout.tv_sec < 1000 && timeout.tv_nsec != 0) { + enc_micros(buffer, + timeout.tv_sec * 1000000 + + (timeout.tv_nsec / 1000 + (timeout.tv_nsec % 1000 != 0))); + } else { + enc_seconds(buffer, timeout.tv_sec + (timeout.tv_nsec != 0)); + } +} + +static int is_all_whitespace(const char *p) { + while (*p == ' ') p++; + return *p == 0; +} + +int grpc_chttp2_decode_timeout(const char *buffer, gpr_timespec *timeout) { + gpr_uint32 x = 0; + const char *p = buffer; + int have_digit = 0; + /* skip whitespace */ + for (; *p == ' '; p++) + ; + /* decode numeric part */ + for (; *p >= '0' && *p <= '9'; p++) { + gpr_uint32 xp = x * 10 + *p - '0'; + have_digit = 1; + if (xp < x) { + *timeout = gpr_inf_future(GPR_CLOCK_REALTIME); + return 1; + } + x = xp; + } + if (!have_digit) return 0; + /* skip whitespace */ + for (; *p == ' '; p++) + ; + /* decode unit specifier */ + switch (*p) { + case 'n': + *timeout = gpr_time_from_nanos(x, GPR_TIMESPAN); + break; + case 'u': + *timeout = gpr_time_from_micros(x, GPR_TIMESPAN); + break; + case 'm': + *timeout = gpr_time_from_millis(x, GPR_TIMESPAN); + break; + case 'S': + *timeout = gpr_time_from_seconds(x, GPR_TIMESPAN); + break; + case 'M': + *timeout = gpr_time_from_minutes(x, GPR_TIMESPAN); + break; + case 'H': + *timeout = gpr_time_from_hours(x, GPR_TIMESPAN); + break; + default: + return 0; + } + p++; + return is_all_whitespace(p); +} diff --git a/src/core/transport/chttp2/timeout_encoding.h b/src/core/transport/chttp2/timeout_encoding.h new file mode 100644 index 00000000..9d8756e7 --- /dev/null +++ b/src/core/transport/chttp2/timeout_encoding.h @@ -0,0 +1,47 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_TRANSPORT_CHTTP2_TIMEOUT_ENCODING_H +#define GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_TIMEOUT_ENCODING_H + +#include "src/core/support/string.h" +#include + +#define GRPC_CHTTP2_TIMEOUT_ENCODE_MIN_BUFSIZE (GPR_LTOA_MIN_BUFSIZE + 1) + +/* Encode/decode timeouts to the GRPC over HTTP2 format; + encoding may round up arbitrarily */ +void grpc_chttp2_encode_timeout(gpr_timespec timeout, char *buffer); +int grpc_chttp2_decode_timeout(const char *buffer, gpr_timespec *timeout); + +#endif /* GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_TIMEOUT_ENCODING_H */ diff --git a/src/core/transport/chttp2/varint.c b/src/core/transport/chttp2/varint.c new file mode 100644 index 00000000..0722c9ad --- /dev/null +++ b/src/core/transport/chttp2/varint.c @@ -0,0 +1,65 @@ +/* + * + * Copyright 2015, 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 "src/core/transport/chttp2/varint.h" + +int grpc_chttp2_hpack_varint_length(gpr_uint32 tail_value) { + if (tail_value < (1 << 7)) { + return 2; + } else if (tail_value < (1 << 14)) { + return 3; + } else if (tail_value < (1 << 21)) { + return 4; + } else if (tail_value < (1 << 28)) { + return 5; + } else { + return 6; + } +} + +void grpc_chttp2_hpack_write_varint_tail(gpr_uint32 tail_value, + gpr_uint8* target, int tail_length) { + switch (tail_length) { + case 5: + target[4] = (gpr_uint8)((tail_value >> 28) | 0x80); + case 4: + target[3] = (gpr_uint8)((tail_value >> 21) | 0x80); + case 3: + target[2] = (gpr_uint8)((tail_value >> 14) | 0x80); + case 2: + target[1] = (gpr_uint8)((tail_value >> 7) | 0x80); + case 1: + target[0] = (gpr_uint8)((tail_value) | 0x80); + } + target[tail_length - 1] &= 0x7f; +} diff --git a/src/core/transport/chttp2/varint.h b/src/core/transport/chttp2/varint.h new file mode 100644 index 00000000..0a6fb552 --- /dev/null +++ b/src/core/transport/chttp2/varint.h @@ -0,0 +1,73 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_TRANSPORT_CHTTP2_VARINT_H +#define GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_VARINT_H + +#include + +/* Helpers for hpack varint encoding */ + +/* length of a value that needs varint tail encoding (it's bigger than can be + bitpacked into the opcode byte) - returned value includes the length of the + opcode byte */ +int grpc_chttp2_hpack_varint_length(gpr_uint32 tail_value); + +void grpc_chttp2_hpack_write_varint_tail(gpr_uint32 tail_value, + gpr_uint8* target, int tail_length); + +/* maximum value that can be bitpacked with the opcode if the opcode has a + prefix + of length prefix_bits */ +#define GRPC_CHTTP2_MAX_IN_PREFIX(prefix_bits) ((1 << (8 - (prefix_bits))) - 1) + +/* length required to bitpack a value */ +#define GRPC_CHTTP2_VARINT_LENGTH(n, prefix_bits) \ + ((n) < GRPC_CHTTP2_MAX_IN_PREFIX(prefix_bits) \ + ? 1 \ + : grpc_chttp2_hpack_varint_length( \ + (n)-GRPC_CHTTP2_MAX_IN_PREFIX(prefix_bits))) + +#define GRPC_CHTTP2_WRITE_VARINT(n, prefix_bits, prefix_or, target, length) \ + do { \ + gpr_uint8* tgt = target; \ + if ((length) == 1) { \ + (tgt)[0] = (prefix_or) | (n); \ + } else { \ + (tgt)[0] = (prefix_or) | GRPC_CHTTP2_MAX_IN_PREFIX(prefix_bits); \ + grpc_chttp2_hpack_write_varint_tail( \ + (n)-GRPC_CHTTP2_MAX_IN_PREFIX(prefix_bits), (tgt) + 1, (length)-1); \ + } \ + } while (0) + +#endif /* GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_VARINT_H */ diff --git a/src/core/transport/chttp2/writing.c b/src/core/transport/chttp2/writing.c new file mode 100644 index 00000000..ac79044e --- /dev/null +++ b/src/core/transport/chttp2/writing.c @@ -0,0 +1,246 @@ +/* + * + * Copyright 2015, 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 "src/core/transport/chttp2/internal.h" +#include "src/core/transport/chttp2/http2_errors.h" + +#include + +static void finalize_outbuf(grpc_chttp2_transport_writing *transport_writing); + +int grpc_chttp2_unlocking_check_writes( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_transport_writing *transport_writing) { + grpc_chttp2_stream_global *stream_global; + grpc_chttp2_stream_writing *stream_writing; + grpc_chttp2_stream_global *first_reinserted_stream = NULL; + gpr_uint32 window_delta; + + /* simple writes are queued to qbuf, and flushed here */ + gpr_slice_buffer_swap(&transport_global->qbuf, &transport_writing->outbuf); + GPR_ASSERT(transport_global->qbuf.count == 0); + + if (transport_global->dirtied_local_settings && + !transport_global->sent_local_settings) { + gpr_slice_buffer_add( + &transport_writing->outbuf, + grpc_chttp2_settings_create( + transport_global->settings[GRPC_SENT_SETTINGS], + transport_global->settings[GRPC_LOCAL_SETTINGS], + transport_global->force_send_settings, GRPC_CHTTP2_NUM_SETTINGS)); + transport_global->force_send_settings = 0; + transport_global->dirtied_local_settings = 0; + transport_global->sent_local_settings = 1; + } + + /* for each grpc_chttp2_stream that's become writable, frame it's data + (according to available window sizes) and add to the output buffer */ + while (grpc_chttp2_list_pop_writable_stream( + transport_global, transport_writing, &stream_global, &stream_writing)) { + if (stream_global == first_reinserted_stream) { + /* prevent infinite loop */ + grpc_chttp2_list_add_first_writable_stream(transport_global, + stream_global); + break; + } + + stream_writing->id = stream_global->id; + stream_writing->send_closed = GRPC_DONT_SEND_CLOSED; + + if (stream_global->outgoing_sopb) { + window_delta = + grpc_chttp2_preencode(stream_global->outgoing_sopb->ops, + &stream_global->outgoing_sopb->nops, + GPR_MIN(transport_global->outgoing_window, + stream_global->outgoing_window), + &stream_writing->sopb); + GRPC_CHTTP2_FLOWCTL_TRACE_TRANSPORT( + "write", transport_global, outgoing_window, -(gpr_int64)window_delta); + GRPC_CHTTP2_FLOWCTL_TRACE_STREAM("write", transport_global, stream_global, + outgoing_window, + -(gpr_int64)window_delta); + transport_global->outgoing_window -= window_delta; + stream_global->outgoing_window -= window_delta; + + if (stream_global->write_state == GRPC_WRITE_STATE_QUEUED_CLOSE && + stream_global->outgoing_sopb->nops == 0) { + if (!transport_global->is_client && !stream_global->read_closed) { + stream_writing->send_closed = GRPC_SEND_CLOSED_WITH_RST_STREAM; + } else { + stream_writing->send_closed = GRPC_SEND_CLOSED; + } + } + + if (stream_global->outgoing_window > 0 && + stream_global->outgoing_sopb->nops != 0) { + grpc_chttp2_list_add_writable_stream(transport_global, stream_global); + if (first_reinserted_stream == NULL && + transport_global->outgoing_window == 0) { + first_reinserted_stream = stream_global; + } + } + } + + if (!stream_global->read_closed && + stream_global->unannounced_incoming_window > 0) { + GPR_ASSERT(stream_writing->announce_window == 0); + GRPC_CHTTP2_FLOWCTL_TRACE_STREAM( + "write", transport_writing, stream_writing, announce_window, + stream_global->unannounced_incoming_window); + stream_writing->announce_window = + stream_global->unannounced_incoming_window; + GRPC_CHTTP2_FLOWCTL_TRACE_STREAM( + "write", transport_global, stream_global, incoming_window, + stream_global->unannounced_incoming_window); + GRPC_CHTTP2_FLOWCTL_TRACE_STREAM( + "write", transport_global, stream_global, unannounced_incoming_window, + -(gpr_int64)stream_global->unannounced_incoming_window); + stream_global->incoming_window += + stream_global->unannounced_incoming_window; + stream_global->unannounced_incoming_window = 0; + grpc_chttp2_list_add_incoming_window_updated(transport_global, + stream_global); + stream_global->writing_now |= GRPC_CHTTP2_WRITING_WINDOW; + } + if (stream_writing->sopb.nops > 0 || + stream_writing->send_closed != GRPC_DONT_SEND_CLOSED) { + stream_global->writing_now |= GRPC_CHTTP2_WRITING_DATA; + } + if (stream_global->writing_now != 0) { + grpc_chttp2_list_add_writing_stream(transport_writing, stream_writing); + } + } + + /* if the grpc_chttp2_transport is ready to send a window update, do so here + also; 3/4 is a magic number that will likely get tuned soon */ + if (transport_global->incoming_window < + transport_global->connection_window_target * 3 / 4) { + window_delta = transport_global->connection_window_target - + transport_global->incoming_window; + gpr_slice_buffer_add(&transport_writing->outbuf, + grpc_chttp2_window_update_create(0, window_delta)); + GRPC_CHTTP2_FLOWCTL_TRACE_TRANSPORT("write", transport_global, + incoming_window, window_delta); + transport_global->incoming_window += window_delta; + } + + return transport_writing->outbuf.count > 0 || + grpc_chttp2_list_have_writing_streams(transport_writing); +} + +void grpc_chttp2_perform_writes( + grpc_chttp2_transport_writing *transport_writing, grpc_endpoint *endpoint) { + GPR_ASSERT(transport_writing->outbuf.count > 0 || + grpc_chttp2_list_have_writing_streams(transport_writing)); + + finalize_outbuf(transport_writing); + + GPR_ASSERT(transport_writing->outbuf.count > 0); + GPR_ASSERT(endpoint); + + switch (grpc_endpoint_write(endpoint, &transport_writing->outbuf, + &transport_writing->done_cb)) { + case GRPC_ENDPOINT_DONE: + grpc_chttp2_terminate_writing(transport_writing, 1); + break; + case GRPC_ENDPOINT_ERROR: + grpc_chttp2_terminate_writing(transport_writing, 0); + break; + case GRPC_ENDPOINT_PENDING: + break; + } +} + +static void finalize_outbuf(grpc_chttp2_transport_writing *transport_writing) { + grpc_chttp2_stream_writing *stream_writing; + + while ( + grpc_chttp2_list_pop_writing_stream(transport_writing, &stream_writing)) { + if (stream_writing->sopb.nops > 0 || + stream_writing->send_closed != GRPC_DONT_SEND_CLOSED) { + grpc_chttp2_encode(stream_writing->sopb.ops, stream_writing->sopb.nops, + stream_writing->send_closed != GRPC_DONT_SEND_CLOSED, + stream_writing->id, + &transport_writing->hpack_compressor, + &transport_writing->outbuf); + stream_writing->sopb.nops = 0; + } + if (stream_writing->announce_window > 0) { + gpr_slice_buffer_add( + &transport_writing->outbuf, + grpc_chttp2_window_update_create(stream_writing->id, + stream_writing->announce_window)); + GRPC_CHTTP2_FLOWCTL_TRACE_STREAM( + "write", transport_writing, stream_writing, announce_window, + -(gpr_int64)stream_writing->announce_window); + stream_writing->announce_window = 0; + } + if (stream_writing->send_closed == GRPC_SEND_CLOSED_WITH_RST_STREAM) { + gpr_slice_buffer_add(&transport_writing->outbuf, + grpc_chttp2_rst_stream_create(stream_writing->id, + GRPC_CHTTP2_NO_ERROR)); + } + grpc_chttp2_list_add_written_stream(transport_writing, stream_writing); + } +} + +void grpc_chttp2_cleanup_writing( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_transport_writing *transport_writing) { + grpc_chttp2_stream_writing *stream_writing; + grpc_chttp2_stream_global *stream_global; + + while (grpc_chttp2_list_pop_written_stream( + transport_global, transport_writing, &stream_global, &stream_writing)) { + GPR_ASSERT(stream_global->writing_now != 0); + if (stream_writing->send_closed != GRPC_DONT_SEND_CLOSED) { + stream_global->write_state = GRPC_WRITE_STATE_SENT_CLOSE; + if (!transport_global->is_client) { + stream_global->read_closed = 1; + } + } + if (stream_global->writing_now & GRPC_CHTTP2_WRITING_DATA) { + if (stream_global->outgoing_sopb != NULL && + stream_global->outgoing_sopb->nops == 0) { + GPR_ASSERT(stream_global->write_state != GRPC_WRITE_STATE_QUEUED_CLOSE); + stream_global->outgoing_sopb = NULL; + grpc_chttp2_schedule_closure(transport_global, + stream_global->send_done_closure, 1); + } + } + stream_global->writing_now = 0; + grpc_chttp2_list_add_read_write_state_changed(transport_global, + stream_global); + } + gpr_slice_buffer_reset_and_unref(&transport_writing->outbuf); +} diff --git a/src/core/transport/chttp2_transport.c b/src/core/transport/chttp2_transport.c new file mode 100644 index 00000000..9e3d7dd5 --- /dev/null +++ b/src/core/transport/chttp2_transport.c @@ -0,0 +1,1290 @@ +/* + * + * Copyright 2015, 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 "src/core/transport/chttp2_transport.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "src/core/profiling/timers.h" +#include "src/core/support/string.h" +#include "src/core/transport/chttp2/http2_errors.h" +#include "src/core/transport/chttp2/internal.h" +#include "src/core/transport/chttp2/status_conversion.h" +#include "src/core/transport/chttp2/timeout_encoding.h" +#include "src/core/transport/transport_impl.h" + +#define DEFAULT_WINDOW 65535 +#define DEFAULT_CONNECTION_WINDOW_TARGET (1024 * 1024) +#define MAX_WINDOW 0x7fffffffu + +#define MAX_CLIENT_STREAM_ID 0x7fffffffu + +int grpc_http_trace = 0; +int grpc_flowctl_trace = 0; + +#define TRANSPORT_FROM_WRITING(tw) \ + ((grpc_chttp2_transport *)((char *)(tw)-offsetof(grpc_chttp2_transport, \ + writing))) + +#define TRANSPORT_FROM_PARSING(tw) \ + ((grpc_chttp2_transport *)((char *)(tw)-offsetof(grpc_chttp2_transport, \ + parsing))) + +#define TRANSPORT_FROM_GLOBAL(tg) \ + ((grpc_chttp2_transport *)((char *)(tg)-offsetof(grpc_chttp2_transport, \ + global))) + +#define STREAM_FROM_GLOBAL(sg) \ + ((grpc_chttp2_stream *)((char *)(sg)-offsetof(grpc_chttp2_stream, global))) + +static const grpc_transport_vtable vtable; + +static void lock(grpc_chttp2_transport *t); +static void unlock(grpc_chttp2_transport *t); + +static void unlock_check_read_write_state(grpc_chttp2_transport *t); + +/* forward declarations of various callbacks that we'll build closures around */ +static void writing_action(void *t, int iomgr_success_ignored); + +/** Set a transport level setting, and push it to our peer */ +static void push_setting(grpc_chttp2_transport *t, grpc_chttp2_setting_id id, + gpr_uint32 value); + +/** Endpoint callback to process incoming data */ +static void recv_data(void *tp, int success); + +/** Start disconnection chain */ +static void drop_connection(grpc_chttp2_transport *t); + +/** Perform a transport_op */ +static void perform_stream_op_locked( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global *stream_global, grpc_transport_stream_op *op); + +/** Cancel a stream: coming from the transport API */ +static void cancel_from_api(grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global *stream_global, + grpc_status_code status); + +static void close_from_api(grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global *stream_global, + grpc_status_code status, + gpr_slice *optional_message); + +/** Add endpoint from this transport to pollset */ +static void add_to_pollset_locked(grpc_chttp2_transport *t, + grpc_pollset *pollset); +static void add_to_pollset_set_locked(grpc_chttp2_transport *t, + grpc_pollset_set *pollset_set); + +/** Start new streams that have been created if we can */ +static void maybe_start_some_streams( + grpc_chttp2_transport_global *transport_global); + +static void connectivity_state_set( + grpc_chttp2_transport_global *transport_global, + grpc_connectivity_state state, const char *reason); + +/* + * CONSTRUCTION/DESTRUCTION/REFCOUNTING + */ + +static void destruct_transport(grpc_chttp2_transport *t) { + size_t i; + + gpr_mu_lock(&t->mu); + + GPR_ASSERT(t->ep == NULL); + + gpr_slice_buffer_destroy(&t->global.qbuf); + + gpr_slice_buffer_destroy(&t->writing.outbuf); + grpc_chttp2_hpack_compressor_destroy(&t->writing.hpack_compressor); + + gpr_slice_buffer_destroy(&t->parsing.qbuf); + gpr_slice_buffer_destroy(&t->read_buffer); + grpc_chttp2_hpack_parser_destroy(&t->parsing.hpack_parser); + grpc_chttp2_goaway_parser_destroy(&t->parsing.goaway_parser); + + GRPC_MDSTR_UNREF(t->parsing.str_grpc_timeout); + + for (i = 0; i < STREAM_LIST_COUNT; i++) { + GPR_ASSERT(t->lists[i].head == NULL); + GPR_ASSERT(t->lists[i].tail == NULL); + } + + GPR_ASSERT(grpc_chttp2_stream_map_size(&t->parsing_stream_map) == 0); + GPR_ASSERT(grpc_chttp2_stream_map_size(&t->new_stream_map) == 0); + + grpc_chttp2_stream_map_destroy(&t->parsing_stream_map); + grpc_chttp2_stream_map_destroy(&t->new_stream_map); + grpc_connectivity_state_destroy(&t->channel_callback.state_tracker); + + gpr_mu_unlock(&t->mu); + gpr_mu_destroy(&t->mu); + + /* callback remaining pings: they're not allowed to call into the transpot, + and maybe they hold resources that need to be freed */ + while (t->global.pings.next != &t->global.pings) { + grpc_chttp2_outstanding_ping *ping = t->global.pings.next; + grpc_iomgr_add_delayed_callback(ping->on_recv, 0); + ping->next->prev = ping->prev; + ping->prev->next = ping->next; + gpr_free(ping); + } + + grpc_mdctx_unref(t->metadata_context); + + gpr_free(t->peer_string); + gpr_free(t); +} + +#ifdef REFCOUNTING_DEBUG +#define REF_TRANSPORT(t, r) ref_transport(t, r, __FILE__, __LINE__) +#define UNREF_TRANSPORT(t, r) unref_transport(t, r, __FILE__, __LINE__) +static void unref_transport(grpc_chttp2_transport *t, const char *reason, + const char *file, int line) { + gpr_log(GPR_DEBUG, "chttp2:unref:%p %d->%d %s [%s:%d]", t, t->refs.count, + t->refs.count - 1, reason, file, line); + if (!gpr_unref(&t->refs)) return; + destruct_transport(t); +} + +static void ref_transport(grpc_chttp2_transport *t, const char *reason, + const char *file, int line) { + gpr_log(GPR_DEBUG, "chttp2: ref:%p %d->%d %s [%s:%d]", t, t->refs.count, + t->refs.count + 1, reason, file, line); + gpr_ref(&t->refs); +} +#else +#define REF_TRANSPORT(t, r) ref_transport(t) +#define UNREF_TRANSPORT(t, r) unref_transport(t) +static void unref_transport(grpc_chttp2_transport *t) { + if (!gpr_unref(&t->refs)) return; + destruct_transport(t); +} + +static void ref_transport(grpc_chttp2_transport *t) { gpr_ref(&t->refs); } +#endif + +static void init_transport(grpc_chttp2_transport *t, + const grpc_channel_args *channel_args, + grpc_endpoint *ep, grpc_mdctx *mdctx, + int is_client) { + size_t i; + int j; + + GPR_ASSERT(strlen(GRPC_CHTTP2_CLIENT_CONNECT_STRING) == + GRPC_CHTTP2_CLIENT_CONNECT_STRLEN); + + memset(t, 0, sizeof(*t)); + + t->base.vtable = &vtable; + t->ep = ep; + /* one ref is for destroy, the other for when ep becomes NULL */ + gpr_ref_init(&t->refs, 2); + /* ref is dropped at transport close() */ + gpr_ref_init(&t->shutdown_ep_refs, 1); + gpr_mu_init(&t->mu); + grpc_mdctx_ref(mdctx); + t->peer_string = grpc_endpoint_get_peer(ep); + t->metadata_context = mdctx; + t->endpoint_reading = 1; + t->global.next_stream_id = is_client ? 1 : 2; + t->global.is_client = is_client; + t->global.outgoing_window = DEFAULT_WINDOW; + t->global.incoming_window = DEFAULT_WINDOW; + t->global.connection_window_target = DEFAULT_CONNECTION_WINDOW_TARGET; + t->global.ping_counter = 1; + t->global.pings.next = t->global.pings.prev = &t->global.pings; + t->parsing.is_client = is_client; + t->parsing.str_grpc_timeout = + grpc_mdstr_from_string(t->metadata_context, "grpc-timeout", 0); + t->parsing.deframe_state = + is_client ? GRPC_DTS_FH_0 : GRPC_DTS_CLIENT_PREFIX_0; + t->writing.is_client = is_client; + grpc_connectivity_state_init(&t->channel_callback.state_tracker, + GRPC_CHANNEL_READY, "transport"); + + gpr_slice_buffer_init(&t->global.qbuf); + + gpr_slice_buffer_init(&t->writing.outbuf); + grpc_chttp2_hpack_compressor_init(&t->writing.hpack_compressor, mdctx); + grpc_iomgr_closure_init(&t->writing_action, writing_action, t); + + gpr_slice_buffer_init(&t->parsing.qbuf); + grpc_chttp2_goaway_parser_init(&t->parsing.goaway_parser); + grpc_chttp2_hpack_parser_init(&t->parsing.hpack_parser, t->metadata_context); + + grpc_iomgr_closure_init(&t->writing.done_cb, grpc_chttp2_terminate_writing, + &t->writing); + grpc_iomgr_closure_init(&t->recv_data, recv_data, t); + gpr_slice_buffer_init(&t->read_buffer); + + if (is_client) { + gpr_slice_buffer_add( + &t->global.qbuf, + gpr_slice_from_copied_string(GRPC_CHTTP2_CLIENT_CONNECT_STRING)); + } + /* 8 is a random stab in the dark as to a good initial size: it's small enough + that it shouldn't waste memory for infrequently used connections, yet + large enough that the exponential growth should happen nicely when it's + needed. + TODO(ctiller): tune this */ + grpc_chttp2_stream_map_init(&t->parsing_stream_map, 8); + grpc_chttp2_stream_map_init(&t->new_stream_map, 8); + + /* copy in initial settings to all setting sets */ + for (i = 0; i < GRPC_CHTTP2_NUM_SETTINGS; i++) { + t->parsing.settings[i] = grpc_chttp2_settings_parameters[i].default_value; + for (j = 0; j < GRPC_NUM_SETTING_SETS; j++) { + t->global.settings[j][i] = + grpc_chttp2_settings_parameters[i].default_value; + } + } + t->global.dirtied_local_settings = 1; + /* Hack: it's common for implementations to assume 65536 bytes initial send + window -- this should by rights be 0 */ + t->global.force_send_settings = 1 << GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + t->global.sent_local_settings = 0; + + /* configure http2 the way we like it */ + if (is_client) { + push_setting(t, GRPC_CHTTP2_SETTINGS_ENABLE_PUSH, 0); + push_setting(t, GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 0); + } + push_setting(t, GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, DEFAULT_WINDOW); + + if (channel_args) { + for (i = 0; i < channel_args->num_args; i++) { + if (0 == + strcmp(channel_args->args[i].key, GRPC_ARG_MAX_CONCURRENT_STREAMS)) { + if (is_client) { + gpr_log(GPR_ERROR, "%s: is ignored on the client", + GRPC_ARG_MAX_CONCURRENT_STREAMS); + } else if (channel_args->args[i].type != GRPC_ARG_INTEGER) { + gpr_log(GPR_ERROR, "%s: must be an integer", + GRPC_ARG_MAX_CONCURRENT_STREAMS); + } else { + push_setting(t, GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, + channel_args->args[i].value.integer); + } + } else if (0 == strcmp(channel_args->args[i].key, + GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER)) { + if (channel_args->args[i].type != GRPC_ARG_INTEGER) { + gpr_log(GPR_ERROR, "%s: must be an integer", + GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER); + } else if ((t->global.next_stream_id & 1) != + (channel_args->args[i].value.integer & 1)) { + gpr_log(GPR_ERROR, "%s: low bit must be %d on %s", + GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER, + t->global.next_stream_id & 1, + is_client ? "client" : "server"); + } else { + t->global.next_stream_id = channel_args->args[i].value.integer; + } + } + } + } +} + +static void destroy_transport(grpc_transport *gt) { + grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; + + lock(t); + t->destroying = 1; + drop_connection(t); + unlock(t); + + UNREF_TRANSPORT(t, "destroy"); +} + +/** block grpc_endpoint_shutdown being called until a paired + allow_endpoint_shutdown is made */ +static void prevent_endpoint_shutdown(grpc_chttp2_transport *t) { + GPR_ASSERT(t->ep); + gpr_ref(&t->shutdown_ep_refs); +} + +static void allow_endpoint_shutdown_locked(grpc_chttp2_transport *t) { + if (gpr_unref(&t->shutdown_ep_refs)) { + if (t->ep) { + grpc_endpoint_shutdown(t->ep); + } + } +} + +static void allow_endpoint_shutdown_unlocked(grpc_chttp2_transport *t) { + if (gpr_unref(&t->shutdown_ep_refs)) { + gpr_mu_lock(&t->mu); + if (t->ep) { + grpc_endpoint_shutdown(t->ep); + } + gpr_mu_unlock(&t->mu); + } +} + +static void destroy_endpoint(grpc_chttp2_transport *t) { + grpc_endpoint_destroy(t->ep); + t->ep = NULL; + UNREF_TRANSPORT( + t, "disconnect"); /* safe because we'll still have the ref for write */ +} + +static void close_transport_locked(grpc_chttp2_transport *t) { + if (!t->closed) { + t->closed = 1; + connectivity_state_set(&t->global, GRPC_CHANNEL_FATAL_FAILURE, + "close_transport"); + if (t->ep) { + allow_endpoint_shutdown_locked(t); + } + } +} + +static int init_stream(grpc_transport *gt, grpc_stream *gs, + const void *server_data, + grpc_transport_stream_op *initial_op) { + grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; + grpc_chttp2_stream *s = (grpc_chttp2_stream *)gs; + + memset(s, 0, sizeof(*s)); + + grpc_chttp2_incoming_metadata_buffer_init(&s->parsing.incoming_metadata); + grpc_chttp2_incoming_metadata_buffer_init(&s->global.incoming_metadata); + grpc_sopb_init(&s->writing.sopb); + grpc_sopb_init(&s->global.incoming_sopb); + grpc_chttp2_data_parser_init(&s->parsing.data_parser); + + REF_TRANSPORT(t, "stream"); + + lock(t); + grpc_chttp2_register_stream(t, s); + if (server_data) { + GPR_ASSERT(t->parsing_active); + s->global.id = (gpr_uint32)(gpr_uintptr)server_data; + s->global.outgoing_window = + t->global.settings[GRPC_PEER_SETTINGS] + [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]; + s->global.max_recv_bytes = s->parsing.incoming_window = + s->global.incoming_window = + t->global.settings[GRPC_SENT_SETTINGS] + [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]; + *t->accepting_stream = s; + grpc_chttp2_stream_map_add(&t->parsing_stream_map, s->global.id, s); + s->global.in_stream_map = 1; + } + + if (initial_op) perform_stream_op_locked(&t->global, &s->global, initial_op); + unlock(t); + + return 0; +} + +static void destroy_stream(grpc_transport *gt, grpc_stream *gs) { + grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; + grpc_chttp2_stream *s = (grpc_chttp2_stream *)gs; + int i; + + gpr_mu_lock(&t->mu); + + GPR_ASSERT(s->global.published_state == GRPC_STREAM_CLOSED || + s->global.id == 0); + GPR_ASSERT(!s->global.in_stream_map); + if (grpc_chttp2_unregister_stream(t, s) && t->global.sent_goaway) { + close_transport_locked(t); + } + if (!t->parsing_active && s->global.id) { + GPR_ASSERT(grpc_chttp2_stream_map_find(&t->parsing_stream_map, + s->global.id) == NULL); + } + + grpc_chttp2_list_remove_incoming_window_updated(&t->global, &s->global); + grpc_chttp2_list_remove_writable_stream(&t->global, &s->global); + + gpr_mu_unlock(&t->mu); + + for (i = 0; i < STREAM_LIST_COUNT; i++) { + if (s->included[i]) { + gpr_log(GPR_ERROR, "%s stream %d still included in list %d", + t->global.is_client ? "client" : "server", s->global.id, i); + abort(); + } + } + + GPR_ASSERT(s->global.outgoing_sopb == NULL); + GPR_ASSERT(s->global.publish_sopb == NULL); + grpc_sopb_destroy(&s->writing.sopb); + grpc_sopb_destroy(&s->global.incoming_sopb); + grpc_chttp2_data_parser_destroy(&s->parsing.data_parser); + grpc_chttp2_incoming_metadata_buffer_destroy(&s->parsing.incoming_metadata); + grpc_chttp2_incoming_metadata_buffer_destroy(&s->global.incoming_metadata); + grpc_chttp2_incoming_metadata_live_op_buffer_end( + &s->global.outstanding_metadata); + + UNREF_TRANSPORT(t, "stream"); +} + +grpc_chttp2_stream_parsing *grpc_chttp2_parsing_lookup_stream( + grpc_chttp2_transport_parsing *transport_parsing, gpr_uint32 id) { + grpc_chttp2_transport *t = TRANSPORT_FROM_PARSING(transport_parsing); + grpc_chttp2_stream *s = + grpc_chttp2_stream_map_find(&t->parsing_stream_map, id); + return s ? &s->parsing : NULL; +} + +grpc_chttp2_stream_parsing *grpc_chttp2_parsing_accept_stream( + grpc_chttp2_transport_parsing *transport_parsing, gpr_uint32 id) { + grpc_chttp2_stream *accepting; + grpc_chttp2_transport *t = TRANSPORT_FROM_PARSING(transport_parsing); + GPR_ASSERT(t->accepting_stream == NULL); + t->accepting_stream = &accepting; + t->channel_callback.accept_stream(t->channel_callback.accept_stream_user_data, + &t->base, (void *)(gpr_uintptr)id); + t->accepting_stream = NULL; + return &accepting->parsing; +} + +/* + * LOCK MANAGEMENT + */ + +/* We take a grpc_chttp2_transport-global lock in response to calls coming in + from above, + and in response to data being received from below. New data to be written + is always queued, as are callbacks to process data. During unlock() we + check our todo lists and initiate callbacks and flush writes. */ + +static void lock(grpc_chttp2_transport *t) { gpr_mu_lock(&t->mu); } + +static void unlock(grpc_chttp2_transport *t) { + grpc_iomgr_closure *run_closures; + + unlock_check_read_write_state(t); + if (!t->writing_active && !t->closed && + grpc_chttp2_unlocking_check_writes(&t->global, &t->writing)) { + t->writing_active = 1; + REF_TRANSPORT(t, "writing"); + grpc_chttp2_schedule_closure(&t->global, &t->writing_action, 1); + prevent_endpoint_shutdown(t); + } + + run_closures = t->global.pending_closures_head; + t->global.pending_closures_head = NULL; + t->global.pending_closures_tail = NULL; + + gpr_mu_unlock(&t->mu); + + while (run_closures) { + grpc_iomgr_closure *next = run_closures->next; + run_closures->cb(run_closures->cb_arg, run_closures->success); + run_closures = next; + } +} + +/* + * OUTPUT PROCESSING + */ + +static void push_setting(grpc_chttp2_transport *t, grpc_chttp2_setting_id id, + gpr_uint32 value) { + const grpc_chttp2_setting_parameters *sp = + &grpc_chttp2_settings_parameters[id]; + gpr_uint32 use_value = GPR_CLAMP(value, sp->min_value, sp->max_value); + if (use_value != value) { + gpr_log(GPR_INFO, "Requested parameter %s clamped from %d to %d", sp->name, + value, use_value); + } + if (use_value != t->global.settings[GRPC_LOCAL_SETTINGS][id]) { + t->global.settings[GRPC_LOCAL_SETTINGS][id] = use_value; + t->global.dirtied_local_settings = 1; + } +} + +void grpc_chttp2_terminate_writing(void *transport_writing_ptr, int success) { + grpc_chttp2_transport_writing *transport_writing = transport_writing_ptr; + grpc_chttp2_transport *t = TRANSPORT_FROM_WRITING(transport_writing); + + lock(t); + + allow_endpoint_shutdown_locked(t); + + if (!success) { + drop_connection(t); + } + + /* cleanup writing related jazz */ + grpc_chttp2_cleanup_writing(&t->global, &t->writing); + + /* leave the writing flag up on shutdown to prevent further writes in unlock() + from starting */ + t->writing_active = 0; + if (t->ep && !t->endpoint_reading) { + destroy_endpoint(t); + } + + unlock(t); + + UNREF_TRANSPORT(t, "writing"); +} + +static void writing_action(void *gt, int iomgr_success_ignored) { + grpc_chttp2_transport *t = gt; + grpc_chttp2_perform_writes(&t->writing, t->ep); +} + +void grpc_chttp2_add_incoming_goaway( + grpc_chttp2_transport_global *transport_global, gpr_uint32 goaway_error, + gpr_slice goaway_text) { + char *msg = gpr_dump_slice(goaway_text, GPR_DUMP_HEX | GPR_DUMP_ASCII); + gpr_log(GPR_DEBUG, "got goaway [%d]: %s", goaway_error, msg); + gpr_free(msg); + gpr_slice_unref(goaway_text); + transport_global->seen_goaway = 1; + connectivity_state_set(transport_global, GRPC_CHANNEL_FATAL_FAILURE, + "got_goaway"); +} + +static void maybe_start_some_streams( + grpc_chttp2_transport_global *transport_global) { + grpc_chttp2_stream_global *stream_global; + /* start streams where we have free grpc_chttp2_stream ids and free + * concurrency */ + while (transport_global->next_stream_id <= MAX_CLIENT_STREAM_ID && + transport_global->concurrent_stream_count < + transport_global + ->settings[GRPC_PEER_SETTINGS] + [GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS] && + grpc_chttp2_list_pop_waiting_for_concurrency(transport_global, + &stream_global)) { + GRPC_CHTTP2_IF_TRACING(gpr_log( + GPR_DEBUG, "HTTP:%s: Allocating new grpc_chttp2_stream %p to id %d", + transport_global->is_client ? "CLI" : "SVR", stream_global, + transport_global->next_stream_id)); + + GPR_ASSERT(stream_global->id == 0); + stream_global->id = transport_global->next_stream_id; + transport_global->next_stream_id += 2; + + if (transport_global->next_stream_id >= MAX_CLIENT_STREAM_ID) { + connectivity_state_set(transport_global, GRPC_CHANNEL_TRANSIENT_FAILURE, + "no_more_stream_ids"); + } + + stream_global->outgoing_window = + transport_global->settings[GRPC_PEER_SETTINGS] + [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]; + stream_global->incoming_window = + transport_global->settings[GRPC_SENT_SETTINGS] + [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]; + stream_global->max_recv_bytes = + GPR_MAX(stream_global->incoming_window, stream_global->max_recv_bytes); + grpc_chttp2_stream_map_add( + &TRANSPORT_FROM_GLOBAL(transport_global)->new_stream_map, + stream_global->id, STREAM_FROM_GLOBAL(stream_global)); + stream_global->in_stream_map = 1; + transport_global->concurrent_stream_count++; + grpc_chttp2_list_add_incoming_window_updated(transport_global, + stream_global); + grpc_chttp2_list_add_writable_stream(transport_global, stream_global); + } + /* cancel out streams that will never be started */ + while (transport_global->next_stream_id >= MAX_CLIENT_STREAM_ID && + grpc_chttp2_list_pop_waiting_for_concurrency(transport_global, + &stream_global)) { + cancel_from_api(transport_global, stream_global, GRPC_STATUS_UNAVAILABLE); + } +} + +static void perform_stream_op_locked( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global *stream_global, grpc_transport_stream_op *op) { + if (op->cancel_with_status != GRPC_STATUS_OK) { + cancel_from_api(transport_global, stream_global, op->cancel_with_status); + } + + if (op->close_with_status != GRPC_STATUS_OK) { + close_from_api(transport_global, stream_global, op->close_with_status, + op->optional_close_message); + } + + if (op->send_ops) { + GPR_ASSERT(stream_global->outgoing_sopb == NULL); + stream_global->send_done_closure = op->on_done_send; + if (!stream_global->cancelled) { + stream_global->written_anything = 1; + stream_global->outgoing_sopb = op->send_ops; + if (op->is_last_send && + stream_global->write_state == GRPC_WRITE_STATE_OPEN) { + stream_global->write_state = GRPC_WRITE_STATE_QUEUED_CLOSE; + } + if (stream_global->id == 0) { + GRPC_CHTTP2_IF_TRACING(gpr_log( + GPR_DEBUG, + "HTTP:%s: New grpc_chttp2_stream %p waiting for concurrency", + transport_global->is_client ? "CLI" : "SVR", stream_global)); + grpc_chttp2_list_add_waiting_for_concurrency(transport_global, + stream_global); + maybe_start_some_streams(transport_global); + } else if (stream_global->outgoing_window > 0) { + grpc_chttp2_list_add_writable_stream(transport_global, stream_global); + } + } else { + grpc_sopb_reset(op->send_ops); + grpc_chttp2_schedule_closure(transport_global, + stream_global->send_done_closure, 0); + } + } + + if (op->recv_ops) { + GPR_ASSERT(stream_global->publish_sopb == NULL); + GPR_ASSERT(stream_global->published_state != GRPC_STREAM_CLOSED); + stream_global->recv_done_closure = op->on_done_recv; + stream_global->publish_sopb = op->recv_ops; + stream_global->publish_sopb->nops = 0; + stream_global->publish_state = op->recv_state; + if (stream_global->max_recv_bytes < op->max_recv_bytes) { + GRPC_CHTTP2_FLOWCTL_TRACE_STREAM( + "op", transport_global, stream_global, max_recv_bytes, + op->max_recv_bytes - stream_global->max_recv_bytes); + GRPC_CHTTP2_FLOWCTL_TRACE_STREAM( + "op", transport_global, stream_global, unannounced_incoming_window, + op->max_recv_bytes - stream_global->max_recv_bytes); + stream_global->unannounced_incoming_window += + op->max_recv_bytes - stream_global->max_recv_bytes; + stream_global->max_recv_bytes = op->max_recv_bytes; + } + grpc_chttp2_incoming_metadata_live_op_buffer_end( + &stream_global->outstanding_metadata); + grpc_chttp2_list_add_read_write_state_changed(transport_global, + stream_global); + if (stream_global->id != 0) { + grpc_chttp2_list_add_writable_stream(transport_global, stream_global); + } + } + + if (op->bind_pollset) { + add_to_pollset_locked(TRANSPORT_FROM_GLOBAL(transport_global), + op->bind_pollset); + } + + if (op->on_consumed) { + grpc_chttp2_schedule_closure(transport_global, op->on_consumed, 1); + } +} + +static void perform_stream_op(grpc_transport *gt, grpc_stream *gs, + grpc_transport_stream_op *op) { + grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; + grpc_chttp2_stream *s = (grpc_chttp2_stream *)gs; + + lock(t); + perform_stream_op_locked(&t->global, &s->global, op); + unlock(t); +} + +static void send_ping_locked(grpc_chttp2_transport *t, + grpc_iomgr_closure *on_recv) { + grpc_chttp2_outstanding_ping *p = gpr_malloc(sizeof(*p)); + p->next = &t->global.pings; + p->prev = p->next->prev; + p->prev->next = p->next->prev = p; + p->id[0] = (t->global.ping_counter >> 56) & 0xff; + p->id[1] = (t->global.ping_counter >> 48) & 0xff; + p->id[2] = (t->global.ping_counter >> 40) & 0xff; + p->id[3] = (t->global.ping_counter >> 32) & 0xff; + p->id[4] = (t->global.ping_counter >> 24) & 0xff; + p->id[5] = (t->global.ping_counter >> 16) & 0xff; + p->id[6] = (t->global.ping_counter >> 8) & 0xff; + p->id[7] = t->global.ping_counter & 0xff; + p->on_recv = on_recv; + gpr_slice_buffer_add(&t->global.qbuf, grpc_chttp2_ping_create(0, p->id)); +} + +static void perform_transport_op(grpc_transport *gt, grpc_transport_op *op) { + grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; + int close_transport = 0; + + lock(t); + + if (op->on_consumed) { + grpc_chttp2_schedule_closure(&t->global, op->on_consumed, 1); + } + + if (op->on_connectivity_state_change) { + grpc_connectivity_state_notify_on_state_change( + &t->channel_callback.state_tracker, op->connectivity_state, + op->on_connectivity_state_change); + } + + if (op->send_goaway) { + t->global.sent_goaway = 1; + grpc_chttp2_goaway_append( + t->global.last_incoming_stream_id, + grpc_chttp2_grpc_status_to_http2_error(op->goaway_status), + gpr_slice_ref(*op->goaway_message), &t->global.qbuf); + close_transport = !grpc_chttp2_has_streams(t); + } + + if (op->set_accept_stream != NULL) { + t->channel_callback.accept_stream = op->set_accept_stream; + t->channel_callback.accept_stream_user_data = + op->set_accept_stream_user_data; + } + + if (op->bind_pollset) { + add_to_pollset_locked(t, op->bind_pollset); + } + + if (op->bind_pollset_set) { + add_to_pollset_set_locked(t, op->bind_pollset_set); + } + + if (op->send_ping) { + send_ping_locked(t, op->send_ping); + } + + if (op->disconnect) { + close_transport_locked(t); + } + + unlock(t); + + if (close_transport) { + lock(t); + close_transport_locked(t); + unlock(t); + } +} + +/* + * INPUT PROCESSING + */ + +static grpc_stream_state compute_state(gpr_uint8 write_closed, + gpr_uint8 read_closed) { + if (write_closed && read_closed) return GRPC_STREAM_CLOSED; + if (write_closed) return GRPC_STREAM_SEND_CLOSED; + if (read_closed) return GRPC_STREAM_RECV_CLOSED; + return GRPC_STREAM_OPEN; +} + +static void remove_stream(grpc_chttp2_transport *t, gpr_uint32 id) { + size_t new_stream_count; + grpc_chttp2_stream *s = + grpc_chttp2_stream_map_delete(&t->parsing_stream_map, id); + if (!s) { + s = grpc_chttp2_stream_map_delete(&t->new_stream_map, id); + } + grpc_chttp2_list_remove_writable_stream(&t->global, &s->global); + GPR_ASSERT(s); + s->global.in_stream_map = 0; + if (t->parsing.incoming_stream == &s->parsing) { + t->parsing.incoming_stream = NULL; + grpc_chttp2_parsing_become_skip_parser(&t->parsing); + } + if (grpc_chttp2_unregister_stream(t, s) && t->global.sent_goaway) { + close_transport_locked(t); + } + + new_stream_count = grpc_chttp2_stream_map_size(&t->parsing_stream_map) + + grpc_chttp2_stream_map_size(&t->new_stream_map); + if (new_stream_count != t->global.concurrent_stream_count) { + t->global.concurrent_stream_count = new_stream_count; + maybe_start_some_streams(&t->global); + } +} + +static void unlock_check_read_write_state(grpc_chttp2_transport *t) { + grpc_chttp2_transport_global *transport_global = &t->global; + grpc_chttp2_stream_global *stream_global; + grpc_stream_state state; + + if (!t->parsing_active) { + /* if a stream is in the stream map, and gets cancelled, we need to ensure + we are not parsing before continuing the cancellation to keep things in + a sane state */ + while (grpc_chttp2_list_pop_closed_waiting_for_parsing(transport_global, + &stream_global)) { + GPR_ASSERT(stream_global->in_stream_map); + GPR_ASSERT(stream_global->write_state != GRPC_WRITE_STATE_OPEN); + GPR_ASSERT(stream_global->read_closed); + remove_stream(t, stream_global->id); + grpc_chttp2_list_add_read_write_state_changed(transport_global, + stream_global); + } + } + + if (!t->writing_active) { + while (grpc_chttp2_list_pop_cancelled_waiting_for_writing(transport_global, + &stream_global)) { + grpc_chttp2_list_add_read_write_state_changed(transport_global, + stream_global); + } + } + + while (grpc_chttp2_list_pop_read_write_state_changed(transport_global, + &stream_global)) { + if (stream_global->cancelled) { + if (t->writing_active && + stream_global->write_state != GRPC_WRITE_STATE_SENT_CLOSE) { + grpc_chttp2_list_add_cancelled_waiting_for_writing(transport_global, + stream_global); + } else { + stream_global->write_state = GRPC_WRITE_STATE_SENT_CLOSE; + if (stream_global->outgoing_sopb != NULL) { + grpc_sopb_reset(stream_global->outgoing_sopb); + stream_global->outgoing_sopb = NULL; + grpc_chttp2_schedule_closure(transport_global, + stream_global->send_done_closure, 1); + } + stream_global->read_closed = 1; + if (!stream_global->published_cancelled) { + char buffer[GPR_LTOA_MIN_BUFSIZE]; + gpr_ltoa(stream_global->cancelled_status, buffer); + grpc_chttp2_incoming_metadata_buffer_add( + &stream_global->incoming_metadata, + grpc_mdelem_from_strings(t->metadata_context, "grpc-status", + buffer)); + grpc_chttp2_incoming_metadata_buffer_place_metadata_batch_into( + &stream_global->incoming_metadata, &stream_global->incoming_sopb); + stream_global->published_cancelled = 1; + } + } + } + if (stream_global->write_state == GRPC_WRITE_STATE_SENT_CLOSE && + stream_global->read_closed && stream_global->in_stream_map) { + if (t->parsing_active) { + grpc_chttp2_list_add_closed_waiting_for_parsing(transport_global, + stream_global); + } else { + remove_stream(t, stream_global->id); + } + } + if (!stream_global->publish_sopb) { + continue; + } + if (stream_global->writing_now != 0) { + continue; + } + /* FIXME(ctiller): we include in_stream_map in our computation of + whether the stream is write-closed. This is completely bogus, + but has the effect of delaying stream-closed until the stream + is indeed evicted from the stream map, making it safe to delete. + To fix this will require having an edge after stream-closed + indicating that the stream is closed AND safe to delete. */ + state = compute_state( + stream_global->write_state == GRPC_WRITE_STATE_SENT_CLOSE && + !stream_global->in_stream_map, + stream_global->read_closed); + if (stream_global->incoming_sopb.nops == 0 && + state == stream_global->published_state) { + continue; + } + grpc_chttp2_incoming_metadata_buffer_postprocess_sopb_and_begin_live_op( + &stream_global->incoming_metadata, &stream_global->incoming_sopb, + &stream_global->outstanding_metadata); + grpc_sopb_swap(stream_global->publish_sopb, &stream_global->incoming_sopb); + stream_global->published_state = *stream_global->publish_state = state; + grpc_chttp2_schedule_closure(transport_global, + stream_global->recv_done_closure, 1); + stream_global->recv_done_closure = NULL; + stream_global->publish_sopb = NULL; + stream_global->publish_state = NULL; + } +} + +static void cancel_from_api(grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global *stream_global, + grpc_status_code status) { + stream_global->cancelled = 1; + stream_global->cancelled_status = status; + if (stream_global->id != 0) { + gpr_slice_buffer_add( + &transport_global->qbuf, + grpc_chttp2_rst_stream_create( + stream_global->id, grpc_chttp2_grpc_status_to_http2_error(status))); + } + grpc_chttp2_list_add_read_write_state_changed(transport_global, + stream_global); +} + +static void close_from_api(grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global *stream_global, + grpc_status_code status, + gpr_slice *optional_message) { + gpr_slice hdr; + gpr_slice status_hdr; + gpr_slice message_pfx; + gpr_uint8 *p; + gpr_uint32 len = 0; + + GPR_ASSERT(status >= 0 && (int)status < 100); + + stream_global->cancelled = 1; + stream_global->cancelled_status = status; + GPR_ASSERT(stream_global->id != 0); + GPR_ASSERT(!stream_global->written_anything); + + /* Hand roll a header block. + This is unnecessarily ugly - at some point we should find a more elegant + solution. + It's complicated by the fact that our send machinery would be dead by the + time we got around to sending this, so instead we ignore HPACK compression + and just write the uncompressed bytes onto the wire. */ + status_hdr = gpr_slice_malloc(15 + (status >= 10)); + p = GPR_SLICE_START_PTR(status_hdr); + *p++ = 0x40; /* literal header */ + *p++ = 11; /* len(grpc-status) */ + *p++ = 'g'; + *p++ = 'r'; + *p++ = 'p'; + *p++ = 'c'; + *p++ = '-'; + *p++ = 's'; + *p++ = 't'; + *p++ = 'a'; + *p++ = 't'; + *p++ = 'u'; + *p++ = 's'; + if (status < 10) { + *p++ = 1; + *p++ = '0' + status; + } else { + *p++ = 2; + *p++ = '0' + (status / 10); + *p++ = '0' + (status % 10); + } + GPR_ASSERT(p == GPR_SLICE_END_PTR(status_hdr)); + len += GPR_SLICE_LENGTH(status_hdr); + + if (optional_message) { + GPR_ASSERT(GPR_SLICE_LENGTH(*optional_message) < 127); + message_pfx = gpr_slice_malloc(15); + p = GPR_SLICE_START_PTR(message_pfx); + *p++ = 0x40; + *p++ = 12; /* len(grpc-message) */ + *p++ = 'g'; + *p++ = 'r'; + *p++ = 'p'; + *p++ = 'c'; + *p++ = '-'; + *p++ = 'm'; + *p++ = 'e'; + *p++ = 's'; + *p++ = 's'; + *p++ = 'a'; + *p++ = 'g'; + *p++ = 'e'; + *p++ = GPR_SLICE_LENGTH(*optional_message); + GPR_ASSERT(p == GPR_SLICE_END_PTR(message_pfx)); + len += GPR_SLICE_LENGTH(message_pfx); + len += GPR_SLICE_LENGTH(*optional_message); + } + + hdr = gpr_slice_malloc(9); + p = GPR_SLICE_START_PTR(hdr); + *p++ = len >> 16; + *p++ = len >> 8; + *p++ = len; + *p++ = GRPC_CHTTP2_FRAME_HEADER; + *p++ = GRPC_CHTTP2_DATA_FLAG_END_STREAM | GRPC_CHTTP2_DATA_FLAG_END_HEADERS; + *p++ = stream_global->id >> 24; + *p++ = stream_global->id >> 16; + *p++ = stream_global->id >> 8; + *p++ = stream_global->id; + GPR_ASSERT(p == GPR_SLICE_END_PTR(hdr)); + + gpr_slice_buffer_add(&transport_global->qbuf, hdr); + gpr_slice_buffer_add(&transport_global->qbuf, status_hdr); + if (optional_message) { + gpr_slice_buffer_add(&transport_global->qbuf, message_pfx); + gpr_slice_buffer_add(&transport_global->qbuf, + gpr_slice_ref(*optional_message)); + } + + gpr_slice_buffer_add( + &transport_global->qbuf, + grpc_chttp2_rst_stream_create(stream_global->id, GRPC_CHTTP2_NO_ERROR)); + + grpc_chttp2_list_add_read_write_state_changed(transport_global, + stream_global); +} + +static void cancel_stream_cb(grpc_chttp2_transport_global *transport_global, + void *user_data, + grpc_chttp2_stream_global *stream_global) { + cancel_from_api(transport_global, stream_global, GRPC_STATUS_UNAVAILABLE); +} + +static void end_all_the_calls(grpc_chttp2_transport *t) { + grpc_chttp2_for_all_streams(&t->global, NULL, cancel_stream_cb); +} + +static void drop_connection(grpc_chttp2_transport *t) { + close_transport_locked(t); + end_all_the_calls(t); +} + +/** update window from a settings change */ +static void update_global_window(void *args, gpr_uint32 id, void *stream) { + grpc_chttp2_transport *t = args; + grpc_chttp2_stream *s = stream; + grpc_chttp2_transport_global *transport_global = &t->global; + grpc_chttp2_stream_global *stream_global = &s->global; + int was_zero; + int is_zero; + + GRPC_CHTTP2_FLOWCTL_TRACE_STREAM("settings", transport_global, stream_global, + outgoing_window, + t->parsing.initial_window_update); + was_zero = stream_global->outgoing_window <= 0; + stream_global->outgoing_window += t->parsing.initial_window_update; + is_zero = stream_global->outgoing_window <= 0; + + if (was_zero && !is_zero) { + grpc_chttp2_list_add_writable_stream(transport_global, stream_global); + } +} + +static void read_error_locked(grpc_chttp2_transport *t) { + t->endpoint_reading = 0; + if (!t->writing_active && t->ep) { + destroy_endpoint(t); + } +} + +/* tcp read callback */ +static int recv_data_loop(grpc_chttp2_transport *t, int *success) { + size_t i; + int keep_reading = 0; + + lock(t); + i = 0; + GPR_ASSERT(!t->parsing_active); + if (!t->closed) { + t->parsing_active = 1; + /* merge stream lists */ + grpc_chttp2_stream_map_move_into(&t->new_stream_map, + &t->parsing_stream_map); + grpc_chttp2_prepare_to_read(&t->global, &t->parsing); + gpr_mu_unlock(&t->mu); + for (; i < t->read_buffer.count && + grpc_chttp2_perform_read(&t->parsing, t->read_buffer.slices[i]); + i++) + ; + gpr_mu_lock(&t->mu); + if (i != t->read_buffer.count) { + drop_connection(t); + } + /* merge stream lists */ + grpc_chttp2_stream_map_move_into(&t->new_stream_map, + &t->parsing_stream_map); + t->global.concurrent_stream_count = + grpc_chttp2_stream_map_size(&t->parsing_stream_map); + if (t->parsing.initial_window_update != 0) { + grpc_chttp2_stream_map_for_each(&t->parsing_stream_map, + update_global_window, t); + t->parsing.initial_window_update = 0; + } + /* handle higher level things */ + grpc_chttp2_publish_reads(&t->global, &t->parsing); + t->parsing_active = 0; + } + if (!*success || i != t->read_buffer.count) { + drop_connection(t); + read_error_locked(t); + } else if (!t->closed) { + keep_reading = 1; + REF_TRANSPORT(t, "keep_reading"); + prevent_endpoint_shutdown(t); + } + gpr_slice_buffer_reset_and_unref(&t->read_buffer); + unlock(t); + + if (keep_reading) { + int ret = -1; + switch (grpc_endpoint_read(t->ep, &t->read_buffer, &t->recv_data)) { + case GRPC_ENDPOINT_DONE: + *success = 1; + ret = 1; + break; + case GRPC_ENDPOINT_ERROR: + *success = 0; + ret = 1; + break; + case GRPC_ENDPOINT_PENDING: + ret = 0; + break; + } + allow_endpoint_shutdown_unlocked(t); + UNREF_TRANSPORT(t, "keep_reading"); + return ret; + } else { + UNREF_TRANSPORT(t, "recv_data"); + return 0; + } + + gpr_log(GPR_ERROR, "should never reach here"); + abort(); +} + +static void recv_data(void *tp, int success) { + grpc_chttp2_transport *t = tp; + + while (recv_data_loop(t, &success)) + ; +} + +/* + * CALLBACK LOOP + */ + +static void schedule_closure_for_connectivity(void *a, + grpc_iomgr_closure *closure) { + grpc_chttp2_schedule_closure(a, closure, 1); +} + +static void connectivity_state_set( + grpc_chttp2_transport_global *transport_global, + grpc_connectivity_state state, const char *reason) { + GRPC_CHTTP2_IF_TRACING( + gpr_log(GPR_DEBUG, "set connectivity_state=%d", state)); + grpc_connectivity_state_set_with_scheduler( + &TRANSPORT_FROM_GLOBAL(transport_global)->channel_callback.state_tracker, + state, schedule_closure_for_connectivity, transport_global, reason); +} + +void grpc_chttp2_schedule_closure( + grpc_chttp2_transport_global *transport_global, grpc_iomgr_closure *closure, + int success) { + closure->success = success; + if (transport_global->pending_closures_tail == NULL) { + transport_global->pending_closures_head = + transport_global->pending_closures_tail = closure; + } else { + transport_global->pending_closures_tail->next = closure; + transport_global->pending_closures_tail = closure; + } + closure->next = NULL; +} + +/* + * POLLSET STUFF + */ + +static void add_to_pollset_locked(grpc_chttp2_transport *t, + grpc_pollset *pollset) { + if (t->ep) { + grpc_endpoint_add_to_pollset(t->ep, pollset); + } +} + +static void add_to_pollset_set_locked(grpc_chttp2_transport *t, + grpc_pollset_set *pollset_set) { + if (t->ep) { + grpc_endpoint_add_to_pollset_set(t->ep, pollset_set); + } +} + +/* + * TRACING + */ + +void grpc_chttp2_flowctl_trace(const char *file, int line, const char *reason, + const char *context, const char *var, + int is_client, gpr_uint32 stream_id, + gpr_int64 current_value, gpr_int64 delta) { + char *identifier; + char *context_scope; + char *context_thread; + char *underscore_pos = strchr(context, '_'); + GPR_ASSERT(underscore_pos); + context_thread = gpr_strdup(underscore_pos + 1); + context_scope = gpr_strdup(context); + context_scope[underscore_pos - context] = 0; + if (stream_id) { + gpr_asprintf(&identifier, "%s[%d]", context_scope, stream_id); + } else { + identifier = gpr_strdup(context_scope); + } + gpr_log(GPR_INFO, + "FLOWCTL: %s %-10s %8s %-27s %8lld %c %8lld = %8lld %-10s [%s:%d]", + is_client ? "client" : "server", identifier, context_thread, var, + current_value, delta < 0 ? '-' : '+', delta < 0 ? -delta : delta, + current_value + delta, reason, file, line); + gpr_free(identifier); + gpr_free(context_thread); + gpr_free(context_scope); +} + +/* + * INTEGRATION GLUE + */ + +static char *chttp2_get_peer(grpc_transport *t) { + return gpr_strdup(((grpc_chttp2_transport *)t)->peer_string); +} + +static const grpc_transport_vtable vtable = {sizeof(grpc_chttp2_stream), + init_stream, + perform_stream_op, + perform_transport_op, + destroy_stream, + destroy_transport, + chttp2_get_peer}; + +grpc_transport *grpc_create_chttp2_transport( + const grpc_channel_args *channel_args, grpc_endpoint *ep, grpc_mdctx *mdctx, + int is_client) { + grpc_chttp2_transport *t = gpr_malloc(sizeof(grpc_chttp2_transport)); + init_transport(t, channel_args, ep, mdctx, is_client); + return &t->base; +} + +void grpc_chttp2_transport_start_reading(grpc_transport *transport, + gpr_slice *slices, size_t nslices) { + grpc_chttp2_transport *t = (grpc_chttp2_transport *)transport; + REF_TRANSPORT(t, "recv_data"); /* matches unref inside recv_data */ + gpr_slice_buffer_addn(&t->read_buffer, slices, nslices); + recv_data(t, 1); +} diff --git a/src/core/transport/chttp2_transport.h b/src/core/transport/chttp2_transport.h new file mode 100644 index 00000000..fa0d6e41 --- /dev/null +++ b/src/core/transport/chttp2_transport.h @@ -0,0 +1,50 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_TRANSPORT_CHTTP2_TRANSPORT_H +#define GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_TRANSPORT_H + +#include "src/core/iomgr/endpoint.h" +#include "src/core/transport/transport.h" + +extern int grpc_http_trace; +extern int grpc_flowctl_trace; + +grpc_transport *grpc_create_chttp2_transport( + const grpc_channel_args *channel_args, grpc_endpoint *ep, + grpc_mdctx *metadata_context, int is_client); + +void grpc_chttp2_transport_start_reading(grpc_transport *transport, + gpr_slice *slices, size_t nslices); + +#endif /* GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_TRANSPORT_H */ diff --git a/src/core/transport/connectivity_state.c b/src/core/transport/connectivity_state.c new file mode 100644 index 00000000..61d26f06 --- /dev/null +++ b/src/core/transport/connectivity_state.c @@ -0,0 +1,148 @@ +/* + * + * Copyright 2015, 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 "src/core/transport/connectivity_state.h" +#include +#include +#include + +int grpc_connectivity_state_trace = 0; + +const char *grpc_connectivity_state_name(grpc_connectivity_state state) { + switch (state) { + case GRPC_CHANNEL_IDLE: + return "IDLE"; + case GRPC_CHANNEL_CONNECTING: + return "CONNECTING"; + case GRPC_CHANNEL_READY: + return "READY"; + case GRPC_CHANNEL_TRANSIENT_FAILURE: + return "TRANSIENT_FAILURE"; + case GRPC_CHANNEL_FATAL_FAILURE: + return "FATAL_FAILURE"; + } + abort(); + return "UNKNOWN"; +} + +void grpc_connectivity_state_init(grpc_connectivity_state_tracker *tracker, + grpc_connectivity_state init_state, + const char *name) { + tracker->current_state = init_state; + tracker->watchers = NULL; + tracker->name = gpr_strdup(name); +} + +void grpc_connectivity_state_destroy(grpc_connectivity_state_tracker *tracker) { + grpc_connectivity_state_watcher *w; + while ((w = tracker->watchers)) { + tracker->watchers = w->next; + + if (GRPC_CHANNEL_FATAL_FAILURE != *w->current) { + *w->current = GRPC_CHANNEL_FATAL_FAILURE; + grpc_iomgr_add_callback(w->notify); + } else { + grpc_iomgr_add_delayed_callback(w->notify, 0); + } + gpr_free(w); + } + gpr_free(tracker->name); +} + +grpc_connectivity_state grpc_connectivity_state_check( + grpc_connectivity_state_tracker *tracker) { + return tracker->current_state; +} + +int grpc_connectivity_state_notify_on_state_change( + grpc_connectivity_state_tracker *tracker, grpc_connectivity_state *current, + grpc_iomgr_closure *notify) { + if (grpc_connectivity_state_trace) { + gpr_log(GPR_DEBUG, "CONWATCH: %s: from %s [cur=%s]", tracker->name, + grpc_connectivity_state_name(*current), + grpc_connectivity_state_name(tracker->current_state)); + } + if (tracker->current_state != *current) { + *current = tracker->current_state; + grpc_iomgr_add_callback(notify); + } else { + grpc_connectivity_state_watcher *w = gpr_malloc(sizeof(*w)); + w->current = current; + w->notify = notify; + w->next = tracker->watchers; + tracker->watchers = w; + } + return tracker->current_state == GRPC_CHANNEL_IDLE; +} + +void grpc_connectivity_state_set_with_scheduler( + grpc_connectivity_state_tracker *tracker, grpc_connectivity_state state, + void (*scheduler)(void *arg, grpc_iomgr_closure *closure), void *arg, + const char *reason) { + grpc_connectivity_state_watcher *new = NULL; + grpc_connectivity_state_watcher *w; + if (grpc_connectivity_state_trace) { + gpr_log(GPR_DEBUG, "SET: %s: %s --> %s [%s]", tracker->name, + grpc_connectivity_state_name(tracker->current_state), + grpc_connectivity_state_name(state), reason); + } + if (tracker->current_state == state) { + return; + } + GPR_ASSERT(tracker->current_state != GRPC_CHANNEL_FATAL_FAILURE); + tracker->current_state = state; + while ((w = tracker->watchers)) { + tracker->watchers = w->next; + + if (state != *w->current) { + *w->current = state; + scheduler(arg, w->notify); + gpr_free(w); + } else { + w->next = new; + new = w; + } + } + tracker->watchers = new; +} + +static void default_scheduler(void *ignored, grpc_iomgr_closure *closure) { + grpc_iomgr_add_callback(closure); +} + +void grpc_connectivity_state_set(grpc_connectivity_state_tracker *tracker, + grpc_connectivity_state state, + const char *reason) { + grpc_connectivity_state_set_with_scheduler(tracker, state, default_scheduler, + NULL, reason); +} diff --git a/src/core/transport/connectivity_state.h b/src/core/transport/connectivity_state.h new file mode 100644 index 00000000..a3b0b80c --- /dev/null +++ b/src/core/transport/connectivity_state.h @@ -0,0 +1,81 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_TRANSPORT_CONNECTIVITY_STATE_H +#define GRPC_INTERNAL_CORE_TRANSPORT_CONNECTIVITY_STATE_H + +#include +#include "src/core/iomgr/iomgr.h" + +typedef struct grpc_connectivity_state_watcher { + /** we keep watchers in a linked list */ + struct grpc_connectivity_state_watcher *next; + /** closure to notify on change */ + grpc_iomgr_closure *notify; + /** the current state as believed by the watcher */ + grpc_connectivity_state *current; +} grpc_connectivity_state_watcher; + +typedef struct { + /** current connectivity state */ + grpc_connectivity_state current_state; + /** all our watchers */ + grpc_connectivity_state_watcher *watchers; + /** a name to help debugging */ + char *name; +} grpc_connectivity_state_tracker; + +extern int grpc_connectivity_state_trace; + +void grpc_connectivity_state_init(grpc_connectivity_state_tracker *tracker, + grpc_connectivity_state init_state, + const char *name); +void grpc_connectivity_state_destroy(grpc_connectivity_state_tracker *tracker); + +void grpc_connectivity_state_set(grpc_connectivity_state_tracker *tracker, + grpc_connectivity_state state, + const char *reason); +void grpc_connectivity_state_set_with_scheduler( + grpc_connectivity_state_tracker *tracker, grpc_connectivity_state state, + void (*scheduler)(void *arg, grpc_iomgr_closure *closure), void *arg, + const char *reason); + +grpc_connectivity_state grpc_connectivity_state_check( + grpc_connectivity_state_tracker *tracker); + +/** Return 1 if the channel should start connecting, 0 otherwise */ +int grpc_connectivity_state_notify_on_state_change( + grpc_connectivity_state_tracker *tracker, grpc_connectivity_state *current, + grpc_iomgr_closure *notify); + +#endif /* GRPC_INTERNAL_CORE_TRANSPORT_CONNECTIVITY_STATE_H */ diff --git a/src/core/transport/metadata.c b/src/core/transport/metadata.c new file mode 100644 index 00000000..61638764 --- /dev/null +++ b/src/core/transport/metadata.c @@ -0,0 +1,716 @@ +/* + * + * Copyright 2015, 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 "src/core/iomgr/sockaddr.h" +#include "src/core/transport/metadata.h" + +#include +#include +#include + +#include +#include +#include +#include "src/core/support/murmur_hash.h" +#include "src/core/transport/chttp2/bin_encoder.h" +#include + +#define INITIAL_STRTAB_CAPACITY 4 +#define INITIAL_MDTAB_CAPACITY 4 + +#ifdef GRPC_METADATA_REFCOUNT_DEBUG +#define DEBUG_ARGS , const char *file, int line +#define FWD_DEBUG_ARGS , file, line +#define INTERNAL_STRING_REF(s) internal_string_ref((s), __FILE__, __LINE__) +#define INTERNAL_STRING_UNREF(s) internal_string_unref((s), __FILE__, __LINE__) +#define REF_MD_LOCKED(s) ref_md_locked((s), __FILE__, __LINE__) +#else +#define DEBUG_ARGS +#define FWD_DEBUG_ARGS +#define INTERNAL_STRING_REF(s) internal_string_ref((s)) +#define INTERNAL_STRING_UNREF(s) internal_string_unref((s)) +#define REF_MD_LOCKED(s) ref_md_locked((s)) +#endif + +typedef struct internal_string { + /* must be byte compatible with grpc_mdstr */ + gpr_slice slice; + gpr_uint32 hash; + + /* private only data */ + gpr_uint32 refs; + gpr_uint8 has_base64_and_huffman_encoded; + gpr_slice_refcount refcount; + + gpr_slice base64_and_huffman; + + grpc_mdctx *context; + + struct internal_string *bucket_next; +} internal_string; + +typedef struct internal_metadata { + /* must be byte compatible with grpc_mdelem */ + internal_string *key; + internal_string *value; + + gpr_atm refcnt; + + /* private only data */ + gpr_mu mu_user_data; + void *user_data; + void (*destroy_user_data)(void *user_data); + + grpc_mdctx *context; + struct internal_metadata *bucket_next; +} internal_metadata; + +struct grpc_mdctx { + gpr_uint32 hash_seed; + int refs; + + gpr_mu mu; + + internal_string **strtab; + size_t strtab_count; + size_t strtab_capacity; + + internal_metadata **mdtab; + size_t mdtab_count; + size_t mdtab_free; + size_t mdtab_capacity; +}; + +static void internal_string_ref(internal_string *s DEBUG_ARGS); +static void internal_string_unref(internal_string *s DEBUG_ARGS); +static void discard_metadata(grpc_mdctx *ctx); +static void gc_mdtab(grpc_mdctx *ctx); +static void metadata_context_destroy_locked(grpc_mdctx *ctx); + +static void lock(grpc_mdctx *ctx) { gpr_mu_lock(&ctx->mu); } + +static void unlock(grpc_mdctx *ctx) { + /* If the context has been orphaned we'd like to delete it soon. We check + conditions in unlock as it signals the end of mutations on a context. + + We need to ensure all grpc_mdelem and grpc_mdstr elements have been deleted + first. This is equivalent to saying that both tables have zero counts, + which is equivalent to saying that strtab_count is zero (as mdelem's MUST + reference an mdstr for their key and value slots). + + To encourage that to happen, we start discarding zero reference count + mdelems on every unlock (instead of the usual 'I'm too loaded' trigger + case), since otherwise we can be stuck waiting for a garbage collection + that will never happen. */ + if (ctx->refs == 0) { +/* uncomment if you're having trouble diagnosing an mdelem leak to make + things clearer (slows down destruction a lot, however) */ +#ifdef GRPC_METADATA_REFCOUNT_DEBUG + gc_mdtab(ctx); +#endif + if (ctx->mdtab_count && ctx->mdtab_count == ctx->mdtab_free) { + discard_metadata(ctx); + } + if (ctx->strtab_count == 0) { + metadata_context_destroy_locked(ctx); + return; + } + } + gpr_mu_unlock(&ctx->mu); +} + +static void ref_md_locked(internal_metadata *md DEBUG_ARGS) { +#ifdef GRPC_METADATA_REFCOUNT_DEBUG + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "ELM REF:%p:%d->%d: '%s' = '%s'", md, + gpr_atm_no_barrier_load(&md->refcnt), + gpr_atm_no_barrier_load(&md->refcnt) + 1, + grpc_mdstr_as_c_string((grpc_mdstr *)md->key), + grpc_mdstr_as_c_string((grpc_mdstr *)md->value)); +#endif + if (0 == gpr_atm_no_barrier_fetch_add(&md->refcnt, 1)) { + md->context->mdtab_free--; + } +} + +grpc_mdctx *grpc_mdctx_create_with_seed(gpr_uint32 seed) { + grpc_mdctx *ctx = gpr_malloc(sizeof(grpc_mdctx)); + + ctx->refs = 1; + ctx->hash_seed = seed; + gpr_mu_init(&ctx->mu); + ctx->strtab = gpr_malloc(sizeof(internal_string *) * INITIAL_STRTAB_CAPACITY); + memset(ctx->strtab, 0, sizeof(grpc_mdstr *) * INITIAL_STRTAB_CAPACITY); + ctx->strtab_count = 0; + ctx->strtab_capacity = INITIAL_STRTAB_CAPACITY; + ctx->mdtab = gpr_malloc(sizeof(internal_metadata *) * INITIAL_MDTAB_CAPACITY); + memset(ctx->mdtab, 0, sizeof(grpc_mdelem *) * INITIAL_MDTAB_CAPACITY); + ctx->mdtab_count = 0; + ctx->mdtab_capacity = INITIAL_MDTAB_CAPACITY; + ctx->mdtab_free = 0; + + return ctx; +} + +grpc_mdctx *grpc_mdctx_create(void) { + /* This seed is used to prevent remote connections from controlling hash table + * collisions. It needs to be somewhat unpredictable to a remote connection. + */ + return grpc_mdctx_create_with_seed(gpr_now(GPR_CLOCK_REALTIME).tv_nsec); +} + +static void discard_metadata(grpc_mdctx *ctx) { + size_t i; + internal_metadata *next, *cur; + + for (i = 0; i < ctx->mdtab_capacity; i++) { + cur = ctx->mdtab[i]; + while (cur) { + GPR_ASSERT(gpr_atm_acq_load(&cur->refcnt) == 0); + next = cur->bucket_next; + INTERNAL_STRING_UNREF(cur->key); + INTERNAL_STRING_UNREF(cur->value); + if (cur->user_data) { + cur->destroy_user_data(cur->user_data); + } + gpr_mu_destroy(&cur->mu_user_data); + gpr_free(cur); + cur = next; + ctx->mdtab_free--; + ctx->mdtab_count--; + } + ctx->mdtab[i] = NULL; + } +} + +static void metadata_context_destroy_locked(grpc_mdctx *ctx) { + GPR_ASSERT(ctx->strtab_count == 0); + GPR_ASSERT(ctx->mdtab_count == 0); + GPR_ASSERT(ctx->mdtab_free == 0); + gpr_free(ctx->strtab); + gpr_free(ctx->mdtab); + gpr_mu_unlock(&ctx->mu); + gpr_mu_destroy(&ctx->mu); + gpr_free(ctx); +} + +void grpc_mdctx_ref(grpc_mdctx *ctx) { + lock(ctx); + GPR_ASSERT(ctx->refs > 0); + ctx->refs++; + unlock(ctx); +} + +void grpc_mdctx_unref(grpc_mdctx *ctx) { + lock(ctx); + GPR_ASSERT(ctx->refs > 0); + ctx->refs--; + unlock(ctx); +} + +static void grow_strtab(grpc_mdctx *ctx) { + size_t capacity = ctx->strtab_capacity * 2; + size_t i; + internal_string **strtab = gpr_malloc(sizeof(internal_string *) * capacity); + internal_string *s, *next; + memset(strtab, 0, sizeof(internal_string *) * capacity); + + for (i = 0; i < ctx->strtab_capacity; i++) { + for (s = ctx->strtab[i]; s; s = next) { + next = s->bucket_next; + s->bucket_next = strtab[s->hash % capacity]; + strtab[s->hash % capacity] = s; + } + } + + gpr_free(ctx->strtab); + ctx->strtab = strtab; + ctx->strtab_capacity = capacity; +} + +static void internal_destroy_string(internal_string *is) { + internal_string **prev_next; + internal_string *cur; + grpc_mdctx *ctx = is->context; + if (is->has_base64_and_huffman_encoded) { + gpr_slice_unref(is->base64_and_huffman); + } + for (prev_next = &ctx->strtab[is->hash % ctx->strtab_capacity], + cur = *prev_next; + cur != is; prev_next = &cur->bucket_next, cur = cur->bucket_next) + ; + *prev_next = cur->bucket_next; + ctx->strtab_count--; + gpr_free(is); +} + +static void internal_string_ref(internal_string *s DEBUG_ARGS) { +#ifdef GRPC_METADATA_REFCOUNT_DEBUG + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "STR REF:%p:%d->%d: '%s'", s, + s->refs, s->refs + 1, grpc_mdstr_as_c_string((grpc_mdstr *)s)); +#endif + ++s->refs; +} + +static void internal_string_unref(internal_string *s DEBUG_ARGS) { +#ifdef GRPC_METADATA_REFCOUNT_DEBUG + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "STR UNREF:%p:%d->%d: '%s'", s, + s->refs, s->refs - 1, grpc_mdstr_as_c_string((grpc_mdstr *)s)); +#endif + GPR_ASSERT(s->refs > 0); + if (0 == --s->refs) { + internal_destroy_string(s); + } +} + +static void slice_ref(void *p) { + internal_string *is = + (internal_string *)((char *)p - offsetof(internal_string, refcount)); + grpc_mdctx *ctx = is->context; + lock(ctx); + INTERNAL_STRING_REF(is); + unlock(ctx); +} + +static void slice_unref(void *p) { + internal_string *is = + (internal_string *)((char *)p - offsetof(internal_string, refcount)); + grpc_mdctx *ctx = is->context; + lock(ctx); + INTERNAL_STRING_UNREF(is); + unlock(ctx); +} + +grpc_mdstr *grpc_mdstr_from_string(grpc_mdctx *ctx, const char *str, + int canonicalize_key) { + if (canonicalize_key) { + size_t len; + size_t i; + int canonical = 1; + + for (i = 0; str[i]; i++) { + if (str[i] >= 'A' && str[i] <= 'Z') { + canonical = 0; + /* Keep going in loop just to get string length */ + } + } + len = i; + + if (canonical) { + return grpc_mdstr_from_buffer(ctx, (const gpr_uint8 *)str, len); + } else { + char *copy = gpr_malloc(len); + grpc_mdstr *ret; + for (i = 0; i < len; i++) { + if (str[i] >= 'A' && str[i] <= 'Z') { + copy[i] = str[i] - 'A' + 'a'; + } else { + copy[i] = str[i]; + } + } + ret = grpc_mdstr_from_buffer(ctx, (const gpr_uint8 *)copy, len); + gpr_free(copy); + return ret; + } + } + return grpc_mdstr_from_buffer(ctx, (const gpr_uint8 *)str, strlen(str)); +} + +grpc_mdstr *grpc_mdstr_from_slice(grpc_mdctx *ctx, gpr_slice slice) { + grpc_mdstr *result = grpc_mdstr_from_buffer(ctx, GPR_SLICE_START_PTR(slice), + GPR_SLICE_LENGTH(slice)); + gpr_slice_unref(slice); + return result; +} + +grpc_mdstr *grpc_mdstr_from_buffer(grpc_mdctx *ctx, const gpr_uint8 *buf, + size_t length) { + gpr_uint32 hash = gpr_murmur_hash3(buf, length, ctx->hash_seed); + internal_string *s; + + lock(ctx); + + /* search for an existing string */ + for (s = ctx->strtab[hash % ctx->strtab_capacity]; s; s = s->bucket_next) { + if (s->hash == hash && GPR_SLICE_LENGTH(s->slice) == length && + 0 == memcmp(buf, GPR_SLICE_START_PTR(s->slice), length)) { + INTERNAL_STRING_REF(s); + unlock(ctx); + return (grpc_mdstr *)s; + } + } + + /* not found: create a new string */ + if (length + 1 < GPR_SLICE_INLINED_SIZE) { + /* string data goes directly into the slice */ + s = gpr_malloc(sizeof(internal_string)); + s->refs = 1; + s->slice.refcount = NULL; + memcpy(s->slice.data.inlined.bytes, buf, length); + s->slice.data.inlined.bytes[length] = 0; + s->slice.data.inlined.length = length; + } else { + /* string data goes after the internal_string header, and we +1 for null + terminator */ + s = gpr_malloc(sizeof(internal_string) + length + 1); + s->refs = 1; + s->refcount.ref = slice_ref; + s->refcount.unref = slice_unref; + s->slice.refcount = &s->refcount; + s->slice.data.refcounted.bytes = (gpr_uint8 *)(s + 1); + s->slice.data.refcounted.length = length; + memcpy(s->slice.data.refcounted.bytes, buf, length); + /* add a null terminator for cheap c string conversion when desired */ + s->slice.data.refcounted.bytes[length] = 0; + } + s->has_base64_and_huffman_encoded = 0; + s->hash = hash; + s->context = ctx; + s->bucket_next = ctx->strtab[hash % ctx->strtab_capacity]; + ctx->strtab[hash % ctx->strtab_capacity] = s; + + ctx->strtab_count++; + + if (ctx->strtab_count > ctx->strtab_capacity * 2) { + grow_strtab(ctx); + } + + unlock(ctx); + + return (grpc_mdstr *)s; +} + +static void gc_mdtab(grpc_mdctx *ctx) { + size_t i; + internal_metadata **prev_next; + internal_metadata *md, *next; + + for (i = 0; i < ctx->mdtab_capacity; i++) { + prev_next = &ctx->mdtab[i]; + for (md = ctx->mdtab[i]; md; md = next) { + next = md->bucket_next; + if (gpr_atm_acq_load(&md->refcnt) == 0) { + INTERNAL_STRING_UNREF(md->key); + INTERNAL_STRING_UNREF(md->value); + if (md->user_data) { + md->destroy_user_data(md->user_data); + } + gpr_free(md); + *prev_next = next; + ctx->mdtab_free--; + ctx->mdtab_count--; + } else { + prev_next = &md->bucket_next; + } + } + } + + GPR_ASSERT(ctx->mdtab_free == 0); +} + +static void grow_mdtab(grpc_mdctx *ctx) { + size_t capacity = ctx->mdtab_capacity * 2; + size_t i; + internal_metadata **mdtab = + gpr_malloc(sizeof(internal_metadata *) * capacity); + internal_metadata *md, *next; + gpr_uint32 hash; + memset(mdtab, 0, sizeof(internal_metadata *) * capacity); + + for (i = 0; i < ctx->mdtab_capacity; i++) { + for (md = ctx->mdtab[i]; md; md = next) { + hash = GRPC_MDSTR_KV_HASH(md->key->hash, md->value->hash); + next = md->bucket_next; + md->bucket_next = mdtab[hash % capacity]; + mdtab[hash % capacity] = md; + } + } + + gpr_free(ctx->mdtab); + ctx->mdtab = mdtab; + ctx->mdtab_capacity = capacity; +} + +static void rehash_mdtab(grpc_mdctx *ctx) { + if (ctx->mdtab_free > ctx->mdtab_capacity / 4) { + gc_mdtab(ctx); + } else { + grow_mdtab(ctx); + } +} + +grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_mdctx *ctx, + grpc_mdstr *mkey, + grpc_mdstr *mvalue) { + internal_string *key = (internal_string *)mkey; + internal_string *value = (internal_string *)mvalue; + gpr_uint32 hash = GRPC_MDSTR_KV_HASH(mkey->hash, mvalue->hash); + internal_metadata *md; + + GPR_ASSERT(key->context == ctx); + GPR_ASSERT(value->context == ctx); + + lock(ctx); + + /* search for an existing pair */ + for (md = ctx->mdtab[hash % ctx->mdtab_capacity]; md; md = md->bucket_next) { + if (md->key == key && md->value == value) { + REF_MD_LOCKED(md); + INTERNAL_STRING_UNREF(key); + INTERNAL_STRING_UNREF(value); + unlock(ctx); + return (grpc_mdelem *)md; + } + } + + /* not found: create a new pair */ + md = gpr_malloc(sizeof(internal_metadata)); + gpr_atm_rel_store(&md->refcnt, 1); + md->context = ctx; + md->key = key; + md->value = value; + md->user_data = NULL; + md->destroy_user_data = NULL; + md->bucket_next = ctx->mdtab[hash % ctx->mdtab_capacity]; + gpr_mu_init(&md->mu_user_data); +#ifdef GRPC_METADATA_REFCOUNT_DEBUG + gpr_log(GPR_DEBUG, "ELM NEW:%p:%d: '%s' = '%s'", md, + gpr_atm_no_barrier_load(&md->refcnt), + grpc_mdstr_as_c_string((grpc_mdstr *)md->key), + grpc_mdstr_as_c_string((grpc_mdstr *)md->value)); +#endif + ctx->mdtab[hash % ctx->mdtab_capacity] = md; + ctx->mdtab_count++; + + if (ctx->mdtab_count > ctx->mdtab_capacity * 2) { + rehash_mdtab(ctx); + } + + unlock(ctx); + + return (grpc_mdelem *)md; +} + +grpc_mdelem *grpc_mdelem_from_strings(grpc_mdctx *ctx, const char *key, + const char *value) { + return grpc_mdelem_from_metadata_strings( + ctx, grpc_mdstr_from_string(ctx, key, 0), + grpc_mdstr_from_string(ctx, value, 0)); +} + +grpc_mdelem *grpc_mdelem_from_slices(grpc_mdctx *ctx, gpr_slice key, + gpr_slice value) { + return grpc_mdelem_from_metadata_strings(ctx, grpc_mdstr_from_slice(ctx, key), + grpc_mdstr_from_slice(ctx, value)); +} + +grpc_mdelem *grpc_mdelem_from_string_and_buffer(grpc_mdctx *ctx, + const char *key, + const gpr_uint8 *value, + size_t value_length, + int canonicalize_key) { + return grpc_mdelem_from_metadata_strings( + ctx, grpc_mdstr_from_string(ctx, key, canonicalize_key), + grpc_mdstr_from_buffer(ctx, value, value_length)); +} + +grpc_mdelem *grpc_mdelem_ref(grpc_mdelem *gmd DEBUG_ARGS) { + internal_metadata *md = (internal_metadata *)gmd; +#ifdef GRPC_METADATA_REFCOUNT_DEBUG + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "ELM REF:%p:%d->%d: '%s' = '%s'", md, + gpr_atm_no_barrier_load(&md->refcnt), + gpr_atm_no_barrier_load(&md->refcnt) + 1, + grpc_mdstr_as_c_string((grpc_mdstr *)md->key), + grpc_mdstr_as_c_string((grpc_mdstr *)md->value)); +#endif + /* we can assume the ref count is >= 1 as the application is calling + this function - meaning that no adjustment to mdtab_free is necessary, + simplifying the logic here to be just an atomic increment */ + /* use C assert to have this removed in opt builds */ + assert(gpr_atm_no_barrier_load(&md->refcnt) >= 1); + gpr_atm_no_barrier_fetch_add(&md->refcnt, 1); + return gmd; +} + +void grpc_mdelem_unref(grpc_mdelem *gmd DEBUG_ARGS) { + internal_metadata *md = (internal_metadata *)gmd; + grpc_mdctx *ctx = md->context; + lock(ctx); +#ifdef GRPC_METADATA_REFCOUNT_DEBUG + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "ELM UNREF:%p:%d->%d: '%s' = '%s'", md, + gpr_atm_no_barrier_load(&md->refcnt), + gpr_atm_no_barrier_load(&md->refcnt) - 1, + grpc_mdstr_as_c_string((grpc_mdstr *)md->key), + grpc_mdstr_as_c_string((grpc_mdstr *)md->value)); +#endif + assert(gpr_atm_no_barrier_load(&md->refcnt) >= 1); + if (1 == gpr_atm_full_fetch_add(&md->refcnt, -1)) { + ctx->mdtab_free++; + } + unlock(ctx); +} + +const char *grpc_mdstr_as_c_string(grpc_mdstr *s) { + return (const char *)GPR_SLICE_START_PTR(s->slice); +} + +grpc_mdstr *grpc_mdstr_ref(grpc_mdstr *gs DEBUG_ARGS) { + internal_string *s = (internal_string *)gs; + grpc_mdctx *ctx = s->context; + lock(ctx); + internal_string_ref(s FWD_DEBUG_ARGS); + unlock(ctx); + return gs; +} + +void grpc_mdstr_unref(grpc_mdstr *gs DEBUG_ARGS) { + internal_string *s = (internal_string *)gs; + grpc_mdctx *ctx = s->context; + lock(ctx); + internal_string_unref(s FWD_DEBUG_ARGS); + unlock(ctx); +} + +size_t grpc_mdctx_get_mdtab_capacity_test_only(grpc_mdctx *ctx) { + return ctx->mdtab_capacity; +} + +size_t grpc_mdctx_get_mdtab_count_test_only(grpc_mdctx *ctx) { + return ctx->mdtab_count; +} + +size_t grpc_mdctx_get_mdtab_free_test_only(grpc_mdctx *ctx) { + return ctx->mdtab_free; +} + +void *grpc_mdelem_get_user_data(grpc_mdelem *md, + void (*if_destroy_func)(void *)) { + internal_metadata *im = (internal_metadata *)md; + void *result; + gpr_mu_lock(&im->mu_user_data); + result = im->destroy_user_data == if_destroy_func ? im->user_data : NULL; + gpr_mu_unlock(&im->mu_user_data); + return result; +} + +void grpc_mdelem_set_user_data(grpc_mdelem *md, void (*destroy_func)(void *), + void *user_data) { + internal_metadata *im = (internal_metadata *)md; + GPR_ASSERT((user_data == NULL) == (destroy_func == NULL)); + gpr_mu_lock(&im->mu_user_data); + if (im->destroy_user_data) { + /* user data can only be set once */ + gpr_mu_unlock(&im->mu_user_data); + if (destroy_func != NULL) { + destroy_func(user_data); + } + return; + } + im->destroy_user_data = destroy_func; + im->user_data = user_data; + gpr_mu_unlock(&im->mu_user_data); +} + +gpr_slice grpc_mdstr_as_base64_encoded_and_huffman_compressed(grpc_mdstr *gs) { + internal_string *s = (internal_string *)gs; + gpr_slice slice; + grpc_mdctx *ctx = s->context; + lock(ctx); + if (!s->has_base64_and_huffman_encoded) { + s->base64_and_huffman = + grpc_chttp2_base64_encode_and_huffman_compress(s->slice); + s->has_base64_and_huffman_encoded = 1; + } + slice = s->base64_and_huffman; + unlock(ctx); + return slice; +} + +void grpc_mdctx_lock(grpc_mdctx *ctx) { lock(ctx); } + +void grpc_mdctx_locked_mdelem_unref(grpc_mdctx *ctx, + grpc_mdelem *gmd DEBUG_ARGS) { + internal_metadata *md = (internal_metadata *)gmd; + grpc_mdctx *elem_ctx = md->context; + GPR_ASSERT(ctx == elem_ctx); +#ifdef GRPC_METADATA_REFCOUNT_DEBUG + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "ELM UNREF:%p:%d->%d: '%s' = '%s'", md, + gpr_atm_no_barrier_load(&md->refcnt), + gpr_atm_no_barrier_load(&md->refcnt) - 1, + grpc_mdstr_as_c_string((grpc_mdstr *)md->key), + grpc_mdstr_as_c_string((grpc_mdstr *)md->value)); +#endif + assert(gpr_atm_no_barrier_load(&md->refcnt) >= 1); + if (1 == gpr_atm_full_fetch_add(&md->refcnt, -1)) { + ctx->mdtab_free++; + } +} + +void grpc_mdctx_unlock(grpc_mdctx *ctx) { unlock(ctx); } + +static int conforms_to(grpc_mdstr *s, const gpr_uint8 *legal_bits) { + const gpr_uint8 *p = GPR_SLICE_START_PTR(s->slice); + const gpr_uint8 *e = GPR_SLICE_END_PTR(s->slice); + for (; p != e; p++) { + int idx = *p; + int byte = idx / 8; + int bit = idx % 8; + if ((legal_bits[byte] & (1 << bit)) == 0) return 0; + } + return 1; +} + +int grpc_mdstr_is_legal_header(grpc_mdstr *s) { + static const gpr_uint8 legal_header_bits[256 / 8] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xff, 0x03, 0x00, 0x00, 0x00, + 0x80, 0xfe, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + return conforms_to(s, legal_header_bits); +} + +int grpc_mdstr_is_legal_nonbin_header(grpc_mdstr *s) { + static const gpr_uint8 legal_header_bits[256 / 8] = { + 0x00, 0x00, 0x00, 0x00, 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + return conforms_to(s, legal_header_bits); +} + +int grpc_mdstr_is_bin_suffixed(grpc_mdstr *s) { + /* TODO(ctiller): consider caching this */ + return grpc_is_binary_header((const char *)GPR_SLICE_START_PTR(s->slice), + GPR_SLICE_LENGTH(s->slice)); +} diff --git a/src/core/transport/metadata.h b/src/core/transport/metadata.h new file mode 100644 index 00000000..eb17747b --- /dev/null +++ b/src/core/transport/metadata.h @@ -0,0 +1,184 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_TRANSPORT_METADATA_H +#define GRPC_INTERNAL_CORE_TRANSPORT_METADATA_H + +#include +#include + +/* This file provides a mechanism for tracking metadata through the grpc stack. + It's not intended for consumption outside of the library. + + Metadata is tracked in the context of a grpc_mdctx. For the time being there + is one of these per-channel, avoiding cross channel interference with memory + use and lock contention. + + The context tracks unique strings (grpc_mdstr) and pairs of strings + (grpc_mdelem). Any of these objects can be checked for equality by comparing + their pointers. These objects are reference counted. + + grpc_mdelem can additionally store a (non-NULL) user data pointer. This + pointer is intended to be used to cache semantic meaning of a metadata + element. For example, an OAuth token may cache the credentials it represents + and the time at which it expires in the mdelem user data. + + Combining this metadata cache and the hpack compression table allows us to + simply lookup complete preparsed objects quickly, incurring a few atomic + ops per metadata element on the fast path. + + grpc_mdelem instances MAY live longer than their refcount implies, and are + garbage collected periodically, meaning cached data can easily outlive a + single request. */ + +/* Forward declarations */ +typedef struct grpc_mdctx grpc_mdctx; +typedef struct grpc_mdstr grpc_mdstr; +typedef struct grpc_mdelem grpc_mdelem; + +/* if changing this, make identical changes in internal_string in metadata.c */ +struct grpc_mdstr { + const gpr_slice slice; + const gpr_uint32 hash; + /* there is a private part to this in metadata.c */ +}; + +/* if changing this, make identical changes in internal_metadata in + metadata.c */ +struct grpc_mdelem { + grpc_mdstr *const key; + grpc_mdstr *const value; + /* there is a private part to this in metadata.c */ +}; + +/* Create/orphan a metadata context */ +grpc_mdctx *grpc_mdctx_create(void); +grpc_mdctx *grpc_mdctx_create_with_seed(gpr_uint32 seed); +void grpc_mdctx_ref(grpc_mdctx *mdctx); +void grpc_mdctx_unref(grpc_mdctx *mdctx); + +/* Test only accessors to internal state - only for testing this code - do not + rely on it outside of metadata_test.c */ +size_t grpc_mdctx_get_mdtab_capacity_test_only(grpc_mdctx *mdctx); +size_t grpc_mdctx_get_mdtab_count_test_only(grpc_mdctx *mdctx); +size_t grpc_mdctx_get_mdtab_free_test_only(grpc_mdctx *mdctx); + +/* Constructors for grpc_mdstr instances; take a variety of data types that + clients may have handy */ +grpc_mdstr *grpc_mdstr_from_string(grpc_mdctx *ctx, const char *str, + int perform_key_canonicalization); +/* Unrefs the slice. */ +grpc_mdstr *grpc_mdstr_from_slice(grpc_mdctx *ctx, gpr_slice slice); +grpc_mdstr *grpc_mdstr_from_buffer(grpc_mdctx *ctx, const gpr_uint8 *str, + size_t length); + +/* Returns a borrowed slice from the mdstr with its contents base64 encoded + and huffman compressed */ +gpr_slice grpc_mdstr_as_base64_encoded_and_huffman_compressed(grpc_mdstr *str); + +/* Constructors for grpc_mdelem instances; take a variety of data types that + clients may have handy */ +grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_mdctx *ctx, grpc_mdstr *key, + grpc_mdstr *value); +grpc_mdelem *grpc_mdelem_from_strings(grpc_mdctx *ctx, const char *key, + const char *value); +/* Unrefs the slices. */ +grpc_mdelem *grpc_mdelem_from_slices(grpc_mdctx *ctx, gpr_slice key, + gpr_slice value); +grpc_mdelem *grpc_mdelem_from_string_and_buffer(grpc_mdctx *ctx, + const char *key, + const gpr_uint8 *value, + size_t value_length, + int canonicalize_key); + +/* Mutator and accessor for grpc_mdelem user data. The destructor function + is used as a type tag and is checked during user_data fetch. */ +void *grpc_mdelem_get_user_data(grpc_mdelem *md, + void (*if_destroy_func)(void *)); +void grpc_mdelem_set_user_data(grpc_mdelem *md, void (*destroy_func)(void *), + void *user_data); + +/* Reference counting */ +#ifdef GRPC_METADATA_REFCOUNT_DEBUG +#define GRPC_MDSTR_REF(s) grpc_mdstr_ref((s), __FILE__, __LINE__) +#define GRPC_MDSTR_UNREF(s) grpc_mdstr_unref((s), __FILE__, __LINE__) +#define GRPC_MDELEM_REF(s) grpc_mdelem_ref((s), __FILE__, __LINE__) +#define GRPC_MDELEM_UNREF(s) grpc_mdelem_unref((s), __FILE__, __LINE__) +grpc_mdstr *grpc_mdstr_ref(grpc_mdstr *s, const char *file, int line); +void grpc_mdstr_unref(grpc_mdstr *s, const char *file, int line); +grpc_mdelem *grpc_mdelem_ref(grpc_mdelem *md, const char *file, int line); +void grpc_mdelem_unref(grpc_mdelem *md, const char *file, int line); +#else +#define GRPC_MDSTR_REF(s) grpc_mdstr_ref((s)) +#define GRPC_MDSTR_UNREF(s) grpc_mdstr_unref((s)) +#define GRPC_MDELEM_REF(s) grpc_mdelem_ref((s)) +#define GRPC_MDELEM_UNREF(s) grpc_mdelem_unref((s)) +grpc_mdstr *grpc_mdstr_ref(grpc_mdstr *s); +void grpc_mdstr_unref(grpc_mdstr *s); +grpc_mdelem *grpc_mdelem_ref(grpc_mdelem *md); +void grpc_mdelem_unref(grpc_mdelem *md); +#endif + +/* Recover a char* from a grpc_mdstr. The returned string is null terminated. + Does not promise that the returned string has no embedded nulls however. */ +const char *grpc_mdstr_as_c_string(grpc_mdstr *s); + +int grpc_mdstr_is_legal_header(grpc_mdstr *s); +int grpc_mdstr_is_legal_nonbin_header(grpc_mdstr *s); +int grpc_mdstr_is_bin_suffixed(grpc_mdstr *s); + +/* Batch mode metadata functions. + These API's have equivalents above, but allow taking the mdctx just once, + performing a bunch of work, and then leaving the mdctx. */ + +/* Lock the metadata context: it's only safe to call _locked_ functions against + this context from the calling thread until grpc_mdctx_unlock is called */ +void grpc_mdctx_lock(grpc_mdctx *ctx); +#ifdef GRPC_METADATA_REFCOUNT_DEBUG +#define GRPC_MDCTX_LOCKED_MDELEM_UNREF(ctx, elem) \ + grpc_mdctx_locked_mdelem_unref((ctx), (elem), __FILE__, __LINE__) +/* Unref a metadata element */ +void grpc_mdctx_locked_mdelem_unref(grpc_mdctx *ctx, grpc_mdelem *elem, + const char *file, int line); +#else +#define GRPC_MDCTX_LOCKED_MDELEM_UNREF(ctx, elem) \ + grpc_mdctx_locked_mdelem_unref((ctx), (elem)) +/* Unref a metadata element */ +void grpc_mdctx_locked_mdelem_unref(grpc_mdctx *ctx, grpc_mdelem *elem); +#endif +/* Unlock the metadata context */ +void grpc_mdctx_unlock(grpc_mdctx *ctx); + +#define GRPC_MDSTR_KV_HASH(k_hash, v_hash) (GPR_ROTL((k_hash), 2) ^ (v_hash)) + +#endif /* GRPC_INTERNAL_CORE_TRANSPORT_METADATA_H */ diff --git a/src/core/transport/stream_op.c b/src/core/transport/stream_op.c new file mode 100644 index 00000000..038586d4 --- /dev/null +++ b/src/core/transport/stream_op.c @@ -0,0 +1,331 @@ +/* + * + * Copyright 2015, 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 "src/core/transport/stream_op.h" + +#include + +#include +#include + +/* Exponential growth function: Given x, return a larger x. + Currently we grow by 1.5 times upon reallocation. */ +#define GROW(x) (3 * (x) / 2) + +void grpc_sopb_init(grpc_stream_op_buffer *sopb) { + sopb->ops = sopb->inlined_ops; + sopb->nops = 0; + sopb->capacity = GRPC_SOPB_INLINE_ELEMENTS; +} + +void grpc_sopb_destroy(grpc_stream_op_buffer *sopb) { + grpc_stream_ops_unref_owned_objects(sopb->ops, sopb->nops); + if (sopb->ops != sopb->inlined_ops) gpr_free(sopb->ops); +} + +void grpc_sopb_reset(grpc_stream_op_buffer *sopb) { + grpc_stream_ops_unref_owned_objects(sopb->ops, sopb->nops); + sopb->nops = 0; +} + +void grpc_sopb_swap(grpc_stream_op_buffer *a, grpc_stream_op_buffer *b) { + GPR_SWAP(size_t, a->nops, b->nops); + GPR_SWAP(size_t, a->capacity, b->capacity); + + if (a->ops == a->inlined_ops) { + if (b->ops == b->inlined_ops) { + /* swap contents of inlined buffer */ + grpc_stream_op temp[GRPC_SOPB_INLINE_ELEMENTS]; + memcpy(temp, a->ops, b->nops * sizeof(grpc_stream_op)); + memcpy(a->ops, b->ops, a->nops * sizeof(grpc_stream_op)); + memcpy(b->ops, temp, b->nops * sizeof(grpc_stream_op)); + } else { + /* a is inlined, b is not - copy a inlined into b, fix pointers */ + a->ops = b->ops; + b->ops = b->inlined_ops; + memcpy(b->ops, a->inlined_ops, b->nops * sizeof(grpc_stream_op)); + } + } else if (b->ops == b->inlined_ops) { + /* b is inlined, a is not - copy b inlined int a, fix pointers */ + b->ops = a->ops; + a->ops = a->inlined_ops; + memcpy(a->ops, b->inlined_ops, a->nops * sizeof(grpc_stream_op)); + } else { + /* no inlining: easy swap */ + GPR_SWAP(grpc_stream_op *, a->ops, b->ops); + } +} + +void grpc_stream_ops_unref_owned_objects(grpc_stream_op *ops, size_t nops) { + size_t i; + for (i = 0; i < nops; i++) { + switch (ops[i].type) { + case GRPC_OP_SLICE: + gpr_slice_unref(ops[i].data.slice); + break; + case GRPC_OP_METADATA: + grpc_metadata_batch_destroy(&ops[i].data.metadata); + break; + case GRPC_NO_OP: + case GRPC_OP_BEGIN_MESSAGE: + break; + } + } +} + +static void expandto(grpc_stream_op_buffer *sopb, size_t new_capacity) { + sopb->capacity = new_capacity; + if (sopb->ops == sopb->inlined_ops) { + sopb->ops = gpr_malloc(sizeof(grpc_stream_op) * new_capacity); + memcpy(sopb->ops, sopb->inlined_ops, sopb->nops * sizeof(grpc_stream_op)); + } else { + sopb->ops = gpr_realloc(sopb->ops, sizeof(grpc_stream_op) * new_capacity); + } +} + +static grpc_stream_op *add(grpc_stream_op_buffer *sopb) { + grpc_stream_op *out; + + GPR_ASSERT(sopb->nops <= sopb->capacity); + if (sopb->nops == sopb->capacity) { + expandto(sopb, GROW(sopb->capacity)); + } + out = sopb->ops + sopb->nops; + sopb->nops++; + return out; +} + +void grpc_sopb_add_no_op(grpc_stream_op_buffer *sopb) { + add(sopb)->type = GRPC_NO_OP; +} + +void grpc_sopb_add_begin_message(grpc_stream_op_buffer *sopb, gpr_uint32 length, + gpr_uint32 flags) { + grpc_stream_op *op = add(sopb); + op->type = GRPC_OP_BEGIN_MESSAGE; + op->data.begin_message.length = length; + op->data.begin_message.flags = flags; +} + +void grpc_sopb_add_metadata(grpc_stream_op_buffer *sopb, + grpc_metadata_batch b) { + grpc_stream_op *op = add(sopb); + op->type = GRPC_OP_METADATA; + op->data.metadata = b; +} + +void grpc_sopb_add_slice(grpc_stream_op_buffer *sopb, gpr_slice slice) { + grpc_stream_op *op = add(sopb); + op->type = GRPC_OP_SLICE; + op->data.slice = slice; +} + +void grpc_sopb_append(grpc_stream_op_buffer *sopb, grpc_stream_op *ops, + size_t nops) { + size_t orig_nops = sopb->nops; + size_t new_nops = orig_nops + nops; + + if (new_nops > sopb->capacity) { + expandto(sopb, GPR_MAX(GROW(sopb->capacity), new_nops)); + } + + memcpy(sopb->ops + orig_nops, ops, sizeof(grpc_stream_op) * nops); + sopb->nops = new_nops; +} + +void grpc_sopb_move_to(grpc_stream_op_buffer *src, grpc_stream_op_buffer *dst) { + if (src->nops == 0) { + return; + } + if (dst->nops == 0) { + grpc_sopb_swap(src, dst); + return; + } + grpc_sopb_append(dst, src->ops, src->nops); + src->nops = 0; +} + +static void assert_valid_list(grpc_mdelem_list *list) { +#ifndef NDEBUG + grpc_linked_mdelem *l; + + GPR_ASSERT((list->head == NULL) == (list->tail == NULL)); + if (!list->head) return; + GPR_ASSERT(list->head->prev == NULL); + GPR_ASSERT(list->tail->next == NULL); + GPR_ASSERT((list->head == list->tail) == (list->head->next == NULL)); + + for (l = list->head; l; l = l->next) { + GPR_ASSERT(l->md); + GPR_ASSERT((l->prev == NULL) == (l == list->head)); + GPR_ASSERT((l->next == NULL) == (l == list->tail)); + if (l->next) GPR_ASSERT(l->next->prev == l); + if (l->prev) GPR_ASSERT(l->prev->next == l); + } +#endif /* NDEBUG */ +} + +#ifndef NDEBUG +void grpc_metadata_batch_assert_ok(grpc_metadata_batch *batch) { + assert_valid_list(&batch->list); + assert_valid_list(&batch->garbage); +} +#endif /* NDEBUG */ + +void grpc_metadata_batch_init(grpc_metadata_batch *batch) { + batch->list.head = batch->list.tail = batch->garbage.head = + batch->garbage.tail = NULL; + batch->deadline = gpr_inf_future(GPR_CLOCK_REALTIME); +} + +void grpc_metadata_batch_destroy(grpc_metadata_batch *batch) { + grpc_linked_mdelem *l; + for (l = batch->list.head; l; l = l->next) { + GRPC_MDELEM_UNREF(l->md); + } + for (l = batch->garbage.head; l; l = l->next) { + GRPC_MDELEM_UNREF(l->md); + } +} + +void grpc_metadata_batch_add_head(grpc_metadata_batch *batch, + grpc_linked_mdelem *storage, + grpc_mdelem *elem_to_add) { + GPR_ASSERT(elem_to_add); + storage->md = elem_to_add; + grpc_metadata_batch_link_head(batch, storage); +} + +static void link_head(grpc_mdelem_list *list, grpc_linked_mdelem *storage) { + assert_valid_list(list); + GPR_ASSERT(storage->md); + storage->prev = NULL; + storage->next = list->head; + if (list->head != NULL) { + list->head->prev = storage; + } else { + list->tail = storage; + } + list->head = storage; + assert_valid_list(list); +} + +void grpc_metadata_batch_link_head(grpc_metadata_batch *batch, + grpc_linked_mdelem *storage) { + link_head(&batch->list, storage); +} + +void grpc_metadata_batch_add_tail(grpc_metadata_batch *batch, + grpc_linked_mdelem *storage, + grpc_mdelem *elem_to_add) { + GPR_ASSERT(elem_to_add); + storage->md = elem_to_add; + grpc_metadata_batch_link_tail(batch, storage); +} + +static void link_tail(grpc_mdelem_list *list, grpc_linked_mdelem *storage) { + assert_valid_list(list); + GPR_ASSERT(storage->md); + storage->prev = list->tail; + storage->next = NULL; + storage->reserved = NULL; + if (list->tail != NULL) { + list->tail->next = storage; + } else { + list->head = storage; + } + list->tail = storage; + assert_valid_list(list); +} + +void grpc_metadata_batch_link_tail(grpc_metadata_batch *batch, + grpc_linked_mdelem *storage) { + link_tail(&batch->list, storage); +} + +void grpc_metadata_batch_merge(grpc_metadata_batch *target, + grpc_metadata_batch *add) { + grpc_linked_mdelem *l; + grpc_linked_mdelem *next; + for (l = add->list.head; l; l = next) { + next = l->next; + link_tail(&target->list, l); + } + for (l = add->garbage.head; l; l = next) { + next = l->next; + link_tail(&target->garbage, l); + } +} + +void grpc_metadata_batch_move(grpc_metadata_batch *dst, + grpc_metadata_batch *src) { + *dst = *src; + memset(src, 0, sizeof(grpc_metadata_batch)); +} + +void grpc_metadata_batch_filter(grpc_metadata_batch *batch, + grpc_mdelem *(*filter)(void *user_data, + grpc_mdelem *elem), + void *user_data) { + grpc_linked_mdelem *l; + grpc_linked_mdelem *next; + + assert_valid_list(&batch->list); + assert_valid_list(&batch->garbage); + for (l = batch->list.head; l; l = next) { + grpc_mdelem *orig = l->md; + grpc_mdelem *filt = filter(user_data, orig); + next = l->next; + if (filt == NULL) { + if (l->prev) { + l->prev->next = l->next; + } + if (l->next) { + l->next->prev = l->prev; + } + if (batch->list.head == l) { + batch->list.head = l->next; + } + if (batch->list.tail == l) { + batch->list.tail = l->prev; + } + assert_valid_list(&batch->list); + link_head(&batch->garbage, l); + } else if (filt != orig) { + GRPC_MDELEM_UNREF(orig); + l->md = filt; + } + } + assert_valid_list(&batch->list); + assert_valid_list(&batch->garbage); +} diff --git a/src/core/transport/stream_op.h b/src/core/transport/stream_op.h new file mode 100644 index 00000000..37f18b02 --- /dev/null +++ b/src/core/transport/stream_op.h @@ -0,0 +1,212 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_TRANSPORT_STREAM_OP_H +#define GRPC_INTERNAL_CORE_TRANSPORT_STREAM_OP_H + +#include +#include +#include +#include +#include "src/core/transport/metadata.h" + +/* this many stream ops are inlined into a sopb before allocating */ +#define GRPC_SOPB_INLINE_ELEMENTS 4 + +/* Operations that can be performed on a stream. + Used by grpc_stream_op. */ +typedef enum grpc_stream_op_code { + /* Do nothing code. Useful if rewriting a batch to exclude some operations. + Must be ignored by receivers */ + GRPC_NO_OP, + GRPC_OP_METADATA, + /* Begin a message/metadata element/status - as defined by + grpc_message_type. */ + GRPC_OP_BEGIN_MESSAGE, + /* Add a slice of data to the current message/metadata element/status. + Must not overflow the forward declared length. */ + GRPC_OP_SLICE +} grpc_stream_op_code; + +/** Internal bit flag for grpc_begin_message's \a flags signaling the use of + * compression for the message */ +#define GRPC_WRITE_INTERNAL_COMPRESS (0x80000000u) +/** Mask of all valid internal flags. */ +#define GRPC_WRITE_INTERNAL_USED_MASK (GRPC_WRITE_INTERNAL_COMPRESS) + +/* Arguments for GRPC_OP_BEGIN_MESSAGE */ +typedef struct grpc_begin_message { + /* How many bytes of data will this message contain */ + gpr_uint32 length; + /* Write flags for the message: see grpc.h GRPC_WRITE_* for the public bits, + * GRPC_WRITE_INTERNAL_* for the internal ones. */ + gpr_uint32 flags; +} grpc_begin_message; + +typedef struct grpc_linked_mdelem { + grpc_mdelem *md; + struct grpc_linked_mdelem *next; + struct grpc_linked_mdelem *prev; + void *reserved; +} grpc_linked_mdelem; + +typedef struct grpc_mdelem_list { + grpc_linked_mdelem *head; + grpc_linked_mdelem *tail; +} grpc_mdelem_list; + +typedef struct grpc_metadata_batch { + /** Metadata elements in this batch */ + grpc_mdelem_list list; + /** Elements that have been removed from the batch, but have + not yet been unreffed - used to allow collecting garbage + under a single metadata context lock */ + grpc_mdelem_list garbage; + /** Used to calculate grpc-timeout at the point of sending, + or gpr_inf_future if this batch does not need to send a + grpc-timeout */ + gpr_timespec deadline; +} grpc_metadata_batch; + +void grpc_metadata_batch_init(grpc_metadata_batch *batch); +void grpc_metadata_batch_destroy(grpc_metadata_batch *batch); +void grpc_metadata_batch_merge(grpc_metadata_batch *target, + grpc_metadata_batch *add); + +/** Moves the metadata information from \a src to \a dst. Upon return, \a src is + * zeroed. */ +void grpc_metadata_batch_move(grpc_metadata_batch *dst, + grpc_metadata_batch *src); + +/** Add \a storage to the beginning of \a batch. storage->md is + assumed to be valid. + \a storage is owned by the caller and must survive for the + lifetime of batch. This usually means it should be around + for the lifetime of the call. */ +void grpc_metadata_batch_link_head(grpc_metadata_batch *batch, + grpc_linked_mdelem *storage); +/** Add \a storage to the end of \a batch. storage->md is + assumed to be valid. + \a storage is owned by the caller and must survive for the + lifetime of batch. This usually means it should be around + for the lifetime of the call. */ +void grpc_metadata_batch_link_tail(grpc_metadata_batch *batch, + grpc_linked_mdelem *storage); + +/** Add \a elem_to_add as the first element in \a batch, using + \a storage as backing storage for the linked list element. + \a storage is owned by the caller and must survive for the + lifetime of batch. This usually means it should be around + for the lifetime of the call. + Takes ownership of \a elem_to_add */ +void grpc_metadata_batch_add_head(grpc_metadata_batch *batch, + grpc_linked_mdelem *storage, + grpc_mdelem *elem_to_add); +/** Add \a elem_to_add as the last element in \a batch, using + \a storage as backing storage for the linked list element. + \a storage is owned by the caller and must survive for the + lifetime of batch. This usually means it should be around + for the lifetime of the call. + Takes ownership of \a elem_to_add */ +void grpc_metadata_batch_add_tail(grpc_metadata_batch *batch, + grpc_linked_mdelem *storage, + grpc_mdelem *elem_to_add); + +/** For each element in \a batch, execute \a filter. + The return value from \a filter will be substituted for the + grpc_mdelem passed to \a filter. If \a filter returns NULL, + the element will be moved to the garbage list. */ +void grpc_metadata_batch_filter(grpc_metadata_batch *batch, + grpc_mdelem *(*filter)(void *user_data, + grpc_mdelem *elem), + void *user_data); + +#ifndef NDEBUG +void grpc_metadata_batch_assert_ok(grpc_metadata_batch *comd); +#else +#define grpc_metadata_batch_assert_ok(comd) \ + do { \ + } while (0) +#endif + +/* Represents a single operation performed on a stream/transport */ +typedef struct grpc_stream_op { + /* the operation to be applied */ + enum grpc_stream_op_code type; + /* the arguments to this operation. union fields are named according to the + associated op-code */ + union { + grpc_begin_message begin_message; + grpc_metadata_batch metadata; + gpr_slice slice; + } data; +} grpc_stream_op; + +/** A stream op buffer is a wrapper around stream operations that is + * dynamically extendable. */ +typedef struct grpc_stream_op_buffer { + grpc_stream_op *ops; + size_t nops; + size_t capacity; + grpc_stream_op inlined_ops[GRPC_SOPB_INLINE_ELEMENTS]; +} grpc_stream_op_buffer; + +/* Initialize a stream op buffer */ +void grpc_sopb_init(grpc_stream_op_buffer *sopb); +/* Destroy a stream op buffer */ +void grpc_sopb_destroy(grpc_stream_op_buffer *sopb); +/* Reset a sopb to no elements */ +void grpc_sopb_reset(grpc_stream_op_buffer *sopb); +/* Swap two sopbs */ +void grpc_sopb_swap(grpc_stream_op_buffer *a, grpc_stream_op_buffer *b); + +void grpc_stream_ops_unref_owned_objects(grpc_stream_op *ops, size_t nops); + +/* Append a GRPC_NO_OP to a buffer */ +void grpc_sopb_add_no_op(grpc_stream_op_buffer *sopb); +/* Append a GRPC_OP_BEGIN to a buffer */ +void grpc_sopb_add_begin_message(grpc_stream_op_buffer *sopb, gpr_uint32 length, + gpr_uint32 flags); +void grpc_sopb_add_metadata(grpc_stream_op_buffer *sopb, + grpc_metadata_batch metadata); +/* Append a GRPC_SLICE to a buffer - does not ref/unref the slice */ +void grpc_sopb_add_slice(grpc_stream_op_buffer *sopb, gpr_slice slice); +/* Append a buffer to a buffer - does not ref/unref any internal objects */ +void grpc_sopb_append(grpc_stream_op_buffer *sopb, grpc_stream_op *ops, + size_t nops); + +void grpc_sopb_move_to(grpc_stream_op_buffer *src, grpc_stream_op_buffer *dst); + +char *grpc_sopb_string(grpc_stream_op_buffer *sopb); + +#endif /* GRPC_INTERNAL_CORE_TRANSPORT_STREAM_OP_H */ diff --git a/src/core/transport/transport.c b/src/core/transport/transport.c new file mode 100644 index 00000000..c0d92cf9 --- /dev/null +++ b/src/core/transport/transport.c @@ -0,0 +1,138 @@ +/* + * + * Copyright 2015, 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 "src/core/transport/transport.h" +#include +#include +#include "src/core/transport/transport_impl.h" + +size_t grpc_transport_stream_size(grpc_transport *transport) { + return transport->vtable->sizeof_stream; +} + +void grpc_transport_destroy(grpc_transport *transport) { + transport->vtable->destroy(transport); +} + +int grpc_transport_init_stream(grpc_transport *transport, grpc_stream *stream, + const void *server_data, + grpc_transport_stream_op *initial_op) { + return transport->vtable->init_stream(transport, stream, server_data, + initial_op); +} + +void grpc_transport_perform_stream_op(grpc_transport *transport, + grpc_stream *stream, + grpc_transport_stream_op *op) { + transport->vtable->perform_stream_op(transport, stream, op); +} + +void grpc_transport_perform_op(grpc_transport *transport, + grpc_transport_op *op) { + transport->vtable->perform_op(transport, op); +} + +void grpc_transport_destroy_stream(grpc_transport *transport, + grpc_stream *stream) { + transport->vtable->destroy_stream(transport, stream); +} + +char *grpc_transport_get_peer(grpc_transport *transport) { + return transport->vtable->get_peer(transport); +} + +void grpc_transport_stream_op_finish_with_failure( + grpc_transport_stream_op *op) { + if (op->send_ops) { + op->on_done_send->cb(op->on_done_send->cb_arg, 0); + } + if (op->recv_ops) { + op->on_done_recv->cb(op->on_done_recv->cb_arg, 0); + } + if (op->on_consumed) { + op->on_consumed->cb(op->on_consumed->cb_arg, 0); + } +} + +void grpc_transport_stream_op_add_cancellation(grpc_transport_stream_op *op, + grpc_status_code status) { + GPR_ASSERT(status != GRPC_STATUS_OK); + if (op->cancel_with_status == GRPC_STATUS_OK) { + op->cancel_with_status = status; + } + if (op->close_with_status != GRPC_STATUS_OK) { + op->close_with_status = GRPC_STATUS_OK; + if (op->optional_close_message != NULL) { + gpr_slice_unref(*op->optional_close_message); + op->optional_close_message = NULL; + } + } +} + +typedef struct { + gpr_slice message; + grpc_iomgr_closure *then_call; + grpc_iomgr_closure closure; +} close_message_data; + +static void free_message(void *p, int iomgr_success) { + close_message_data *cmd = p; + gpr_slice_unref(cmd->message); + if (cmd->then_call != NULL) { + cmd->then_call->cb(cmd->then_call->cb_arg, iomgr_success); + } + gpr_free(cmd); +} + +void grpc_transport_stream_op_add_close(grpc_transport_stream_op *op, + grpc_status_code status, + gpr_slice *optional_message) { + close_message_data *cmd; + GPR_ASSERT(status != GRPC_STATUS_OK); + if (op->cancel_with_status != GRPC_STATUS_OK || + op->close_with_status != GRPC_STATUS_OK) { + if (optional_message) { + gpr_slice_unref(*optional_message); + } + return; + } + if (optional_message) { + cmd = gpr_malloc(sizeof(*cmd)); + cmd->message = *optional_message; + cmd->then_call = op->on_consumed; + grpc_iomgr_closure_init(&cmd->closure, free_message, cmd); + op->on_consumed = &cmd->closure; + op->optional_close_message = &cmd->message; + } + op->close_with_status = status; +} diff --git a/src/core/transport/transport.h b/src/core/transport/transport.h new file mode 100644 index 00000000..92c1f38c --- /dev/null +++ b/src/core/transport/transport.h @@ -0,0 +1,199 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_TRANSPORT_TRANSPORT_H +#define GRPC_INTERNAL_CORE_TRANSPORT_TRANSPORT_H + +#include + +#include "src/core/iomgr/pollset.h" +#include "src/core/iomgr/pollset_set.h" +#include "src/core/transport/stream_op.h" +#include "src/core/channel/context.h" + +/* forward declarations */ +typedef struct grpc_transport grpc_transport; + +/* grpc_stream doesn't actually exist. It's used as a typesafe + opaque pointer for whatever data the transport wants to track + for a stream. */ +typedef struct grpc_stream grpc_stream; + +/* Represents the send/recv closed state of a stream. */ +typedef enum grpc_stream_state { + /* the stream is open for sends and receives */ + GRPC_STREAM_OPEN, + /* the stream is closed for sends, but may still receive data */ + GRPC_STREAM_SEND_CLOSED, + /* the stream is closed for receives, but may still send data */ + GRPC_STREAM_RECV_CLOSED, + /* the stream is closed for both sends and receives */ + GRPC_STREAM_CLOSED +} grpc_stream_state; + +/* Transport stream op: a set of operations to perform on a transport + against a single stream */ +typedef struct grpc_transport_stream_op { + grpc_iomgr_closure *on_consumed; + + grpc_stream_op_buffer *send_ops; + int is_last_send; + grpc_iomgr_closure *on_done_send; + + grpc_stream_op_buffer *recv_ops; + grpc_stream_state *recv_state; + /** The number of bytes this peer is currently prepared to receive. + These bytes will be eventually used to replenish per-stream flow control + windows. */ + gpr_uint32 max_recv_bytes; + grpc_iomgr_closure *on_done_recv; + + grpc_pollset *bind_pollset; + + /** If != GRPC_STATUS_OK, cancel this stream */ + grpc_status_code cancel_with_status; + + /** If != GRPC_STATUS_OK, send grpc-status, grpc-message, and close this + stream for both reading and writing */ + grpc_status_code close_with_status; + gpr_slice *optional_close_message; + + /* Indexes correspond to grpc_context_index enum values */ + grpc_call_context_element *context; +} grpc_transport_stream_op; + +/** Transport op: a set of operations to perform on a transport as a whole */ +typedef struct grpc_transport_op { + /** called when processing of this op is done */ + grpc_iomgr_closure *on_consumed; + /** connectivity monitoring */ + grpc_iomgr_closure *on_connectivity_state_change; + grpc_connectivity_state *connectivity_state; + /** should the transport be disconnected */ + int disconnect; + /** should we send a goaway? + after a goaway is sent, once there are no more active calls on + the transport, the transport should disconnect */ + int send_goaway; + /** what should the goaway contain? */ + grpc_status_code goaway_status; + gpr_slice *goaway_message; + /** set the callback for accepting new streams; + this is a permanent callback, unlike the other one-shot closures */ + void (*set_accept_stream)(void *user_data, grpc_transport *transport, + const void *server_data); + void *set_accept_stream_user_data; + /** add this transport to a pollset */ + grpc_pollset *bind_pollset; + /** add this transport to a pollset_set */ + grpc_pollset_set *bind_pollset_set; + /** send a ping, call this back if not NULL */ + grpc_iomgr_closure *send_ping; +} grpc_transport_op; + +/* Returns the amount of memory required to store a grpc_stream for this + transport */ +size_t grpc_transport_stream_size(grpc_transport *transport); + +/* Initialize transport data for a stream. + + Returns 0 on success, any other (transport-defined) value for failure. + + Arguments: + transport - the transport on which to create this stream + stream - a pointer to uninitialized memory to initialize + server_data - either NULL for a client initiated stream, or a pointer + supplied from the accept_stream callback function */ +int grpc_transport_init_stream(grpc_transport *transport, grpc_stream *stream, + const void *server_data, + grpc_transport_stream_op *initial_op); + +/* Destroy transport data for a stream. + + Requires: a recv_batch with final_state == GRPC_STREAM_CLOSED has been + received by the up-layer. Must not be called in the same call stack as + recv_frame. + + Arguments: + transport - the transport on which to create this stream + stream - the grpc_stream to destroy (memory is still owned by the + caller, but any child memory must be cleaned up) */ +void grpc_transport_destroy_stream(grpc_transport *transport, + grpc_stream *stream); + +void grpc_transport_stream_op_finish_with_failure(grpc_transport_stream_op *op); + +void grpc_transport_stream_op_add_cancellation(grpc_transport_stream_op *op, + grpc_status_code status); + +void grpc_transport_stream_op_add_close(grpc_transport_stream_op *op, + grpc_status_code status, + gpr_slice *optional_message); + +char *grpc_transport_stream_op_string(grpc_transport_stream_op *op); + +/* Send a batch of operations on a transport + + Takes ownership of any objects contained in ops. + + Arguments: + transport - the transport on which to initiate the stream + stream - the stream on which to send the operations. This must be + non-NULL and previously initialized by the same transport. + op - a grpc_transport_stream_op specifying the op to perform */ +void grpc_transport_perform_stream_op(grpc_transport *transport, + grpc_stream *stream, + grpc_transport_stream_op *op); + +void grpc_transport_perform_op(grpc_transport *transport, + grpc_transport_op *op); + +/* Send a ping on a transport + + Calls cb with user data when a response is received. */ +void grpc_transport_ping(grpc_transport *transport, grpc_iomgr_closure *cb); + +/* Advise peer of pending connection termination. */ +void grpc_transport_goaway(grpc_transport *transport, grpc_status_code status, + gpr_slice debug_data); + +/* Close a transport. Aborts all open streams. */ +void grpc_transport_close(grpc_transport *transport); + +/* Destroy the transport */ +void grpc_transport_destroy(grpc_transport *transport); + +/* Get the transports peer */ +char *grpc_transport_get_peer(grpc_transport *transport); + +#endif /* GRPC_INTERNAL_CORE_TRANSPORT_TRANSPORT_H */ diff --git a/src/core/transport/transport_impl.h b/src/core/transport/transport_impl.h new file mode 100644 index 00000000..d3bbdf6c --- /dev/null +++ b/src/core/transport/transport_impl.h @@ -0,0 +1,72 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_TRANSPORT_TRANSPORT_IMPL_H +#define GRPC_INTERNAL_CORE_TRANSPORT_TRANSPORT_IMPL_H + +#include "src/core/transport/transport.h" + +typedef struct grpc_transport_vtable { + /* Memory required for a single stream element - this is allocated by upper + layers and initialized by the transport */ + size_t sizeof_stream; /* = sizeof(transport stream) */ + + /* implementation of grpc_transport_init_stream */ + int (*init_stream)(grpc_transport *self, grpc_stream *stream, + const void *server_data, + grpc_transport_stream_op *initial_op); + + /* implementation of grpc_transport_perform_stream_op */ + void (*perform_stream_op)(grpc_transport *self, grpc_stream *stream, + grpc_transport_stream_op *op); + + /* implementation of grpc_transport_perform_op */ + void (*perform_op)(grpc_transport *self, grpc_transport_op *op); + + /* implementation of grpc_transport_destroy_stream */ + void (*destroy_stream)(grpc_transport *self, grpc_stream *stream); + + /* implementation of grpc_transport_destroy */ + void (*destroy)(grpc_transport *self); + + /* implementation of grpc_transport_get_peer */ + char *(*get_peer)(grpc_transport *self); +} grpc_transport_vtable; + +/* an instance of a grpc transport */ +struct grpc_transport { + /* pointer to a vtable defining operations on this transport */ + const grpc_transport_vtable *vtable; +}; + +#endif /* GRPC_INTERNAL_CORE_TRANSPORT_TRANSPORT_IMPL_H */ diff --git a/src/core/transport/transport_op_string.c b/src/core/transport/transport_op_string.c new file mode 100644 index 00000000..f62c340e --- /dev/null +++ b/src/core/transport/transport_op_string.c @@ -0,0 +1,166 @@ +/* + * + * Copyright 2015, 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 "src/core/channel/channel_stack.h" + +#include +#include +#include + +#include "src/core/support/string.h" +#include +#include +#include + +/* These routines are here to facilitate debugging - they produce string + representations of various transport data structures */ + +static void put_metadata(gpr_strvec *b, grpc_mdelem *md) { + gpr_strvec_add(b, gpr_strdup("key=")); + gpr_strvec_add(b, + gpr_dump_slice(md->key->slice, GPR_DUMP_HEX | GPR_DUMP_ASCII)); + + gpr_strvec_add(b, gpr_strdup(" value=")); + gpr_strvec_add( + b, gpr_dump_slice(md->value->slice, GPR_DUMP_HEX | GPR_DUMP_ASCII)); +} + +static void put_metadata_list(gpr_strvec *b, grpc_metadata_batch md) { + grpc_linked_mdelem *m; + for (m = md.list.head; m != NULL; m = m->next) { + if (m != md.list.head) gpr_strvec_add(b, gpr_strdup(", ")); + put_metadata(b, m->md); + } + if (gpr_time_cmp(md.deadline, gpr_inf_future(md.deadline.clock_type)) != 0) { + char *tmp; + gpr_asprintf(&tmp, " deadline=%d.%09d", md.deadline.tv_sec, + md.deadline.tv_nsec); + gpr_strvec_add(b, tmp); + } +} + +char *grpc_sopb_string(grpc_stream_op_buffer *sopb) { + char *out; + char *tmp; + size_t i; + gpr_strvec b; + gpr_strvec_init(&b); + + for (i = 0; i < sopb->nops; i++) { + grpc_stream_op *op = &sopb->ops[i]; + if (i > 0) gpr_strvec_add(&b, gpr_strdup(", ")); + switch (op->type) { + case GRPC_NO_OP: + gpr_strvec_add(&b, gpr_strdup("NO_OP")); + break; + case GRPC_OP_BEGIN_MESSAGE: + gpr_asprintf(&tmp, "BEGIN_MESSAGE:%d", op->data.begin_message.length); + gpr_strvec_add(&b, tmp); + break; + case GRPC_OP_SLICE: + gpr_asprintf(&tmp, "SLICE:%d", GPR_SLICE_LENGTH(op->data.slice)); + gpr_strvec_add(&b, tmp); + break; + case GRPC_OP_METADATA: + gpr_strvec_add(&b, gpr_strdup("METADATA{")); + put_metadata_list(&b, op->data.metadata); + gpr_strvec_add(&b, gpr_strdup("}")); + break; + } + } + + out = gpr_strvec_flatten(&b, NULL); + gpr_strvec_destroy(&b); + + return out; +} + +char *grpc_transport_stream_op_string(grpc_transport_stream_op *op) { + char *tmp; + char *out; + int first = 1; + + gpr_strvec b; + gpr_strvec_init(&b); + + if (op->send_ops) { + if (!first) gpr_strvec_add(&b, gpr_strdup(" ")); + first = 0; + gpr_asprintf(&tmp, "SEND%s:%p", op->is_last_send ? "_LAST" : "", + op->on_done_send); + gpr_strvec_add(&b, tmp); + gpr_strvec_add(&b, gpr_strdup("[")); + gpr_strvec_add(&b, grpc_sopb_string(op->send_ops)); + gpr_strvec_add(&b, gpr_strdup("]")); + } + + if (op->recv_ops) { + if (!first) gpr_strvec_add(&b, gpr_strdup(" ")); + first = 0; + gpr_asprintf(&tmp, "RECV:%p:max_recv_bytes=%d", op->on_done_recv, + op->max_recv_bytes); + gpr_strvec_add(&b, tmp); + } + + if (op->bind_pollset) { + if (!first) gpr_strvec_add(&b, gpr_strdup(" ")); + first = 0; + gpr_strvec_add(&b, gpr_strdup("BIND")); + } + + if (op->cancel_with_status != GRPC_STATUS_OK) { + if (!first) gpr_strvec_add(&b, gpr_strdup(" ")); + first = 0; + gpr_asprintf(&tmp, "CANCEL:%d", op->cancel_with_status); + gpr_strvec_add(&b, tmp); + } + + if (op->on_consumed != NULL) { + if (!first) gpr_strvec_add(&b, gpr_strdup(" ")); + first = 0; + gpr_asprintf(&tmp, "ON_CONSUMED:%p", op->on_consumed); + gpr_strvec_add(&b, tmp); + } + + out = gpr_strvec_flatten(&b, NULL); + gpr_strvec_destroy(&b); + + return out; +} + +void grpc_call_log_op(char *file, int line, gpr_log_severity severity, + grpc_call_element *elem, grpc_transport_stream_op *op) { + char *str = grpc_transport_stream_op_string(op); + gpr_log(file, line, severity, "OP[%s:%p]: %s", elem->filter->name, elem, str); + gpr_free(str); +} diff --git a/src/core/tsi/fake_transport_security.c b/src/core/tsi/fake_transport_security.c new file mode 100644 index 00000000..29127c42 --- /dev/null +++ b/src/core/tsi/fake_transport_security.c @@ -0,0 +1,525 @@ +/* + * + * Copyright 2015, 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 "src/core/tsi/fake_transport_security.h" + +#include +#include + +#include +#include +#include +#include "src/core/tsi/transport_security.h" + +/* --- Constants. ---*/ +#define TSI_FAKE_FRAME_HEADER_SIZE 4 +#define TSI_FAKE_FRAME_INITIAL_ALLOCATED_SIZE 64 +#define TSI_FAKE_DEFAULT_FRAME_SIZE 16384 + +/* --- Structure definitions. ---*/ + +/* a frame is encoded like this: + | size | data | + where the size field value is the size of the size field plus the size of + the data encoded in little endian on 4 bytes. */ +typedef struct { + unsigned char* data; + size_t size; + size_t allocated_size; + size_t offset; + int needs_draining; +} tsi_fake_frame; + +typedef enum { + TSI_FAKE_CLIENT_INIT = 0, + TSI_FAKE_SERVER_INIT = 1, + TSI_FAKE_CLIENT_FINISHED = 2, + TSI_FAKE_SERVER_FINISHED = 3, + TSI_FAKE_HANDSHAKE_MESSAGE_MAX = 4 +} tsi_fake_handshake_message; + +typedef struct { + tsi_handshaker base; + int is_client; + tsi_fake_handshake_message next_message_to_send; + int needs_incoming_message; + tsi_fake_frame incoming; + tsi_fake_frame outgoing; + tsi_result result; +} tsi_fake_handshaker; + +typedef struct { + tsi_frame_protector base; + tsi_fake_frame protect_frame; + tsi_fake_frame unprotect_frame; + size_t max_frame_size; +} tsi_fake_frame_protector; + +/* --- Utils. ---*/ + +static const char* tsi_fake_handshake_message_strings[] = { + "CLIENT_INIT", "SERVER_INIT", "CLIENT_FINISHED", "SERVER_FINISHED"}; + +static const char* tsi_fake_handshake_message_to_string(int msg) { + if (msg < 0 || msg >= TSI_FAKE_HANDSHAKE_MESSAGE_MAX) { + gpr_log(GPR_ERROR, "Invalid message %d", msg); + return "UNKNOWN"; + } + return tsi_fake_handshake_message_strings[msg]; +} + +static tsi_result tsi_fake_handshake_message_from_string( + const char* msg_string, tsi_fake_handshake_message* msg) { + int i; + for (i = 0; i < TSI_FAKE_HANDSHAKE_MESSAGE_MAX; i++) { + if (strncmp(msg_string, tsi_fake_handshake_message_strings[i], + strlen(tsi_fake_handshake_message_strings[i])) == 0) { + *msg = i; + return TSI_OK; + } + } + gpr_log(GPR_ERROR, "Invalid handshake message."); + return TSI_DATA_CORRUPTED; +} + +static gpr_uint32 load32_little_endian(const unsigned char* buf) { + return ((gpr_uint32)(buf[0]) | (gpr_uint32)(buf[1] << 8) | + (gpr_uint32)(buf[2] << 16) | (gpr_uint32)(buf[3] << 24)); +} + +static void store32_little_endian(gpr_uint32 value, unsigned char* buf) { + buf[3] = (unsigned char)(value >> 24) & 0xFF; + buf[2] = (unsigned char)(value >> 16) & 0xFF; + buf[1] = (unsigned char)(value >> 8) & 0xFF; + buf[0] = (unsigned char)(value)&0xFF; +} + +static void tsi_fake_frame_reset(tsi_fake_frame* frame, int needs_draining) { + frame->offset = 0; + frame->needs_draining = needs_draining; + if (!needs_draining) frame->size = 0; +} + +/* Returns 1 if successful, 0 otherwise. */ +static int tsi_fake_frame_ensure_size(tsi_fake_frame* frame) { + if (frame->data == NULL) { + frame->allocated_size = frame->size; + frame->data = malloc(frame->allocated_size); + if (frame->data == NULL) return 0; + } else if (frame->size > frame->allocated_size) { + unsigned char* new_data = realloc(frame->data, frame->size); + if (new_data == NULL) { + free(frame->data); + frame->data = NULL; + return 0; + } + frame->data = new_data; + frame->allocated_size = frame->size; + } + return 1; +} + +/* This method should not be called if frame->needs_framing is not 0. */ +static tsi_result fill_frame_from_bytes(const unsigned char* incoming_bytes, + size_t* incoming_bytes_size, + tsi_fake_frame* frame) { + size_t available_size = *incoming_bytes_size; + size_t to_read_size = 0; + const unsigned char* bytes_cursor = incoming_bytes; + + if (frame->needs_draining) return TSI_INTERNAL_ERROR; + if (frame->data == NULL) { + frame->allocated_size = TSI_FAKE_FRAME_INITIAL_ALLOCATED_SIZE; + frame->data = malloc(frame->allocated_size); + if (frame->data == NULL) return TSI_OUT_OF_RESOURCES; + } + + if (frame->offset < TSI_FAKE_FRAME_HEADER_SIZE) { + to_read_size = TSI_FAKE_FRAME_HEADER_SIZE - frame->offset; + if (to_read_size > available_size) { + /* Just fill what we can and exit. */ + memcpy(frame->data + frame->offset, bytes_cursor, available_size); + bytes_cursor += available_size; + frame->offset += available_size; + *incoming_bytes_size = bytes_cursor - incoming_bytes; + return TSI_INCOMPLETE_DATA; + } + memcpy(frame->data + frame->offset, bytes_cursor, to_read_size); + bytes_cursor += to_read_size; + frame->offset += to_read_size; + available_size -= to_read_size; + frame->size = load32_little_endian(frame->data); + if (!tsi_fake_frame_ensure_size(frame)) return TSI_OUT_OF_RESOURCES; + } + + to_read_size = frame->size - frame->offset; + if (to_read_size > available_size) { + memcpy(frame->data + frame->offset, bytes_cursor, available_size); + frame->offset += available_size; + bytes_cursor += available_size; + *incoming_bytes_size = bytes_cursor - incoming_bytes; + return TSI_INCOMPLETE_DATA; + } + memcpy(frame->data + frame->offset, bytes_cursor, to_read_size); + bytes_cursor += to_read_size; + *incoming_bytes_size = bytes_cursor - incoming_bytes; + tsi_fake_frame_reset(frame, 1 /* needs_draining */); + return TSI_OK; +} + +/* This method should not be called if frame->needs_framing is 0. */ +static tsi_result drain_frame_to_bytes(unsigned char* outgoing_bytes, + size_t* outgoing_bytes_size, + tsi_fake_frame* frame) { + size_t to_write_size = frame->size - frame->offset; + if (!frame->needs_draining) return TSI_INTERNAL_ERROR; + if (*outgoing_bytes_size < to_write_size) { + memcpy(outgoing_bytes, frame->data + frame->offset, *outgoing_bytes_size); + frame->offset += *outgoing_bytes_size; + return TSI_INCOMPLETE_DATA; + } + memcpy(outgoing_bytes, frame->data + frame->offset, to_write_size); + *outgoing_bytes_size = to_write_size; + tsi_fake_frame_reset(frame, 0 /* needs_draining */); + return TSI_OK; +} + +static tsi_result bytes_to_frame(unsigned char* bytes, size_t bytes_size, + tsi_fake_frame* frame) { + frame->offset = 0; + frame->size = bytes_size + TSI_FAKE_FRAME_HEADER_SIZE; + if (!tsi_fake_frame_ensure_size(frame)) return TSI_OUT_OF_RESOURCES; + store32_little_endian(frame->size, frame->data); + memcpy(frame->data + TSI_FAKE_FRAME_HEADER_SIZE, bytes, bytes_size); + tsi_fake_frame_reset(frame, 1 /* needs draining */); + return TSI_OK; +} + +static void tsi_fake_frame_destruct(tsi_fake_frame* frame) { + if (frame->data != NULL) free(frame->data); +} + +/* --- tsi_frame_protector methods implementation. ---*/ + +static tsi_result fake_protector_protect(tsi_frame_protector* self, + const unsigned char* unprotected_bytes, + size_t* unprotected_bytes_size, + unsigned char* protected_output_frames, + size_t* protected_output_frames_size) { + tsi_result result = TSI_OK; + tsi_fake_frame_protector* impl = (tsi_fake_frame_protector*)self; + unsigned char frame_header[TSI_FAKE_FRAME_HEADER_SIZE]; + tsi_fake_frame* frame = &impl->protect_frame; + size_t saved_output_size = *protected_output_frames_size; + size_t drained_size = 0; + size_t* num_bytes_written = protected_output_frames_size; + *num_bytes_written = 0; + + /* Try to drain first. */ + if (frame->needs_draining) { + drained_size = saved_output_size - *num_bytes_written; + result = + drain_frame_to_bytes(protected_output_frames, &drained_size, frame); + *num_bytes_written += drained_size; + protected_output_frames += drained_size; + if (result != TSI_OK) { + if (result == TSI_INCOMPLETE_DATA) { + *unprotected_bytes_size = 0; + result = TSI_OK; + } + return result; + } + } + + /* Now process the unprotected_bytes. */ + if (frame->needs_draining) return TSI_INTERNAL_ERROR; + if (frame->size == 0) { + /* New frame, create a header. */ + size_t written_in_frame_size = 0; + store32_little_endian(impl->max_frame_size, frame_header); + written_in_frame_size = TSI_FAKE_FRAME_HEADER_SIZE; + result = fill_frame_from_bytes(frame_header, &written_in_frame_size, frame); + if (result != TSI_INCOMPLETE_DATA) { + gpr_log(GPR_ERROR, "fill_frame_from_bytes returned %s", + tsi_result_to_string(result)); + return result; + } + } + result = + fill_frame_from_bytes(unprotected_bytes, unprotected_bytes_size, frame); + if (result != TSI_OK) { + if (result == TSI_INCOMPLETE_DATA) result = TSI_OK; + return result; + } + + /* Try to drain again. */ + if (!frame->needs_draining) return TSI_INTERNAL_ERROR; + if (frame->offset != 0) return TSI_INTERNAL_ERROR; + drained_size = saved_output_size - *num_bytes_written; + result = drain_frame_to_bytes(protected_output_frames, &drained_size, frame); + *num_bytes_written += drained_size; + if (result == TSI_INCOMPLETE_DATA) result = TSI_OK; + return result; +} + +static tsi_result fake_protector_protect_flush( + tsi_frame_protector* self, unsigned char* protected_output_frames, + size_t* protected_output_frames_size, size_t* still_pending_size) { + tsi_result result = TSI_OK; + tsi_fake_frame_protector* impl = (tsi_fake_frame_protector*)self; + tsi_fake_frame* frame = &impl->protect_frame; + if (!frame->needs_draining) { + /* Create a short frame. */ + frame->size = frame->offset; + frame->offset = 0; + frame->needs_draining = 1; + store32_little_endian(frame->size, frame->data); /* Overwrite header. */ + } + result = drain_frame_to_bytes(protected_output_frames, + protected_output_frames_size, frame); + if (result == TSI_INCOMPLETE_DATA) result = TSI_OK; + *still_pending_size = frame->size - frame->offset; + return result; +} + +static tsi_result fake_protector_unprotect( + tsi_frame_protector* self, const unsigned char* protected_frames_bytes, + size_t* protected_frames_bytes_size, unsigned char* unprotected_bytes, + size_t* unprotected_bytes_size) { + tsi_result result = TSI_OK; + tsi_fake_frame_protector* impl = (tsi_fake_frame_protector*)self; + tsi_fake_frame* frame = &impl->unprotect_frame; + size_t saved_output_size = *unprotected_bytes_size; + size_t drained_size = 0; + size_t* num_bytes_written = unprotected_bytes_size; + *num_bytes_written = 0; + + /* Try to drain first. */ + if (frame->needs_draining) { + /* Go past the header if needed. */ + if (frame->offset == 0) frame->offset = TSI_FAKE_FRAME_HEADER_SIZE; + drained_size = saved_output_size - *num_bytes_written; + result = drain_frame_to_bytes(unprotected_bytes, &drained_size, frame); + unprotected_bytes += drained_size; + *num_bytes_written += drained_size; + if (result != TSI_OK) { + if (result == TSI_INCOMPLETE_DATA) { + *protected_frames_bytes_size = 0; + result = TSI_OK; + } + return result; + } + } + + /* Now process the protected_bytes. */ + if (frame->needs_draining) return TSI_INTERNAL_ERROR; + result = fill_frame_from_bytes(protected_frames_bytes, + protected_frames_bytes_size, frame); + if (result != TSI_OK) { + if (result == TSI_INCOMPLETE_DATA) result = TSI_OK; + return result; + } + + /* Try to drain again. */ + if (!frame->needs_draining) return TSI_INTERNAL_ERROR; + if (frame->offset != 0) return TSI_INTERNAL_ERROR; + frame->offset = TSI_FAKE_FRAME_HEADER_SIZE; /* Go past the header. */ + drained_size = saved_output_size - *num_bytes_written; + result = drain_frame_to_bytes(unprotected_bytes, &drained_size, frame); + *num_bytes_written += drained_size; + if (result == TSI_INCOMPLETE_DATA) result = TSI_OK; + return result; +} + +static void fake_protector_destroy(tsi_frame_protector* self) { + tsi_fake_frame_protector* impl = (tsi_fake_frame_protector*)self; + tsi_fake_frame_destruct(&impl->protect_frame); + tsi_fake_frame_destruct(&impl->unprotect_frame); + free(self); +} + +static const tsi_frame_protector_vtable frame_protector_vtable = { + fake_protector_protect, fake_protector_protect_flush, + fake_protector_unprotect, fake_protector_destroy, +}; + +/* --- tsi_handshaker methods implementation. ---*/ + +static tsi_result fake_handshaker_get_bytes_to_send_to_peer( + tsi_handshaker* self, unsigned char* bytes, size_t* bytes_size) { + tsi_fake_handshaker* impl = (tsi_fake_handshaker*)self; + tsi_result result = TSI_OK; + if (impl->needs_incoming_message || impl->result == TSI_OK) { + *bytes_size = 0; + return TSI_OK; + } + if (!impl->outgoing.needs_draining) { + int next_message_to_send = impl->next_message_to_send + 2; + const char* msg_string = + tsi_fake_handshake_message_to_string(impl->next_message_to_send); + result = bytes_to_frame((unsigned char*)msg_string, strlen(msg_string), + &impl->outgoing); + if (result != TSI_OK) return result; + if (next_message_to_send > TSI_FAKE_HANDSHAKE_MESSAGE_MAX) { + next_message_to_send = TSI_FAKE_HANDSHAKE_MESSAGE_MAX; + } + if (tsi_tracing_enabled) { + gpr_log(GPR_INFO, "%s prepared %s.", + impl->is_client ? "Client" : "Server", + tsi_fake_handshake_message_to_string(impl->next_message_to_send)); + } + impl->next_message_to_send = next_message_to_send; + } + result = drain_frame_to_bytes(bytes, bytes_size, &impl->outgoing); + if (result != TSI_OK) return result; + if (!impl->is_client && + impl->next_message_to_send == TSI_FAKE_HANDSHAKE_MESSAGE_MAX) { + /* We're done. */ + if (tsi_tracing_enabled) { + gpr_log(GPR_INFO, "Server is done."); + } + impl->result = TSI_OK; + } else { + impl->needs_incoming_message = 1; + } + return TSI_OK; +} + +static tsi_result fake_handshaker_process_bytes_from_peer( + tsi_handshaker* self, const unsigned char* bytes, size_t* bytes_size) { + tsi_result result = TSI_OK; + tsi_fake_handshaker* impl = (tsi_fake_handshaker*)self; + tsi_fake_handshake_message expected_msg = impl->next_message_to_send - 1; + tsi_fake_handshake_message received_msg; + + if (!impl->needs_incoming_message || impl->result == TSI_OK) { + *bytes_size = 0; + return TSI_OK; + } + result = fill_frame_from_bytes(bytes, bytes_size, &impl->incoming); + if (result != TSI_OK) return result; + + /* We now have a complete frame. */ + result = tsi_fake_handshake_message_from_string( + (const char*)impl->incoming.data + TSI_FAKE_FRAME_HEADER_SIZE, + &received_msg); + if (result != TSI_OK) { + impl->result = result; + return result; + } + if (received_msg != expected_msg) { + gpr_log(GPR_ERROR, "Invalid received message (%s instead of %s)", + tsi_fake_handshake_message_to_string(received_msg), + tsi_fake_handshake_message_to_string(expected_msg)); + } + if (tsi_tracing_enabled) { + gpr_log(GPR_INFO, "%s received %s.", impl->is_client ? "Client" : "Server", + tsi_fake_handshake_message_to_string(received_msg)); + } + tsi_fake_frame_reset(&impl->incoming, 0 /* needs_draining */); + impl->needs_incoming_message = 0; + if (impl->next_message_to_send == TSI_FAKE_HANDSHAKE_MESSAGE_MAX) { + /* We're done. */ + if (tsi_tracing_enabled) { + gpr_log(GPR_INFO, "%s is done.", impl->is_client ? "Client" : "Server"); + } + impl->result = TSI_OK; + } + return TSI_OK; +} + +static tsi_result fake_handshaker_get_result(tsi_handshaker* self) { + tsi_fake_handshaker* impl = (tsi_fake_handshaker*)self; + return impl->result; +} + +static tsi_result fake_handshaker_extract_peer(tsi_handshaker* self, + tsi_peer* peer) { + tsi_result result = tsi_construct_peer(1, peer); + if (result != TSI_OK) return result; + result = tsi_construct_string_peer_property_from_cstring( + TSI_CERTIFICATE_TYPE_PEER_PROPERTY, TSI_FAKE_CERTIFICATE_TYPE, + &peer->properties[0]); + if (result != TSI_OK) tsi_peer_destruct(peer); + return result; +} + +static tsi_result fake_handshaker_create_frame_protector( + tsi_handshaker* self, size_t* max_protected_frame_size, + tsi_frame_protector** protector) { + *protector = tsi_create_fake_protector(max_protected_frame_size); + if (*protector == NULL) return TSI_OUT_OF_RESOURCES; + return TSI_OK; +} + +static void fake_handshaker_destroy(tsi_handshaker* self) { + tsi_fake_handshaker* impl = (tsi_fake_handshaker*)self; + tsi_fake_frame_destruct(&impl->incoming); + tsi_fake_frame_destruct(&impl->outgoing); + free(self); +} + +static const tsi_handshaker_vtable handshaker_vtable = { + fake_handshaker_get_bytes_to_send_to_peer, + fake_handshaker_process_bytes_from_peer, + fake_handshaker_get_result, + fake_handshaker_extract_peer, + fake_handshaker_create_frame_protector, + fake_handshaker_destroy, +}; + +tsi_handshaker* tsi_create_fake_handshaker(int is_client) { + tsi_fake_handshaker* impl = calloc(1, sizeof(tsi_fake_handshaker)); + impl->base.vtable = &handshaker_vtable; + impl->is_client = is_client; + impl->result = TSI_HANDSHAKE_IN_PROGRESS; + if (is_client) { + impl->needs_incoming_message = 0; + impl->next_message_to_send = TSI_FAKE_CLIENT_INIT; + } else { + impl->needs_incoming_message = 1; + impl->next_message_to_send = TSI_FAKE_SERVER_INIT; + } + return &impl->base; +} + +tsi_frame_protector* tsi_create_fake_protector( + size_t* max_protected_frame_size) { + tsi_fake_frame_protector* impl = calloc(1, sizeof(tsi_fake_frame_protector)); + if (impl == NULL) return NULL; + impl->max_frame_size = (max_protected_frame_size == NULL) + ? TSI_FAKE_DEFAULT_FRAME_SIZE + : *max_protected_frame_size; + impl->base.vtable = &frame_protector_vtable; + return &impl->base; +} diff --git a/src/core/tsi/fake_transport_security.h b/src/core/tsi/fake_transport_security.h new file mode 100644 index 00000000..1fa11349 --- /dev/null +++ b/src/core/tsi/fake_transport_security.h @@ -0,0 +1,61 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_TSI_FAKE_TRANSPORT_SECURITY_H +#define GRPC_INTERNAL_CORE_TSI_FAKE_TRANSPORT_SECURITY_H + +#include "src/core/tsi/transport_security_interface.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Value for the TSI_CERTIFICATE_TYPE_PEER_PROPERTY property for FAKE certs. */ +#define TSI_FAKE_CERTIFICATE_TYPE "FAKE" + +/* Creates a fake handshaker that will create a fake frame protector. + + No cryptography is performed in these objects. They just simulate handshake + messages going back and forth for the handshaker and do some framing on + cleartext data for the protector. */ +tsi_handshaker* tsi_create_fake_handshaker(int is_client); + +/* Creates a protector directly without going through the handshake phase. */ +tsi_frame_protector* tsi_create_fake_protector( + size_t* max_protected_frame_size); + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_INTERNAL_CORE_TSI_FAKE_TRANSPORT_SECURITY_H */ diff --git a/src/core/tsi/ssl_transport_security.c b/src/core/tsi/ssl_transport_security.c new file mode 100644 index 00000000..0b416f6c --- /dev/null +++ b/src/core/tsi/ssl_transport_security.c @@ -0,0 +1,1433 @@ +/* + * + * Copyright 2015, 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 "src/core/tsi/ssl_transport_security.h" + +#include +#include + +#include +#include +#include +#include +#include "src/core/tsi/transport_security.h" + +#include +#include /* For OPENSSL_free */ +#include +#include +#include +#include + +/* --- Constants. ---*/ + +#define TSI_SSL_MAX_PROTECTED_FRAME_SIZE_UPPER_BOUND 16384 +#define TSI_SSL_MAX_PROTECTED_FRAME_SIZE_LOWER_BOUND 1024 + +/* Putting a macro like this and littering the source file with #if is really + bad practice. + TODO(jboeuf): refactor all the #if / #endif in a separate module. */ +#ifndef TSI_OPENSSL_ALPN_SUPPORT +#define TSI_OPENSSL_ALPN_SUPPORT 1 +#endif + +/* TODO(jboeuf): I have not found a way to get this number dynamically from the + SSL structure. This is what we would ultimately want though... */ +#define TSI_SSL_MAX_PROTECTION_OVERHEAD 100 + +/* --- Structure definitions. ---*/ + +struct tsi_ssl_handshaker_factory { + tsi_result (*create_handshaker)(tsi_ssl_handshaker_factory* self, + const char* server_name_indication, + tsi_handshaker** handshaker); + void (*destroy)(tsi_ssl_handshaker_factory* self); +}; + +typedef struct { + tsi_ssl_handshaker_factory base; + SSL_CTX* ssl_context; + unsigned char* alpn_protocol_list; + size_t alpn_protocol_list_length; +} tsi_ssl_client_handshaker_factory; + +typedef struct { + tsi_ssl_handshaker_factory base; + + /* Several contexts to support SNI. + The tsi_peer array contains the subject names of the server certificates + associated with the contexts at the same index. */ + SSL_CTX** ssl_contexts; + tsi_peer* ssl_context_x509_subject_names; + size_t ssl_context_count; + unsigned char* alpn_protocol_list; + size_t alpn_protocol_list_length; +} tsi_ssl_server_handshaker_factory; + +typedef struct { + tsi_handshaker base; + SSL* ssl; + BIO* into_ssl; + BIO* from_ssl; + tsi_result result; +} tsi_ssl_handshaker; + +typedef struct { + tsi_frame_protector base; + SSL* ssl; + BIO* into_ssl; + BIO* from_ssl; + unsigned char* buffer; + size_t buffer_size; + size_t buffer_offset; +} tsi_ssl_frame_protector; + +/* --- Library Initialization. ---*/ + +static gpr_once init_openssl_once = GPR_ONCE_INIT; +static gpr_mu* openssl_mutexes = NULL; + +static void openssl_locking_cb(int mode, int type, const char* file, int line) { + if (mode & CRYPTO_LOCK) { + gpr_mu_lock(&openssl_mutexes[type]); + } else { + gpr_mu_unlock(&openssl_mutexes[type]); + } +} + +static unsigned long openssl_thread_id_cb(void) { + return (unsigned long)gpr_thd_currentid(); +} + +static void init_openssl(void) { + int i; + SSL_library_init(); + SSL_load_error_strings(); + OpenSSL_add_all_algorithms(); + openssl_mutexes = malloc(CRYPTO_num_locks() * sizeof(gpr_mu)); + GPR_ASSERT(openssl_mutexes != NULL); + for (i = 0; i < CRYPTO_num_locks(); i++) { + gpr_mu_init(&openssl_mutexes[i]); + } + CRYPTO_set_locking_callback(openssl_locking_cb); + CRYPTO_set_id_callback(openssl_thread_id_cb); +} + +/* --- Ssl utils. ---*/ + +static const char* ssl_error_string(int error) { + switch (error) { + case SSL_ERROR_NONE: + return "SSL_ERROR_NONE"; + case SSL_ERROR_ZERO_RETURN: + return "SSL_ERROR_ZERO_RETURN"; + case SSL_ERROR_WANT_READ: + return "SSL_ERROR_WANT_READ"; + case SSL_ERROR_WANT_WRITE: + return "SSL_ERROR_WANT_WRITE"; + case SSL_ERROR_WANT_CONNECT: + return "SSL_ERROR_WANT_CONNECT"; + case SSL_ERROR_WANT_ACCEPT: + return "SSL_ERROR_WANT_ACCEPT"; + case SSL_ERROR_WANT_X509_LOOKUP: + return "SSL_ERROR_WANT_X509_LOOKUP"; + case SSL_ERROR_SYSCALL: + return "SSL_ERROR_SYSCALL"; + case SSL_ERROR_SSL: + return "SSL_ERROR_SSL"; + default: + return "Unknown error"; + } +} + +/* TODO(jboeuf): Remove when we are past the debugging phase with this code. */ +static void ssl_log_where_info(const SSL* ssl, int where, int flag, + const char* msg) { + if ((where & flag) && tsi_tracing_enabled) { + gpr_log(GPR_INFO, "%20.20s - %30.30s - %5.10s", msg, + SSL_state_string_long(ssl), SSL_state_string(ssl)); + } +} + +/* Used for debugging. TODO(jboeuf): Remove when code is mature enough. */ +static void ssl_info_callback(const SSL* ssl, int where, int ret) { + if (ret == 0) { + gpr_log(GPR_ERROR, "ssl_info_callback: error occured.\n"); + return; + } + + ssl_log_where_info(ssl, where, SSL_CB_LOOP, "LOOP"); + ssl_log_where_info(ssl, where, SSL_CB_HANDSHAKE_START, "HANDSHAKE START"); + ssl_log_where_info(ssl, where, SSL_CB_HANDSHAKE_DONE, "HANDSHAKE DONE"); +} + +/* Returns 1 if name looks like an IP address, 0 otherwise. + This is a very rough heuristic as it does not handle IPV6 or things like: + 0300.0250.00.01, 0xC0.0Xa8.0x0.0x1, 000030052000001, 0xc0.052000001 */ +static int looks_like_ip_address(const char* name) { + size_t i; + size_t dot_count = 0; + size_t num_size = 0; + for (i = 0; i < strlen(name); i++) { + if (name[i] >= '0' && name[i] <= '9') { + if (num_size > 3) return 0; + num_size++; + } else if (name[i] == '.') { + if (dot_count > 3 || num_size == 0) return 0; + dot_count++; + num_size = 0; + } else { + return 0; + } + } + if (dot_count < 3 || num_size == 0) return 0; + return 1; +} + +/* Gets the subject CN from an X509 cert. */ +static tsi_result ssl_get_x509_common_name(X509* cert, unsigned char** utf8, + size_t* utf8_size) { + int common_name_index = -1; + X509_NAME_ENTRY* common_name_entry = NULL; + ASN1_STRING* common_name_asn1 = NULL; + X509_NAME* subject_name = X509_get_subject_name(cert); + int utf8_returned_size = 0; + if (subject_name == NULL) { + gpr_log(GPR_ERROR, "Could not get subject name from certificate."); + return TSI_NOT_FOUND; + } + common_name_index = + X509_NAME_get_index_by_NID(subject_name, NID_commonName, -1); + if (common_name_index == -1) { + gpr_log(GPR_ERROR, + "Could not get common name of subject from certificate."); + return TSI_NOT_FOUND; + } + common_name_entry = X509_NAME_get_entry(subject_name, common_name_index); + if (common_name_entry == NULL) { + gpr_log(GPR_ERROR, "Could not get common name entry from certificate."); + return TSI_INTERNAL_ERROR; + } + common_name_asn1 = X509_NAME_ENTRY_get_data(common_name_entry); + if (common_name_asn1 == NULL) { + gpr_log(GPR_ERROR, + "Could not get common name entry asn1 from certificate."); + return TSI_INTERNAL_ERROR; + } + utf8_returned_size = ASN1_STRING_to_UTF8(utf8, common_name_asn1); + if (utf8_returned_size < 0) { + gpr_log(GPR_ERROR, "Could not extract utf8 from asn1 string."); + return TSI_OUT_OF_RESOURCES; + } + *utf8_size = utf8_returned_size; + return TSI_OK; +} + +/* Gets the subject CN of an X509 cert as a tsi_peer_property. */ +static tsi_result peer_property_from_x509_common_name( + X509* cert, tsi_peer_property* property) { + unsigned char* common_name; + size_t common_name_size; + tsi_result result = + ssl_get_x509_common_name(cert, &common_name, &common_name_size); + if (result != TSI_OK) { + if (result == TSI_NOT_FOUND) { + common_name = NULL; + common_name_size = 0; + } else { + return result; + } + } + result = tsi_construct_string_peer_property( + TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY, + common_name == NULL ? "" : (const char*)common_name, common_name_size, + property); + OPENSSL_free(common_name); + return result; +} + +/* Gets the subject SANs from an X509 cert as a tsi_peer_property. */ +static tsi_result add_subject_alt_names_properties_to_peer( + tsi_peer* peer, GENERAL_NAMES* subject_alt_names, + int subject_alt_name_count) { + int i; + tsi_result result = TSI_OK; + + /* Reset for DNS entries filtering. */ + peer->property_count -= subject_alt_name_count; + + for (i = 0; i < subject_alt_name_count; i++) { + GENERAL_NAME* subject_alt_name = + sk_GENERAL_NAME_value(subject_alt_names, i); + /* Filter out the non-dns entries names. */ + if (subject_alt_name->type == GEN_DNS) { + unsigned char* dns_name = NULL; + int dns_name_size = + ASN1_STRING_to_UTF8(&dns_name, subject_alt_name->d.dNSName); + if (dns_name_size < 0) { + gpr_log(GPR_ERROR, "Could not get utf8 from asn1 string."); + result = TSI_INTERNAL_ERROR; + break; + } + result = tsi_construct_string_peer_property( + TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY, + (const char*)dns_name, dns_name_size, + &peer->properties[peer->property_count++]); + OPENSSL_free(dns_name); + if (result != TSI_OK) break; + } + } + return result; +} + +/* Gets information about the peer's X509 cert as a tsi_peer object. */ +static tsi_result peer_from_x509(X509* cert, int include_certificate_type, + tsi_peer* peer) { + /* TODO(jboeuf): Maybe add more properties. */ + GENERAL_NAMES* subject_alt_names = + X509_get_ext_d2i(cert, NID_subject_alt_name, 0, 0); + int subject_alt_name_count = + (subject_alt_names != NULL) ? sk_GENERAL_NAME_num(subject_alt_names) : 0; + size_t property_count = (include_certificate_type ? 1 : 0) + + 1 /* common name */ + subject_alt_name_count; + tsi_result result = tsi_construct_peer(property_count, peer); + if (result != TSI_OK) return result; + do { + if (include_certificate_type) { + result = tsi_construct_string_peer_property_from_cstring( + TSI_CERTIFICATE_TYPE_PEER_PROPERTY, TSI_X509_CERTIFICATE_TYPE, + &peer->properties[0]); + if (result != TSI_OK) break; + } + result = peer_property_from_x509_common_name( + cert, &peer->properties[include_certificate_type ? 1 : 0]); + if (result != TSI_OK) break; + + if (subject_alt_name_count != 0) { + result = add_subject_alt_names_properties_to_peer(peer, subject_alt_names, + subject_alt_name_count); + if (result != TSI_OK) break; + } + } while (0); + + if (subject_alt_names != NULL) { + sk_GENERAL_NAME_pop_free(subject_alt_names, GENERAL_NAME_free); + } + if (result != TSI_OK) tsi_peer_destruct(peer); + return result; +} + +/* Logs the SSL error stack. */ +static void log_ssl_error_stack(void) { + unsigned long err; + while ((err = ERR_get_error()) != 0) { + char details[256]; + ERR_error_string_n(err, details, sizeof(details)); + gpr_log(GPR_ERROR, "%s", details); + } +} + +/* Performs an SSL_read and handle errors. */ +static tsi_result do_ssl_read(SSL* ssl, unsigned char* unprotected_bytes, + size_t* unprotected_bytes_size) { + int read_from_ssl = SSL_read(ssl, unprotected_bytes, *unprotected_bytes_size); + if (read_from_ssl == 0) { + gpr_log(GPR_ERROR, "SSL_read returned 0 unexpectedly."); + return TSI_INTERNAL_ERROR; + } + if (read_from_ssl < 0) { + read_from_ssl = SSL_get_error(ssl, read_from_ssl); + switch (read_from_ssl) { + case SSL_ERROR_WANT_READ: + /* We need more data to finish the frame. */ + *unprotected_bytes_size = 0; + return TSI_OK; + case SSL_ERROR_WANT_WRITE: + gpr_log( + GPR_ERROR, + "Peer tried to renegotiate SSL connection. This is unsupported."); + return TSI_UNIMPLEMENTED; + case SSL_ERROR_SSL: + gpr_log(GPR_ERROR, "Corruption detected."); + log_ssl_error_stack(); + return TSI_DATA_CORRUPTED; + default: + gpr_log(GPR_ERROR, "SSL_read failed with error %s.", + ssl_error_string(read_from_ssl)); + return TSI_PROTOCOL_FAILURE; + } + } + *unprotected_bytes_size = read_from_ssl; + return TSI_OK; +} + +/* Performs an SSL_write and handle errors. */ +static tsi_result do_ssl_write(SSL* ssl, unsigned char* unprotected_bytes, + size_t unprotected_bytes_size) { + int ssl_write_result = + SSL_write(ssl, unprotected_bytes, unprotected_bytes_size); + if (ssl_write_result < 0) { + ssl_write_result = SSL_get_error(ssl, ssl_write_result); + if (ssl_write_result == SSL_ERROR_WANT_READ) { + gpr_log(GPR_ERROR, + "Peer tried to renegotiate SSL connection. This is unsupported."); + return TSI_UNIMPLEMENTED; + } else { + gpr_log(GPR_ERROR, "SSL_write failed with error %s.", + ssl_error_string(ssl_write_result)); + return TSI_INTERNAL_ERROR; + } + } + return TSI_OK; +} + +/* Loads an in-memory PEM certificate chain into the SSL context. */ +static tsi_result ssl_ctx_use_certificate_chain( + SSL_CTX* context, const unsigned char* pem_cert_chain, + size_t pem_cert_chain_size) { + tsi_result result = TSI_OK; + X509* certificate = NULL; + BIO* pem = BIO_new_mem_buf((void*)pem_cert_chain, pem_cert_chain_size); + if (pem == NULL) return TSI_OUT_OF_RESOURCES; + + do { + certificate = PEM_read_bio_X509_AUX(pem, NULL, NULL, ""); + if (certificate == NULL) { + result = TSI_INVALID_ARGUMENT; + break; + } + if (!SSL_CTX_use_certificate(context, certificate)) { + result = TSI_INVALID_ARGUMENT; + break; + } + while (1) { + X509* certificate_authority = PEM_read_bio_X509(pem, NULL, NULL, ""); + if (certificate_authority == NULL) { + ERR_clear_error(); + break; /* Done reading. */ + } + if (!SSL_CTX_add_extra_chain_cert(context, certificate_authority)) { + X509_free(certificate_authority); + result = TSI_INVALID_ARGUMENT; + break; + } + /* We don't need to free certificate_authority as its ownership has been + transfered to the context. That is not the case for certificate though. + */ + } + } while (0); + + if (certificate != NULL) X509_free(certificate); + BIO_free(pem); + return result; +} + +/* Loads an in-memory PEM private key into the SSL context. */ +static tsi_result ssl_ctx_use_private_key(SSL_CTX* context, + const unsigned char* pem_key, + size_t pem_key_size) { + tsi_result result = TSI_OK; + EVP_PKEY* private_key = NULL; + BIO* pem = BIO_new_mem_buf((void*)pem_key, pem_key_size); + if (pem == NULL) return TSI_OUT_OF_RESOURCES; + do { + private_key = PEM_read_bio_PrivateKey(pem, NULL, NULL, ""); + if (private_key == NULL) { + result = TSI_INVALID_ARGUMENT; + break; + } + if (!SSL_CTX_use_PrivateKey(context, private_key)) { + result = TSI_INVALID_ARGUMENT; + break; + } + } while (0); + if (private_key != NULL) EVP_PKEY_free(private_key); + BIO_free(pem); + return result; +} + +/* Loads in-memory PEM verification certs into the SSL context and optionally + returns the verification cert names (root_names can be NULL). */ +static tsi_result ssl_ctx_load_verification_certs( + SSL_CTX* context, const unsigned char* pem_roots, size_t pem_roots_size, + STACK_OF(X509_NAME) * *root_names) { + tsi_result result = TSI_OK; + size_t num_roots = 0; + X509* root = NULL; + X509_NAME* root_name = NULL; + BIO* pem = BIO_new_mem_buf((void*)pem_roots, pem_roots_size); + X509_STORE* root_store = SSL_CTX_get_cert_store(context); + if (root_store == NULL) return TSI_INVALID_ARGUMENT; + if (pem == NULL) return TSI_OUT_OF_RESOURCES; + if (root_names != NULL) { + *root_names = sk_X509_NAME_new_null(); + if (*root_names == NULL) return TSI_OUT_OF_RESOURCES; + } + + while (1) { + root = PEM_read_bio_X509_AUX(pem, NULL, NULL, ""); + if (root == NULL) { + ERR_clear_error(); + break; /* We're at the end of stream. */ + } + if (root_names != NULL) { + root_name = X509_get_subject_name(root); + if (root_name == NULL) { + gpr_log(GPR_ERROR, "Could not get name from root certificate."); + result = TSI_INVALID_ARGUMENT; + break; + } + root_name = X509_NAME_dup(root_name); + if (root_name == NULL) { + result = TSI_OUT_OF_RESOURCES; + break; + } + sk_X509_NAME_push(*root_names, root_name); + root_name = NULL; + } + if (!X509_STORE_add_cert(root_store, root)) { + gpr_log(GPR_ERROR, "Could not add root certificate to ssl context."); + result = TSI_INTERNAL_ERROR; + break; + } + X509_free(root); + num_roots++; + } + + if (num_roots == 0) { + gpr_log(GPR_ERROR, "Could not load any root certificate."); + result = TSI_INVALID_ARGUMENT; + } + + if (result != TSI_OK) { + if (root != NULL) X509_free(root); + if (root_names != NULL) { + sk_X509_NAME_pop_free(*root_names, X509_NAME_free); + *root_names = NULL; + if (root_name != NULL) X509_NAME_free(root_name); + } + } + BIO_free(pem); + return result; +} + +/* Populates the SSL context with a private key and a cert chain, and sets the + cipher list and the ephemeral ECDH key. */ +static tsi_result populate_ssl_context( + SSL_CTX* context, const unsigned char* pem_private_key, + size_t pem_private_key_size, const unsigned char* pem_certificate_chain, + size_t pem_certificate_chain_size, const char* cipher_list) { + tsi_result result = TSI_OK; + if (pem_certificate_chain != NULL) { + result = ssl_ctx_use_certificate_chain(context, pem_certificate_chain, + pem_certificate_chain_size); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Invalid cert chain file."); + return result; + } + } + if (pem_private_key != NULL) { + result = + ssl_ctx_use_private_key(context, pem_private_key, pem_private_key_size); + if (result != TSI_OK || !SSL_CTX_check_private_key(context)) { + gpr_log(GPR_ERROR, "Invalid private key."); + return result != TSI_OK ? result : TSI_INVALID_ARGUMENT; + } + } + if ((cipher_list != NULL) && !SSL_CTX_set_cipher_list(context, cipher_list)) { + gpr_log(GPR_ERROR, "Invalid cipher list: %s.", cipher_list); + return TSI_INVALID_ARGUMENT; + } + { + EC_KEY* ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + if (!SSL_CTX_set_tmp_ecdh(context, ecdh)) { + gpr_log(GPR_ERROR, "Could not set ephemeral ECDH key."); + EC_KEY_free(ecdh); + return TSI_INTERNAL_ERROR; + } + SSL_CTX_set_options(context, SSL_OP_SINGLE_ECDH_USE); + EC_KEY_free(ecdh); + } + return TSI_OK; +} + +/* Extracts the CN and the SANs from an X509 cert as a peer object. */ +static tsi_result extract_x509_subject_names_from_pem_cert( + const unsigned char* pem_cert, size_t pem_cert_size, tsi_peer* peer) { + tsi_result result = TSI_OK; + X509* cert = NULL; + BIO* pem = BIO_new_mem_buf((void*)pem_cert, pem_cert_size); + if (pem == NULL) return TSI_OUT_OF_RESOURCES; + + cert = PEM_read_bio_X509(pem, NULL, NULL, ""); + if (cert == NULL) { + gpr_log(GPR_ERROR, "Invalid certificate"); + result = TSI_INVALID_ARGUMENT; + } else { + result = peer_from_x509(cert, 0, peer); + } + if (cert != NULL) X509_free(cert); + BIO_free(pem); + return result; +} + +/* Builds the alpn protocol name list according to rfc 7301. */ +static tsi_result build_alpn_protocol_name_list( + const unsigned char** alpn_protocols, + const unsigned char* alpn_protocols_lengths, uint16_t num_alpn_protocols, + unsigned char** protocol_name_list, size_t* protocol_name_list_length) { + uint16_t i; + unsigned char* current; + *protocol_name_list = NULL; + *protocol_name_list_length = 0; + if (num_alpn_protocols == 0) return TSI_INVALID_ARGUMENT; + for (i = 0; i < num_alpn_protocols; i++) { + if (alpn_protocols_lengths[i] == 0) { + gpr_log(GPR_ERROR, "Invalid 0-length protocol name."); + return TSI_INVALID_ARGUMENT; + } + *protocol_name_list_length += alpn_protocols_lengths[i] + 1; + } + *protocol_name_list = malloc(*protocol_name_list_length); + if (*protocol_name_list == NULL) return TSI_OUT_OF_RESOURCES; + current = *protocol_name_list; + for (i = 0; i < num_alpn_protocols; i++) { + *(current++) = alpn_protocols_lengths[i]; + memcpy(current, alpn_protocols[i], alpn_protocols_lengths[i]); + current += alpn_protocols_lengths[i]; + } + /* Safety check. */ + if ((current < *protocol_name_list) || + ((gpr_uintptr)(current - *protocol_name_list) != + *protocol_name_list_length)) { + return TSI_INTERNAL_ERROR; + } + return TSI_OK; +} + +/* --- tsi_frame_protector methods implementation. ---*/ + +static tsi_result ssl_protector_protect(tsi_frame_protector* self, + const unsigned char* unprotected_bytes, + size_t* unprotected_bytes_size, + unsigned char* protected_output_frames, + size_t* protected_output_frames_size) { + tsi_ssl_frame_protector* impl = (tsi_ssl_frame_protector*)self; + int read_from_ssl; + size_t available; + tsi_result result = TSI_OK; + + /* First see if we have some pending data in the SSL BIO. */ + size_t pending_in_ssl = BIO_pending(impl->from_ssl); + if (pending_in_ssl > 0) { + *unprotected_bytes_size = 0; + read_from_ssl = BIO_read(impl->from_ssl, protected_output_frames, + *protected_output_frames_size); + if (read_from_ssl < 0) { + gpr_log(GPR_ERROR, + "Could not read from BIO even though some data is pending"); + return TSI_INTERNAL_ERROR; + } + *protected_output_frames_size = read_from_ssl; + return TSI_OK; + } + + /* Now see if we can send a complete frame. */ + available = impl->buffer_size - impl->buffer_offset; + if (available > *unprotected_bytes_size) { + /* If we cannot, just copy the data in our internal buffer. */ + memcpy(impl->buffer + impl->buffer_offset, unprotected_bytes, + *unprotected_bytes_size); + impl->buffer_offset += *unprotected_bytes_size; + *protected_output_frames_size = 0; + return TSI_OK; + } + + /* If we can, prepare the buffer, send it to SSL_write and read. */ + memcpy(impl->buffer + impl->buffer_offset, unprotected_bytes, available); + result = do_ssl_write(impl->ssl, impl->buffer, impl->buffer_size); + if (result != TSI_OK) return result; + + read_from_ssl = BIO_read(impl->from_ssl, protected_output_frames, + *protected_output_frames_size); + if (read_from_ssl < 0) { + gpr_log(GPR_ERROR, "Could not read from BIO after SSL_write."); + return TSI_INTERNAL_ERROR; + } + *protected_output_frames_size = read_from_ssl; + *unprotected_bytes_size = available; + impl->buffer_offset = 0; + return TSI_OK; +} + +static tsi_result ssl_protector_protect_flush( + tsi_frame_protector* self, unsigned char* protected_output_frames, + size_t* protected_output_frames_size, size_t* still_pending_size) { + tsi_result result = TSI_OK; + tsi_ssl_frame_protector* impl = (tsi_ssl_frame_protector*)self; + int read_from_ssl = 0; + + if (impl->buffer_offset != 0) { + result = do_ssl_write(impl->ssl, impl->buffer, impl->buffer_offset); + if (result != TSI_OK) return result; + impl->buffer_offset = 0; + } + + *still_pending_size = BIO_pending(impl->from_ssl); + if (*still_pending_size == 0) return TSI_OK; + + read_from_ssl = BIO_read(impl->from_ssl, protected_output_frames, + *protected_output_frames_size); + if (read_from_ssl <= 0) { + gpr_log(GPR_ERROR, "Could not read from BIO after SSL_write."); + return TSI_INTERNAL_ERROR; + } + *protected_output_frames_size = read_from_ssl; + *still_pending_size = BIO_pending(impl->from_ssl); + return TSI_OK; +} + +static tsi_result ssl_protector_unprotect( + tsi_frame_protector* self, const unsigned char* protected_frames_bytes, + size_t* protected_frames_bytes_size, unsigned char* unprotected_bytes, + size_t* unprotected_bytes_size) { + tsi_result result = TSI_OK; + int written_into_ssl = 0; + size_t output_bytes_size = *unprotected_bytes_size; + size_t output_bytes_offset = 0; + tsi_ssl_frame_protector* impl = (tsi_ssl_frame_protector*)self; + + /* First, try to read remaining data from ssl. */ + result = do_ssl_read(impl->ssl, unprotected_bytes, unprotected_bytes_size); + if (result != TSI_OK) return result; + if (*unprotected_bytes_size == output_bytes_size) { + /* We have read everything we could and cannot process any more input. */ + *protected_frames_bytes_size = 0; + return TSI_OK; + } + output_bytes_offset = *unprotected_bytes_size; + unprotected_bytes += output_bytes_offset; + *unprotected_bytes_size = output_bytes_size - output_bytes_offset; + + /* Then, try to write some data to ssl. */ + written_into_ssl = BIO_write(impl->into_ssl, protected_frames_bytes, + *protected_frames_bytes_size); + if (written_into_ssl < 0) { + gpr_log(GPR_ERROR, "Sending protected frame to ssl failed with %d", + written_into_ssl); + return TSI_INTERNAL_ERROR; + } + *protected_frames_bytes_size = written_into_ssl; + + /* Now try to read some data again. */ + result = do_ssl_read(impl->ssl, unprotected_bytes, unprotected_bytes_size); + if (result == TSI_OK) { + /* Don't forget to output the total number of bytes read. */ + *unprotected_bytes_size += output_bytes_offset; + } + return result; +} + +static void ssl_protector_destroy(tsi_frame_protector* self) { + tsi_ssl_frame_protector* impl = (tsi_ssl_frame_protector*)self; + if (impl->buffer != NULL) free(impl->buffer); + if (impl->ssl != NULL) SSL_free(impl->ssl); + free(self); +} + +static const tsi_frame_protector_vtable frame_protector_vtable = { + ssl_protector_protect, ssl_protector_protect_flush, ssl_protector_unprotect, + ssl_protector_destroy, +}; + +/* --- tsi_handshaker methods implementation. ---*/ + +static tsi_result ssl_handshaker_get_bytes_to_send_to_peer(tsi_handshaker* self, + unsigned char* bytes, + size_t* bytes_size) { + tsi_ssl_handshaker* impl = (tsi_ssl_handshaker*)self; + int bytes_read_from_ssl = 0; + if (bytes == NULL || bytes_size == NULL || *bytes_size == 0 || + *bytes_size > INT_MAX) { + return TSI_INVALID_ARGUMENT; + } + bytes_read_from_ssl = BIO_read(impl->from_ssl, bytes, *bytes_size); + if (bytes_read_from_ssl < 0) { + *bytes_size = 0; + if (!BIO_should_retry(impl->from_ssl)) { + impl->result = TSI_INTERNAL_ERROR; + return impl->result; + } else { + return TSI_OK; + } + } + *bytes_size = (size_t)bytes_read_from_ssl; + return BIO_pending(impl->from_ssl) == 0 ? TSI_OK : TSI_INCOMPLETE_DATA; +} + +static tsi_result ssl_handshaker_get_result(tsi_handshaker* self) { + tsi_ssl_handshaker* impl = (tsi_ssl_handshaker*)self; + if ((impl->result == TSI_HANDSHAKE_IN_PROGRESS) && + SSL_is_init_finished(impl->ssl)) { + impl->result = TSI_OK; + } + return impl->result; +} + +static tsi_result ssl_handshaker_process_bytes_from_peer( + tsi_handshaker* self, const unsigned char* bytes, size_t* bytes_size) { + tsi_ssl_handshaker* impl = (tsi_ssl_handshaker*)self; + int bytes_written_into_ssl_size = 0; + if (bytes == NULL || bytes_size == 0 || *bytes_size > INT_MAX) { + return TSI_INVALID_ARGUMENT; + } + bytes_written_into_ssl_size = BIO_write(impl->into_ssl, bytes, *bytes_size); + if (bytes_written_into_ssl_size < 0) { + gpr_log(GPR_ERROR, "Could not write to memory BIO."); + impl->result = TSI_INTERNAL_ERROR; + return impl->result; + } + *bytes_size = bytes_written_into_ssl_size; + + if (!tsi_handshaker_is_in_progress(self)) { + impl->result = TSI_OK; + return impl->result; + } else { + /* Get ready to get some bytes from SSL. */ + int ssl_result = SSL_do_handshake(impl->ssl); + ssl_result = SSL_get_error(impl->ssl, ssl_result); + switch (ssl_result) { + case SSL_ERROR_WANT_READ: + if (BIO_pending(impl->from_ssl) == 0) { + /* We need more data. */ + return TSI_INCOMPLETE_DATA; + } else { + return TSI_OK; + } + case SSL_ERROR_NONE: + return TSI_OK; + default: { + char err_str[256]; + ERR_error_string_n(ERR_get_error(), err_str, sizeof(err_str)); + gpr_log(GPR_ERROR, "Handshake failed with fatal error %s: %s.", + ssl_error_string(ssl_result), err_str); + impl->result = TSI_PROTOCOL_FAILURE; + return impl->result; + } + } + } +} + +static tsi_result ssl_handshaker_extract_peer(tsi_handshaker* self, + tsi_peer* peer) { + tsi_result result = TSI_OK; + const unsigned char* alpn_selected = NULL; + unsigned int alpn_selected_len; + tsi_ssl_handshaker* impl = (tsi_ssl_handshaker*)self; + X509* peer_cert = SSL_get_peer_certificate(impl->ssl); + if (peer_cert != NULL) { + result = peer_from_x509(peer_cert, 1, peer); + X509_free(peer_cert); + if (result != TSI_OK) return result; + } +#if TSI_OPENSSL_ALPN_SUPPORT + SSL_get0_alpn_selected(impl->ssl, &alpn_selected, &alpn_selected_len); +#endif /* TSI_OPENSSL_ALPN_SUPPORT */ + if (alpn_selected == NULL) { + /* Try npn. */ + SSL_get0_next_proto_negotiated(impl->ssl, &alpn_selected, + &alpn_selected_len); + } + if (alpn_selected != NULL) { + size_t i; + tsi_peer_property* new_properties = + calloc(1, sizeof(tsi_peer_property) * (peer->property_count + 1)); + if (new_properties == NULL) return TSI_OUT_OF_RESOURCES; + for (i = 0; i < peer->property_count; i++) { + new_properties[i] = peer->properties[i]; + } + result = tsi_construct_string_peer_property( + TSI_SSL_ALPN_SELECTED_PROTOCOL, (const char*)alpn_selected, + alpn_selected_len, &new_properties[peer->property_count]); + if (result != TSI_OK) { + free(new_properties); + return result; + } + if (peer->properties != NULL) free(peer->properties); + peer->property_count++; + peer->properties = new_properties; + } + return result; +} + +static tsi_result ssl_handshaker_create_frame_protector( + tsi_handshaker* self, size_t* max_output_protected_frame_size, + tsi_frame_protector** protector) { + size_t actual_max_output_protected_frame_size = + TSI_SSL_MAX_PROTECTED_FRAME_SIZE_UPPER_BOUND; + tsi_ssl_handshaker* impl = (tsi_ssl_handshaker*)self; + tsi_ssl_frame_protector* protector_impl = + calloc(1, sizeof(tsi_ssl_frame_protector)); + if (protector_impl == NULL) { + return TSI_OUT_OF_RESOURCES; + } + + if (max_output_protected_frame_size != NULL) { + if (*max_output_protected_frame_size > + TSI_SSL_MAX_PROTECTED_FRAME_SIZE_UPPER_BOUND) { + *max_output_protected_frame_size = + TSI_SSL_MAX_PROTECTED_FRAME_SIZE_UPPER_BOUND; + } else if (*max_output_protected_frame_size < + TSI_SSL_MAX_PROTECTED_FRAME_SIZE_LOWER_BOUND) { + *max_output_protected_frame_size = + TSI_SSL_MAX_PROTECTED_FRAME_SIZE_LOWER_BOUND; + } + actual_max_output_protected_frame_size = *max_output_protected_frame_size; + } + protector_impl->buffer_size = + actual_max_output_protected_frame_size - TSI_SSL_MAX_PROTECTION_OVERHEAD; + protector_impl->buffer = malloc(protector_impl->buffer_size); + if (protector_impl->buffer == NULL) { + gpr_log(GPR_ERROR, + "Could not allocated buffer for tsi_ssl_frame_protector."); + free(protector_impl); + return TSI_INTERNAL_ERROR; + } + + /* Transfer ownership of ssl to the frame protector. It is OK as the caller + * cannot call anything else but destroy on the handshaker after this call. */ + protector_impl->ssl = impl->ssl; + impl->ssl = NULL; + protector_impl->into_ssl = impl->into_ssl; + protector_impl->from_ssl = impl->from_ssl; + + protector_impl->base.vtable = &frame_protector_vtable; + *protector = &protector_impl->base; + return TSI_OK; +} + +static void ssl_handshaker_destroy(tsi_handshaker* self) { + tsi_ssl_handshaker* impl = (tsi_ssl_handshaker*)self; + SSL_free(impl->ssl); /* The BIO objects are owned by ssl */ + free(impl); +} + +static const tsi_handshaker_vtable handshaker_vtable = { + ssl_handshaker_get_bytes_to_send_to_peer, + ssl_handshaker_process_bytes_from_peer, + ssl_handshaker_get_result, + ssl_handshaker_extract_peer, + ssl_handshaker_create_frame_protector, + ssl_handshaker_destroy, +}; + +/* --- tsi_ssl_handshaker_factory common methods. --- */ + +tsi_result tsi_ssl_handshaker_factory_create_handshaker( + tsi_ssl_handshaker_factory* self, const char* server_name_indication, + tsi_handshaker** handshaker) { + if (self == NULL || handshaker == NULL) return TSI_INVALID_ARGUMENT; + return self->create_handshaker(self, server_name_indication, handshaker); +} + +void tsi_ssl_handshaker_factory_destroy(tsi_ssl_handshaker_factory* self) { + if (self == NULL) return; + self->destroy(self); +} + +static tsi_result create_tsi_ssl_handshaker(SSL_CTX* ctx, int is_client, + const char* server_name_indication, + tsi_handshaker** handshaker) { + SSL* ssl = SSL_new(ctx); + BIO* into_ssl = NULL; + BIO* from_ssl = NULL; + tsi_ssl_handshaker* impl = NULL; + *handshaker = NULL; + if (ctx == NULL) { + gpr_log(GPR_ERROR, "SSL Context is null. Should never happen."); + return TSI_INTERNAL_ERROR; + } + if (ssl == NULL) { + return TSI_OUT_OF_RESOURCES; + } + SSL_set_info_callback(ssl, ssl_info_callback); + + into_ssl = BIO_new(BIO_s_mem()); + from_ssl = BIO_new(BIO_s_mem()); + if (into_ssl == NULL || from_ssl == NULL) { + gpr_log(GPR_ERROR, "BIO_new failed."); + SSL_free(ssl); + if (into_ssl != NULL) BIO_free(into_ssl); + if (from_ssl != NULL) BIO_free(into_ssl); + return TSI_OUT_OF_RESOURCES; + } + SSL_set_bio(ssl, into_ssl, from_ssl); + if (is_client) { + int ssl_result; + SSL_set_connect_state(ssl); + if (server_name_indication != NULL) { + if (!SSL_set_tlsext_host_name(ssl, server_name_indication)) { + gpr_log(GPR_ERROR, "Invalid server name indication %s.", + server_name_indication); + SSL_free(ssl); + return TSI_INTERNAL_ERROR; + } + } + ssl_result = SSL_do_handshake(ssl); + ssl_result = SSL_get_error(ssl, ssl_result); + if (ssl_result != SSL_ERROR_WANT_READ) { + gpr_log(GPR_ERROR, + "Unexpected error received from first SSL_do_handshake call: %s", + ssl_error_string(ssl_result)); + SSL_free(ssl); + return TSI_INTERNAL_ERROR; + } + } else { + SSL_set_accept_state(ssl); + } + + impl = calloc(1, sizeof(tsi_ssl_handshaker)); + if (impl == NULL) { + SSL_free(ssl); + return TSI_OUT_OF_RESOURCES; + } + impl->ssl = ssl; + impl->into_ssl = into_ssl; + impl->from_ssl = from_ssl; + impl->result = TSI_HANDSHAKE_IN_PROGRESS; + impl->base.vtable = &handshaker_vtable; + *handshaker = &impl->base; + return TSI_OK; +} + +static int select_protocol_list(const unsigned char** out, + unsigned char* outlen, + const unsigned char* client_list, + unsigned int client_list_len, + const unsigned char* server_list, + unsigned int server_list_len) { + const unsigned char* client_current = client_list; + while ((unsigned int)(client_current - client_list) < client_list_len) { + unsigned char client_current_len = *(client_current++); + const unsigned char* server_current = server_list; + while ((server_current >= server_list) && + (gpr_uintptr)(server_current - server_list) < server_list_len) { + unsigned char server_current_len = *(server_current++); + if ((client_current_len == server_current_len) && + !memcmp(client_current, server_current, server_current_len)) { + *out = server_current; + *outlen = server_current_len; + return SSL_TLSEXT_ERR_OK; + } + server_current += server_current_len; + } + client_current += client_current_len; + } + return SSL_TLSEXT_ERR_NOACK; +} + +/* --- tsi_ssl__client_handshaker_factory methods implementation. --- */ + +static tsi_result ssl_client_handshaker_factory_create_handshaker( + tsi_ssl_handshaker_factory* self, const char* server_name_indication, + tsi_handshaker** handshaker) { + tsi_ssl_client_handshaker_factory* impl = + (tsi_ssl_client_handshaker_factory*)self; + return create_tsi_ssl_handshaker(impl->ssl_context, 1, server_name_indication, + handshaker); +} + +static void ssl_client_handshaker_factory_destroy( + tsi_ssl_handshaker_factory* self) { + tsi_ssl_client_handshaker_factory* impl = + (tsi_ssl_client_handshaker_factory*)self; + if (impl->ssl_context != NULL) SSL_CTX_free(impl->ssl_context); + if (impl->alpn_protocol_list != NULL) free(impl->alpn_protocol_list); + free(impl); +} + +static int client_handshaker_factory_npn_callback(SSL* ssl, unsigned char** out, + unsigned char* outlen, + const unsigned char* in, + unsigned int inlen, + void* arg) { + tsi_ssl_client_handshaker_factory* factory = + (tsi_ssl_client_handshaker_factory*)arg; + return select_protocol_list((const unsigned char**)out, outlen, + factory->alpn_protocol_list, + factory->alpn_protocol_list_length, in, inlen); +} + +/* --- tsi_ssl_server_handshaker_factory methods implementation. --- */ + +static tsi_result ssl_server_handshaker_factory_create_handshaker( + tsi_ssl_handshaker_factory* self, const char* server_name_indication, + tsi_handshaker** handshaker) { + tsi_ssl_server_handshaker_factory* impl = + (tsi_ssl_server_handshaker_factory*)self; + if (impl->ssl_context_count == 0 || server_name_indication != NULL) { + return TSI_INVALID_ARGUMENT; + } + /* Create the handshaker with the first context. We will switch if needed + because of SNI in ssl_server_handshaker_factory_servername_callback. */ + return create_tsi_ssl_handshaker(impl->ssl_contexts[0], 0, NULL, handshaker); +} + +static void ssl_server_handshaker_factory_destroy( + tsi_ssl_handshaker_factory* self) { + tsi_ssl_server_handshaker_factory* impl = + (tsi_ssl_server_handshaker_factory*)self; + size_t i; + for (i = 0; i < impl->ssl_context_count; i++) { + if (impl->ssl_contexts[i] != NULL) { + SSL_CTX_free(impl->ssl_contexts[i]); + tsi_peer_destruct(&impl->ssl_context_x509_subject_names[i]); + } + } + if (impl->ssl_contexts != NULL) free(impl->ssl_contexts); + if (impl->ssl_context_x509_subject_names != NULL) { + free(impl->ssl_context_x509_subject_names); + } + if (impl->alpn_protocol_list != NULL) free(impl->alpn_protocol_list); + free(impl); +} + +static int does_entry_match_name(const char* entry, size_t entry_length, + const char* name) { + const char* dot; + const char* name_subdomain = NULL; + size_t name_length = strlen(name); + size_t name_subdomain_length; + if (entry_length == 0) return 0; + + /* Take care of '.' terminations. */ + if (name[name_length - 1] == '.') { + name_length--; + } + if (entry[entry_length - 1] == '.') { + entry_length--; + if (entry_length == 0) return 0; + } + + if ((name_length == entry_length) && + strncmp(name, entry, entry_length) == 0) { + return 1; /* Perfect match. */ + } + if (entry[0] != '*') return 0; + + /* Wildchar subdomain matching. */ + if (entry_length < 3 || entry[1] != '.') { /* At least *.x */ + gpr_log(GPR_ERROR, "Invalid wildchar entry."); + return 0; + } + name_subdomain = strchr(name, '.'); + if (name_subdomain == NULL) return 0; + name_subdomain_length = strlen(name_subdomain); + if (name_subdomain_length < 2) return 0; + name_subdomain++; /* Starts after the dot. */ + name_subdomain_length--; + entry += 2; /* Remove *. */ + entry_length -= 2; + dot = strchr(name_subdomain, '.'); + if ((dot == NULL) || (dot == &name_subdomain[name_subdomain_length - 1])) { + gpr_log(GPR_ERROR, "Invalid toplevel subdomain: %s", name_subdomain); + return 0; + } + if (name_subdomain[name_subdomain_length - 1] == '.') { + name_subdomain_length--; + } + return ((entry_length > 0) && (name_subdomain_length == entry_length) && + strncmp(entry, name_subdomain, entry_length) == 0); +} + +static int ssl_server_handshaker_factory_servername_callback(SSL* ssl, int* ap, + void* arg) { + tsi_ssl_server_handshaker_factory* impl = + (tsi_ssl_server_handshaker_factory*)arg; + size_t i = 0; + const char* servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + if (servername == NULL || strlen(servername) == 0) { + return SSL_TLSEXT_ERR_NOACK; + } + + for (i = 0; i < impl->ssl_context_count; i++) { + if (tsi_ssl_peer_matches_name(&impl->ssl_context_x509_subject_names[i], + servername)) { + SSL_set_SSL_CTX(ssl, impl->ssl_contexts[i]); + return SSL_TLSEXT_ERR_OK; + } + } + gpr_log(GPR_ERROR, "No match found for server name: %s.", servername); + return SSL_TLSEXT_ERR_ALERT_WARNING; +} + +#if TSI_OPENSSL_ALPN_SUPPORT +static int server_handshaker_factory_alpn_callback( + SSL* ssl, const unsigned char** out, unsigned char* outlen, + const unsigned char* in, unsigned int inlen, void* arg) { + tsi_ssl_server_handshaker_factory* factory = + (tsi_ssl_server_handshaker_factory*)arg; + return select_protocol_list(out, outlen, in, inlen, + factory->alpn_protocol_list, + factory->alpn_protocol_list_length); +} +#endif /* TSI_OPENSSL_ALPN_SUPPORT */ + +static int server_handshaker_factory_npn_advertised_callback( + SSL* ssl, const unsigned char** out, unsigned int* outlen, void* arg) { + tsi_ssl_server_handshaker_factory* factory = + (tsi_ssl_server_handshaker_factory*)arg; + *out = factory->alpn_protocol_list; + *outlen = factory->alpn_protocol_list_length; + return SSL_TLSEXT_ERR_OK; +} + +/* --- tsi_ssl_handshaker_factory constructors. --- */ + +tsi_result tsi_create_ssl_client_handshaker_factory( + const unsigned char* pem_private_key, size_t pem_private_key_size, + const unsigned char* pem_cert_chain, size_t pem_cert_chain_size, + const unsigned char* pem_root_certs, size_t pem_root_certs_size, + const char* cipher_list, const unsigned char** alpn_protocols, + const unsigned char* alpn_protocols_lengths, uint16_t num_alpn_protocols, + tsi_ssl_handshaker_factory** factory) { + SSL_CTX* ssl_context = NULL; + tsi_ssl_client_handshaker_factory* impl = NULL; + tsi_result result = TSI_OK; + + gpr_once_init(&init_openssl_once, init_openssl); + + if (factory == NULL) return TSI_INVALID_ARGUMENT; + *factory = NULL; + if (pem_root_certs == NULL) return TSI_INVALID_ARGUMENT; + + ssl_context = SSL_CTX_new(TLSv1_2_method()); + if (ssl_context == NULL) { + gpr_log(GPR_ERROR, "Could not create ssl context."); + return TSI_INVALID_ARGUMENT; + } + + impl = calloc(1, sizeof(tsi_ssl_client_handshaker_factory)); + if (impl == NULL) { + SSL_CTX_free(ssl_context); + return TSI_OUT_OF_RESOURCES; + } + impl->ssl_context = ssl_context; + + do { + result = + populate_ssl_context(ssl_context, pem_private_key, pem_private_key_size, + pem_cert_chain, pem_cert_chain_size, cipher_list); + if (result != TSI_OK) break; + result = ssl_ctx_load_verification_certs(ssl_context, pem_root_certs, + pem_root_certs_size, NULL); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Cannot load server root certificates."); + break; + } + + if (num_alpn_protocols != 0) { + result = build_alpn_protocol_name_list( + alpn_protocols, alpn_protocols_lengths, num_alpn_protocols, + &impl->alpn_protocol_list, &impl->alpn_protocol_list_length); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Building alpn list failed with error %s.", + tsi_result_to_string(result)); + break; + } +#if TSI_OPENSSL_ALPN_SUPPORT + if (SSL_CTX_set_alpn_protos(ssl_context, impl->alpn_protocol_list, + impl->alpn_protocol_list_length)) { + gpr_log(GPR_ERROR, "Could not set alpn protocol list to context."); + result = TSI_INVALID_ARGUMENT; + break; + } +#endif /* TSI_OPENSSL_ALPN_SUPPORT */ + SSL_CTX_set_next_proto_select_cb( + ssl_context, client_handshaker_factory_npn_callback, impl); + } + } while (0); + if (result != TSI_OK) { + ssl_client_handshaker_factory_destroy(&impl->base); + return result; + } + SSL_CTX_set_verify(ssl_context, SSL_VERIFY_PEER, NULL); + /* TODO(jboeuf): Add revocation verification. */ + + impl->base.create_handshaker = + ssl_client_handshaker_factory_create_handshaker; + impl->base.destroy = ssl_client_handshaker_factory_destroy; + *factory = &impl->base; + return TSI_OK; +} + +tsi_result tsi_create_ssl_server_handshaker_factory( + const unsigned char** pem_private_keys, + const size_t* pem_private_keys_sizes, const unsigned char** pem_cert_chains, + const size_t* pem_cert_chains_sizes, size_t key_cert_pair_count, + const unsigned char* pem_client_root_certs, + size_t pem_client_root_certs_size, int force_client_auth, + const char* cipher_list, const unsigned char** alpn_protocols, + const unsigned char* alpn_protocols_lengths, uint16_t num_alpn_protocols, + tsi_ssl_handshaker_factory** factory) { + tsi_ssl_server_handshaker_factory* impl = NULL; + tsi_result result = TSI_OK; + size_t i = 0; + + gpr_once_init(&init_openssl_once, init_openssl); + + if (factory == NULL) return TSI_INVALID_ARGUMENT; + *factory = NULL; + if (key_cert_pair_count == 0 || pem_private_keys == NULL || + pem_cert_chains == NULL) { + return TSI_INVALID_ARGUMENT; + } + + impl = calloc(1, sizeof(tsi_ssl_server_handshaker_factory)); + if (impl == NULL) return TSI_OUT_OF_RESOURCES; + impl->base.create_handshaker = + ssl_server_handshaker_factory_create_handshaker; + impl->base.destroy = ssl_server_handshaker_factory_destroy; + impl->ssl_contexts = calloc(key_cert_pair_count, sizeof(SSL_CTX*)); + impl->ssl_context_x509_subject_names = + calloc(key_cert_pair_count, sizeof(tsi_peer)); + if (impl->ssl_contexts == NULL || + impl->ssl_context_x509_subject_names == NULL) { + tsi_ssl_handshaker_factory_destroy(&impl->base); + return TSI_OUT_OF_RESOURCES; + } + impl->ssl_context_count = key_cert_pair_count; + + if (num_alpn_protocols > 0) { + result = build_alpn_protocol_name_list( + alpn_protocols, alpn_protocols_lengths, num_alpn_protocols, + &impl->alpn_protocol_list, &impl->alpn_protocol_list_length); + if (result != TSI_OK) { + tsi_ssl_handshaker_factory_destroy(&impl->base); + return result; + } + } + + for (i = 0; i < key_cert_pair_count; i++) { + do { + impl->ssl_contexts[i] = SSL_CTX_new(TLSv1_2_method()); + if (impl->ssl_contexts[i] == NULL) { + gpr_log(GPR_ERROR, "Could not create ssl context."); + result = TSI_OUT_OF_RESOURCES; + break; + } + result = populate_ssl_context( + impl->ssl_contexts[i], pem_private_keys[i], pem_private_keys_sizes[i], + pem_cert_chains[i], pem_cert_chains_sizes[i], cipher_list); + if (result != TSI_OK) break; + + if (pem_client_root_certs != NULL) { + int flags = SSL_VERIFY_PEER; + STACK_OF(X509_NAME)* root_names = NULL; + result = ssl_ctx_load_verification_certs( + impl->ssl_contexts[i], pem_client_root_certs, + pem_client_root_certs_size, &root_names); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Invalid verification certs."); + break; + } + SSL_CTX_set_client_CA_list(impl->ssl_contexts[i], root_names); + if (force_client_auth) flags |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + SSL_CTX_set_verify(impl->ssl_contexts[i], flags, NULL); + /* TODO(jboeuf): Add revocation verification. */ + } + + result = extract_x509_subject_names_from_pem_cert( + pem_cert_chains[i], pem_cert_chains_sizes[i], + &impl->ssl_context_x509_subject_names[i]); + if (result != TSI_OK) break; + + SSL_CTX_set_tlsext_servername_callback( + impl->ssl_contexts[i], + ssl_server_handshaker_factory_servername_callback); + SSL_CTX_set_tlsext_servername_arg(impl->ssl_contexts[i], impl); +#if TSI_OPENSSL_ALPN_SUPPORT + SSL_CTX_set_alpn_select_cb(impl->ssl_contexts[i], + server_handshaker_factory_alpn_callback, impl); +#endif /* TSI_OPENSSL_ALPN_SUPPORT */ + SSL_CTX_set_next_protos_advertised_cb( + impl->ssl_contexts[i], + server_handshaker_factory_npn_advertised_callback, impl); + } while (0); + + if (result != TSI_OK) { + tsi_ssl_handshaker_factory_destroy(&impl->base); + return result; + } + } + *factory = &impl->base; + return TSI_OK; +} + +/* --- tsi_ssl utils. --- */ + +int tsi_ssl_peer_matches_name(const tsi_peer* peer, const char* name) { + size_t i = 0; + size_t san_count = 0; + const tsi_peer_property* cn_property = NULL; + + /* For now reject what looks like an IP address. */ + if (looks_like_ip_address(name)) return 0; + + /* Check the SAN first. */ + for (i = 0; i < peer->property_count; i++) { + const tsi_peer_property* property = &peer->properties[i]; + if (property->name == NULL) continue; + if (strcmp(property->name, + TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY) == 0) { + san_count++; + if (does_entry_match_name(property->value.data, property->value.length, + name)) { + return 1; + } + } else if (strcmp(property->name, + TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY) == 0) { + cn_property = property; + } + } + + /* If there's no SAN, try the CN. */ + if (san_count == 0 && cn_property != NULL) { + if (does_entry_match_name(cn_property->value.data, + cn_property->value.length, name)) { + return 1; + } + } + + return 0; /* Not found. */ +} diff --git a/src/core/tsi/ssl_transport_security.h b/src/core/tsi/ssl_transport_security.h new file mode 100644 index 00000000..cdf4f294 --- /dev/null +++ b/src/core/tsi/ssl_transport_security.h @@ -0,0 +1,173 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_TSI_SSL_TRANSPORT_SECURITY_H +#define GRPC_INTERNAL_CORE_TSI_SSL_TRANSPORT_SECURITY_H + +#include "src/core/tsi/transport_security_interface.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Value for the TSI_CERTIFICATE_TYPE_PEER_PROPERTY property for X509 certs. */ +#define TSI_X509_CERTIFICATE_TYPE "X509" + +/* This property is of type TSI_PEER_PROPERTY_STRING. */ +#define TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY "x509_subject_common_name" +#define TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY \ + "x509_subject_alternative_name" + +#define TSI_SSL_ALPN_SELECTED_PROTOCOL "ssl_alpn_selected_protocol" + +/* --- tsi_ssl_handshaker_factory object --- + + This object creates tsi_handshaker objects implemented in terms of the + TLS 1.2 specificiation. */ + +typedef struct tsi_ssl_handshaker_factory tsi_ssl_handshaker_factory; + +/* Creates a client handshaker factory. + - pem_private_key is the buffer containing the PEM encoding of the client's + private key. This parameter can be NULL if the client does not have a + private key. + - pem_private_key_size is the size of the associated buffer. + - pem_cert_chain is the buffer containing the PEM encoding of the client's + certificate chain. This parameter can be NULL if the client does not have + a certificate chain. + - pem_cert_chain_size is the size of the associated buffer. + - pem_roots_cert is the buffer containing the PEM encoding of the server + root certificates. This parameter cannot be NULL. + - pem_roots_cert_size is the size of the associated buffer. + - cipher_suites contains an optional list of the ciphers that the client + supports. The format of this string is described in: + https://www.openssl.org/docs/apps/ciphers.html. + This parameter can be set to NULL to use the default set of ciphers. + TODO(jboeuf): Revisit the format of this parameter. + - alpn_protocols is an array containing the protocol names that the + handshakers created with this factory support. This parameter can be NULL. + - alpn_protocols_lengths is an array containing the lengths of the alpn + protocols specified in alpn_protocols. This parameter can be NULL. + - num_alpn_protocols is the number of alpn protocols and associated lengths + specified. If this parameter is 0, the other alpn parameters must be NULL. + - factory is the address of the factory pointer to be created. + + - This method returns TSI_OK on success or TSI_INVALID_PARAMETER in the case + where a parameter is invalid. */ +tsi_result tsi_create_ssl_client_handshaker_factory( + const unsigned char* pem_private_key, size_t pem_private_key_size, + const unsigned char* pem_cert_chain, size_t pem_cert_chain_size, + const unsigned char* pem_root_certs, size_t pem_root_certs_size, + const char* cipher_suites, const unsigned char** alpn_protocols, + const unsigned char* alpn_protocols_lengths, uint16_t num_alpn_protocols, + tsi_ssl_handshaker_factory** factory); + +/* Creates a server handshaker factory. + - version indicates which version of the specification to use. + - pem_private_keys is an array containing the PEM encoding of the server's + private keys. This parameter cannot be NULL. The size of the array is + given by the key_cert_pair_count parameter. + - pem_private_keys_sizes is the array containing the sizes of the associated + buffers. + - pem_cert_chains is an array containing the PEM encoding of the server's + cert chains. This parameter cannot be NULL. The size of the array is + given by the key_cert_pair_count parameter. + - pem_cert_chains_sizes is the array containing the sizes of the associated + buffers. + - key_cert_pair_count indicates the number of items in the private_key_files + and cert_chain_files parameters. + - pem_client_roots is the buffer containing the PEM encoding of the client + root certificates. This parameter may be NULL in which case the server will + not authenticate the client. If not NULL, the force_client_auth parameter + specifies if the server will accept only authenticated clients or both + authenticated and non-authenticated clients. + - pem_client_root_certs_size is the size of the associated buffer. + - force_client_auth, if set to non-zero will force the client to authenticate + with an SSL cert. Note that this option is ignored if pem_client_root_certs + is NULL or pem_client_roots_certs_size is 0 + - cipher_suites contains an optional list of the ciphers that the server + supports. The format of this string is described in: + https://www.openssl.org/docs/apps/ciphers.html. + This parameter can be set to NULL to use the default set of ciphers. + TODO(jboeuf): Revisit the format of this parameter. + - alpn_protocols is an array containing the protocol names that the + handshakers created with this factory support. This parameter can be NULL. + - alpn_protocols_lengths is an array containing the lengths of the alpn + protocols specified in alpn_protocols. This parameter can be NULL. + - num_alpn_protocols is the number of alpn protocols and associated lengths + specified. If this parameter is 0, the other alpn parameters must be NULL. + - factory is the address of the factory pointer to be created. + + - This method returns TSI_OK on success or TSI_INVALID_PARAMETER in the case + where a parameter is invalid. */ +tsi_result tsi_create_ssl_server_handshaker_factory( + const unsigned char** pem_private_keys, + const size_t* pem_private_keys_sizes, const unsigned char** pem_cert_chains, + const size_t* pem_cert_chains_sizes, size_t key_cert_pair_count, + const unsigned char* pem_client_root_certs, + size_t pem_client_root_certs_size, int force_client_auth, + const char* cipher_suites, const unsigned char** alpn_protocols, + const unsigned char* alpn_protocols_lengths, uint16_t num_alpn_protocols, + tsi_ssl_handshaker_factory** factory); + +/* Creates a handshaker. + - self is the factory from which the handshaker will be created. + - server_name_indication indicates the name of the server the client is + trying to connect to which will be relayed to the server using the SNI + extension. + This parameter must be NULL for a server handshaker factory. + - handhshaker is the address of the handshaker pointer to be created. + + - This method returns TSI_OK on success or TSI_INVALID_PARAMETER in the case + where a parameter is invalid. */ +tsi_result tsi_ssl_handshaker_factory_create_handshaker( + tsi_ssl_handshaker_factory* self, const char* server_name_indication, + tsi_handshaker** handshaker); + +/* Destroys the handshaker factory. WARNING: it is unsafe to destroy a factory + while handshakers created with this factory are still in use. */ +void tsi_ssl_handshaker_factory_destroy(tsi_ssl_handshaker_factory* self); + +/* Util that checks that an ssl peer matches a specific name. + Still TODO(jboeuf): + - handle mixed case. + - handle %encoded chars. + - handle public suffix wildchar more strictly (e.g. *.co.uk) + - handle IP addresses in SAN. */ +int tsi_ssl_peer_matches_name(const tsi_peer* peer, const char* name); + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_INTERNAL_CORE_TSI_SSL_TRANSPORT_SECURITY_H */ diff --git a/src/core/tsi/test_creds/README b/src/core/tsi/test_creds/README new file mode 100644 index 00000000..eb8482d6 --- /dev/null +++ b/src/core/tsi/test_creds/README @@ -0,0 +1,62 @@ +The test credentials (CONFIRMEDTESTKEY) have been generated with the following +commands: + +Bad credentials (badclient.* / badserver.*): +============================================ + +These are self-signed certificates: + +$ openssl req -x509 -newkey rsa:1024 -keyout badserver.key -out badserver.pem \ + -days 3650 -nodes + +When prompted for certificate information, everything is default except the +common name which is set to badserver.test.google.com. + + +Valid test credentials: +======================= + +The ca is self-signed: +---------------------- + +$ openssl req -x509 -new -newkey rsa:1024 -nodes -out ca.pem -config ca-openssl.cnf -days 3650 -extensions v3_req +When prompted for certificate information, everything is default. + +client is issued by CA: +----------------------- + +$ openssl genrsa -out client.key.rsa 1024 +$ openssl pkcs8 -topk8 -in client.key.rsa -out client.key -nocrypt +$ rm client.key.rsa +$ openssl req -new -key client.key -out client.csr + +When prompted for certificate information, everything is default except the +common name which is set to testclient. + +$ openssl ca -in client.csr -out client.pem + +server0 is issued by CA: +------------------------ + +$ openssl genrsa -out server0.key.rsa 1024 +$ openssl pkcs8 -topk8 -in server0.key.rsa -out server0.key -nocrypt +$ rm server0.key.rsa +$ openssl req -new -key server0.key -out server0.csr + +When prompted for certificate information, everything is default except the +common name which is set to *.test.google.com.au. + +$ openssl ca -in server0.csr -out server0.pem + +server1 is issued by CA with a special config for subject alternative names: +---------------------------------------------------------------------------- + +$ openssl genrsa -out server1.key.rsa 1024 +$ openssl pkcs8 -topk8 -in server1.key.rsa -out server1.key -nocrypt +$ rm server1.key.rsa +$ openssl req -new -key server1.key -out server1.csr -config server1-openssl.cnf + +When prompted for certificate information, everything is default except the +common name which is set to *.test.google.com. + +$ openssl ca -in server1.csr -out server1.pem diff --git a/src/core/tsi/test_creds/badclient.key b/src/core/tsi/test_creds/badclient.key new file mode 100644 index 00000000..58326851 --- /dev/null +++ b/src/core/tsi/test_creds/badclient.key @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALJfYnFn4nkj52WF +E5W2qUxCfjsEFyuXYYKS/07UPWsv3gpZhtjXgdeGL+dpwEBC0IRDBfGnkMp6YY5S +O7rnEz0X3r/fvgYy+dEl2jnaA6zgc7RzMGl9U11d56gP9FiDC2190mvP/hpq2xLZ +CTbIximpmaoQyxuuH1bbYunesIG/AgMBAAECgYAdqJCEzMIyZE7oaW0tOpcB0BiP +FYoIvH4BKRH8eHvR476mt+YdDhBP1scGUmYeCT4Ej+RgHv2LPTgVYwT9eciP2+E/ +CBCNRel0Sw9JepwW0r+jWJtDY1pp6YXAgNRGX2UflvUsT+o9lZvagf9moLTMyGvU +uLFnsyfLim1B4vXvWQJBANouZllXGZoSrZLtR3VgV4tzRQvJxu84kLeIk64Ov47X +pHVBMTRBfzPEhbBodjr1m5OLaVLqkFcXftzRCrbWoKsCQQDRSoLLXOiLrtJ3DLJC +rX7Y8wrHZrqk5bMdZLGa/UX8RanhVw3+Xp+urd1711umeNJfzu/MCk4a1KkG/CU0 +rqs9AkA4cSx1DD1JSG+yxMNpsAS1xJomFIrsM9vsPt7FdndDwrF+y+CovhDkGYDk +RAHh+svGfZg/pQK2JRPimAmHhzqFAkEAu6Ya70s2FUeB3Mu9aJs2CD6hg3dQEVkB +53DI7TX48d9kGW58VX1xnqS02LyWqAPcW5qm1kLHFLdndaPNmBaj4QJBAJugl367 +9d9t/QLTSuULLaoYv2vJT3s1y9HN89EoaDDEkPVfQu6GVEXgIBtim1sI/VPSzI8H +aXvaTUwblFWSM70= +-----END PRIVATE KEY----- diff --git a/src/core/tsi/test_creds/badclient.pem b/src/core/tsi/test_creds/badclient.pem new file mode 100644 index 00000000..17859702 --- /dev/null +++ b/src/core/tsi/test_creds/badclient.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICoDCCAgmgAwIBAgIJANIz2/zoRiapMA0GCSqGSIb3DQEBBQUAMGkxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQxIjAgBgNVBAMMGWJhZGNsaWVudC50ZXN0Lmdvb2dsZS5j +b20wHhcNMTQwNzI4MjAwODI1WhcNMjQwNzI1MjAwODI1WjBpMQswCQYDVQQGEwJB +VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 +cyBQdHkgTHRkMSIwIAYDVQQDDBliYWRjbGllbnQudGVzdC5nb29nbGUuY29tMIGf +MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCyX2JxZ+J5I+dlhROVtqlMQn47BBcr +l2GCkv9O1D1rL94KWYbY14HXhi/nacBAQtCEQwXxp5DKemGOUju65xM9F96/374G +MvnRJdo52gOs4HO0czBpfVNdXeeoD/RYgwttfdJrz/4aatsS2Qk2yMYpqZmqEMsb +rh9W22Lp3rCBvwIDAQABo1AwTjAdBgNVHQ4EFgQU523AJMR8Ds9V8fhf7gu1i0MM +UqAwHwYDVR0jBBgwFoAU523AJMR8Ds9V8fhf7gu1i0MMUqAwDAYDVR0TBAUwAwEB +/zANBgkqhkiG9w0BAQUFAAOBgQCI/tvSBYH1iyfLaCTBKwpdj36+MkR9EeJJmImx +X+bjhKWXwsBX4PDMWvdusr++QGUYtyoya+hfYMXRhXua39mD54xgloQNuu9REDwX +Ffto+aOw3BcYducz6ofxicFK/Y2VeXDurSMpRv5TfGf2Qr6eOOdaRhj6ed7BibHk +X1VGZA== +-----END CERTIFICATE----- diff --git a/src/core/tsi/test_creds/badserver.key b/src/core/tsi/test_creds/badserver.key new file mode 100644 index 00000000..abfbde10 --- /dev/null +++ b/src/core/tsi/test_creds/badserver.key @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKeZ1e1y29cmBKaW +oIUwJ5neOJUjx+eD/3nRPe+dvLXEd9+db0fG5RYRR0S3mF1Ywuj4PIxlTW2YprUS +oGSw+tcqWNIzxv94HjwYFkkvER3AblXcDBh0P2zAkzg+nf9AcAsMh0QpDTyrXtMl +gqryjq1/vkhFofKMMbY+aXJdG6OBAgMBAAECgYAAgaB51S0A22aMMkxN2rVj6530 +JWWHN4jgD1fGj41wZyWNkWYyq1Ep3ed/N6bIMWp1VbqpGe0/9YQba/D8HOTFHGRt +72YXnP1e/ds8cxU4x4j1vvqSPtXpMmkiXfXijOvCl9mrMH2xjghFAt6/1Nb9xo1m +VdcOB8OdSuOIw6CI+QJBAN5FZUbS+bRXDWII/FaAih1DBpwCxhYEN+TXPJBxSen6 +kOzGt5g+mB6YqRMZ/qshshwPq7bsgFGfJ2lIdS2t3GsCQQDBCKifV5AAkOdOUrkK +HvoX3qnVmyIA8CyvWLcIWpfZ76QAYh0q0StedKdOMXaB1jTeSJ2KU1nlss7UD1Yw +VbrDAkAwjMHpbW3jiVw//Kx5jIwehiRscWKpLnSzBJyTBFvbwsJjJai2lX2OuVO8 ++2GYKb0Iyhd81j3VFkl6grwtpRtPAkB7+n+yt555fpfRKjhGU9b09cHGu7h/OcK5 +bBVCfE0DYHLI/DsXgPiF1g6Onh4rDdUu3xyv9xDKAqnscV099hHZAkEAvcFBfXZs +tk18N+bUcvXTdZjzZbfLCHlJmwPIspZ8G/6Pn63deg4GVYoCvTwGruah+8y734Ph +7PskfPgUQlB7Ag== +-----END PRIVATE KEY----- diff --git a/src/core/tsi/test_creds/badserver.pem b/src/core/tsi/test_creds/badserver.pem new file mode 100644 index 00000000..983c979f --- /dev/null +++ b/src/core/tsi/test_creds/badserver.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICoDCCAgmgAwIBAgIJAPdqwqsKNy81MA0GCSqGSIb3DQEBBQUAMGkxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQxIjAgBgNVBAMMGWJhZHNlcnZlci50ZXN0Lmdvb2dsZS5j +b20wHhcNMTQwNzI4MjAwODU0WhcNMjQwNzI1MjAwODU0WjBpMQswCQYDVQQGEwJB +VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 +cyBQdHkgTHRkMSIwIAYDVQQDDBliYWRzZXJ2ZXIudGVzdC5nb29nbGUuY29tMIGf +MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCnmdXtctvXJgSmlqCFMCeZ3jiVI8fn +g/950T3vnby1xHffnW9HxuUWEUdEt5hdWMLo+DyMZU1tmKa1EqBksPrXKljSM8b/ +eB48GBZJLxEdwG5V3AwYdD9swJM4Pp3/QHALDIdEKQ08q17TJYKq8o6tf75IRaHy +jDG2PmlyXRujgQIDAQABo1AwTjAdBgNVHQ4EFgQU3u/qvHr9knMBeZyAD7mAA/ec +8cUwHwYDVR0jBBgwFoAU3u/qvHr9knMBeZyAD7mAA/ec8cUwDAYDVR0TBAUwAwEB +/zANBgkqhkiG9w0BAQUFAAOBgQA/FmR1SGLguxCCfhp4CYCbrAePSyPWDi48gTwj +vVZf/OMxdVu/H8sBYFf27BjbrEugAw16DElFtgTZ83pLb2BvkUgb6vBUK5sEkgmh +z88zBsgDp8aCf4STDOLFZMBh/E9ZKkm1zogbEmlTjFp/ceSpa2gNv7OuN4WiorOh +Wvw40g== +-----END CERTIFICATE----- diff --git a/src/core/tsi/test_creds/ca-openssl.cnf b/src/core/tsi/test_creds/ca-openssl.cnf new file mode 100644 index 00000000..e97b945e --- /dev/null +++ b/src/core/tsi/test_creds/ca-openssl.cnf @@ -0,0 +1,17 @@ +[req] +distinguished_name = req_distinguished_name +req_extensions = v3_req + +[req_distinguished_name] +countryName = Country Name (2 letter code) +countryName_default = AU +stateOrProvinceName = State or Province Name (full name) +stateOrProvinceName_default = Some-State +organizationName = Organization Name (eg, company) +organizationName_default = Internet Widgits Pty Ltd +commonName = Common Name (eg, YOUR name) +commonName_default = testca + +[v3_req] +basicConstraints = CA:true +keyUsage = critical, keyCertSign diff --git a/src/core/tsi/test_creds/ca.key b/src/core/tsi/test_creds/ca.key new file mode 100644 index 00000000..03c4f950 --- /dev/null +++ b/src/core/tsi/test_creds/ca.key @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAMBA3wVeTGHZR1Ry +e/i+J8a2cu5gXwFV6TnObzGM7bLFCO5i9v4mLo4iFzPsHmWDUxKS3Y8iXbu0eYBl +LoNY0lSvxDx33O+DuwMmVN+DzSD+Eod9zfvwOWHsazYCZT2PhNxnVWIuJXViY4JA +HUGodjx+QAi6yCAurUZGvYXGgZSBAgMBAAECgYAxRi8i9BlFlufGSBVoGmydbJOm +bwLKl9dP3o33ODSP9hok5y6A0w5plWk3AJSF1hPLleK9VcSKYGYnt0clmPVHF35g +bx2rVK8dOT0mn7rz9Zr70jcSz1ETA2QonHZ+Y+niLmcic9At6hRtWiewblUmyFQm +GwggIzi7LOyEUHrEcQJBAOXxyQvnLvtKzXiqcsW/K6rExqVJVk+KF0fzzVyMzTJx +HRBxUVgvGdEJT7j+7P2kcTyafve0BBzDSPIaDyiJ+Y0CQQDWCb7jASFSbu5M3Zcd +Gkr4ZKN1XO3VLQX10b22bQYdF45hrTN2tnzRvVUR4q86VVnXmiGiTqmLkXcA2WWf +pHfFAkAhv9olUBo6MeF0i3frBEMRfm41hk0PwZHnMqZ6pgPcGnQMnMU2rzsXzkkQ +OwJnvAIOxhJKovZTjmofdqmw5odlAkBYVUdRWjsNUTjJwj3GRf6gyq/nFMYWz3EB +RWFdM1ttkDYzu45ctO2IhfHg4sPceDMO1s6AtKQmNI9/azkUjITdAkApNa9yFRzc +TBaDNPd5KVd58LVIzoPQ6i7uMHteLXJUWqSroji6S3s4gKMFJ/dO+ZXIlgQgfJJJ +ZDL4cdrdkeoM +-----END PRIVATE KEY----- diff --git a/src/core/tsi/test_creds/ca.pem b/src/core/tsi/test_creds/ca.pem new file mode 100644 index 00000000..6c8511a7 --- /dev/null +++ b/src/core/tsi/test_creds/ca.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICSjCCAbOgAwIBAgIJAJHGGR4dGioHMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQxDzANBgNVBAMTBnRlc3RjYTAeFw0xNDExMTEyMjMxMjla +Fw0yNDExMDgyMjMxMjlaMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0 +YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMT +BnRlc3RjYTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwEDfBV5MYdlHVHJ7 ++L4nxrZy7mBfAVXpOc5vMYztssUI7mL2/iYujiIXM+weZYNTEpLdjyJdu7R5gGUu +g1jSVK/EPHfc74O7AyZU34PNIP4Sh33N+/A5YexrNgJlPY+E3GdVYi4ldWJjgkAd +Qah2PH5ACLrIIC6tRka9hcaBlIECAwEAAaMgMB4wDAYDVR0TBAUwAwEB/zAOBgNV +HQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQELBQADgYEAHzC7jdYlzAVmddi/gdAeKPau +sPBG/C2HCWqHzpCUHcKuvMzDVkY/MP2o6JIW2DBbY64bO/FceExhjcykgaYtCH/m +oIU63+CFOTtR7otyQAWHqXa7q4SbCDlG7DyRFxqG0txPtGvy12lgldA2+RgcigQG +Dfcog5wrJytaQ6UA0wE= +-----END CERTIFICATE----- diff --git a/src/core/tsi/test_creds/client.key b/src/core/tsi/test_creds/client.key new file mode 100644 index 00000000..f48d0735 --- /dev/null +++ b/src/core/tsi/test_creds/client.key @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICeQIBADANBgkqhkiG9w0BAQEFAASCAmMwggJfAgEAAoGBAOxUR9uhvhbeVUIM +s5WbH0px0mehl2+6sZpNjzvE2KimZpHzMJHukVH0Ffkvhs0b8+S5Ut9VNUAqd3IM +JCCAEGtRNoQhM1t9Yr2zAckSvbRacp+FL/Cj9eDmyo00KsVGaeefA4Dh4OW+ZhkT +NKcldXqkSuj1sEf244JZYuqZp6/tAgMBAAECgYEAi2NSVqpZMafE5YYUTcMGe6QS +k2jtpsqYgggI2RnLJ/2tNZwYI5pwP8QVSbnMaiF4gokD5hGdrNDfTnb2v+yIwYEH +0w8+oG7Z81KodsiZSIDJfTGsAZhVNwOz9y0VD8BBZZ1/274Zh52AUKLjZS/ZwIbS +W2ywya855dPnH/wj+0ECQQD9X8D920kByTNHhBG18biAEZ4pxs9f0OAG8333eVcI +w2lJDLsYDZrCB2ocgA3lUdozlzPC7YDYw8reg0tkiRY5AkEA7sdNzOeQsQRn7++5 +0bP9DtT/iON1gbfxRzCfCfXdoOtfQWIzTePWtURt9X/5D9NofI0Rg5W2oGy/MLe5 +/sXHVQJBAIup5XrJDkQywNZyAUU2ecn2bCWBFjwtqd+LBmuMciI9fOKsZtEKZrz/ +U0lkeMRoSwvXE8wmGLjjrAbdfohrXFkCQQDZEx/LtIl6JINJQiswVe0tWr6k+ASP +1WXoTm+HYpoF/XUvv9LccNF1IazFj34hwRQwhx7w/V52Ieb+p0jUMYGxAkEAjDhd +9pBO1fKXWiXzi9ZKfoyTNcUq3eBSVKwPG2nItg5ycXengjT5sgcWDnciIzW7BIVI +JiqOszq9GWESErAatg== +-----END PRIVATE KEY----- diff --git a/src/core/tsi/test_creds/client.pem b/src/core/tsi/test_creds/client.pem new file mode 100644 index 00000000..e3320910 --- /dev/null +++ b/src/core/tsi/test_creds/client.pem @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICHzCCAYgCAQEwDQYJKoZIhvcNAQEFBQAwVjELMAkGA1UEBhMCQVUxEzARBgNV +BAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0 +ZDEPMA0GA1UEAwwGdGVzdGNhMB4XDTE0MDcxNzIzNTYwMloXDTI0MDcxNDIzNTYw +MlowWjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDETMBEGA1UEAwwKdGVzdGNsaWVudDCB +nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA7FRH26G+Ft5VQgyzlZsfSnHSZ6GX +b7qxmk2PO8TYqKZmkfMwke6RUfQV+S+GzRvz5LlS31U1QCp3cgwkIIAQa1E2hCEz +W31ivbMByRK9tFpyn4Uv8KP14ObKjTQqxUZp558DgOHg5b5mGRM0pyV1eqRK6PWw +R/bjglli6pmnr+0CAwEAATANBgkqhkiG9w0BAQUFAAOBgQAStSm5PM7ubROiKK6/ +T2FkKlhiTOx+Ryenm3Eio59emq+jXl+1nhPySX5G2PQzSR5vd1dIhwgZSR4Gyttk +tRZ57k/NI1brUW8joiEOMJA/Mr7H7asx7wIRYDE91Fs8GkKWd5LhoPAQj+qdG35C +OO+svdkmqH0KZo320ZUqdl2ooQ== +-----END CERTIFICATE----- diff --git a/src/core/tsi/test_creds/server0.key b/src/core/tsi/test_creds/server0.key new file mode 100644 index 00000000..add153c9 --- /dev/null +++ b/src/core/tsi/test_creds/server0.key @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANOmffupIGC8YDau +rOF4eKnHwPszgpkkhWzKsVxhNDBxCVYx4TEjG0XWIO0iyRXupZbUC+7N/8HnEVNa +8F1jYhng14Iiq99cNQbbnuHHhIztmpocrJTxmnhGzoAnRa1Tb+GnAuRoIHRA/V2c +VUE9tbikQugFx/SPgXAw6tfWB+YvAgMBAAECgYEAoEq9qzUBgoHoVEGiSPiWWe8g +5p6yUA1qx2QTQyWTAwT4z0DjjfVKmG99bFsl8+hTnJFnoCp/gnjflEOROwkjp5kG +m0drqOPx1jeipJjpXYTBu49h+WpZ1PF+KhVtxsIm3OOCvh67iWaKyyOVb5Og8aiR +jl6dn/TdG/dlGD8AfUECQQDuNMle6p0oU8amC6O9wIMBroxx2nFstzE6O35PLEzG +/tj0kxxn9Jp2TS9mGaLCzSuXmpjlF4+NOWiBPkrLC2TfAkEA43Xg7uEUkaJAz2/W +m1lIBTLt+4rIQY/2emh33bDcA+rv8rwwrMMIv17/xPx7bs49YqGG5xufD+Rwl6TL +qFXYsQJAPrOwagax1aKvwJeBw3oAQhoTKAkLIEXcdGqipe6QSzVcIIz0xjxxyEAr +AOIwoLxnBCISqwMXq2H4K0UdZPMb2wJAdhdYLY1L6YRMk6XjzImg25oidisKZweA +FvMv8DgHMj2CUAqmVrt3SivfLH1M9C09L3zfFhOAFHcsgX58gav4MQJBANSBnrHj +tIq4l8z79CPUIuu3QyeEh+XwY8s5qE5CNTck0U59lzp9NvENHbkx3KO896TTerko ++8bXHMLkJkHPXms= +-----END PRIVATE KEY----- diff --git a/src/core/tsi/test_creds/server0.pem b/src/core/tsi/test_creds/server0.pem new file mode 100644 index 00000000..ade75d85 --- /dev/null +++ b/src/core/tsi/test_creds/server0.pem @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICHDCCAYUCAQQwDQYJKoZIhvcNAQEFBQAwVjELMAkGA1UEBhMCQVUxEzARBgNV +BAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0 +ZDEPMA0GA1UEAwwGdGVzdGNhMB4XDTE0MDcyMjE3NTk0OVoXDTI0MDcxOTE3NTk0 +OVowVzELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxFDASBgNVBAoM +C0dvb2dsZSBJbmMuMR0wGwYDVQQDDBQqLnRlc3QuZ29vZ2xlLmNvbS5hdTCBnzAN +BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA06Z9+6kgYLxgNq6s4Xh4qcfA+zOCmSSF +bMqxXGE0MHEJVjHhMSMbRdYg7SLJFe6lltQL7s3/wecRU1rwXWNiGeDXgiKr31w1 +Btue4ceEjO2amhyslPGaeEbOgCdFrVNv4acC5GggdED9XZxVQT21uKRC6AXH9I+B +cDDq19YH5i8CAwEAATANBgkqhkiG9w0BAQUFAAOBgQBtfR5qXG9TTI8YcYh7sA4V +GeNoplp0x6p7OG0NLvbJqAkUnkvjIkk1m1R2AUHhbkxzx6G75JIOoNJcWrCzywBA +BIsaTdmnNysf/s1hQJuD3IHiVb+7Ji0jhttnJlYcMid4o0tJO/a2E9YUxR+9cg0i +obb+Ql3qsvKdWBC1dDLDLw== +-----END CERTIFICATE----- diff --git a/src/core/tsi/test_creds/server1-openssl.cnf b/src/core/tsi/test_creds/server1-openssl.cnf new file mode 100644 index 00000000..8a021082 --- /dev/null +++ b/src/core/tsi/test_creds/server1-openssl.cnf @@ -0,0 +1,26 @@ +[req] +distinguished_name = req_distinguished_name +req_extensions = v3_req + +[req_distinguished_name] +countryName = Country Name (2 letter code) +countryName_default = US +stateOrProvinceName = State or Province Name (full name) +stateOrProvinceName_default = Illinois +localityName = Locality Name (eg, city) +localityName_default = Chicago +organizationName = Organization Name (eg, company) +organizationName_default = Example, Co. +commonName = Common Name (eg, YOUR name) +commonName_max = 64 + +[v3_req] +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +subjectAltName = @alt_names + +[alt_names] +DNS.1 = *.test.google.fr +DNS.2 = waterzooi.test.google.be +DNS.3 = *.test.youtube.com +IP.1 = "192.168.1.3" diff --git a/src/core/tsi/test_creds/server1.key b/src/core/tsi/test_creds/server1.key new file mode 100644 index 00000000..143a5b87 --- /dev/null +++ b/src/core/tsi/test_creds/server1.key @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAOHDFScoLCVJpYDD +M4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1BgzkWF+slf +3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd9N8YwbBY +AckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAECgYAn7qGnM2vbjJNBm0VZCkOkTIWm +V10okw7EPJrdL2mkre9NasghNXbE1y5zDshx5Nt3KsazKOxTT8d0Jwh/3KbaN+YY +tTCbKGW0pXDRBhwUHRcuRzScjli8Rih5UOCiZkhefUTcRb6xIhZJuQy71tjaSy0p +dHZRmYyBYO2YEQ8xoQJBAPrJPhMBkzmEYFtyIEqAxQ/o/A6E+E4w8i+KM7nQCK7q +K4JXzyXVAjLfyBZWHGM2uro/fjqPggGD6QH1qXCkI4MCQQDmdKeb2TrKRh5BY1LR +81aJGKcJ2XbcDu6wMZK4oqWbTX2KiYn9GB0woM6nSr/Y6iy1u145YzYxEV/iMwff +DJULAkB8B2MnyzOg0pNFJqBJuH29bKCcHa8gHJzqXhNO5lAlEbMK95p/P2Wi+4Hd +aiEIAF1BF326QJcvYKmwSmrORp85AkAlSNxRJ50OWrfMZnBgzVjDx3xG6KsFQVk2 +ol6VhqL6dFgKUORFUWBvnKSyhjJxurlPEahV6oo6+A+mPhFY8eUvAkAZQyTdupP3 +XEFQKctGz+9+gKkemDp7LBBMEMBXrGTLPhpEfcjv/7KPdnFHYmhYeBTBnuVmTVWe +F98XJ7tIFfJq +-----END PRIVATE KEY----- diff --git a/src/core/tsi/test_creds/server1.pem b/src/core/tsi/test_creds/server1.pem new file mode 100644 index 00000000..8e582e57 --- /dev/null +++ b/src/core/tsi/test_creds/server1.pem @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICmzCCAgSgAwIBAgIBAzANBgkqhkiG9w0BAQUFADBWMQswCQYDVQQGEwJBVTET +MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ +dHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0Y2EwHhcNMTQwNzIyMDYwMDU3WhcNMjQwNzE5 +MDYwMDU3WjBkMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV +BAcTB0NoaWNhZ28xFDASBgNVBAoTC0dvb2dsZSBJbmMuMRowGAYDVQQDFBEqLnRl +c3QuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA4cMVJygs +JUmlgMMzgdi0h1XoCR7+ww1pop04OMMyy7H/i0PJ2W6Y35+b4CM8QrkYeEafUGDO +RYX6yV/cHGGsD/x02ye6ey1UDtkGAD/mpDEx8YCrjAc1Vfvt8Fk6Cn1WVIxV/J30 +3xjBsFgByQ55RBp1OLZfVLo6AleBDSbcxaECAwEAAaNrMGkwCQYDVR0TBAIwADAL +BgNVHQ8EBAMCBeAwTwYDVR0RBEgwRoIQKi50ZXN0Lmdvb2dsZS5mcoIYd2F0ZXJ6 +b29pLnRlc3QuZ29vZ2xlLmJlghIqLnRlc3QueW91dHViZS5jb22HBMCoAQMwDQYJ +KoZIhvcNAQEFBQADgYEAM2Ii0LgTGbJ1j4oqX9bxVcxm+/R5Yf8oi0aZqTJlnLYS +wXcBykxTx181s7WyfJ49WwrYXo78zTDAnf1ma0fPq3e4mpspvyndLh1a+OarHa1e +aT0DIIYk7qeEa1YcVljx2KyLd0r1BBAfrwyGaEPVeJQVYWaOJRU2we/KD4ojf9s= +-----END CERTIFICATE----- diff --git a/src/core/tsi/transport_security.c b/src/core/tsi/transport_security.c new file mode 100644 index 00000000..ec02a478 --- /dev/null +++ b/src/core/tsi/transport_security.c @@ -0,0 +1,280 @@ +/* + * + * Copyright 2015, 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 "src/core/tsi/transport_security.h" + +#include +#include + +/* --- Tracing. --- */ + +int tsi_tracing_enabled = 0; + +/* --- Utils. --- */ + +char* tsi_strdup(const char* src) { + char* dst; + size_t len; + if (!src) return NULL; + len = strlen(src) + 1; + dst = malloc(len); + if (!dst) return NULL; + memcpy(dst, src, len); + return dst; +} + +/* --- tsi_result common implementation. --- */ + +const char* tsi_result_to_string(tsi_result result) { + switch (result) { + case TSI_OK: + return "TSI_OK"; + case TSI_UNKNOWN_ERROR: + return "TSI_UNKNOWN_ERROR"; + case TSI_INVALID_ARGUMENT: + return "TSI_INVALID_ARGUMENT"; + case TSI_PERMISSION_DENIED: + return "TSI_PERMISSION_DENIED"; + case TSI_INCOMPLETE_DATA: + return "TSI_INCOMPLETE_DATA"; + case TSI_FAILED_PRECONDITION: + return "TSI_FAILED_PRECONDITION"; + case TSI_UNIMPLEMENTED: + return "TSI_UNIMPLEMENTED"; + case TSI_INTERNAL_ERROR: + return "TSI_INTERNAL_ERROR"; + case TSI_DATA_CORRUPTED: + return "TSI_DATA_CORRUPTED"; + case TSI_NOT_FOUND: + return "TSI_NOT_FOUND"; + case TSI_PROTOCOL_FAILURE: + return "TSI_PROTOCOL_FAILURE"; + case TSI_HANDSHAKE_IN_PROGRESS: + return "TSI_HANDSHAKE_IN_PROGRESS"; + case TSI_OUT_OF_RESOURCES: + return "TSI_OUT_OF_RESOURCES"; + default: + return "UNKNOWN"; + } +} + +/* --- tsi_frame_protector common implementation. --- + + Calls specific implementation after state/input validation. */ + +tsi_result tsi_frame_protector_protect(tsi_frame_protector* self, + const unsigned char* unprotected_bytes, + size_t* unprotected_bytes_size, + unsigned char* protected_output_frames, + size_t* protected_output_frames_size) { + if (self == NULL || unprotected_bytes == NULL || + unprotected_bytes_size == NULL || protected_output_frames == NULL || + protected_output_frames_size == NULL) { + return TSI_INVALID_ARGUMENT; + } + return self->vtable->protect(self, unprotected_bytes, unprotected_bytes_size, + protected_output_frames, + protected_output_frames_size); +} + +tsi_result tsi_frame_protector_protect_flush( + tsi_frame_protector* self, unsigned char* protected_output_frames, + size_t* protected_output_frames_size, size_t* still_pending_size) { + if (self == NULL || protected_output_frames == NULL || + protected_output_frames == NULL || still_pending_size == NULL) { + return TSI_INVALID_ARGUMENT; + } + return self->vtable->protect_flush(self, protected_output_frames, + protected_output_frames_size, + still_pending_size); +} + +tsi_result tsi_frame_protector_unprotect( + tsi_frame_protector* self, const unsigned char* protected_frames_bytes, + size_t* protected_frames_bytes_size, unsigned char* unprotected_bytes, + size_t* unprotected_bytes_size) { + if (self == NULL || protected_frames_bytes == NULL || + protected_frames_bytes_size == NULL || unprotected_bytes == NULL || + unprotected_bytes_size == NULL) { + return TSI_INVALID_ARGUMENT; + } + return self->vtable->unprotect(self, protected_frames_bytes, + protected_frames_bytes_size, unprotected_bytes, + unprotected_bytes_size); +} + +void tsi_frame_protector_destroy(tsi_frame_protector* self) { + if (self == NULL) return; + self->vtable->destroy(self); +} + +/* --- tsi_handshaker common implementation. --- + + Calls specific implementation after state/input validation. */ + +tsi_result tsi_handshaker_get_bytes_to_send_to_peer(tsi_handshaker* self, + unsigned char* bytes, + size_t* bytes_size) { + if (self == NULL) return TSI_INVALID_ARGUMENT; + if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; + return self->vtable->get_bytes_to_send_to_peer(self, bytes, bytes_size); +} + +tsi_result tsi_handshaker_process_bytes_from_peer(tsi_handshaker* self, + const unsigned char* bytes, + size_t* bytes_size) { + if (self == NULL) return TSI_INVALID_ARGUMENT; + if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; + return self->vtable->process_bytes_from_peer(self, bytes, bytes_size); +} + +tsi_result tsi_handshaker_get_result(tsi_handshaker* self) { + if (self == NULL) return TSI_INVALID_ARGUMENT; + if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; + return self->vtable->get_result(self); +} + +tsi_result tsi_handshaker_extract_peer(tsi_handshaker* self, tsi_peer* peer) { + if (self == NULL || peer == NULL) return TSI_INVALID_ARGUMENT; + memset(peer, 0, sizeof(tsi_peer)); + if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; + if (tsi_handshaker_get_result(self) != TSI_OK) { + return TSI_FAILED_PRECONDITION; + } + return self->vtable->extract_peer(self, peer); +} + +tsi_result tsi_handshaker_create_frame_protector( + tsi_handshaker* self, size_t* max_protected_frame_size, + tsi_frame_protector** protector) { + tsi_result result; + if (self == NULL || protector == NULL) return TSI_INVALID_ARGUMENT; + if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; + if (tsi_handshaker_get_result(self) != TSI_OK) { + return TSI_FAILED_PRECONDITION; + } + result = self->vtable->create_frame_protector(self, max_protected_frame_size, + protector); + if (result == TSI_OK) { + self->frame_protector_created = 1; + } + return result; +} + +void tsi_handshaker_destroy(tsi_handshaker* self) { + if (self == NULL) return; + self->vtable->destroy(self); +} + +/* --- tsi_peer implementation. --- */ + +tsi_peer_property tsi_init_peer_property(void) { + tsi_peer_property property; + memset(&property, 0, sizeof(tsi_peer_property)); + return property; +} + +static void tsi_peer_destroy_list_property(tsi_peer_property* children, + size_t child_count) { + size_t i; + for (i = 0; i < child_count; i++) { + tsi_peer_property_destruct(&children[i]); + } + free(children); +} + +void tsi_peer_property_destruct(tsi_peer_property* property) { + if (property->name != NULL) { + free(property->name); + } + if (property->value.data != NULL) { + free(property->value.data); + } + *property = tsi_init_peer_property(); /* Reset everything to 0. */ +} + +void tsi_peer_destruct(tsi_peer* self) { + if (self == NULL) return; + if (self->properties != NULL) { + tsi_peer_destroy_list_property(self->properties, self->property_count); + self->properties = NULL; + } + self->property_count = 0; +} + +tsi_result tsi_construct_allocated_string_peer_property( + const char* name, size_t value_length, tsi_peer_property* property) { + *property = tsi_init_peer_property(); + if (name != NULL) { + property->name = tsi_strdup(name); + if (property->name == NULL) return TSI_OUT_OF_RESOURCES; + } + if (value_length > 0) { + property->value.data = calloc(1, value_length); + if (property->value.data == NULL) { + tsi_peer_property_destruct(property); + return TSI_OUT_OF_RESOURCES; + } + property->value.length = value_length; + } + return TSI_OK; +} + +tsi_result tsi_construct_string_peer_property_from_cstring( + const char* name, const char* value, tsi_peer_property* property) { + return tsi_construct_string_peer_property(name, value, strlen(value), + property); +} + +tsi_result tsi_construct_string_peer_property(const char* name, + const char* value, + size_t value_length, + tsi_peer_property* property) { + tsi_result result = tsi_construct_allocated_string_peer_property( + name, value_length, property); + if (result != TSI_OK) return result; + if (value_length > 0) { + memcpy(property->value.data, value, value_length); + } + return TSI_OK; +} + +tsi_result tsi_construct_peer(size_t property_count, tsi_peer* peer) { + memset(peer, 0, sizeof(tsi_peer)); + if (property_count > 0) { + peer->properties = calloc(property_count, sizeof(tsi_peer_property)); + if (peer->properties == NULL) return TSI_OUT_OF_RESOURCES; + peer->property_count = property_count; + } + return TSI_OK; +} diff --git a/src/core/tsi/transport_security.h b/src/core/tsi/transport_security.h new file mode 100644 index 00000000..34283f2f --- /dev/null +++ b/src/core/tsi/transport_security.h @@ -0,0 +1,111 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_TSI_TRANSPORT_SECURITY_H +#define GRPC_INTERNAL_CORE_TSI_TRANSPORT_SECURITY_H + +#include "src/core/tsi/transport_security_interface.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern int tsi_tracing_enabled; + +/* Base for tsi_frame_protector implementations. + See transport_security_interface.h for documentation. */ +typedef struct { + tsi_result (*protect)(tsi_frame_protector* self, + const unsigned char* unprotected_bytes, + size_t* unprotected_bytes_size, + unsigned char* protected_output_frames, + size_t* protected_output_frames_size); + tsi_result (*protect_flush)(tsi_frame_protector* self, + unsigned char* protected_output_frames, + size_t* protected_output_frames_size, + size_t* still_pending_size); + tsi_result (*unprotect)(tsi_frame_protector* self, + const unsigned char* protected_frames_bytes, + size_t* protected_frames_bytes_size, + unsigned char* unprotected_bytes, + size_t* unprotected_bytes_size); + void (*destroy)(tsi_frame_protector* self); +} tsi_frame_protector_vtable; + +struct tsi_frame_protector { + const tsi_frame_protector_vtable* vtable; +}; + +/* Base for tsi_handshaker implementations. + See transport_security_interface.h for documentation. */ +typedef struct { + tsi_result (*get_bytes_to_send_to_peer)(tsi_handshaker* self, + unsigned char* bytes, + size_t* bytes_size); + tsi_result (*process_bytes_from_peer)(tsi_handshaker* self, + const unsigned char* bytes, + size_t* bytes_size); + tsi_result (*get_result)(tsi_handshaker* self); + tsi_result (*extract_peer)(tsi_handshaker* self, tsi_peer* peer); + tsi_result (*create_frame_protector)(tsi_handshaker* self, + size_t* max_protected_frame_size, + tsi_frame_protector** protector); + void (*destroy)(tsi_handshaker* self); +} tsi_handshaker_vtable; + +struct tsi_handshaker { + const tsi_handshaker_vtable* vtable; + int frame_protector_created; +}; + +/* Peer and property construction/destruction functions. */ +tsi_result tsi_construct_peer(size_t property_count, tsi_peer* peer); +tsi_peer_property tsi_init_peer_property(void); +void tsi_peer_property_destruct(tsi_peer_property* property); +tsi_result tsi_construct_string_peer_property(const char* name, + const char* value, + size_t value_length, + tsi_peer_property* property); +tsi_result tsi_construct_allocated_string_peer_property( + const char* name, size_t value_length, tsi_peer_property* property); +tsi_result tsi_construct_string_peer_property_from_cstring( + const char* name, const char* value, tsi_peer_property* property); + +/* Utils. */ +char* tsi_strdup(const char* src); /* Sadly, no strdup in C89. */ + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_INTERNAL_CORE_TSI_TRANSPORT_SECURITY_H */ diff --git a/src/core/tsi/transport_security_interface.h b/src/core/tsi/transport_security_interface.h new file mode 100644 index 00000000..03a51683 --- /dev/null +++ b/src/core/tsi/transport_security_interface.h @@ -0,0 +1,344 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_TSI_TRANSPORT_SECURITY_INTERFACE_H +#define GRPC_INTERNAL_CORE_TSI_TRANSPORT_SECURITY_INTERFACE_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* --- tsi result --- */ + +typedef enum { + TSI_OK = 0, + TSI_UNKNOWN_ERROR = 1, + TSI_INVALID_ARGUMENT = 2, + TSI_PERMISSION_DENIED = 3, + TSI_INCOMPLETE_DATA = 4, + TSI_FAILED_PRECONDITION = 5, + TSI_UNIMPLEMENTED = 6, + TSI_INTERNAL_ERROR = 7, + TSI_DATA_CORRUPTED = 8, + TSI_NOT_FOUND = 9, + TSI_PROTOCOL_FAILURE = 10, + TSI_HANDSHAKE_IN_PROGRESS = 11, + TSI_OUT_OF_RESOURCES = 12 +} tsi_result; + +const char* tsi_result_to_string(tsi_result result); + +/* --- tsi tracing --- */ + +/* Set this early to avoid races */ +extern int tsi_tracing_enabled; + +/* --- tsi_frame_protector object --- + + This object protects and unprotects buffers once the handshake is done. + Implementations of this object must be thread compatible. */ + +typedef struct tsi_frame_protector tsi_frame_protector; + +/* Outputs protected frames. + - unprotected_bytes is an input only parameter and points to the data + to be protected. + - unprotected_bytes_size is an input/output parameter used by the caller to + specify how many bytes are available in unprotected_bytes. The output + value is the number of bytes consumed during the call. + - protected_output_frames points to a buffer allocated by the caller that + will be written. + - protected_output_frames_size is an input/output parameter used by the + caller to specify how many bytes are available in protected_output_frames. + As an output, this value indicates the number of bytes written. + - This method returns TSI_OK in case of success or a specific error code in + case of failure. Note that even if all the input unprotected bytes are + consumed, they may not have been processed into the returned protected + output frames. The caller should call the protect_flush method + to make sure that there are no more protected bytes buffered in the + protector. + + A typical way to call this method would be: + + ------------------------------------------------------------------------ + unsigned char protected_buffer[4096]; + size_t protected_buffer_size = sizeof(protected_buffer); + tsi_result result = TSI_OK; + while (message_size > 0) { + size_t protected_buffer_size_to_send = protected_buffer_size; + size_t processed_message_size = message_size; + result = tsi_frame_protector_protect(protector, + message_bytes, + &processed_message_size, + protected_buffer, + &protected_buffer_size_to_send); + if (result != TSI_OK) break; + send_bytes_to_peer(protected_buffer, protected_buffer_size_to_send); + message_bytes += processed_message_size; + message_size -= processed_message_size; + + // Don't forget to flush. + if (message_size == 0) { + size_t still_pending_size; + do { + protected_buffer_size_to_send = protected_buffer_size; + result = tsi_frame_protector_protect_flush( + protector, protected_buffer, + &protected_buffer_size_to_send, &still_pending_size); + if (result != TSI_OK) break; + send_bytes_to_peer(protected_buffer, protected_buffer_size_to_send); + } while (still_pending_size > 0); + } + } + + if (result != TSI_OK) HandleError(result); + ------------------------------------------------------------------------ */ +tsi_result tsi_frame_protector_protect(tsi_frame_protector* self, + const unsigned char* unprotected_bytes, + size_t* unprotected_bytes_size, + unsigned char* protected_output_frames, + size_t* protected_output_frames_size); + +/* Indicates that we need to flush the bytes buffered in the protector and get + the resulting frame. + - protected_output_frames points to a buffer allocated by the caller that + will be written. + - protected_output_frames_size is an input/output parameter used by the + caller to specify how many bytes are available in protected_output_frames. + - still_pending_bytes is an output parameter indicating the number of bytes + that still need to be flushed from the protector.*/ +tsi_result tsi_frame_protector_protect_flush( + tsi_frame_protector* self, unsigned char* protected_output_frames, + size_t* protected_output_frames_size, size_t* still_pending_size); + +/* Outputs unprotected bytes. + - protected_frames_bytes is an input only parameter and points to the + protected frames to be unprotected. + - protected_frames_bytes_size is an input/output only parameter used by the + caller to specify how many bytes are available in protected_bytes. The + output value is the number of bytes consumed during the call. + Implementations will buffer up to a frame of protected data. + - unprotected_bytes points to a buffer allocated by the caller that will be + written. + - unprotected_bytes_size is an input/output parameter used by the caller to + specify how many bytes are available in unprotected_bytes. This + value is expected to be at most max_protected_frame_size minus overhead + which means that max_protected_frame_size is a safe bet. The output value + is the number of bytes actually written. + If *unprotected_bytes_size is unchanged, there may be more data remaining + to unprotect, and the caller should call this function again. + + - This method returns TSI_OK in case of success. Success includes cases where + there is not enough data to output a frame in which case + unprotected_bytes_size will be set to 0 and cases where the internal buffer + needs to be read before new protected data can be processed in which case + protected_frames_size will be set to 0. */ +tsi_result tsi_frame_protector_unprotect( + tsi_frame_protector* self, const unsigned char* protected_frames_bytes, + size_t* protected_frames_bytes_size, unsigned char* unprotected_bytes, + size_t* unprotected_bytes_size); + +/* Destroys the tsi_frame_protector object. */ +void tsi_frame_protector_destroy(tsi_frame_protector* self); + +/* --- tsi_peer objects --- + + tsi_peer objects are a set of properties. The peer owns the properties. */ + +/* This property is of type TSI_PEER_PROPERTY_STRING. */ +#define TSI_CERTIFICATE_TYPE_PEER_PROPERTY "certificate_type" + +/* Property values may contain NULL characters just like C++ strings. + The length field gives the length of the string. */ +typedef struct tsi_peer_property { + char* name; + struct { + char* data; + size_t length; + } value; +} tsi_peer_property; + +typedef struct { + tsi_peer_property* properties; + size_t property_count; +} tsi_peer; + +/* Destructs the tsi_peer object. */ +void tsi_peer_destruct(tsi_peer* self); + +/* --- tsi_handshaker objects ---- + + Implementations of this object must be thread compatible. + + A typical usage of this object would be: + + ------------------------------------------------------------------------ + tsi_result result = TSI_OK; + unsigned char buf[4096]; + size_t buf_offset; + size_t buf_size; + while (1) { + // See if we need to send some bytes to the peer. + do { + size_t buf_size_to_send = sizeof(buf); + result = tsi_handshaker_get_bytes_to_send_to_peer(handshaker, buf, + &buf_size_to_send); + if (buf_size_to_send > 0) send_bytes_to_peer(buf, buf_size_to_send); + } while (result == TSI_INCOMPLETE_DATA); + if (result != TSI_OK) return result; + if (!tsi_handshaker_is_in_progress(handshaker)) break; + + do { + // Read bytes from the peer. + buf_size = sizeof(buf); + buf_offset = 0; + read_bytes_from_peer(buf, &buf_size); + if (buf_size == 0) break; + + // Process the bytes from the peer. We have to be careful as these bytes + // may contain non-handshake data (protected data). If this is the case, + // we will exit from the loop with buf_size > 0. + size_t consumed_by_handshaker = buf_size; + result = tsi_handshaker_process_bytes_from_peer( + handshaker, buf, &consumed_by_handshaker); + buf_size -= consumed_by_handshaker; + buf_offset += consumed_by_handshaker; + } while (result == TSI_INCOMPLETE_DATA); + + if (result != TSI_OK) return result; + if (!tsi_handshaker_is_in_progress(handshaker)) break; + } + + // Check the Peer. + tsi_peer peer; + do { + result = tsi_handshaker_extract_peer(handshaker, &peer); + if (result != TSI_OK) break; + result = check_peer(&peer); + } while (0); + tsi_peer_destruct(&peer); + if (result != TSI_OK) return result; + + // Create the protector. + tsi_frame_protector* protector = NULL; + result = tsi_handshaker_create_frame_protector(handshaker, NULL, + &protector); + if (result != TSI_OK) return result; + + // Do not forget to unprotect outstanding data if any. + if (buf_size > 0) { + result = tsi_frame_protector_unprotect(protector, buf + buf_offset, + buf_size, ..., ...); + .... + } + ... + ------------------------------------------------------------------------ */ +typedef struct tsi_handshaker tsi_handshaker; + +/* Gets bytes that need to be sent to the peer. + - bytes is the buffer that will be written with the data to be sent to the + peer. + - bytes_size is an input/output parameter specifying the capacity of the + bytes parameter as input and the number of bytes written as output. + Returns TSI_OK if all the data to send to the peer has been written or if + nothing has to be sent to the peer (in which base bytes_size outputs to 0), + otherwise returns TSI_INCOMPLETE_DATA which indicates that this method + needs to be called again to get all the bytes to send to the peer (there + was more data to write than the specified bytes_size). In case of a fatal + error in the handshake, another specific error code is returned. */ +tsi_result tsi_handshaker_get_bytes_to_send_to_peer(tsi_handshaker* self, + unsigned char* bytes, + size_t* bytes_size); + +/* Processes bytes received from the peer. + - bytes is the buffer containing the data. + - bytes_size is an input/output parameter specifying the size of the data as + input and the number of bytes consumed as output. + Return TSI_OK if the handshake has all the data it needs to process, + otherwise return TSI_INCOMPLETE_DATA which indicates that this method + needs to be called again to complete the data needed for processing. In + case of a fatal error in the handshake, another specific error code is + returned. */ +tsi_result tsi_handshaker_process_bytes_from_peer(tsi_handshaker* self, + const unsigned char* bytes, + size_t* bytes_size); + +/* Gets the result of the handshaker. + Returns TSI_OK if the hanshake completed successfully and there has been no + errors. Returns TSI_HANDSHAKE_IN_PROGRESS if the handshaker is not done yet + but no error has been encountered so far. Otherwise the handshaker failed + with the returned error. */ +tsi_result tsi_handshaker_get_result(tsi_handshaker* self); + +/* Returns 1 if the handshake is in progress, 0 otherwise. */ +#define tsi_handshaker_is_in_progress(h) \ + (tsi_handshaker_get_result((h)) == TSI_HANDSHAKE_IN_PROGRESS) + +/* This method may return TSI_FAILED_PRECONDITION if + tsi_handshaker_is_in_progress returns 1, it returns TSI_OK otherwise + assuming the handshaker is not in a fatal error state. + The caller is responsible for destructing the peer. */ +tsi_result tsi_handshaker_extract_peer(tsi_handshaker* self, tsi_peer* peer); + +/* This method creates a tsi_frame_protector object after the handshake phase + is done. After this method has been called successfully, the only method + that can be called on this object is Destroy. + - max_output_protected_frame_size is an input/output parameter specifying the + desired max output protected frame size as input and outputing the actual + max output frame size as the output. Passing NULL is OK and will result in + the implementation choosing the default maximum protected frame size. Note + that this size only applies to outgoing frames (generated with + tsi_frame_protector_protect) and not incoming frames (input of + tsi_frame_protector_unprotect). + - protector is an output parameter pointing to the newly created + tsi_frame_protector object. + This method may return TSI_FAILED_PRECONDITION if + tsi_handshaker_is_in_progress returns 1, it returns TSI_OK otherwise assuming + the handshaker is not in a fatal error state. + The caller is responsible for destroying the protector. */ +tsi_result tsi_handshaker_create_frame_protector( + tsi_handshaker* self, size_t* max_output_protected_frame_size, + tsi_frame_protector** protector); + +/* This method releases the tsi_handshaker object. After this method is called, + no other method can be called on the object. */ +void tsi_handshaker_destroy(tsi_handshaker* self); + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_INTERNAL_CORE_TSI_TRANSPORT_SECURITY_INTERFACE_H */ diff --git a/src/cpp/README.md b/src/cpp/README.md new file mode 100644 index 00000000..a2eb9a08 --- /dev/null +++ b/src/cpp/README.md @@ -0,0 +1,9 @@ + +#Overview + +This directory contains source code for C++ implementation of gRPC. + +#Status + +Alpha : Ready for early adopters + diff --git a/src/cpp/client/channel.cc b/src/cpp/client/channel.cc new file mode 100644 index 00000000..dc8e3046 --- /dev/null +++ b/src/cpp/client/channel.cc @@ -0,0 +1,142 @@ +/* + * + * Copyright 2015, 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 + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "src/core/profiling/timers.h" + +namespace grpc { + +Channel::Channel(const grpc::string& host, grpc_channel* channel) + : host_(host), c_channel_(channel) {} + +Channel::~Channel() { grpc_channel_destroy(c_channel_); } + +Call Channel::CreateCall(const RpcMethod& method, ClientContext* context, + CompletionQueue* cq) { + const bool kRegistered = method.channel_tag() && context->authority().empty(); + grpc_call* c_call = NULL; + if (kRegistered) { + c_call = grpc_channel_create_registered_call( + c_channel_, context->propagate_from_call_, + context->propagation_options_.c_bitmask(), cq->cq(), + method.channel_tag(), context->raw_deadline(), nullptr); + } else { + const char* host_str = NULL; + if (!context->authority().empty()) { + host_str = context->authority_.c_str(); + } else if (!host_.empty()) { + host_str = host_.c_str(); + } + c_call = grpc_channel_create_call(c_channel_, context->propagate_from_call_, + context->propagation_options_.c_bitmask(), + cq->cq(), method.name(), host_str, + context->raw_deadline(), nullptr); + } + grpc_census_call_set_context(c_call, context->census_context()); + GRPC_TIMER_MARK(GRPC_PTAG_CPP_CALL_CREATED, c_call); + context->set_call(c_call, shared_from_this()); + return Call(c_call, this, cq); +} + +void Channel::PerformOpsOnCall(CallOpSetInterface* ops, Call* call) { + static const size_t MAX_OPS = 8; + size_t nops = 0; + grpc_op cops[MAX_OPS]; + GRPC_TIMER_BEGIN(GRPC_PTAG_CPP_PERFORM_OPS, call->call()); + ops->FillOps(cops, &nops); + GPR_ASSERT(GRPC_CALL_OK == + grpc_call_start_batch(call->call(), cops, nops, ops, nullptr)); + GRPC_TIMER_END(GRPC_PTAG_CPP_PERFORM_OPS, call->call()); +} + +void* Channel::RegisterMethod(const char* method) { + return grpc_channel_register_call( + c_channel_, method, host_.empty() ? NULL : host_.c_str(), nullptr); +} + +grpc_connectivity_state Channel::GetState(bool try_to_connect) { + return grpc_channel_check_connectivity_state(c_channel_, try_to_connect); +} + +namespace { +class TagSaver GRPC_FINAL : public CompletionQueueTag { + public: + explicit TagSaver(void* tag) : tag_(tag) {} + ~TagSaver() GRPC_OVERRIDE {} + bool FinalizeResult(void** tag, bool* status) GRPC_OVERRIDE { + *tag = tag_; + delete this; + return true; + } + + private: + void* tag_; +}; + +} // namespace + +void Channel::NotifyOnStateChangeImpl(grpc_connectivity_state last_observed, + gpr_timespec deadline, + CompletionQueue* cq, void* tag) { + TagSaver* tag_saver = new TagSaver(tag); + grpc_channel_watch_connectivity_state(c_channel_, last_observed, deadline, + cq->cq(), tag_saver); +} + +bool Channel::WaitForStateChangeImpl(grpc_connectivity_state last_observed, + gpr_timespec deadline) { + CompletionQueue cq; + bool ok = false; + void* tag = NULL; + NotifyOnStateChangeImpl(last_observed, deadline, &cq, NULL); + cq.Next(&tag, &ok); + GPR_ASSERT(tag == NULL); + return ok; +} + +} // namespace grpc diff --git a/src/cpp/client/channel_arguments.cc b/src/cpp/client/channel_arguments.cc new file mode 100644 index 00000000..50422d06 --- /dev/null +++ b/src/cpp/client/channel_arguments.cc @@ -0,0 +1,111 @@ +/* + * + * Copyright 2015, 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 + +#include +#include "src/core/channel/channel_args.h" + +namespace grpc { + +ChannelArguments::ChannelArguments(const ChannelArguments& other) + : strings_(other.strings_) { + args_.reserve(other.args_.size()); + auto list_it_dst = strings_.begin(); + auto list_it_src = other.strings_.begin(); + for (auto a = other.args_.begin(); a != other.args_.end(); ++a) { + grpc_arg ap; + ap.type = a->type; + GPR_ASSERT(list_it_src->c_str() == a->key); + ap.key = const_cast(list_it_dst->c_str()); + ++list_it_src; + ++list_it_dst; + switch (a->type) { + case GRPC_ARG_INTEGER: + ap.value.integer = a->value.integer; + break; + case GRPC_ARG_STRING: + GPR_ASSERT(list_it_src->c_str() == a->value.string); + ap.value.string = const_cast(list_it_dst->c_str()); + ++list_it_src; + ++list_it_dst; + break; + case GRPC_ARG_POINTER: + ap.value.pointer = a->value.pointer; + ap.value.pointer.p = a->value.pointer.copy(ap.value.pointer.p); + break; + } + args_.push_back(ap); + } +} + +void ChannelArguments::Swap(ChannelArguments& other) { + args_.swap(other.args_); + strings_.swap(other.strings_); +} + +void ChannelArguments::SetCompressionAlgorithm( + grpc_compression_algorithm algorithm) { + SetInt(GRPC_COMPRESSION_ALGORITHM_ARG, algorithm); +} + +void ChannelArguments::SetInt(const grpc::string& key, int value) { + grpc_arg arg; + arg.type = GRPC_ARG_INTEGER; + strings_.push_back(key); + arg.key = const_cast(strings_.back().c_str()); + arg.value.integer = value; + + args_.push_back(arg); +} + +void ChannelArguments::SetString(const grpc::string& key, + const grpc::string& value) { + grpc_arg arg; + arg.type = GRPC_ARG_STRING; + strings_.push_back(key); + arg.key = const_cast(strings_.back().c_str()); + strings_.push_back(value); + arg.value.string = const_cast(strings_.back().c_str()); + + args_.push_back(arg); +} + +void ChannelArguments::SetChannelArgs(grpc_channel_args* channel_args) const { + channel_args->num_args = args_.size(); + if (channel_args->num_args > 0) { + channel_args->args = const_cast(&args_[0]); + } +} + +} // namespace grpc diff --git a/src/cpp/client/client_context.cc b/src/cpp/client/client_context.cc new file mode 100644 index 00000000..574656a7 --- /dev/null +++ b/src/cpp/client/client_context.cc @@ -0,0 +1,119 @@ +/* + * + * Copyright 2015, 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 + +#include +#include +#include +#include +#include +#include + +#include "src/core/channel/compress_filter.h" +#include "src/cpp/common/create_auth_context.h" + +namespace grpc { + +ClientContext::ClientContext() + : initial_metadata_received_(false), + call_(nullptr), + deadline_(gpr_inf_future(GPR_CLOCK_REALTIME)), + propagate_from_call_(nullptr) {} + +ClientContext::~ClientContext() { + if (call_) { + grpc_call_destroy(call_); + } +} + +std::unique_ptr ClientContext::FromServerContext( + const ServerContext& context, PropagationOptions options) { + std::unique_ptr ctx(new ClientContext); + ctx->propagate_from_call_ = context.call_; + ctx->propagation_options_ = options; + return ctx; +} + +void ClientContext::AddMetadata(const grpc::string& meta_key, + const grpc::string& meta_value) { + send_initial_metadata_.insert(std::make_pair(meta_key, meta_value)); +} + +void ClientContext::set_call(grpc_call* call, + const std::shared_ptr& channel) { + GPR_ASSERT(call_ == nullptr); + call_ = call; + channel_ = channel; + if (creds_ && !creds_->ApplyToCall(call_)) { + grpc_call_cancel_with_status(call, GRPC_STATUS_CANCELLED, + "Failed to set credentials to rpc.", nullptr); + } +} + +void ClientContext::set_compression_algorithm( + grpc_compression_algorithm algorithm) { + char* algorithm_name = nullptr; + if (!grpc_compression_algorithm_name(algorithm, &algorithm_name)) { + gpr_log(GPR_ERROR, "Name for compression algorithm '%d' unknown.", + algorithm); + abort(); + } + GPR_ASSERT(algorithm_name != nullptr); + AddMetadata(GRPC_COMPRESS_REQUEST_ALGORITHM_KEY, algorithm_name); +} + +std::shared_ptr ClientContext::auth_context() const { + if (auth_context_.get() == nullptr) { + auth_context_ = CreateAuthContext(call_); + } + return auth_context_; +} + +void ClientContext::TryCancel() { + if (call_) { + grpc_call_cancel(call_, nullptr); + } +} + +grpc::string ClientContext::peer() const { + grpc::string peer; + if (call_) { + char* c_peer = grpc_call_get_peer(call_); + peer = c_peer; + gpr_free(c_peer); + } + return peer; +} + +} // namespace grpc diff --git a/src/cpp/client/create_channel.cc b/src/cpp/client/create_channel.cc new file mode 100644 index 00000000..d2b2d301 --- /dev/null +++ b/src/cpp/client/create_channel.cc @@ -0,0 +1,67 @@ +/* + * + * Copyright 2015, 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 +#include + +#include +#include +#include + +#include "src/cpp/client/create_channel_internal.h" + +namespace grpc { +class ChannelArguments; + +std::shared_ptr CreateChannel( + const grpc::string& target, const std::shared_ptr& creds) { + return CreateCustomChannel(target, creds, ChannelArguments()); +} + +std::shared_ptr CreateCustomChannel( + const grpc::string& target, const std::shared_ptr& creds, + const ChannelArguments& args) { + GrpcLibrary init_lib; // We need to call init in case of a bad creds. + ChannelArguments cp_args = args; + std::ostringstream user_agent_prefix; + user_agent_prefix << "grpc-c++/" << grpc_version_string(); + cp_args.SetString(GRPC_ARG_PRIMARY_USER_AGENT_STRING, + user_agent_prefix.str()); + return creds + ? creds->CreateChannel(target, cp_args) + : CreateChannelInternal("", grpc_lame_client_channel_create( + NULL, GRPC_STATUS_INVALID_ARGUMENT, + "Invalid credentials.")); +} + +} // namespace grpc diff --git a/src/cpp/client/create_channel_internal.cc b/src/cpp/client/create_channel_internal.cc new file mode 100644 index 00000000..9c5ab038 --- /dev/null +++ b/src/cpp/client/create_channel_internal.cc @@ -0,0 +1,46 @@ +/* + * + * Copyright 2015, 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 + +#include + +struct grpc_channel; + +namespace grpc { + +std::shared_ptr CreateChannelInternal(const grpc::string& host, + grpc_channel* c_channel) { + return std::shared_ptr(new Channel(host, c_channel)); +} +} // namespace grpc diff --git a/src/cpp/client/create_channel_internal.h b/src/cpp/client/create_channel_internal.h new file mode 100644 index 00000000..4385ec70 --- /dev/null +++ b/src/cpp/client/create_channel_internal.h @@ -0,0 +1,51 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CPP_CLIENT_CREATE_CHANNEL_INTERNAL_H +#define GRPC_INTERNAL_CPP_CLIENT_CREATE_CHANNEL_INTERNAL_H + +#include + +#include + +struct grpc_channel; + +namespace grpc { +class Channel; + +std::shared_ptr CreateChannelInternal(const grpc::string& host, + grpc_channel* c_channel); + +} // namespace grpc + +#endif // GRPC_INTERNAL_CPP_CLIENT_CREATE_CHANNEL_INTERNAL_H diff --git a/src/cpp/client/credentials.cc b/src/cpp/client/credentials.cc new file mode 100644 index 00000000..7a8149e9 --- /dev/null +++ b/src/cpp/client/credentials.cc @@ -0,0 +1,40 @@ +/* + * + * Copyright 2015, 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 + +namespace grpc { + +Credentials::~Credentials() {} + +} // namespace grpc diff --git a/src/cpp/client/generic_stub.cc b/src/cpp/client/generic_stub.cc new file mode 100644 index 00000000..7a2fdf94 --- /dev/null +++ b/src/cpp/client/generic_stub.cc @@ -0,0 +1,50 @@ +/* + * + * Copyright 2015, 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 + +#include + +namespace grpc { + +// begin a call to a named method +std::unique_ptr GenericStub::Call( + ClientContext* context, const grpc::string& method, CompletionQueue* cq, + void* tag) { + return std::unique_ptr( + new GenericClientAsyncReaderWriter( + channel_.get(), cq, + RpcMethod(method.c_str(), RpcMethod::BIDI_STREAMING), context, tag)); +} + +} // namespace grpc diff --git a/src/cpp/client/insecure_credentials.cc b/src/cpp/client/insecure_credentials.cc new file mode 100644 index 00000000..c476f3ce --- /dev/null +++ b/src/cpp/client/insecure_credentials.cc @@ -0,0 +1,68 @@ +/* + * + * Copyright 2015, 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 + +#include +#include +#include +#include +#include +#include "src/cpp/client/create_channel_internal.h" + +namespace grpc { + +namespace { +class InsecureCredentialsImpl GRPC_FINAL : public Credentials { + public: + std::shared_ptr CreateChannel( + const string& target, const grpc::ChannelArguments& args) GRPC_OVERRIDE { + grpc_channel_args channel_args; + args.SetChannelArgs(&channel_args); + return CreateChannelInternal( + "", + grpc_insecure_channel_create(target.c_str(), &channel_args, nullptr)); + } + + // InsecureCredentials should not be applied to a call. + bool ApplyToCall(grpc_call* call) GRPC_OVERRIDE { return false; } + + SecureCredentials* AsSecureCredentials() GRPC_OVERRIDE { return nullptr; } +}; +} // namespace + +std::shared_ptr InsecureCredentials() { + return std::shared_ptr(new InsecureCredentialsImpl()); +} + +} // namespace grpc diff --git a/src/cpp/client/secure_channel_arguments.cc b/src/cpp/client/secure_channel_arguments.cc new file mode 100644 index 00000000..e17d3b58 --- /dev/null +++ b/src/cpp/client/secure_channel_arguments.cc @@ -0,0 +1,54 @@ +/* + * + * Copyright 2015, 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 + +#include +#include "src/core/channel/channel_args.h" + +namespace grpc { + +void ChannelArguments::SetSslTargetNameOverride(const grpc::string& name) { + SetString(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG, name); +} + +grpc::string ChannelArguments::GetSslTargetNameOverride() const { + for (unsigned int i = 0; i < args_.size(); i++) { + if (grpc::string(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG) == args_[i].key) { + return args_[i].value.string; + } + } + return ""; +} + +} // namespace grpc diff --git a/src/cpp/client/secure_credentials.cc b/src/cpp/client/secure_credentials.cc new file mode 100644 index 00000000..2260f6d3 --- /dev/null +++ b/src/cpp/client/secure_credentials.cc @@ -0,0 +1,147 @@ +/* + * + * Copyright 2015, 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 +#include +#include +#include +#include "src/cpp/client/create_channel_internal.h" +#include "src/cpp/client/secure_credentials.h" + +namespace grpc { + +std::shared_ptr SecureCredentials::CreateChannel( + const string& target, const grpc::ChannelArguments& args) { + grpc_channel_args channel_args; + args.SetChannelArgs(&channel_args); + return CreateChannelInternal( + args.GetSslTargetNameOverride(), + grpc_secure_channel_create(c_creds_, target.c_str(), &channel_args, + nullptr)); +} + +bool SecureCredentials::ApplyToCall(grpc_call* call) { + return grpc_call_set_credentials(call, c_creds_) == GRPC_CALL_OK; +} + +namespace { +std::shared_ptr WrapCredentials(grpc_credentials* creds) { + return creds == nullptr + ? nullptr + : std::shared_ptr(new SecureCredentials(creds)); +} +} // namespace + +std::shared_ptr GoogleDefaultCredentials() { + GrpcLibrary init; // To call grpc_init(). + return WrapCredentials(grpc_google_default_credentials_create()); +} + +// Builds SSL Credentials given SSL specific options +std::shared_ptr SslCredentials( + const SslCredentialsOptions& options) { + GrpcLibrary init; // To call grpc_init(). + grpc_ssl_pem_key_cert_pair pem_key_cert_pair = { + options.pem_private_key.c_str(), options.pem_cert_chain.c_str()}; + + grpc_credentials* c_creds = grpc_ssl_credentials_create( + options.pem_root_certs.empty() ? nullptr : options.pem_root_certs.c_str(), + options.pem_private_key.empty() ? nullptr : &pem_key_cert_pair, nullptr); + return WrapCredentials(c_creds); +} + +// Builds credentials for use when running in GCE +std::shared_ptr GoogleComputeEngineCredentials() { + GrpcLibrary init; // To call grpc_init(). + return WrapCredentials( + grpc_google_compute_engine_credentials_create(nullptr)); +} + +// Builds JWT credentials. +std::shared_ptr ServiceAccountJWTAccessCredentials( + const grpc::string& json_key, long token_lifetime_seconds) { + GrpcLibrary init; // To call grpc_init(). + if (token_lifetime_seconds <= 0) { + gpr_log(GPR_ERROR, + "Trying to create JWTCredentials with non-positive lifetime"); + return WrapCredentials(nullptr); + } + gpr_timespec lifetime = + gpr_time_from_seconds(token_lifetime_seconds, GPR_TIMESPAN); + return WrapCredentials(grpc_service_account_jwt_access_credentials_create( + json_key.c_str(), lifetime, nullptr)); +} + +// Builds refresh token credentials. +std::shared_ptr GoogleRefreshTokenCredentials( + const grpc::string& json_refresh_token) { + GrpcLibrary init; // To call grpc_init(). + return WrapCredentials(grpc_google_refresh_token_credentials_create( + json_refresh_token.c_str(), nullptr)); +} + +// Builds access token credentials. +std::shared_ptr AccessTokenCredentials( + const grpc::string& access_token) { + GrpcLibrary init; // To call grpc_init(). + return WrapCredentials( + grpc_access_token_credentials_create(access_token.c_str(), nullptr)); +} + +// Builds IAM credentials. +std::shared_ptr GoogleIAMCredentials( + const grpc::string& authorization_token, + const grpc::string& authority_selector) { + GrpcLibrary init; // To call grpc_init(). + return WrapCredentials(grpc_google_iam_credentials_create( + authorization_token.c_str(), authority_selector.c_str(), nullptr)); +} + +// Combines two credentials objects into a composite credentials. +std::shared_ptr CompositeCredentials( + const std::shared_ptr& creds1, + const std::shared_ptr& creds2) { + // Note that we are not saving shared_ptrs to the two credentials + // passed in here. This is OK because the underlying C objects (i.e., + // creds1 and creds2) into grpc_composite_credentials_create will see their + // refcounts incremented. + SecureCredentials* s1 = creds1->AsSecureCredentials(); + SecureCredentials* s2 = creds2->AsSecureCredentials(); + if (s1 && s2) { + return WrapCredentials(grpc_composite_credentials_create( + s1->GetRawCreds(), s2->GetRawCreds(), nullptr)); + } + return nullptr; +} + +} // namespace grpc diff --git a/src/cpp/client/secure_credentials.h b/src/cpp/client/secure_credentials.h new file mode 100644 index 00000000..8deff856 --- /dev/null +++ b/src/cpp/client/secure_credentials.h @@ -0,0 +1,61 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CPP_CLIENT_SECURE_CREDENTIALS_H +#define GRPC_INTERNAL_CPP_CLIENT_SECURE_CREDENTIALS_H + +#include + +#include +#include + +namespace grpc { + +class SecureCredentials GRPC_FINAL : public Credentials { + public: + explicit SecureCredentials(grpc_credentials* c_creds) : c_creds_(c_creds) {} + ~SecureCredentials() GRPC_OVERRIDE { grpc_credentials_release(c_creds_); } + grpc_credentials* GetRawCreds() { return c_creds_; } + bool ApplyToCall(grpc_call* call) GRPC_OVERRIDE; + + std::shared_ptr CreateChannel( + const string& target, const grpc::ChannelArguments& args) GRPC_OVERRIDE; + SecureCredentials* AsSecureCredentials() GRPC_OVERRIDE { return this; } + + private: + grpc_credentials* const c_creds_; +}; + +} // namespace grpc + +#endif // GRPC_INTERNAL_CPP_CLIENT_SECURE_CREDENTIALS_H diff --git a/src/cpp/common/auth_property_iterator.cc b/src/cpp/common/auth_property_iterator.cc new file mode 100644 index 00000000..a47abaf4 --- /dev/null +++ b/src/cpp/common/auth_property_iterator.cc @@ -0,0 +1,85 @@ +/* + * + * Copyright 2015, 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 + +#include + +namespace grpc { + +AuthPropertyIterator::AuthPropertyIterator() + : property_(nullptr), ctx_(nullptr), index_(0), name_(nullptr) {} + +AuthPropertyIterator::AuthPropertyIterator( + const grpc_auth_property* property, const grpc_auth_property_iterator* iter) + : property_(property), + ctx_(iter->ctx), + index_(iter->index), + name_(iter->name) {} + +AuthPropertyIterator::~AuthPropertyIterator() {} + +AuthPropertyIterator& AuthPropertyIterator::operator++() { + grpc_auth_property_iterator iter = {ctx_, index_, name_}; + property_ = grpc_auth_property_iterator_next(&iter); + ctx_ = iter.ctx; + index_ = iter.index; + name_ = iter.name; + return *this; +} + +AuthPropertyIterator AuthPropertyIterator::operator++(int) { + AuthPropertyIterator tmp(*this); + operator++(); + return tmp; +} + +bool AuthPropertyIterator::operator==(const AuthPropertyIterator& rhs) const { + if (property_ == nullptr || rhs.property_ == nullptr) { + return property_ == rhs.property_; + } else { + return index_ == rhs.index_; + } +} + +bool AuthPropertyIterator::operator!=(const AuthPropertyIterator& rhs) const { + return !operator==(rhs); +} + +const AuthProperty AuthPropertyIterator::operator*() { + return std::pair( + property_->name, + grpc::string_ref(property_->value, property_->value_length)); +} + +} // namespace grpc diff --git a/src/cpp/common/call.cc b/src/cpp/common/call.cc new file mode 100644 index 00000000..5b87c2a8 --- /dev/null +++ b/src/cpp/common/call.cc @@ -0,0 +1,92 @@ +/* + * + * Copyright 2015, 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 + +#include +#include +#include +#include +#include "src/core/profiling/timers.h" + +namespace grpc { + +void FillMetadataMap( + grpc_metadata_array* arr, + std::multimap* metadata) { + for (size_t i = 0; i < arr->count; i++) { + // TODO(yangg) handle duplicates? + metadata->insert(std::pair( + arr->metadata[i].key, grpc::string_ref(arr->metadata[i].value, + arr->metadata[i].value_length))); + } + grpc_metadata_array_destroy(arr); + grpc_metadata_array_init(arr); +} + +// TODO(yangg) if the map is changed before we send, the pointers will be a +// mess. Make sure it does not happen. +grpc_metadata* FillMetadataArray( + const std::multimap& metadata) { + if (metadata.empty()) { + return nullptr; + } + grpc_metadata* metadata_array = + (grpc_metadata*)gpr_malloc(metadata.size() * sizeof(grpc_metadata)); + size_t i = 0; + for (auto iter = metadata.cbegin(); iter != metadata.cend(); ++iter, ++i) { + metadata_array[i].key = iter->first.c_str(); + metadata_array[i].value = iter->second.c_str(); + metadata_array[i].value_length = iter->second.size(); + } + return metadata_array; +} + +Call::Call(grpc_call* call, CallHook* call_hook, CompletionQueue* cq) + : call_hook_(call_hook), cq_(cq), call_(call), max_message_size_(-1) {} + +Call::Call(grpc_call* call, CallHook* call_hook, CompletionQueue* cq, + int max_message_size) + : call_hook_(call_hook), + cq_(cq), + call_(call), + max_message_size_(max_message_size) {} + +void Call::PerformOps(CallOpSetInterface* ops) { + if (max_message_size_ > 0) { + ops->set_max_message_size(max_message_size_); + } + call_hook_->PerformOpsOnCall(ops, this); +} + +} // namespace grpc diff --git a/src/cpp/common/completion_queue.cc b/src/cpp/common/completion_queue.cc new file mode 100644 index 00000000..a175beb4 --- /dev/null +++ b/src/cpp/common/completion_queue.cc @@ -0,0 +1,95 @@ +/* + * Copyright 2015, 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 + +#include + +#include +#include +#include + +namespace grpc { + +CompletionQueue::CompletionQueue() { + cq_ = grpc_completion_queue_create(nullptr); +} + +CompletionQueue::CompletionQueue(grpc_completion_queue* take) : cq_(take) {} + +CompletionQueue::~CompletionQueue() { grpc_completion_queue_destroy(cq_); } + +void CompletionQueue::Shutdown() { grpc_completion_queue_shutdown(cq_); } + +CompletionQueue::NextStatus CompletionQueue::AsyncNextInternal( + void** tag, bool* ok, gpr_timespec deadline) { + for (;;) { + auto ev = grpc_completion_queue_next(cq_, deadline, nullptr); + switch (ev.type) { + case GRPC_QUEUE_TIMEOUT: + return TIMEOUT; + case GRPC_QUEUE_SHUTDOWN: + return SHUTDOWN; + case GRPC_OP_COMPLETE: + auto cq_tag = static_cast(ev.tag); + *ok = ev.success != 0; + *tag = cq_tag; + if (cq_tag->FinalizeResult(tag, ok)) { + return GOT_EVENT; + } + break; + } + } +} + +bool CompletionQueue::Pluck(CompletionQueueTag* tag) { + auto deadline = gpr_inf_future(GPR_CLOCK_REALTIME); + auto ev = grpc_completion_queue_pluck(cq_, tag, deadline, nullptr); + bool ok = ev.success != 0; + void* ignored = tag; + GPR_ASSERT(tag->FinalizeResult(&ignored, &ok)); + GPR_ASSERT(ignored == tag); + // Ignore mutations by FinalizeResult: Pluck returns the C API status + return ev.success != 0; +} + +void CompletionQueue::TryPluck(CompletionQueueTag* tag) { + auto deadline = gpr_time_0(GPR_CLOCK_REALTIME); + auto ev = grpc_completion_queue_pluck(cq_, tag, deadline, nullptr); + if (ev.type == GRPC_QUEUE_TIMEOUT) return; + bool ok = ev.success != 0; + void* ignored = tag; + // the tag must be swallowed if using TryPluck + GPR_ASSERT(!tag->FinalizeResult(&ignored, &ok)); +} + +} // namespace grpc diff --git a/src/cpp/common/create_auth_context.h b/src/cpp/common/create_auth_context.h new file mode 100644 index 00000000..4f3da397 --- /dev/null +++ b/src/cpp/common/create_auth_context.h @@ -0,0 +1,42 @@ +/* + * + * Copyright 2015, 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 + +#include +#include + +namespace grpc { + +std::shared_ptr CreateAuthContext(grpc_call* call); + +} // namespace grpc diff --git a/src/cpp/common/insecure_create_auth_context.cc b/src/cpp/common/insecure_create_auth_context.cc new file mode 100644 index 00000000..b2e15322 --- /dev/null +++ b/src/cpp/common/insecure_create_auth_context.cc @@ -0,0 +1,45 @@ +/* + * + * Copyright 2015, 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 + +#include +#include + +namespace grpc { + +std::shared_ptr CreateAuthContext(grpc_call* call) { + (void)call; + return std::shared_ptr(); +} + +} // namespace grpc diff --git a/src/cpp/common/rpc_method.cc b/src/cpp/common/rpc_method.cc new file mode 100644 index 00000000..1654d4a2 --- /dev/null +++ b/src/cpp/common/rpc_method.cc @@ -0,0 +1,36 @@ +/* + * + * Copyright 2015, 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 + +namespace grpc {} // namespace grpc diff --git a/src/cpp/common/secure_auth_context.cc b/src/cpp/common/secure_auth_context.cc new file mode 100644 index 00000000..8615ac8a --- /dev/null +++ b/src/cpp/common/secure_auth_context.cc @@ -0,0 +1,118 @@ +/* + * + * Copyright 2015, 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 "src/cpp/common/secure_auth_context.h" + +#include + +namespace grpc { + +SecureAuthContext::SecureAuthContext(grpc_auth_context* ctx, + bool take_ownership) + : ctx_(ctx), take_ownership_(take_ownership) {} + +SecureAuthContext::~SecureAuthContext() { + if (take_ownership_) grpc_auth_context_release(ctx_); +} + +std::vector SecureAuthContext::GetPeerIdentity() const { + if (!ctx_) { + return std::vector(); + } + grpc_auth_property_iterator iter = grpc_auth_context_peer_identity(ctx_); + std::vector identity; + const grpc_auth_property* property = nullptr; + while ((property = grpc_auth_property_iterator_next(&iter))) { + identity.push_back( + grpc::string_ref(property->value, property->value_length)); + } + return identity; +} + +grpc::string SecureAuthContext::GetPeerIdentityPropertyName() const { + if (!ctx_) { + return ""; + } + const char* name = grpc_auth_context_peer_identity_property_name(ctx_); + return name == nullptr ? "" : name; +} + +std::vector SecureAuthContext::FindPropertyValues( + const grpc::string& name) const { + if (!ctx_) { + return std::vector(); + } + grpc_auth_property_iterator iter = + grpc_auth_context_find_properties_by_name(ctx_, name.c_str()); + const grpc_auth_property* property = nullptr; + std::vector values; + while ((property = grpc_auth_property_iterator_next(&iter))) { + values.push_back(grpc::string_ref(property->value, property->value_length)); + } + return values; +} + +AuthPropertyIterator SecureAuthContext::begin() const { + if (ctx_) { + grpc_auth_property_iterator iter = + grpc_auth_context_property_iterator(ctx_); + const grpc_auth_property* property = + grpc_auth_property_iterator_next(&iter); + return AuthPropertyIterator(property, &iter); + } else { + return end(); + } +} + +AuthPropertyIterator SecureAuthContext::end() const { + return AuthPropertyIterator(); +} + +void SecureAuthContext::AddProperty(const grpc::string& key, + const grpc::string_ref& value) { + if (!ctx_) return; + grpc_auth_context_add_property(ctx_, key.c_str(), value.data(), value.size()); +} + +bool SecureAuthContext::SetPeerIdentityPropertyName(const grpc::string& name) { + if (!ctx_) return false; + return grpc_auth_context_set_peer_identity_property_name(ctx_, + name.c_str()) != 0; +} + +bool SecureAuthContext::IsPeerAuthenticated() const { + if (!ctx_) return false; + return grpc_auth_context_peer_is_authenticated(ctx_) != 0; +} + +} // namespace grpc diff --git a/src/cpp/common/secure_auth_context.h b/src/cpp/common/secure_auth_context.h new file mode 100644 index 00000000..c9f1dad1 --- /dev/null +++ b/src/cpp/common/secure_auth_context.h @@ -0,0 +1,75 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CPP_COMMON_SECURE_AUTH_CONTEXT_H +#define GRPC_INTERNAL_CPP_COMMON_SECURE_AUTH_CONTEXT_H + +#include + +struct grpc_auth_context; + +namespace grpc { + +class SecureAuthContext GRPC_FINAL : public AuthContext { + public: + SecureAuthContext(grpc_auth_context* ctx, bool take_ownership); + + ~SecureAuthContext() GRPC_OVERRIDE; + + bool IsPeerAuthenticated() const GRPC_OVERRIDE; + + std::vector GetPeerIdentity() const GRPC_OVERRIDE; + + grpc::string GetPeerIdentityPropertyName() const GRPC_OVERRIDE; + + std::vector FindPropertyValues( + const grpc::string& name) const GRPC_OVERRIDE; + + AuthPropertyIterator begin() const GRPC_OVERRIDE; + + AuthPropertyIterator end() const GRPC_OVERRIDE; + + void AddProperty(const grpc::string& key, + const grpc::string_ref& value) GRPC_OVERRIDE; + + virtual bool SetPeerIdentityPropertyName(const grpc::string& name) + GRPC_OVERRIDE; + + private: + grpc_auth_context* ctx_; + bool take_ownership_; +}; + +} // namespace grpc + +#endif // GRPC_INTERNAL_CPP_COMMON_SECURE_AUTH_CONTEXT_H diff --git a/src/cpp/common/secure_create_auth_context.cc b/src/cpp/common/secure_create_auth_context.cc new file mode 100644 index 00000000..40bc298b --- /dev/null +++ b/src/cpp/common/secure_create_auth_context.cc @@ -0,0 +1,50 @@ +/* + * + * Copyright 2015, 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 + +#include +#include +#include +#include "src/cpp/common/secure_auth_context.h" + +namespace grpc { + +std::shared_ptr CreateAuthContext(grpc_call* call) { + if (call == nullptr) { + return std::shared_ptr(); + } + return std::shared_ptr( + new SecureAuthContext(grpc_call_auth_context(call), true)); +} + +} // namespace grpc diff --git a/src/cpp/proto/proto_utils.cc b/src/cpp/proto/proto_utils.cc new file mode 100644 index 00000000..be84c222 --- /dev/null +++ b/src/cpp/proto/proto_utils.cc @@ -0,0 +1,184 @@ +/* + * + * Copyright 2015, 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 + +#include +#include +#include +#include +#include +#include +#include + +const int kMaxBufferLength = 8192; + +class GrpcBufferWriter GRPC_FINAL + : public ::grpc::protobuf::io::ZeroCopyOutputStream { + public: + explicit GrpcBufferWriter(grpc_byte_buffer** bp, + int block_size = kMaxBufferLength) + : block_size_(block_size), byte_count_(0), have_backup_(false) { + *bp = grpc_raw_byte_buffer_create(NULL, 0); + slice_buffer_ = &(*bp)->data.raw.slice_buffer; + } + + ~GrpcBufferWriter() GRPC_OVERRIDE { + if (have_backup_) { + gpr_slice_unref(backup_slice_); + } + } + + bool Next(void** data, int* size) GRPC_OVERRIDE { + if (have_backup_) { + slice_ = backup_slice_; + have_backup_ = false; + } else { + slice_ = gpr_slice_malloc(block_size_); + } + *data = GPR_SLICE_START_PTR(slice_); + byte_count_ += * size = GPR_SLICE_LENGTH(slice_); + gpr_slice_buffer_add(slice_buffer_, slice_); + return true; + } + + void BackUp(int count) GRPC_OVERRIDE { + gpr_slice_buffer_pop(slice_buffer_); + if (count == block_size_) { + backup_slice_ = slice_; + } else { + backup_slice_ = + gpr_slice_split_tail(&slice_, GPR_SLICE_LENGTH(slice_) - count); + gpr_slice_buffer_add(slice_buffer_, slice_); + } + have_backup_ = true; + byte_count_ -= count; + } + + grpc::protobuf::int64 ByteCount() const GRPC_OVERRIDE { return byte_count_; } + + private: + const int block_size_; + gpr_int64 byte_count_; + gpr_slice_buffer* slice_buffer_; + bool have_backup_; + gpr_slice backup_slice_; + gpr_slice slice_; +}; + +class GrpcBufferReader GRPC_FINAL + : public ::grpc::protobuf::io::ZeroCopyInputStream { + public: + explicit GrpcBufferReader(grpc_byte_buffer* buffer) + : byte_count_(0), backup_count_(0) { + grpc_byte_buffer_reader_init(&reader_, buffer); + } + ~GrpcBufferReader() GRPC_OVERRIDE { + grpc_byte_buffer_reader_destroy(&reader_); + } + + bool Next(const void** data, int* size) GRPC_OVERRIDE { + if (backup_count_ > 0) { + *data = GPR_SLICE_START_PTR(slice_) + GPR_SLICE_LENGTH(slice_) - + backup_count_; + *size = backup_count_; + backup_count_ = 0; + return true; + } + if (!grpc_byte_buffer_reader_next(&reader_, &slice_)) { + return false; + } + gpr_slice_unref(slice_); + *data = GPR_SLICE_START_PTR(slice_); + byte_count_ += * size = GPR_SLICE_LENGTH(slice_); + return true; + } + + void BackUp(int count) GRPC_OVERRIDE { backup_count_ = count; } + + bool Skip(int count) GRPC_OVERRIDE { + const void* data; + int size; + while (Next(&data, &size)) { + if (size >= count) { + BackUp(size - count); + return true; + } + // size < count; + count -= size; + } + // error or we have too large count; + return false; + } + + grpc::protobuf::int64 ByteCount() const GRPC_OVERRIDE { + return byte_count_ - backup_count_; + } + + private: + gpr_int64 byte_count_; + gpr_int64 backup_count_; + grpc_byte_buffer_reader reader_; + gpr_slice slice_; +}; + +namespace grpc { + +Status SerializeProto(const grpc::protobuf::Message& msg, + grpc_byte_buffer** bp) { + GrpcBufferWriter writer(bp); + return msg.SerializeToZeroCopyStream(&writer) + ? Status::OK + : Status(StatusCode::INTERNAL, "Failed to serialize message"); +} + +Status DeserializeProto(grpc_byte_buffer* buffer, grpc::protobuf::Message* msg, + int max_message_size) { + if (!buffer) { + return Status(StatusCode::INTERNAL, "No payload"); + } + GrpcBufferReader reader(buffer); + ::grpc::protobuf::io::CodedInputStream decoder(&reader); + if (max_message_size > 0) { + decoder.SetTotalBytesLimit(max_message_size, max_message_size); + } + if (!msg->ParseFromCodedStream(&decoder)) { + return Status(StatusCode::INTERNAL, msg->InitializationErrorString()); + } + if (!decoder.ConsumedEntireMessage()) { + return Status(StatusCode::INTERNAL, "Did not read entire message"); + } + return Status::OK; +} + +} // namespace grpc diff --git a/src/cpp/server/async_generic_service.cc b/src/cpp/server/async_generic_service.cc new file mode 100644 index 00000000..6b9ea532 --- /dev/null +++ b/src/cpp/server/async_generic_service.cc @@ -0,0 +1,48 @@ +/* + * + * Copyright 2015, 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 + +#include + +namespace grpc { + +void AsyncGenericService::RequestCall( + GenericServerContext* ctx, GenericServerAsyncReaderWriter* reader_writer, + CompletionQueue* call_cq, ServerCompletionQueue* notification_cq, + void* tag) { + server_->RequestAsyncGenericCall(ctx, reader_writer, call_cq, notification_cq, + tag); +} + +} // namespace grpc diff --git a/src/cpp/server/create_default_thread_pool.cc b/src/cpp/server/create_default_thread_pool.cc new file mode 100644 index 00000000..f3b07ec8 --- /dev/null +++ b/src/cpp/server/create_default_thread_pool.cc @@ -0,0 +1,50 @@ +/* + * + * Copyright 2015, 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 + +#include "src/cpp/server/dynamic_thread_pool.h" + +#ifndef GRPC_CUSTOM_DEFAULT_THREAD_POOL + +namespace grpc { + +ThreadPoolInterface* CreateDefaultThreadPool() { + int cores = gpr_cpu_num_cores(); + if (!cores) cores = 4; + return new DynamicThreadPool(cores); +} + +} // namespace grpc + +#endif // !GRPC_CUSTOM_DEFAULT_THREAD_POOL diff --git a/src/cpp/server/dynamic_thread_pool.cc b/src/cpp/server/dynamic_thread_pool.cc new file mode 100644 index 00000000..4b226c29 --- /dev/null +++ b/src/cpp/server/dynamic_thread_pool.cc @@ -0,0 +1,134 @@ +/* + * + * Copyright 2015, 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 +#include + +#include "src/cpp/server/dynamic_thread_pool.h" + +namespace grpc { +DynamicThreadPool::DynamicThread::DynamicThread(DynamicThreadPool* pool) + : pool_(pool), + thd_(new grpc::thread(&DynamicThreadPool::DynamicThread::ThreadFunc, + this)) {} +DynamicThreadPool::DynamicThread::~DynamicThread() { + thd_->join(); + thd_.reset(); +} + +void DynamicThreadPool::DynamicThread::ThreadFunc() { + pool_->ThreadFunc(); + // Now that we have killed ourselves, we should reduce the thread count + grpc::unique_lock lock(pool_->mu_); + pool_->nthreads_--; + // Move ourselves to dead list + pool_->dead_threads_.push_back(this); + + if ((pool_->shutdown_) && (pool_->nthreads_ == 0)) { + pool_->shutdown_cv_.notify_one(); + } +} + +void DynamicThreadPool::ThreadFunc() { + for (;;) { + // Wait until work is available or we are shutting down. + grpc::unique_lock lock(mu_); + if (!shutdown_ && callbacks_.empty()) { + // If there are too many threads waiting, then quit this thread + if (threads_waiting_ >= reserve_threads_) { + break; + } + threads_waiting_++; + cv_.wait(lock); + threads_waiting_--; + } + // Drain callbacks before considering shutdown to ensure all work + // gets completed. + if (!callbacks_.empty()) { + auto cb = callbacks_.front(); + callbacks_.pop(); + lock.unlock(); + cb(); + } else if (shutdown_) { + break; + } + } +} + +DynamicThreadPool::DynamicThreadPool(int reserve_threads) + : shutdown_(false), + reserve_threads_(reserve_threads), + nthreads_(0), + threads_waiting_(0) { + for (int i = 0; i < reserve_threads_; i++) { + grpc::lock_guard lock(mu_); + nthreads_++; + new DynamicThread(this); + } +} + +void DynamicThreadPool::ReapThreads(std::list* tlist) { + for (auto t = tlist->begin(); t != tlist->end(); t = tlist->erase(t)) { + delete *t; + } +} + +DynamicThreadPool::~DynamicThreadPool() { + grpc::unique_lock lock(mu_); + shutdown_ = true; + cv_.notify_all(); + while (nthreads_ != 0) { + shutdown_cv_.wait(lock); + } + ReapThreads(&dead_threads_); +} + +void DynamicThreadPool::Add(const std::function& callback) { + grpc::lock_guard lock(mu_); + // Add works to the callbacks list + callbacks_.push(callback); + // Increase pool size or notify as needed + if (threads_waiting_ == 0) { + // Kick off a new thread + nthreads_++; + new DynamicThread(this); + } else { + cv_.notify_one(); + } + // Also use this chance to harvest dead threads + if (!dead_threads_.empty()) { + ReapThreads(&dead_threads_); + } +} + +} // namespace grpc diff --git a/src/cpp/server/dynamic_thread_pool.h b/src/cpp/server/dynamic_thread_pool.h new file mode 100644 index 00000000..5ba7533c --- /dev/null +++ b/src/cpp/server/dynamic_thread_pool.h @@ -0,0 +1,83 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CPP_DYNAMIC_THREAD_POOL_H +#define GRPC_INTERNAL_CPP_DYNAMIC_THREAD_POOL_H + +#include +#include +#include + +#include +#include +#include + +#include "src/cpp/server/thread_pool_interface.h" + +namespace grpc { + +class DynamicThreadPool GRPC_FINAL : public ThreadPoolInterface { + public: + explicit DynamicThreadPool(int reserve_threads); + ~DynamicThreadPool(); + + void Add(const std::function& callback) GRPC_OVERRIDE; + + private: + class DynamicThread { + public: + DynamicThread(DynamicThreadPool* pool); + ~DynamicThread(); + + private: + DynamicThreadPool* pool_; + std::unique_ptr thd_; + void ThreadFunc(); + }; + grpc::mutex mu_; + grpc::condition_variable cv_; + grpc::condition_variable shutdown_cv_; + bool shutdown_; + std::queue> callbacks_; + int reserve_threads_; + int nthreads_; + int threads_waiting_; + std::list dead_threads_; + + void ThreadFunc(); + static void ReapThreads(std::list* tlist); +}; + +} // namespace grpc + +#endif // GRPC_INTERNAL_CPP_DYNAMIC_THREAD_POOL_H diff --git a/src/cpp/server/fixed_size_thread_pool.cc b/src/cpp/server/fixed_size_thread_pool.cc new file mode 100644 index 00000000..2bdc44be --- /dev/null +++ b/src/cpp/server/fixed_size_thread_pool.cc @@ -0,0 +1,85 @@ +/* + * + * Copyright 2015, 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 +#include +#include "src/cpp/server/fixed_size_thread_pool.h" + +namespace grpc { + +void FixedSizeThreadPool::ThreadFunc() { + for (;;) { + // Wait until work is available or we are shutting down. + grpc::unique_lock lock(mu_); + if (!shutdown_ && callbacks_.empty()) { + cv_.wait(lock); + } + // Drain callbacks before considering shutdown to ensure all work + // gets completed. + if (!callbacks_.empty()) { + auto cb = callbacks_.front(); + callbacks_.pop(); + lock.unlock(); + cb(); + } else if (shutdown_) { + return; + } + } +} + +FixedSizeThreadPool::FixedSizeThreadPool(int num_threads) : shutdown_(false) { + for (int i = 0; i < num_threads; i++) { + threads_.push_back( + new grpc::thread(&FixedSizeThreadPool::ThreadFunc, this)); + } +} + +FixedSizeThreadPool::~FixedSizeThreadPool() { + { + grpc::lock_guard lock(mu_); + shutdown_ = true; + cv_.notify_all(); + } + for (auto t = threads_.begin(); t != threads_.end(); t++) { + (*t)->join(); + delete *t; + } +} + +void FixedSizeThreadPool::Add(const std::function& callback) { + grpc::lock_guard lock(mu_); + callbacks_.push(callback); + cv_.notify_one(); +} + +} // namespace grpc diff --git a/src/cpp/server/fixed_size_thread_pool.h b/src/cpp/server/fixed_size_thread_pool.h new file mode 100644 index 00000000..394ae582 --- /dev/null +++ b/src/cpp/server/fixed_size_thread_pool.h @@ -0,0 +1,67 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CPP_FIXED_SIZE_THREAD_POOL_H +#define GRPC_INTERNAL_CPP_FIXED_SIZE_THREAD_POOL_H + +#include +#include + +#include +#include +#include + +#include "src/cpp/server/thread_pool_interface.h" + +namespace grpc { + +class FixedSizeThreadPool GRPC_FINAL : public ThreadPoolInterface { + public: + explicit FixedSizeThreadPool(int num_threads); + ~FixedSizeThreadPool(); + + void Add(const std::function& callback) GRPC_OVERRIDE; + + private: + grpc::mutex mu_; + grpc::condition_variable cv_; + bool shutdown_; + std::queue> callbacks_; + std::vector threads_; + + void ThreadFunc(); +}; + +} // namespace grpc + +#endif // GRPC_INTERNAL_CPP_FIXED_SIZE_THREAD_POOL_H diff --git a/src/cpp/server/insecure_server_credentials.cc b/src/cpp/server/insecure_server_credentials.cc new file mode 100644 index 00000000..ef3cae5f --- /dev/null +++ b/src/cpp/server/insecure_server_credentials.cc @@ -0,0 +1,60 @@ +/* + * + * Copyright 2015, 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 + +#include +#include + +namespace grpc { +namespace { +class InsecureServerCredentialsImpl GRPC_FINAL : public ServerCredentials { + public: + int AddPortToServer(const grpc::string& addr, + grpc_server* server) GRPC_OVERRIDE { + return grpc_server_add_insecure_http2_port(server, addr.c_str()); + } + void SetAuthMetadataProcessor( + const std::shared_ptr& processor) GRPC_OVERRIDE { + (void)processor; + GPR_ASSERT(0); // Should not be called on InsecureServerCredentials. + } +}; +} // namespace + +std::shared_ptr InsecureServerCredentials() { + return std::shared_ptr( + new InsecureServerCredentialsImpl()); +} + +} // namespace grpc diff --git a/src/cpp/server/secure_server_credentials.cc b/src/cpp/server/secure_server_credentials.cc new file mode 100644 index 00000000..90afebfd --- /dev/null +++ b/src/cpp/server/secure_server_credentials.cc @@ -0,0 +1,141 @@ +/* + * + * Copyright 2015, 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 +#include +#include + + +#include "src/cpp/common/secure_auth_context.h" +#include "src/cpp/server/secure_server_credentials.h" + +#include + +namespace grpc { + +void AuthMetadataProcessorAyncWrapper::Destroy(void *wrapper) { + auto* w = reinterpret_cast(wrapper); + delete w; +} + +void AuthMetadataProcessorAyncWrapper::Process( + void* wrapper, grpc_auth_context* context, const grpc_metadata* md, + size_t num_md, grpc_process_auth_metadata_done_cb cb, void* user_data) { + auto* w = reinterpret_cast(wrapper); + if (w->processor_ == nullptr) { + // Early exit. + cb(user_data, nullptr, 0, nullptr, 0, GRPC_STATUS_OK, nullptr); + return; + } + if (w->processor_->IsBlocking()) { + w->thread_pool_->Add( + std::bind(&AuthMetadataProcessorAyncWrapper::InvokeProcessor, w, + context, md, num_md, cb, user_data)); + } else { + // invoke directly. + w->InvokeProcessor(context, md, num_md, cb, user_data); + } +} + +void AuthMetadataProcessorAyncWrapper::InvokeProcessor( + grpc_auth_context* ctx, + const grpc_metadata* md, size_t num_md, + grpc_process_auth_metadata_done_cb cb, void* user_data) { + AuthMetadataProcessor::InputMetadata metadata; + for (size_t i = 0; i < num_md; i++) { + metadata.insert(std::make_pair( + md[i].key, grpc::string_ref(md[i].value, md[i].value_length))); + } + SecureAuthContext context(ctx, false); + AuthMetadataProcessor::OutputMetadata consumed_metadata; + AuthMetadataProcessor::OutputMetadata response_metadata; + + Status status = processor_->Process(metadata, &context, &consumed_metadata, + &response_metadata); + + std::vector consumed_md; + for (auto it = consumed_metadata.begin(); it != consumed_metadata.end(); + ++it) { + consumed_md.push_back({it->first.c_str(), + it->second.data(), + it->second.size(), + 0, + {{nullptr, nullptr, nullptr, nullptr}}}); + } + std::vector response_md; + for (auto it = response_metadata.begin(); it != response_metadata.end(); + ++it) { + response_md.push_back({it->first.c_str(), + it->second.data(), + it->second.size(), + 0, + {{nullptr, nullptr, nullptr, nullptr}}}); + } + auto consumed_md_data = consumed_md.empty() ? nullptr : &consumed_md[0]; + auto response_md_data = response_md.empty() ? nullptr : &response_md[0]; + cb(user_data, consumed_md_data, consumed_md.size(), response_md_data, + response_md.size(), static_cast(status.error_code()), + status.error_message().c_str()); +} + +int SecureServerCredentials::AddPortToServer(const grpc::string& addr, + grpc_server* server) { + return grpc_server_add_secure_http2_port(server, addr.c_str(), creds_); +} + +void SecureServerCredentials::SetAuthMetadataProcessor( + const std::shared_ptr& processor) { + auto *wrapper = new AuthMetadataProcessorAyncWrapper(processor); + grpc_server_credentials_set_auth_metadata_processor( + creds_, {AuthMetadataProcessorAyncWrapper::Process, + AuthMetadataProcessorAyncWrapper::Destroy, wrapper}); +} + +std::shared_ptr SslServerCredentials( + const SslServerCredentialsOptions& options) { + std::vector pem_key_cert_pairs; + for (auto key_cert_pair = options.pem_key_cert_pairs.begin(); + key_cert_pair != options.pem_key_cert_pairs.end(); key_cert_pair++) { + grpc_ssl_pem_key_cert_pair p = {key_cert_pair->private_key.c_str(), + key_cert_pair->cert_chain.c_str()}; + pem_key_cert_pairs.push_back(p); + } + grpc_server_credentials* c_creds = grpc_ssl_server_credentials_create( + options.pem_root_certs.empty() ? nullptr : options.pem_root_certs.c_str(), + pem_key_cert_pairs.empty() ? nullptr : &pem_key_cert_pairs[0], + pem_key_cert_pairs.size(), options.force_client_auth, nullptr); + return std::shared_ptr( + new SecureServerCredentials(c_creds)); +} + +} // namespace grpc diff --git a/src/cpp/server/secure_server_credentials.h b/src/cpp/server/secure_server_credentials.h new file mode 100644 index 00000000..4f003c6b --- /dev/null +++ b/src/cpp/server/secure_server_credentials.h @@ -0,0 +1,88 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CPP_SERVER_SECURE_SERVER_CREDENTIALS_H +#define GRPC_INTERNAL_CPP_SERVER_SECURE_SERVER_CREDENTIALS_H + +#include + +#include + +#include + +#include "src/cpp/server/thread_pool_interface.h" + +namespace grpc { + +class AuthMetadataProcessorAyncWrapper GRPC_FINAL { + public: + static void Destroy(void *wrapper); + + static void Process(void* wrapper, grpc_auth_context* context, + const grpc_metadata* md, size_t num_md, + grpc_process_auth_metadata_done_cb cb, void* user_data); + + AuthMetadataProcessorAyncWrapper( + const std::shared_ptr& processor) + : thread_pool_(CreateDefaultThreadPool()), processor_(processor) {} + + private: + void InvokeProcessor(grpc_auth_context* context, const grpc_metadata* md, + size_t num_md, grpc_process_auth_metadata_done_cb cb, + void* user_data); + std::unique_ptr thread_pool_; + std::shared_ptr processor_; +}; + +class SecureServerCredentials GRPC_FINAL : public ServerCredentials { + public: + explicit SecureServerCredentials(grpc_server_credentials* creds) + : creds_(creds) {} + ~SecureServerCredentials() GRPC_OVERRIDE { + grpc_server_credentials_release(creds_); + } + + int AddPortToServer(const grpc::string& addr, + grpc_server* server) GRPC_OVERRIDE; + + void SetAuthMetadataProcessor( + const std::shared_ptr& processor) GRPC_OVERRIDE; + + private: + grpc_server_credentials* creds_; + std::unique_ptr processor_; +}; + +} // namespace grpc + +#endif // GRPC_INTERNAL_CPP_SERVER_SECURE_SERVER_CREDENTIALS_H diff --git a/src/cpp/server/server.cc b/src/cpp/server/server.cc new file mode 100644 index 00000000..d67205e8 --- /dev/null +++ b/src/cpp/server/server.cc @@ -0,0 +1,564 @@ +/* + * + * Copyright 2015, 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 + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "src/core/profiling/timers.h" +#include "src/cpp/server/thread_pool_interface.h" + +namespace grpc { + +class Server::UnimplementedAsyncRequestContext { + protected: + UnimplementedAsyncRequestContext() : generic_stream_(&server_context_) {} + + GenericServerContext server_context_; + GenericServerAsyncReaderWriter generic_stream_; +}; + +class Server::UnimplementedAsyncRequest GRPC_FINAL + : public UnimplementedAsyncRequestContext, + public GenericAsyncRequest { + public: + UnimplementedAsyncRequest(Server* server, ServerCompletionQueue* cq) + : GenericAsyncRequest(server, &server_context_, &generic_stream_, cq, cq, + NULL, false), + server_(server), + cq_(cq) {} + + bool FinalizeResult(void** tag, bool* status) GRPC_OVERRIDE; + + ServerContext* context() { return &server_context_; } + GenericServerAsyncReaderWriter* stream() { return &generic_stream_; } + + private: + Server* const server_; + ServerCompletionQueue* const cq_; +}; + +typedef SneakyCallOpSet + UnimplementedAsyncResponseOp; +class Server::UnimplementedAsyncResponse GRPC_FINAL + : public UnimplementedAsyncResponseOp { + public: + UnimplementedAsyncResponse(UnimplementedAsyncRequest* request); + ~UnimplementedAsyncResponse() { delete request_; } + + bool FinalizeResult(void** tag, bool* status) GRPC_OVERRIDE { + bool r = UnimplementedAsyncResponseOp::FinalizeResult(tag, status); + delete this; + return r; + } + + private: + UnimplementedAsyncRequest* const request_; +}; + +class Server::ShutdownRequest GRPC_FINAL : public CompletionQueueTag { + public: + bool FinalizeResult(void** tag, bool* status) { + delete this; + return false; + } +}; + +class Server::SyncRequest GRPC_FINAL : public CompletionQueueTag { + public: + SyncRequest(RpcServiceMethod* method, void* tag) + : method_(method), + tag_(tag), + in_flight_(false), + has_request_payload_(method->method_type() == RpcMethod::NORMAL_RPC || + method->method_type() == + RpcMethod::SERVER_STREAMING), + call_details_(nullptr), + cq_(nullptr) { + grpc_metadata_array_init(&request_metadata_); + } + + ~SyncRequest() { + if (call_details_) { + delete call_details_; + } + grpc_metadata_array_destroy(&request_metadata_); + } + + static SyncRequest* Wait(CompletionQueue* cq, bool* ok) { + void* tag = nullptr; + *ok = false; + if (!cq->Next(&tag, ok)) { + return nullptr; + } + auto* mrd = static_cast(tag); + GPR_ASSERT(mrd->in_flight_); + return mrd; + } + + static bool AsyncWait(CompletionQueue* cq, SyncRequest** req, bool* ok, + gpr_timespec deadline) { + void* tag = nullptr; + *ok = false; + switch (cq->AsyncNext(&tag, ok, deadline)) { + case CompletionQueue::TIMEOUT: + *req = nullptr; + return true; + case CompletionQueue::SHUTDOWN: + *req = nullptr; + return false; + case CompletionQueue::GOT_EVENT: + *req = static_cast(tag); + GPR_ASSERT((*req)->in_flight_); + return true; + } + gpr_log(GPR_ERROR, "Should never reach here"); + abort(); + } + + void SetupRequest() { cq_ = grpc_completion_queue_create(nullptr); } + + void TeardownRequest() { + grpc_completion_queue_destroy(cq_); + cq_ = nullptr; + } + + void Request(grpc_server* server, grpc_completion_queue* notify_cq) { + GPR_ASSERT(cq_ && !in_flight_); + in_flight_ = true; + if (tag_) { + GPR_ASSERT(GRPC_CALL_OK == + grpc_server_request_registered_call( + server, tag_, &call_, &deadline_, &request_metadata_, + has_request_payload_ ? &request_payload_ : nullptr, cq_, + notify_cq, this)); + } else { + if (!call_details_) { + call_details_ = new grpc_call_details; + grpc_call_details_init(call_details_); + } + GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call( + server, &call_, call_details_, + &request_metadata_, cq_, notify_cq, this)); + } + } + + bool FinalizeResult(void** tag, bool* status) GRPC_OVERRIDE { + if (!*status) { + grpc_completion_queue_destroy(cq_); + } + if (call_details_) { + deadline_ = call_details_->deadline; + grpc_call_details_destroy(call_details_); + grpc_call_details_init(call_details_); + } + return true; + } + + class CallData GRPC_FINAL { + public: + explicit CallData(Server* server, SyncRequest* mrd) + : cq_(mrd->cq_), + call_(mrd->call_, server, &cq_, server->max_message_size_), + ctx_(mrd->deadline_, mrd->request_metadata_.metadata, + mrd->request_metadata_.count), + has_request_payload_(mrd->has_request_payload_), + request_payload_(mrd->request_payload_), + method_(mrd->method_) { + ctx_.set_call(mrd->call_); + ctx_.cq_ = &cq_; + GPR_ASSERT(mrd->in_flight_); + mrd->in_flight_ = false; + mrd->request_metadata_.count = 0; + } + + ~CallData() { + if (has_request_payload_ && request_payload_) { + grpc_byte_buffer_destroy(request_payload_); + } + } + + void Run() { + ctx_.BeginCompletionOp(&call_); + method_->handler()->RunHandler(MethodHandler::HandlerParameter( + &call_, &ctx_, request_payload_, call_.max_message_size())); + request_payload_ = nullptr; + void* ignored_tag; + bool ignored_ok; + cq_.Shutdown(); + GPR_ASSERT(cq_.Next(&ignored_tag, &ignored_ok) == false); + } + + private: + CompletionQueue cq_; + Call call_; + ServerContext ctx_; + const bool has_request_payload_; + grpc_byte_buffer* request_payload_; + RpcServiceMethod* const method_; + }; + + private: + RpcServiceMethod* const method_; + void* const tag_; + bool in_flight_; + const bool has_request_payload_; + grpc_call* call_; + grpc_call_details* call_details_; + gpr_timespec deadline_; + grpc_metadata_array request_metadata_; + grpc_byte_buffer* request_payload_; + grpc_completion_queue* cq_; +}; + +static grpc_server* CreateServer(int max_message_size) { + if (max_message_size > 0) { + grpc_arg arg; + arg.type = GRPC_ARG_INTEGER; + arg.key = const_cast(GRPC_ARG_MAX_MESSAGE_LENGTH); + arg.value.integer = max_message_size; + grpc_channel_args args = {1, &arg}; + return grpc_server_create(&args, nullptr); + } else { + return grpc_server_create(nullptr, nullptr); + } +} + +Server::Server(ThreadPoolInterface* thread_pool, bool thread_pool_owned, + int max_message_size) + : max_message_size_(max_message_size), + started_(false), + shutdown_(false), + num_running_cb_(0), + sync_methods_(new std::list), + has_generic_service_(false), + server_(CreateServer(max_message_size)), + thread_pool_(thread_pool), + thread_pool_owned_(thread_pool_owned) { + grpc_server_register_completion_queue(server_, cq_.cq(), nullptr); +} + +Server::~Server() { + { + grpc::unique_lock lock(mu_); + if (started_ && !shutdown_) { + lock.unlock(); + Shutdown(); + } + } + void* got_tag; + bool ok; + GPR_ASSERT(!cq_.Next(&got_tag, &ok)); + grpc_server_destroy(server_); + if (thread_pool_owned_) { + delete thread_pool_; + } + delete sync_methods_; +} + +bool Server::RegisterService(const grpc::string* host, RpcService* service) { + for (int i = 0; i < service->GetMethodCount(); ++i) { + RpcServiceMethod* method = service->GetMethod(i); + void* tag = grpc_server_register_method(server_, method->name(), + host ? host->c_str() : nullptr); + if (!tag) { + 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; + } + service->request_args_[i] = tag; + } + return true; +} + +void Server::RegisterAsyncGenericService(AsyncGenericService* service) { + GPR_ASSERT(service->server_ == nullptr && + "Can only register an async generic service against one server."); + service->server_ = this; + has_generic_service_ = true; +} + +int Server::AddListeningPort(const grpc::string& addr, + ServerCredentials* creds) { + GPR_ASSERT(!started_); + return creds->AddPortToServer(addr, server_); +} + +bool Server::Start(ServerCompletionQueue** cqs, size_t num_cqs) { + GPR_ASSERT(!started_); + started_ = true; + grpc_server_start(server_); + + if (!has_generic_service_) { + if (!sync_methods_->empty()) { + unknown_method_.reset(new RpcServiceMethod( + "unknown", RpcMethod::BIDI_STREAMING, new UnknownMethodHandler)); + // Use of emplace_back with just constructor arguments is not accepted + // here by gcc-4.4 because it can't match the anonymous nullptr with a + // proper constructor implicitly. Construct the object and use push_back. + sync_methods_->push_back(SyncRequest(unknown_method_.get(), nullptr)); + } + for (size_t i = 0; i < num_cqs; i++) { + new UnimplementedAsyncRequest(this, cqs[i]); + } + } + // Start processing rpcs. + if (!sync_methods_->empty()) { + for (auto m = sync_methods_->begin(); m != sync_methods_->end(); m++) { + m->SetupRequest(); + m->Request(server_, cq_.cq()); + } + + ScheduleCallback(); + } + + return true; +} + +void Server::ShutdownInternal(gpr_timespec deadline) { + grpc::unique_lock lock(mu_); + if (started_ && !shutdown_) { + shutdown_ = true; + grpc_server_shutdown_and_notify(server_, cq_.cq(), new ShutdownRequest()); + cq_.Shutdown(); + // Spin, eating requests until the completion queue is completely shutdown. + // If the deadline expires then cancel anything that's pending and keep + // spinning forever until the work is actually drained. + // Since nothing else needs to touch state guarded by mu_, holding it + // through this loop is fine. + SyncRequest* request; + bool ok; + while (SyncRequest::AsyncWait(&cq_, &request, &ok, deadline)) { + if (request == NULL) { // deadline expired + grpc_server_cancel_all_calls(server_); + deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC); + } else if (ok) { + SyncRequest::CallData call_data(this, request); + } + } + + // Wait for running callbacks to finish. + while (num_running_cb_ != 0) { + callback_cv_.wait(lock); + } + } +} + +void Server::Wait() { + grpc::unique_lock lock(mu_); + while (num_running_cb_ != 0) { + callback_cv_.wait(lock); + } +} + +void Server::PerformOpsOnCall(CallOpSetInterface* ops, Call* call) { + static const size_t MAX_OPS = 8; + size_t nops = 0; + grpc_op cops[MAX_OPS]; + ops->FillOps(cops, &nops); + auto result = grpc_call_start_batch(call->call(), cops, nops, ops, nullptr); + GPR_ASSERT(GRPC_CALL_OK == result); +} + +Server::BaseAsyncRequest::BaseAsyncRequest( + Server* server, ServerContext* context, + ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq, void* tag, + bool delete_on_finalize) + : server_(server), + context_(context), + stream_(stream), + call_cq_(call_cq), + tag_(tag), + delete_on_finalize_(delete_on_finalize), + call_(nullptr) { + memset(&initial_metadata_array_, 0, sizeof(initial_metadata_array_)); +} + +Server::BaseAsyncRequest::~BaseAsyncRequest() {} + +bool Server::BaseAsyncRequest::FinalizeResult(void** tag, bool* status) { + if (*status) { + for (size_t i = 0; i < initial_metadata_array_.count; i++) { + context_->client_metadata_.insert( + std::pair( + initial_metadata_array_.metadata[i].key, + grpc::string_ref( + initial_metadata_array_.metadata[i].value, + initial_metadata_array_.metadata[i].value_length))); + } + } + grpc_metadata_array_destroy(&initial_metadata_array_); + context_->set_call(call_); + context_->cq_ = call_cq_; + Call call(call_, server_, call_cq_, server_->max_message_size_); + if (*status && call_) { + context_->BeginCompletionOp(&call); + } + // just the pointers inside call are copied here + stream_->BindCall(&call); + *tag = tag_; + if (delete_on_finalize_) { + delete this; + } + return true; +} + +Server::RegisteredAsyncRequest::RegisteredAsyncRequest( + Server* server, ServerContext* context, + ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq, void* tag) + : BaseAsyncRequest(server, context, stream, call_cq, tag, true) {} + +void Server::RegisteredAsyncRequest::IssueRequest( + void* registered_method, grpc_byte_buffer** payload, + ServerCompletionQueue* notification_cq) { + grpc_server_request_registered_call( + server_->server_, registered_method, &call_, &context_->deadline_, + &initial_metadata_array_, payload, call_cq_->cq(), notification_cq->cq(), + this); +} + +Server::GenericAsyncRequest::GenericAsyncRequest( + Server* server, GenericServerContext* context, + ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq, + ServerCompletionQueue* notification_cq, void* tag, bool delete_on_finalize) + : BaseAsyncRequest(server, context, stream, call_cq, tag, + delete_on_finalize) { + grpc_call_details_init(&call_details_); + GPR_ASSERT(notification_cq); + GPR_ASSERT(call_cq); + grpc_server_request_call(server->server_, &call_, &call_details_, + &initial_metadata_array_, call_cq->cq(), + notification_cq->cq(), this); +} + +bool Server::GenericAsyncRequest::FinalizeResult(void** tag, bool* status) { + // TODO(yangg) remove the copy here. + if (*status) { + static_cast(context_)->method_ = + call_details_.method; + static_cast(context_)->host_ = call_details_.host; + } + gpr_free(call_details_.method); + gpr_free(call_details_.host); + return BaseAsyncRequest::FinalizeResult(tag, status); +} + +bool Server::UnimplementedAsyncRequest::FinalizeResult(void** tag, + bool* status) { + if (GenericAsyncRequest::FinalizeResult(tag, status) && *status) { + new UnimplementedAsyncRequest(server_, cq_); + new UnimplementedAsyncResponse(this); + } else { + delete this; + } + return false; +} + +Server::UnimplementedAsyncResponse::UnimplementedAsyncResponse( + UnimplementedAsyncRequest* request) + : request_(request) { + Status status(StatusCode::UNIMPLEMENTED, ""); + UnknownMethodHandler::FillOps(request_->context(), this); + request_->stream()->call_.PerformOps(this); +} + +void Server::ScheduleCallback() { + { + grpc::unique_lock lock(mu_); + num_running_cb_++; + } + thread_pool_->Add(std::bind(&Server::RunRpc, this)); +} + +void Server::RunRpc() { + // Wait for one more incoming rpc. + bool ok; + auto* mrd = SyncRequest::Wait(&cq_, &ok); + if (mrd) { + ScheduleCallback(); + if (ok) { + SyncRequest::CallData cd(this, mrd); + { + mrd->SetupRequest(); + grpc::unique_lock lock(mu_); + if (!shutdown_) { + mrd->Request(server_, cq_.cq()); + } else { + // destroy the structure that was created + mrd->TeardownRequest(); + } + } + cd.Run(); + } + } + + { + grpc::unique_lock lock(mu_); + num_running_cb_--; + if (shutdown_) { + callback_cv_.notify_all(); + } + } +} + +} // namespace grpc diff --git a/src/cpp/server/server_builder.cc b/src/cpp/server/server_builder.cc new file mode 100644 index 00000000..99bc8147 --- /dev/null +++ b/src/cpp/server/server_builder.cc @@ -0,0 +1,138 @@ +/* + * + * Copyright 2015, 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 + +#include +#include +#include +#include +#include "src/cpp/server/thread_pool_interface.h" +#include "src/cpp/server/fixed_size_thread_pool.h" + +namespace grpc { + +ServerBuilder::ServerBuilder() + : max_message_size_(-1), generic_service_(nullptr), thread_pool_(nullptr) {} + +std::unique_ptr ServerBuilder::AddCompletionQueue() { + ServerCompletionQueue* cq = new ServerCompletionQueue(); + cqs_.push_back(cq); + return std::unique_ptr(cq); +} + +void ServerBuilder::RegisterService(SynchronousService* service) { + services_.emplace_back(new NamedService(service->service())); +} + +void ServerBuilder::RegisterAsyncService(AsynchronousService* service) { + async_services_.emplace_back(new NamedService(service)); +} + +void ServerBuilder::RegisterService(const grpc::string& addr, + SynchronousService* service) { + services_.emplace_back( + new NamedService(addr, service->service())); +} + +void ServerBuilder::RegisterAsyncService(const grpc::string& addr, + AsynchronousService* service) { + async_services_.emplace_back( + new NamedService(addr, service)); +} + +void ServerBuilder::RegisterAsyncGenericService(AsyncGenericService* service) { + if (generic_service_) { + gpr_log(GPR_ERROR, + "Adding multiple AsyncGenericService is unsupported for now. " + "Dropping the service %p", + service); + return; + } + generic_service_ = service; +} + +void ServerBuilder::AddListeningPort(const grpc::string& addr, + std::shared_ptr creds, + int* selected_port) { + Port port = {addr, creds, selected_port}; + ports_.push_back(port); +} + +std::unique_ptr 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 server( + new Server(thread_pool_, thread_pool_owned, max_message_size_)); + for (auto cq = cqs_.begin(); cq != cqs_.end(); ++cq) { + grpc_server_register_completion_queue(server->server_, (*cq)->cq(), + nullptr); + } + for (auto service = services_.begin(); service != services_.end(); + service++) { + if (!server->RegisterService((*service)->host.get(), (*service)->service)) { + 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_); + } + for (auto port = ports_.begin(); port != ports_.end(); port++) { + int r = server->AddListeningPort(port->addr, port->creds.get()); + if (!r) return nullptr; + if (port->selected_port != nullptr) { + *port->selected_port = r; + } + } + auto cqs_data = cqs_.empty() ? nullptr : &cqs_[0]; + if (!server->Start(cqs_data, cqs_.size())) { + return nullptr; + } + return server; +} + +} // namespace grpc diff --git a/src/cpp/server/server_context.cc b/src/cpp/server/server_context.cc new file mode 100644 index 00000000..8193e706 --- /dev/null +++ b/src/cpp/server/server_context.cc @@ -0,0 +1,221 @@ +/* + * + * Copyright 2015, 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 + +#include +#include +#include +#include +#include +#include + +#include "src/core/channel/compress_filter.h" +#include "src/cpp/common/create_auth_context.h" + +namespace grpc { + +// CompletionOp + +class ServerContext::CompletionOp GRPC_FINAL : public CallOpSetInterface { + public: + // initial refs: one in the server context, one in the cq + CompletionOp() + : has_tag_(false), + tag_(nullptr), + refs_(2), + finalized_(false), + cancelled_(0) {} + + void FillOps(grpc_op* ops, size_t* nops) GRPC_OVERRIDE; + bool FinalizeResult(void** tag, bool* status) GRPC_OVERRIDE; + + bool CheckCancelled(CompletionQueue* cq); + + void set_tag(void* tag) { + has_tag_ = true; + tag_ = tag; + } + + void Unref(); + + private: + bool has_tag_; + void* tag_; + grpc::mutex mu_; + int refs_; + bool finalized_; + int cancelled_; +}; + +void ServerContext::CompletionOp::Unref() { + grpc::unique_lock lock(mu_); + if (--refs_ == 0) { + lock.unlock(); + delete this; + } +} + +bool ServerContext::CompletionOp::CheckCancelled(CompletionQueue* cq) { + cq->TryPluck(this); + grpc::lock_guard g(mu_); + return finalized_ ? cancelled_ != 0 : false; +} + +void ServerContext::CompletionOp::FillOps(grpc_op* ops, size_t* nops) { + ops->op = GRPC_OP_RECV_CLOSE_ON_SERVER; + ops->data.recv_close_on_server.cancelled = &cancelled_; + ops->flags = 0; + ops->reserved = NULL; + *nops = 1; +} + +bool ServerContext::CompletionOp::FinalizeResult(void** tag, bool* status) { + grpc::unique_lock lock(mu_); + finalized_ = true; + bool ret = false; + if (has_tag_) { + *tag = tag_; + ret = true; + } + if (!*status) cancelled_ = 1; + if (--refs_ == 0) { + lock.unlock(); + delete this; + } + return ret; +} + +// ServerContext body + +ServerContext::ServerContext() + : completion_op_(nullptr), + has_notify_when_done_tag_(false), + async_notify_when_done_tag_(nullptr), + call_(nullptr), + cq_(nullptr), + sent_initial_metadata_(false) {} + +ServerContext::ServerContext(gpr_timespec deadline, grpc_metadata* metadata, + size_t metadata_count) + : completion_op_(nullptr), + has_notify_when_done_tag_(false), + async_notify_when_done_tag_(nullptr), + deadline_(deadline), + call_(nullptr), + cq_(nullptr), + sent_initial_metadata_(false) { + for (size_t i = 0; i < metadata_count; i++) { + client_metadata_.insert(std::pair( + metadata[i].key, + grpc::string_ref(metadata[i].value, metadata[i].value_length))); + } +} + +ServerContext::~ServerContext() { + if (call_) { + grpc_call_destroy(call_); + } + if (completion_op_) { + completion_op_->Unref(); + } +} + +void ServerContext::BeginCompletionOp(Call* call) { + GPR_ASSERT(!completion_op_); + completion_op_ = new CompletionOp(); + if (has_notify_when_done_tag_) { + completion_op_->set_tag(async_notify_when_done_tag_); + } + call->PerformOps(completion_op_); +} + +void ServerContext::AddInitialMetadata(const grpc::string& key, + const grpc::string& value) { + initial_metadata_.insert(std::make_pair(key, value)); +} + +void ServerContext::AddTrailingMetadata(const grpc::string& key, + const grpc::string& value) { + trailing_metadata_.insert(std::make_pair(key, value)); +} + +bool ServerContext::IsCancelled() const { + return completion_op_ && completion_op_->CheckCancelled(cq_); +} + +void ServerContext::set_compression_level(grpc_compression_level level) { + const grpc_compression_algorithm algorithm_for_level = + grpc_compression_algorithm_for_level(level); + set_compression_algorithm(algorithm_for_level); +} + +void ServerContext::set_compression_algorithm( + grpc_compression_algorithm algorithm) { + char* algorithm_name = NULL; + if (!grpc_compression_algorithm_name(algorithm, &algorithm_name)) { + gpr_log(GPR_ERROR, "Name for compression algorithm '%d' unknown.", + algorithm); + abort(); + } + GPR_ASSERT(algorithm_name != NULL); + AddInitialMetadata(GRPC_COMPRESS_REQUEST_ALGORITHM_KEY, algorithm_name); +} + +void ServerContext::set_call(grpc_call* call) { + call_ = call; + auth_context_ = CreateAuthContext(call); +} + +std::shared_ptr ServerContext::auth_context() const { + if (auth_context_.get() == nullptr) { + auth_context_ = CreateAuthContext(call_); + } + return auth_context_; +} + +grpc::string ServerContext::peer() const { + grpc::string peer; + if (call_) { + char* c_peer = grpc_call_get_peer(call_); + peer = c_peer; + gpr_free(c_peer); + } + return peer; +} + +const struct census_context* ServerContext::census_context() const { + return grpc_census_call_get_context(call_); +} + +} // namespace grpc diff --git a/src/cpp/server/server_credentials.cc b/src/cpp/server/server_credentials.cc new file mode 100644 index 00000000..84959161 --- /dev/null +++ b/src/cpp/server/server_credentials.cc @@ -0,0 +1,40 @@ +/* + * + * Copyright 2015, 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 + +namespace grpc { + +ServerCredentials::~ServerCredentials() {} + +} // namespace grpc diff --git a/src/cpp/server/thread_pool_interface.h b/src/cpp/server/thread_pool_interface.h new file mode 100644 index 00000000..1ebe30fe --- /dev/null +++ b/src/cpp/server/thread_pool_interface.h @@ -0,0 +1,54 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CPP_THREAD_POOL_INTERFACE_H +#define GRPC_INTERNAL_CPP_THREAD_POOL_INTERFACE_H + +#include + +namespace grpc { + +// A thread pool interface for running callbacks. +class ThreadPoolInterface { + public: + virtual ~ThreadPoolInterface() {} + + // Schedule the given callback for execution. + virtual void Add(const std::function& callback) = 0; +}; + +ThreadPoolInterface* CreateDefaultThreadPool(); + +} // namespace grpc + +#endif // GRPC_INTERNAL_CPP_THREAD_POOL_INTERFACE_H diff --git a/src/cpp/util/byte_buffer.cc b/src/cpp/util/byte_buffer.cc new file mode 100644 index 00000000..755234d7 --- /dev/null +++ b/src/cpp/util/byte_buffer.cc @@ -0,0 +1,82 @@ +/* + * + * Copyright 2015, 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 +#include + +namespace grpc { + +ByteBuffer::ByteBuffer(const Slice* slices, size_t nslices) { + // TODO(yangg) maybe expose some core API to simplify this + std::vector c_slices(nslices); + for (size_t i = 0; i < nslices; i++) { + c_slices[i] = slices[i].slice_; + } + buffer_ = grpc_raw_byte_buffer_create(c_slices.data(), nslices); +} + +ByteBuffer::~ByteBuffer() { + if (buffer_) { + grpc_byte_buffer_destroy(buffer_); + } +} + +void ByteBuffer::Clear() { + if (buffer_) { + grpc_byte_buffer_destroy(buffer_); + buffer_ = nullptr; + } +} + +void ByteBuffer::Dump(std::vector* slices) const { + slices->clear(); + if (!buffer_) { + return; + } + grpc_byte_buffer_reader reader; + grpc_byte_buffer_reader_init(&reader, buffer_); + gpr_slice s; + while (grpc_byte_buffer_reader_next(&reader, &s)) { + slices->push_back(Slice(s, Slice::STEAL_REF)); + } +} + +size_t ByteBuffer::Length() const { + if (buffer_) { + return grpc_byte_buffer_length(buffer_); + } else { + return 0; + } +} + +} // namespace grpc diff --git a/src/cpp/util/slice.cc b/src/cpp/util/slice.cc new file mode 100644 index 00000000..7e88423b --- /dev/null +++ b/src/cpp/util/slice.cc @@ -0,0 +1,48 @@ +/* + * + * Copyright 2015, 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 + +namespace grpc { + +Slice::Slice() : slice_(gpr_empty_slice()) {} + +Slice::~Slice() { gpr_slice_unref(slice_); } + +Slice::Slice(gpr_slice slice, AddRef) : slice_(gpr_slice_ref(slice)) {} + +Slice::Slice(gpr_slice slice, StealRef) : slice_(slice) {} + +Slice::Slice(const Slice& other) : slice_(gpr_slice_ref(other.slice_)) {} + +} // namespace grpc diff --git a/src/cpp/util/status.cc b/src/cpp/util/status.cc new file mode 100644 index 00000000..ad9850cf --- /dev/null +++ b/src/cpp/util/status.cc @@ -0,0 +1,41 @@ +/* + * + * Copyright 2015, 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 + +namespace grpc { + +const Status& Status::OK = Status(); +const Status& Status::CANCELLED = Status(StatusCode::CANCELLED, ""); + +} // namespace grpc diff --git a/src/cpp/util/string_ref.cc b/src/cpp/util/string_ref.cc new file mode 100644 index 00000000..c42033f6 --- /dev/null +++ b/src/cpp/util/string_ref.cc @@ -0,0 +1,116 @@ +/* + * + * Copyright 2015, 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 + +#include + +#include +#include + +namespace grpc { + +const size_t string_ref::npos; + +string_ref& string_ref::operator=(const string_ref& rhs) { + data_ = rhs.data_; + length_ = rhs.length_; + return *this; +} + +string_ref::string_ref(const char* s) : data_(s), length_(strlen(s)) {} + +string_ref string_ref::substr(size_t pos, size_t n) const { + if (pos > length_) pos = length_; + if (n > (length_ - pos)) n = length_ - pos; + return string_ref(data_ + pos, n); +} + +int string_ref::compare(string_ref x) const { + size_t min_size = length_ < x.length_ ? length_ : x.length_; + int r = memcmp(data_, x.data_, min_size); + if (r < 0) return -1; + if (r > 0) return 1; + if (length_ < x.length_) return -1; + if (length_ > x.length_) return 1; + return 0; +} + +bool string_ref::starts_with(string_ref x) const { + return length_ >= x.length_ && (memcmp(data_, x.data_, x.length_) == 0); +} + +bool string_ref::ends_with(string_ref x) const { + return length_ >= x.length_ && + (memcmp(data_ + (length_ - x.length_), x.data_, x.length_) == 0); +} + +size_t string_ref::find(string_ref s) const { + auto it = std::search(cbegin(), cend(), s.cbegin(), s.cend()); + return it == cend() ? npos : std::distance(cbegin(), it); +} + +size_t string_ref::find(char c) const { + auto it = std::find(cbegin(), cend(), c); + return it == cend() ? npos : std::distance(cbegin(), it); +} + +bool operator==(string_ref x, string_ref y) { + return x.compare(y) == 0; +} + +bool operator!=(string_ref x, string_ref y) { + return x.compare(y) != 0; +} + +bool operator<(string_ref x, string_ref y) { + return x.compare(y) < 0; +} + +bool operator<=(string_ref x, string_ref y) { + return x.compare(y) <= 0; +} + +bool operator>(string_ref x, string_ref y) { + return x.compare(y) > 0; +} + +bool operator>=(string_ref x, string_ref y) { + return x.compare(y) >= 0; +} + +std::ostream& operator<<(std::ostream& out, const string_ref& string) { + return out << grpc::string(string.begin(), string.end()); +} + +} // namespace grpc diff --git a/src/cpp/util/time.cc b/src/cpp/util/time.cc new file mode 100644 index 00000000..b3401eb2 --- /dev/null +++ b/src/cpp/util/time.cc @@ -0,0 +1,95 @@ +/* + * + * Copyright 2015, 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 + +#ifndef GRPC_CXX0X_NO_CHRONO + +#include +#include + +using std::chrono::duration_cast; +using std::chrono::nanoseconds; +using std::chrono::seconds; +using std::chrono::system_clock; +using std::chrono::high_resolution_clock; + +namespace grpc { + +void Timepoint2Timespec(const system_clock::time_point& from, + gpr_timespec* to) { + system_clock::duration deadline = from.time_since_epoch(); + seconds secs = duration_cast(deadline); + if (from == system_clock::time_point::max() || + secs.count() >= gpr_inf_future(GPR_CLOCK_REALTIME).tv_sec || + secs.count() < 0) { + *to = gpr_inf_future(GPR_CLOCK_REALTIME); + return; + } + nanoseconds nsecs = duration_cast(deadline - secs); + to->tv_sec = secs.count(); + to->tv_nsec = nsecs.count(); + to->clock_type = GPR_CLOCK_REALTIME; +} + +void TimepointHR2Timespec(const high_resolution_clock::time_point& from, + gpr_timespec* to) { + high_resolution_clock::duration deadline = from.time_since_epoch(); + seconds secs = duration_cast(deadline); + if (from == high_resolution_clock::time_point::max() || + secs.count() >= gpr_inf_future(GPR_CLOCK_REALTIME).tv_sec || + secs.count() < 0) { + *to = gpr_inf_future(GPR_CLOCK_REALTIME); + return; + } + nanoseconds nsecs = duration_cast(deadline - secs); + to->tv_sec = secs.count(); + to->tv_nsec = nsecs.count(); + to->clock_type = GPR_CLOCK_REALTIME; +} + +system_clock::time_point Timespec2Timepoint(gpr_timespec t) { + if (gpr_time_cmp(t, gpr_inf_future(t.clock_type)) == 0) { + return system_clock::time_point::max(); + } + t = gpr_convert_clock_type(t, GPR_CLOCK_REALTIME); + system_clock::time_point tp; + tp += duration_cast(seconds(t.tv_sec)); + tp += + duration_cast(nanoseconds(t.tv_nsec)); + return tp; +} + +} // namespace grpc + +#endif // !GRPC_CXX0X_NO_CHRONO diff --git a/src/csharp/.gitignore b/src/csharp/.gitignore new file mode 100644 index 00000000..deac5502 --- /dev/null +++ b/src/csharp/.gitignore @@ -0,0 +1,12 @@ +*.userprefs +*.csproj.user +StyleCop.Cache +test-results +packages +Grpc.v12.suo +Grpc.sdf + +TestResult.xml +/TestResults +.vs/ +*.nupkg diff --git a/src/csharp/.nuget/packages.config b/src/csharp/.nuget/packages.config new file mode 100644 index 00000000..a7df95cf --- /dev/null +++ b/src/csharp/.nuget/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/csharp/Grpc.Auth/.gitignore b/src/csharp/Grpc.Auth/.gitignore new file mode 100644 index 00000000..c2dd6641 --- /dev/null +++ b/src/csharp/Grpc.Auth/.gitignore @@ -0,0 +1,3 @@ +bin +obj +*.nupkg diff --git a/src/csharp/Grpc.Auth/AuthInterceptors.cs b/src/csharp/Grpc.Auth/AuthInterceptors.cs new file mode 100644 index 00000000..c8ab4d9a --- /dev/null +++ b/src/csharp/Grpc.Auth/AuthInterceptors.cs @@ -0,0 +1,88 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Threading; + +using Google.Apis.Auth.OAuth2; +using Grpc.Core; +using Grpc.Core.Utils; + +namespace Grpc.Auth +{ + /// + /// Factory methods to create authorization interceptors. Interceptors created can be registered with gRPC client classes (autogenerated client stubs that + /// inherit from ). + /// + public static class AuthInterceptors + { + private const string AuthorizationHeader = "Authorization"; + private const string Schema = "Bearer"; + + /// + /// Creates interceptor that will obtain access token from any credential type that implements + /// ITokenAccess. (e.g. GoogleCredential). + /// + /// The credential to use to obtain access tokens. + /// The header interceptor. + public static HeaderInterceptor FromCredential(ITokenAccess credential) + { + return new HeaderInterceptor((method, authUri, metadata) => + { + // TODO(jtattermusch): Rethink synchronous wait to obtain the result. + var accessToken = credential.GetAccessTokenForRequestAsync(authUri, CancellationToken.None) + .ConfigureAwait(false).GetAwaiter().GetResult(); + metadata.Add(CreateBearerTokenHeader(accessToken)); + }); + } + + /// + /// Creates OAuth2 interceptor that will use given access token as authorization. + /// + /// OAuth2 access token. + /// The header interceptor. + public static HeaderInterceptor FromAccessToken(string accessToken) + { + Preconditions.CheckNotNull(accessToken); + return new HeaderInterceptor((method, authUri, metadata) => + { + metadata.Add(CreateBearerTokenHeader(accessToken)); + }); + } + + private static Metadata.Entry CreateBearerTokenHeader(string accessToken) + { + return new Metadata.Entry(AuthorizationHeader, Schema + " " + accessToken); + } + } +} diff --git a/src/csharp/Grpc.Auth/Grpc.Auth.csproj b/src/csharp/Grpc.Auth/Grpc.Auth.csproj new file mode 100644 index 00000000..4fb087d4 --- /dev/null +++ b/src/csharp/Grpc.Auth/Grpc.Auth.csproj @@ -0,0 +1,103 @@ + + + + Debug + AnyCPU + {AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA} + Library + Grpc.Auth + Grpc.Auth + v4.5 + bin\$(Configuration)\Grpc.Auth.Xml + 4f8487a9 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + false + + + pdbonly + true + bin\Release + prompt + 4 + false + + + pdbonly + true + bin\ReleaseSigned + prompt + 4 + True + C:\keys\Grpc.snk + + + + + + + + ..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll + + + ..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.dll + + + ..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.PlatformServices.dll + + + ..\packages\Google.Apis.Core.1.9.3\lib\portable-net40+sl50+win+wpa81+wp80\Google.Apis.Core.dll + + + ..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll + + + ..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll + + + ..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll + + + ..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll + + + ..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll + + + ..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll + + + + + Version.cs + + + + + + + + {CCC4440E-49F7-4790-B0AF-FEABB0837AE7} + Grpc.Core + + + + + + + + + + + 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}. + + + + \ No newline at end of file diff --git a/src/csharp/Grpc.Auth/Grpc.Auth.nuspec b/src/csharp/Grpc.Auth/Grpc.Auth.nuspec new file mode 100644 index 00000000..f1f8f7c7 --- /dev/null +++ b/src/csharp/Grpc.Auth/Grpc.Auth.nuspec @@ -0,0 +1,28 @@ + + + + Grpc.Auth + gRPC C# Auth + Auth library for C# implementation of gRPC - an RPC library and framework + Auth library for C# implementation of gRPC - an RPC library and framework. See project site for more info. + $version$ + Google Inc. + grpc-packages + https://github.com/grpc/grpc/blob/master/LICENSE + https://github.com/grpc/grpc + false + Release $version$ of gRPC C# + Copyright 2015, Google Inc. + gRPC RPC Protocol HTTP/2 Auth OAuth2 + + + + + + + + + + + + diff --git a/src/csharp/Grpc.Auth/Properties/AssemblyInfo.cs b/src/csharp/Grpc.Auth/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..83396c54 --- /dev/null +++ b/src/csharp/Grpc.Auth/Properties/AssemblyInfo.cs @@ -0,0 +1,11 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyTitle("Grpc.Auth")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("Google Inc. All rights reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] diff --git a/src/csharp/Grpc.Auth/app.config b/src/csharp/Grpc.Auth/app.config new file mode 100644 index 00000000..84d7534d --- /dev/null +++ b/src/csharp/Grpc.Auth/app.config @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/csharp/Grpc.Auth/packages.config b/src/csharp/Grpc.Auth/packages.config new file mode 100644 index 00000000..7a02c95d --- /dev/null +++ b/src/csharp/Grpc.Auth/packages.config @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/csharp/Grpc.Core.Tests/.gitignore b/src/csharp/Grpc.Core.Tests/.gitignore new file mode 100644 index 00000000..775a9440 --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/.gitignore @@ -0,0 +1,3 @@ +test-results +bin +obj \ No newline at end of file diff --git a/src/csharp/Grpc.Core.Tests/ChannelOptionsTest.cs b/src/csharp/Grpc.Core.Tests/ChannelOptionsTest.cs new file mode 100644 index 00000000..52be77c8 --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/ChannelOptionsTest.cs @@ -0,0 +1,105 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Collections.Generic; +using Grpc.Core; +using Grpc.Core.Internal; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.Core.Internal.Tests +{ + public class ChannelOptionsTest + { + [Test] + public void IntOption() + { + var option = new ChannelOption("somename", 1); + + Assert.AreEqual(ChannelOption.OptionType.Integer, option.Type); + Assert.AreEqual("somename", option.Name); + Assert.AreEqual(1, option.IntValue); + Assert.Throws(typeof(InvalidOperationException), () => { var s = option.StringValue; }); + } + + [Test] + public void StringOption() + { + var option = new ChannelOption("somename", "ABCDEF"); + + Assert.AreEqual(ChannelOption.OptionType.String, option.Type); + Assert.AreEqual("somename", option.Name); + Assert.AreEqual("ABCDEF", option.StringValue); + Assert.Throws(typeof(InvalidOperationException), () => { var s = option.IntValue; }); + } + + [Test] + public void ConstructorPreconditions() + { + Assert.Throws(typeof(ArgumentNullException), () => { new ChannelOption(null, "abc"); }); + Assert.Throws(typeof(ArgumentNullException), () => { new ChannelOption(null, 1); }); + Assert.Throws(typeof(ArgumentNullException), () => { new ChannelOption("abc", null); }); + } + + [Test] + public void CreateChannelArgsNull() + { + var channelArgs = ChannelOptions.CreateChannelArgs(null); + Assert.IsTrue(channelArgs.IsInvalid); + } + + [Test] + public void CreateChannelArgsEmpty() + { + var options = new List(); + var channelArgs = ChannelOptions.CreateChannelArgs(options); + channelArgs.Dispose(); + } + + [Test] + public void CreateChannelArgs() + { + var options = new List + { + new ChannelOption("ABC", "XYZ"), + new ChannelOption("somename", "IJKLM"), + new ChannelOption("intoption", 12345), + new ChannelOption("GHIJK", 12345), + }; + + var channelArgs = ChannelOptions.CreateChannelArgs(options); + channelArgs.Dispose(); + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/ChannelTest.cs b/src/csharp/Grpc.Core.Tests/ChannelTest.cs new file mode 100644 index 00000000..dfbd9287 --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/ChannelTest.cs @@ -0,0 +1,82 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using Grpc.Core; +using Grpc.Core.Internal; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.Core.Tests +{ + public class ChannelTest + { + [Test] + public void Constructor_RejectsInvalidParams() + { + Assert.Throws(typeof(ArgumentNullException), () => new Channel(null, Credentials.Insecure)); + } + + [Test] + public void State_IdleAfterCreation() + { + var channel = new Channel("localhost", Credentials.Insecure); + Assert.AreEqual(ChannelState.Idle, channel.State); + channel.ShutdownAsync().Wait(); + } + + [Test] + public void WaitForStateChangedAsync_InvalidArgument() + { + var channel = new Channel("localhost", Credentials.Insecure); + Assert.Throws(typeof(ArgumentException), () => channel.WaitForStateChangedAsync(ChannelState.FatalFailure)); + channel.ShutdownAsync().Wait(); + } + + [Test] + public void ResolvedTarget() + { + var channel = new Channel("127.0.0.1", Credentials.Insecure); + Assert.IsTrue(channel.ResolvedTarget.Contains("127.0.0.1")); + channel.ShutdownAsync().Wait(); + } + + [Test] + public void Shutdown_AllowedOnlyOnce() + { + var channel = new Channel("localhost", Credentials.Insecure); + channel.ShutdownAsync().Wait(); + Assert.Throws(typeof(InvalidOperationException), () => channel.ShutdownAsync().GetAwaiter().GetResult()); + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/ClientBaseTest.cs b/src/csharp/Grpc.Core.Tests/ClientBaseTest.cs new file mode 100644 index 00000000..2dc10ebe --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/ClientBaseTest.cs @@ -0,0 +1,62 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using Grpc.Core; +using Grpc.Core.Internal; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.Core.Tests +{ + public class ClientBaseTest + { + [Test] + public void GetAuthUriBase_Valid() + { + Assert.AreEqual("https://some.googleapi.com/", ClientBase.GetAuthUriBase("some.googleapi.com")); + Assert.AreEqual("https://some.googleapi.com/", ClientBase.GetAuthUriBase("dns:///some.googleapi.com/")); + Assert.AreEqual("https://some.googleapi.com/", ClientBase.GetAuthUriBase("dns:///some.googleapi.com:443/")); + Assert.AreEqual("https://some.googleapi.com/", ClientBase.GetAuthUriBase("some.googleapi.com:443/")); + } + + [Test] + public void GetAuthUriBase_Invalid() + { + Assert.IsNull(ClientBase.GetAuthUriBase("some.googleapi.com:")); + Assert.IsNull(ClientBase.GetAuthUriBase("https://some.googleapi.com/")); + Assert.IsNull(ClientBase.GetAuthUriBase("dns://some.googleapi.com:443")); // just two slashes + Assert.IsNull(ClientBase.GetAuthUriBase("")); + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs new file mode 100644 index 00000000..68279a20 --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs @@ -0,0 +1,286 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core; +using Grpc.Core.Internal; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.Core.Tests +{ + public class ClientServerTest + { + const string Host = "127.0.0.1"; + + MockServiceHelper helper; + Server server; + Channel channel; + + [SetUp] + public void Init() + { + helper = new MockServiceHelper(Host); + server = helper.GetServer(); + server.Start(); + channel = helper.GetChannel(); + } + + [TearDown] + public void Cleanup() + { + channel.ShutdownAsync().Wait(); + server.ShutdownAsync().Wait(); + } + + [Test] + public async Task UnaryCall() + { + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => + { + return request; + }); + + Assert.AreEqual("ABC", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "ABC")); + + Assert.AreEqual("ABC", await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "ABC")); + } + + [Test] + public void UnaryCall_ServerHandlerThrows() + { + helper.UnaryHandler = new UnaryServerMethod((request, context) => + { + throw new Exception("This was thrown on purpose by a test"); + }); + + var ex = Assert.Throws(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc")); + Assert.AreEqual(StatusCode.Unknown, ex.Status.StatusCode); + + var ex2 = Assert.Throws(async () => await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "abc")); + Assert.AreEqual(StatusCode.Unknown, ex2.Status.StatusCode); + } + + [Test] + public void UnaryCall_ServerHandlerThrowsRpcException() + { + helper.UnaryHandler = new UnaryServerMethod((request, context) => + { + throw new RpcException(new Status(StatusCode.Unauthenticated, "")); + }); + + var ex = Assert.Throws(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc")); + Assert.AreEqual(StatusCode.Unauthenticated, ex.Status.StatusCode); + + var ex2 = Assert.Throws(async () => await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "abc")); + Assert.AreEqual(StatusCode.Unauthenticated, ex2.Status.StatusCode); + } + + [Test] + public void UnaryCall_ServerHandlerSetsStatus() + { + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => + { + context.Status = new Status(StatusCode.Unauthenticated, ""); + return ""; + }); + + var ex = Assert.Throws(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc")); + Assert.AreEqual(StatusCode.Unauthenticated, ex.Status.StatusCode); + + var ex2 = Assert.Throws(async () => await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "abc")); + Assert.AreEqual(StatusCode.Unauthenticated, ex2.Status.StatusCode); + } + + [Test] + public async Task ClientStreamingCall() + { + helper.ClientStreamingHandler = new ClientStreamingServerMethod(async (requestStream, context) => + { + string result = ""; + await requestStream.ForEachAsync(async (request) => + { + result += request; + }); + await Task.Delay(100); + return result; + }); + + var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall()); + await call.RequestStream.WriteAllAsync(new string[] { "A", "B", "C" }); + Assert.AreEqual("ABC", await call.ResponseAsync); + } + + [Test] + public async Task ClientStreamingCall_CancelAfterBegin() + { + var barrier = new TaskCompletionSource(); + + helper.ClientStreamingHandler = new ClientStreamingServerMethod(async (requestStream, context) => + { + barrier.SetResult(null); + await requestStream.ToListAsync(); + return ""; + }); + + var cts = new CancellationTokenSource(); + var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall(new CallOptions(cancellationToken: cts.Token))); + + await barrier.Task; // make sure the handler has started. + cts.Cancel(); + + var ex = Assert.Throws(async () => await call.ResponseAsync); + Assert.AreEqual(StatusCode.Cancelled, ex.Status.StatusCode); + } + + [Test] + public async Task AsyncUnaryCall_EchoMetadata() + { + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => + { + foreach (Metadata.Entry metadataEntry in context.RequestHeaders) + { + if (metadataEntry.Key != "user-agent") + { + context.ResponseTrailers.Add(metadataEntry); + } + } + return ""; + }); + + var headers = new Metadata + { + { "ascii-header", "abcdefg" }, + { "binary-header-bin", new byte[] { 1, 2, 3, 0, 0xff } } + }; + var call = Calls.AsyncUnaryCall(helper.CreateUnaryCall(new CallOptions(headers: headers)), "ABC"); + await call; + + Assert.AreEqual(StatusCode.OK, call.GetStatus().StatusCode); + + var trailers = call.GetTrailers(); + Assert.AreEqual(2, trailers.Count); + Assert.AreEqual(headers[0].Key, trailers[0].Key); + Assert.AreEqual(headers[0].Value, trailers[0].Value); + + Assert.AreEqual(headers[1].Key, trailers[1].Key); + CollectionAssert.AreEqual(headers[1].ValueBytes, trailers[1].ValueBytes); + } + + [Test] + public void UnaryCallPerformance() + { + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => + { + return request; + }); + + var callDetails = helper.CreateUnaryCall(); + BenchmarkUtil.RunBenchmark(100, 100, + () => { Calls.BlockingUnaryCall(callDetails, "ABC"); }); + } + + [Test] + public void UnknownMethodHandler() + { + var nonexistentMethod = new Method( + MethodType.Unary, + MockServiceHelper.ServiceName, + "NonExistentMethod", + Marshallers.StringMarshaller, + Marshallers.StringMarshaller); + + var callDetails = new CallInvocationDetails(channel, nonexistentMethod, new CallOptions()); + + var ex = Assert.Throws(() => Calls.BlockingUnaryCall(callDetails, "abc")); + Assert.AreEqual(StatusCode.Unimplemented, ex.Status.StatusCode); + } + + [Test] + public void UserAgentStringPresent() + { + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => + { + return context.RequestHeaders.Where(entry => entry.Key == "user-agent").Single().Value; + }); + + string userAgent = Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"); + Assert.IsTrue(userAgent.StartsWith("grpc-csharp/")); + } + + [Test] + public void PeerInfoPresent() + { + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => + { + return context.Peer; + }); + + string peer = Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"); + Assert.IsTrue(peer.Contains(Host)); + } + + [Test] + public async Task Channel_WaitForStateChangedAsync() + { + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => + { + return request; + }); + + Assert.Throws(typeof(TaskCanceledException), + async () => await channel.WaitForStateChangedAsync(channel.State, DateTime.UtcNow.AddMilliseconds(10))); + + var stateChangedTask = channel.WaitForStateChangedAsync(channel.State); + + await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "abc"); + + await stateChangedTask; + Assert.AreEqual(ChannelState.Ready, channel.State); + } + + [Test] + public async Task Channel_ConnectAsync() + { + await channel.ConnectAsync(); + Assert.AreEqual(ChannelState.Ready, channel.State); + + await channel.ConnectAsync(DateTime.UtcNow.AddMilliseconds(1000)); + Assert.AreEqual(ChannelState.Ready, channel.State); + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/CompressionTest.cs b/src/csharp/Grpc.Core.Tests/CompressionTest.cs new file mode 100644 index 00000000..378c8185 --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/CompressionTest.cs @@ -0,0 +1,122 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core; +using Grpc.Core.Internal; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.Core.Tests +{ + public class CompressionTest + { + MockServiceHelper helper; + Server server; + Channel channel; + + [SetUp] + public void Init() + { + helper = new MockServiceHelper(); + + server = helper.GetServer(); + server.Start(); + channel = helper.GetChannel(); + } + + [TearDown] + public void Cleanup() + { + channel.ShutdownAsync().Wait(); + server.ShutdownAsync().Wait(); + } + + [Test] + public void WriteOptions_Unary() + { + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => + { + context.WriteOptions = new WriteOptions(WriteFlags.NoCompress); + return request; + }); + + var callOptions = new CallOptions(writeOptions: new WriteOptions(WriteFlags.NoCompress)); + Calls.BlockingUnaryCall(helper.CreateUnaryCall(callOptions), "abc"); + } + + [Test] + public async Task WriteOptions_DuplexStreaming() + { + helper.DuplexStreamingHandler = new DuplexStreamingServerMethod(async (requestStream, responseStream, context) => + { + await requestStream.ToListAsync(); + + context.WriteOptions = new WriteOptions(WriteFlags.NoCompress); + + await context.WriteResponseHeadersAsync(new Metadata { { "ascii-header", "abcdefg" } }); + + await responseStream.WriteAsync("X"); + + responseStream.WriteOptions = null; + await responseStream.WriteAsync("Y"); + + responseStream.WriteOptions = new WriteOptions(WriteFlags.NoCompress); + await responseStream.WriteAsync("Z"); + }); + + var callOptions = new CallOptions(writeOptions: new WriteOptions(WriteFlags.NoCompress)); + var call = Calls.AsyncDuplexStreamingCall(helper.CreateDuplexStreamingCall(callOptions)); + + // check that write options from call options are propagated to request stream. + Assert.IsTrue((call.RequestStream.WriteOptions.Flags & WriteFlags.NoCompress) != 0); + + call.RequestStream.WriteOptions = new WriteOptions(); + await call.RequestStream.WriteAsync("A"); + + call.RequestStream.WriteOptions = null; + await call.RequestStream.WriteAsync("B"); + + call.RequestStream.WriteOptions = new WriteOptions(WriteFlags.NoCompress); + await call.RequestStream.WriteAsync("C"); + + await call.RequestStream.CompleteAsync(); + + await call.ResponseStream.ToListAsync(); + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/ContextPropagationTest.cs b/src/csharp/Grpc.Core.Tests/ContextPropagationTest.cs new file mode 100644 index 00000000..2db3f286 --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/ContextPropagationTest.cs @@ -0,0 +1,147 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core; +using Grpc.Core.Internal; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.Core.Tests +{ + public class ContextPropagationTest + { + MockServiceHelper helper; + Server server; + Channel channel; + + [SetUp] + public void Init() + { + helper = new MockServiceHelper(); + + server = helper.GetServer(); + server.Start(); + channel = helper.GetChannel(); + } + + [TearDown] + public void Cleanup() + { + channel.ShutdownAsync().Wait(); + server.ShutdownAsync().Wait(); + } + + [Test] + public async Task PropagateCancellation() + { + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => + { + // check that we didn't obtain the default cancellation token. + Assert.IsTrue(context.CancellationToken.CanBeCanceled); + return "PASS"; + }); + + helper.ClientStreamingHandler = new ClientStreamingServerMethod(async (requestStream, context) => + { + var propagationToken = context.CreatePropagationToken(); + Assert.IsNotNull(propagationToken.ParentCall); + + var callOptions = new CallOptions(propagationToken: propagationToken); + return await Calls.AsyncUnaryCall(helper.CreateUnaryCall(callOptions), "xyz"); + }); + + var cts = new CancellationTokenSource(); + var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall(new CallOptions(cancellationToken: cts.Token))); + await call.RequestStream.CompleteAsync(); + Assert.AreEqual("PASS", await call); + } + + [Test] + public async Task PropagateDeadline() + { + var deadline = DateTime.UtcNow.AddDays(7); + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => + { + Assert.IsTrue(context.Deadline < deadline.AddMinutes(1)); + Assert.IsTrue(context.Deadline > deadline.AddMinutes(-1)); + return "PASS"; + }); + + helper.ClientStreamingHandler = new ClientStreamingServerMethod(async (requestStream, context) => + { + Assert.Throws(typeof(ArgumentException), () => + { + // Trying to override deadline while propagating deadline from parent call will throw. + Calls.BlockingUnaryCall(helper.CreateUnaryCall( + new CallOptions(deadline: DateTime.UtcNow.AddDays(8), + propagationToken: context.CreatePropagationToken())), ""); + }); + + var callOptions = new CallOptions(propagationToken: context.CreatePropagationToken()); + return await Calls.AsyncUnaryCall(helper.CreateUnaryCall(callOptions), "xyz"); + }); + + var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall(new CallOptions(deadline: deadline))); + await call.RequestStream.CompleteAsync(); + Assert.AreEqual("PASS", await call); + } + + [Test] + public async Task SuppressDeadlinePropagation() + { + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => + { + Assert.AreEqual(DateTime.MaxValue, context.Deadline); + return "PASS"; + }); + + helper.ClientStreamingHandler = new ClientStreamingServerMethod(async (requestStream, context) => + { + Assert.IsTrue(context.CancellationToken.CanBeCanceled); + + var callOptions = new CallOptions(propagationToken: context.CreatePropagationToken(new ContextPropagationOptions(propagateDeadline: false))); + return await Calls.AsyncUnaryCall(helper.CreateUnaryCall(callOptions), "xyz"); + }); + + var cts = new CancellationTokenSource(); + var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall(new CallOptions(deadline: DateTime.UtcNow.AddDays(7)))); + await call.RequestStream.CompleteAsync(); + Assert.AreEqual("PASS", await call); + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj new file mode 100644 index 00000000..f7309360 --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj @@ -0,0 +1,106 @@ + + + + Debug + AnyCPU + {86EC5CB4-4EA2-40A2-8057-86542A0353BB} + Library + Grpc.Core.Tests + Grpc.Core.Tests + v4.5 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + + + pdbonly + true + bin\Release + prompt + 4 + + + pdbonly + true + bin\ReleaseSigned + prompt + 4 + True + C:\keys\Grpc.snk + + + + ..\packages\NUnitTestAdapter.2.0.0\lib\nunit.core.dll + False + + + ..\packages\NUnitTestAdapter.2.0.0\lib\nunit.core.interfaces.dll + False + + + ..\packages\NUnit.2.6.4\lib\nunit.framework.dll + + + ..\packages\NUnitTestAdapter.2.0.0\lib\nunit.util.dll + False + + + ..\packages\NUnitTestAdapter.2.0.0\lib\NUnit.VisualStudio.TestAdapter.dll + False + + + + ..\packages\Ix-Async.1.2.3\lib\net45\System.Interactive.Async.dll + + + + + Version.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {CCC4440E-49F7-4790-B0AF-FEABB0837AE7} + Grpc.Core + + + + + Designer + + + + + + + \ No newline at end of file diff --git a/src/csharp/Grpc.Core.Tests/GrpcEnvironmentTest.cs b/src/csharp/Grpc.Core.Tests/GrpcEnvironmentTest.cs new file mode 100644 index 00000000..78295cf6 --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/GrpcEnvironmentTest.cs @@ -0,0 +1,90 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Threading; +using Grpc.Core; +using NUnit.Framework; + +namespace Grpc.Core.Tests +{ + public class GrpcEnvironmentTest + { + [Test] + public void InitializeAndShutdownGrpcEnvironment() + { + var env = GrpcEnvironment.AddRef(); + Assert.IsNotNull(env.CompletionQueue); + GrpcEnvironment.Release(); + } + + [Test] + public void SubsequentInvocations() + { + var env1 = GrpcEnvironment.AddRef(); + var env2 = GrpcEnvironment.AddRef(); + Assert.AreSame(env1, env2); + GrpcEnvironment.Release(); + GrpcEnvironment.Release(); + } + + [Test] + public void InitializeAfterShutdown() + { + Assert.AreEqual(0, GrpcEnvironment.GetRefCount()); + + var env1 = GrpcEnvironment.AddRef(); + GrpcEnvironment.Release(); + + var env2 = GrpcEnvironment.AddRef(); + GrpcEnvironment.Release(); + + Assert.AreNotSame(env1, env2); + } + + [Test] + public void ReleaseWithoutAddRef() + { + Assert.AreEqual(0, GrpcEnvironment.GetRefCount()); + Assert.Throws(typeof(InvalidOperationException), () => GrpcEnvironment.Release()); + } + + [Test] + public void GetCoreVersionString() + { + var coreVersion = GrpcEnvironment.GetCoreVersionString(); + var parts = coreVersion.Split('.'); + Assert.AreEqual(4, parts.Length); + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/Internal/AsyncCallTest.cs b/src/csharp/Grpc.Core.Tests/Internal/AsyncCallTest.cs new file mode 100644 index 00000000..685c5f7d --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/Internal/AsyncCallTest.cs @@ -0,0 +1,222 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +using Grpc.Core.Internal; +using NUnit.Framework; + +namespace Grpc.Core.Internal.Tests +{ + public class AsyncCallTest + { + Channel channel; + FakeNativeCall fakeCall; + AsyncCall asyncCall; + + [SetUp] + public void Init() + { + channel = new Channel("localhost", Credentials.Insecure); + + fakeCall = new FakeNativeCall(); + + var callDetails = new CallInvocationDetails(channel, "someMethod", null, Marshallers.StringMarshaller, Marshallers.StringMarshaller, new CallOptions()); + asyncCall = new AsyncCall(callDetails, fakeCall); + } + + [TearDown] + public void Cleanup() + { + channel.ShutdownAsync().Wait(); + } + + [Test] + public void AsyncUnary_CompletionSuccess() + { + var resultTask = asyncCall.UnaryCallAsync("abc"); + fakeCall.UnaryResponseClientHandler(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata()), new byte[] { 1, 2, 3 }, new Metadata()); + Assert.IsTrue(resultTask.IsCompleted); + Assert.IsTrue(fakeCall.IsDisposed); + Assert.AreEqual(Status.DefaultSuccess, asyncCall.GetStatus()); + } + + [Test] + public void AsyncUnary_CompletionFailure() + { + var resultTask = asyncCall.UnaryCallAsync("abc"); + fakeCall.UnaryResponseClientHandler(false, new ClientSideStatus(new Status(StatusCode.Internal, ""), null), new byte[] { 1, 2, 3 }, new Metadata()); + + Assert.IsTrue(resultTask.IsCompleted); + Assert.IsTrue(fakeCall.IsDisposed); + + Assert.AreEqual(StatusCode.Internal, asyncCall.GetStatus().StatusCode); + Assert.IsNull(asyncCall.GetTrailers()); + var ex = Assert.Throws(() => resultTask.GetAwaiter().GetResult()); + Assert.AreEqual(StatusCode.Internal, ex.Status.StatusCode); + } + + internal class FakeNativeCall : INativeCall + { + public UnaryResponseClientHandler UnaryResponseClientHandler + { + get; + set; + } + + public ReceivedStatusOnClientHandler ReceivedStatusOnClientHandler + { + get; + set; + } + + public ReceivedMessageHandler ReceivedMessageHandler + { + get; + set; + } + + public ReceivedResponseHeadersHandler ReceivedResponseHeadersHandler + { + get; + set; + } + + public SendCompletionHandler SendCompletionHandler + { + get; + set; + } + + public ReceivedCloseOnServerHandler ReceivedCloseOnServerHandler + { + get; + set; + } + + public bool IsCancelled + { + get; + set; + } + + public bool IsDisposed + { + get; + set; + } + + public void Cancel() + { + IsCancelled = true; + } + + public void CancelWithStatus(Status status) + { + IsCancelled = true; + } + + public string GetPeer() + { + return "PEER"; + } + + public void StartUnary(UnaryResponseClientHandler callback, byte[] payload, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags) + { + UnaryResponseClientHandler = callback; + } + + public void StartUnary(BatchContextSafeHandle ctx, byte[] payload, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags) + { + throw new NotImplementedException(); + } + + public void StartClientStreaming(UnaryResponseClientHandler callback, MetadataArraySafeHandle metadataArray) + { + UnaryResponseClientHandler = callback; + } + + public void StartServerStreaming(ReceivedStatusOnClientHandler callback, byte[] payload, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags) + { + ReceivedStatusOnClientHandler = callback; + } + + public void StartDuplexStreaming(ReceivedStatusOnClientHandler callback, MetadataArraySafeHandle metadataArray) + { + ReceivedStatusOnClientHandler = callback; + } + + public void StartReceiveMessage(ReceivedMessageHandler callback) + { + ReceivedMessageHandler = callback; + } + + public void StartReceiveInitialMetadata(ReceivedResponseHeadersHandler callback) + { + ReceivedResponseHeadersHandler = callback; + } + + public void StartSendInitialMetadata(SendCompletionHandler callback, MetadataArraySafeHandle metadataArray) + { + SendCompletionHandler = callback; + } + + public void StartSendMessage(SendCompletionHandler callback, byte[] payload, WriteFlags writeFlags, bool sendEmptyInitialMetadata) + { + SendCompletionHandler = callback; + } + + public void StartSendCloseFromClient(SendCompletionHandler callback) + { + SendCompletionHandler = callback; + } + + public void StartSendStatusFromServer(SendCompletionHandler callback, Status status, MetadataArraySafeHandle metadataArray, bool sendEmptyInitialMetadata) + { + SendCompletionHandler = callback; + } + + public void StartServerSide(ReceivedCloseOnServerHandler callback) + { + ReceivedCloseOnServerHandler = callback; + } + + public void Dispose() + { + IsDisposed = true; + } + } + } +} \ No newline at end of file diff --git a/src/csharp/Grpc.Core.Tests/Internal/ChannelArgsSafeHandleTest.cs b/src/csharp/Grpc.Core.Tests/Internal/ChannelArgsSafeHandleTest.cs new file mode 100644 index 00000000..af0aaa5f --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/Internal/ChannelArgsSafeHandleTest.cs @@ -0,0 +1,75 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using Grpc.Core; +using Grpc.Core.Internal; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.Core.Internal.Tests +{ + public class ChannelArgsSafeHandleTest + { + [Test] + public void CreateEmptyAndDestroy() + { + var channelArgs = ChannelArgsSafeHandle.Create(0); + channelArgs.Dispose(); + } + + [Test] + public void CreateNonEmptyAndDestroy() + { + var channelArgs = ChannelArgsSafeHandle.Create(5); + channelArgs.Dispose(); + } + + [Test] + public void CreateNullAndDestroy() + { + var channelArgs = ChannelArgsSafeHandle.CreateNull(); + channelArgs.Dispose(); + } + + [Test] + public void CreateFillAndDestroy() + { + var channelArgs = ChannelArgsSafeHandle.Create(3); + channelArgs.SetInteger(0, "somekey", 12345); + channelArgs.SetString(1, "somekey", "abcdefghijkl"); + channelArgs.SetString(2, "somekey", "XYZ"); + channelArgs.Dispose(); + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/Internal/CompletionQueueEventTest.cs b/src/csharp/Grpc.Core.Tests/Internal/CompletionQueueEventTest.cs new file mode 100644 index 00000000..188c6406 --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/Internal/CompletionQueueEventTest.cs @@ -0,0 +1,52 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using Grpc.Core; +using Grpc.Core.Internal; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.Core.Internal.Tests +{ + public class CompletionQueueEventTest + { + [Test] + public void CreateAndDestroy() + { + Assert.AreEqual(CompletionQueueEvent.NativeSize, Marshal.SizeOf(typeof(CompletionQueueEvent))); + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/Internal/CompletionQueueSafeHandleTest.cs b/src/csharp/Grpc.Core.Tests/Internal/CompletionQueueSafeHandleTest.cs new file mode 100644 index 00000000..a2ee1832 --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/Internal/CompletionQueueSafeHandleTest.cs @@ -0,0 +1,64 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Threading.Tasks; +using Grpc.Core; +using Grpc.Core.Internal; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.Core.Internal.Tests +{ + public class CompletionQueueSafeHandleTest + { + [Test] + public void CreateAndDestroy() + { + var cq = CompletionQueueSafeHandle.Create(); + cq.Dispose(); + } + + [Test] + public void CreateAndShutdown() + { + var cq = CompletionQueueSafeHandle.Create(); + cq.Shutdown(); + var ev = cq.Next(); + cq.Dispose(); + Assert.AreEqual(GRPCCompletionType.Shutdown, ev.type); + Assert.AreNotEqual(IntPtr.Zero, ev.success); + Assert.AreEqual(IntPtr.Zero, ev.tag); + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/Internal/MetadataArraySafeHandleTest.cs b/src/csharp/Grpc.Core.Tests/Internal/MetadataArraySafeHandleTest.cs new file mode 100644 index 00000000..33534fdd --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/Internal/MetadataArraySafeHandleTest.cs @@ -0,0 +1,84 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using Grpc.Core; +using Grpc.Core.Internal; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.Core.Internal.Tests +{ + public class MetadataArraySafeHandleTest + { + [Test] + public void CreateEmptyAndDestroy() + { + var nativeMetadata = MetadataArraySafeHandle.Create(new Metadata()); + nativeMetadata.Dispose(); + } + + [Test] + public void CreateAndDestroy() + { + var metadata = new Metadata + { + { "host", "somehost" }, + { "header2", "header value" }, + }; + var nativeMetadata = MetadataArraySafeHandle.Create(metadata); + nativeMetadata.Dispose(); + } + + [Test] + public void ReadMetadataFromPtrUnsafe() + { + var metadata = new Metadata + { + { "host", "somehost" }, + { "header2", "header value" } + }; + var nativeMetadata = MetadataArraySafeHandle.Create(metadata); + + var copy = MetadataArraySafeHandle.ReadMetadataFromPtrUnsafe(nativeMetadata.Handle); + Assert.AreEqual(2, copy.Count); + + Assert.AreEqual("host", copy[0].Key); + Assert.AreEqual("somehost", copy[0].Value); + Assert.AreEqual("header2", copy[1].Key); + Assert.AreEqual("header value", copy[1].Value); + + nativeMetadata.Dispose(); + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/Internal/TimespecTest.cs b/src/csharp/Grpc.Core.Tests/Internal/TimespecTest.cs new file mode 100644 index 00000000..874df02b --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/Internal/TimespecTest.cs @@ -0,0 +1,202 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Runtime.InteropServices; +using Grpc.Core.Internal; +using NUnit.Framework; + +namespace Grpc.Core.Internal.Tests +{ + public class TimespecTest + { + [Test] + public void Now_IsInUtc() + { + Assert.AreEqual(DateTimeKind.Utc, Timespec.Now.ToDateTime().Kind); + } + + [Test] + public void Now_AgreesWithUtcNow() + { + var timespec = Timespec.Now; + var utcNow = DateTime.UtcNow; + + TimeSpan difference = utcNow - timespec.ToDateTime(); + + // This test is inherently a race - but the two timestamps + // should really be way less that a minute apart. + Assert.IsTrue(difference.TotalSeconds < 60); + } + + [Test] + public void InfFuture() + { + var timespec = Timespec.InfFuture; + } + + [Test] + public void InfPast() + { + var timespec = Timespec.InfPast; + } + + [Test] + public void TimespecSizeIsNativeSize() + { + Assert.AreEqual(Timespec.NativeSize, Marshal.SizeOf(typeof(Timespec))); + } + + [Test] + public void ToDateTime() + { + Assert.AreEqual(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc), + new Timespec(IntPtr.Zero, 0).ToDateTime()); + + Assert.AreEqual(new DateTime(1970, 1, 1, 0, 0, 10, DateTimeKind.Utc).AddTicks(50), + new Timespec(new IntPtr(10), 5000).ToDateTime()); + + Assert.AreEqual(new DateTime(2015, 7, 21, 4, 21, 48, DateTimeKind.Utc), + new Timespec(new IntPtr(1437452508), 0).ToDateTime()); + + // before epoch + Assert.AreEqual(new DateTime(1969, 12, 31, 23, 59, 55, DateTimeKind.Utc).AddTicks(10), + new Timespec(new IntPtr(-5), 1000).ToDateTime()); + + // infinity + Assert.AreEqual(DateTime.MaxValue, Timespec.InfFuture.ToDateTime()); + Assert.AreEqual(DateTime.MinValue, Timespec.InfPast.ToDateTime()); + + // nanos are rounded to ticks are rounded up + Assert.AreEqual(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddTicks(1), + new Timespec(IntPtr.Zero, 99).ToDateTime()); + + // Illegal inputs + Assert.Throws(typeof(InvalidOperationException), + () => new Timespec(new IntPtr(0), -2).ToDateTime()); + Assert.Throws(typeof(InvalidOperationException), + () => new Timespec(new IntPtr(0), 1000 * 1000 * 1000).ToDateTime()); + Assert.Throws(typeof(InvalidOperationException), + () => new Timespec(new IntPtr(0), 0, GPRClockType.Monotonic).ToDateTime()); + } + + [Test] + public void ToDateTime_ReturnsUtc() + { + Assert.AreEqual(DateTimeKind.Utc, new Timespec(new IntPtr(1437452508), 0).ToDateTime().Kind); + Assert.AreNotEqual(DateTimeKind.Unspecified, new Timespec(new IntPtr(1437452508), 0).ToDateTime().Kind); + } + + [Test] + public void ToDateTime_Overflow() + { + // we can only get overflow in ticks arithmetic on 64-bit + if (IntPtr.Size == 8) + { + var timespec = new Timespec(new IntPtr(long.MaxValue - 100), 0); + Assert.AreNotEqual(Timespec.InfFuture, timespec); + Assert.AreEqual(DateTime.MaxValue, timespec.ToDateTime()); + + Assert.AreEqual(DateTime.MinValue, new Timespec(new IntPtr(long.MinValue + 100), 0).ToDateTime()); + } + else + { + Console.WriteLine("Test cannot be run on this platform, skipping the test."); + } + } + + [Test] + public void ToDateTime_OutOfDateTimeRange() + { + // we can only get out of range on 64-bit, on 32 bit the max + // timestamp is ~ Jan 19 2038, which is far within range of DateTime + // same case for min value. + if (IntPtr.Size == 8) + { + // DateTime range goes up to year 9999, 20000 years from now should + // be out of range. + long seconds = 20000L * 365L * 24L * 3600L; + + var timespec = new Timespec(new IntPtr(seconds), 0); + Assert.AreNotEqual(Timespec.InfFuture, timespec); + Assert.AreEqual(DateTime.MaxValue, timespec.ToDateTime()); + + Assert.AreEqual(DateTime.MinValue, new Timespec(new IntPtr(-seconds), 0).ToDateTime()); + } + else + { + Console.WriteLine("Test cannot be run on this platform, skipping the test"); + } + } + + [Test] + public void FromDateTime() + { + Assert.AreEqual(new Timespec(IntPtr.Zero, 0), + Timespec.FromDateTime(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc))); + + Assert.AreEqual(new Timespec(new IntPtr(10), 5000), + Timespec.FromDateTime(new DateTime(1970, 1, 1, 0, 0, 10, DateTimeKind.Utc).AddTicks(50))); + + Assert.AreEqual(new Timespec(new IntPtr(1437452508), 0), + Timespec.FromDateTime(new DateTime(2015, 7, 21, 4, 21, 48, DateTimeKind.Utc))); + + // before epoch + Assert.AreEqual(new Timespec(new IntPtr(-5), 1000), + Timespec.FromDateTime(new DateTime(1969, 12, 31, 23, 59, 55, DateTimeKind.Utc).AddTicks(10))); + + // infinity + Assert.AreEqual(Timespec.InfFuture, Timespec.FromDateTime(DateTime.MaxValue)); + Assert.AreEqual(Timespec.InfPast, Timespec.FromDateTime(DateTime.MinValue)); + + // illegal inputs + Assert.Throws(typeof(ArgumentException), + () => Timespec.FromDateTime(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Unspecified))); + } + + [Test] + public void FromDateTime_OutOfTimespecRange() + { + // we can only get overflow in Timespec on 32-bit + if (IntPtr.Size == 4) + { + Assert.AreEqual(Timespec.InfFuture, Timespec.FromDateTime(new DateTime(2040, 1, 1, 0, 0, 0, DateTimeKind.Utc))); + Assert.AreEqual(Timespec.InfPast, Timespec.FromDateTime(new DateTime(1800, 1, 1, 0, 0, 0, DateTimeKind.Utc))); + } + else + { + Console.WriteLine("Test cannot be run on this platform, skipping the test."); + } + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/MarshallingErrorsTest.cs b/src/csharp/Grpc.Core.Tests/MarshallingErrorsTest.cs new file mode 100644 index 00000000..83707e0c --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/MarshallingErrorsTest.cs @@ -0,0 +1,176 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +using Grpc.Core; +using Grpc.Core.Internal; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.Core.Tests +{ + public class MarshallingErrorsTest + { + const string Host = "127.0.0.1"; + + MockServiceHelper helper; + Server server; + Channel channel; + + [SetUp] + public void Init() + { + var marshaller = new Marshaller( + (str) => + { + if (str == "UNSERIALIZABLE_VALUE") + { + // Google.Protobuf throws exception inherited from IOException + throw new IOException("Error serializing the message."); + } + return System.Text.Encoding.UTF8.GetBytes(str); + }, + (payload) => + { + var s = System.Text.Encoding.UTF8.GetString(payload); + if (s == "UNPARSEABLE_VALUE") + { + // Google.Protobuf throws exception inherited from IOException + throw new IOException("Error parsing the message."); + } + return s; + }); + helper = new MockServiceHelper(Host, marshaller); + server = helper.GetServer(); + server.Start(); + channel = helper.GetChannel(); + } + + [TearDown] + public void Cleanup() + { + channel.ShutdownAsync().Wait(); + server.ShutdownAsync().Wait(); + } + + [Test] + public void ResponseParsingError_UnaryResponse() + { + helper.UnaryHandler = new UnaryServerMethod((request, context) => + { + return Task.FromResult("UNPARSEABLE_VALUE"); + }); + + var ex = Assert.Throws(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "REQUEST")); + Assert.AreEqual(StatusCode.Internal, ex.Status.StatusCode); + } + + [Test] + public void ResponseParsingError_StreamingResponse() + { + helper.ServerStreamingHandler = new ServerStreamingServerMethod(async (request, responseStream, context) => + { + await responseStream.WriteAsync("UNPARSEABLE_VALUE"); + await Task.Delay(10000); + }); + + var call = Calls.AsyncServerStreamingCall(helper.CreateServerStreamingCall(), "REQUEST"); + var ex = Assert.Throws(async () => await call.ResponseStream.MoveNext()); + Assert.AreEqual(StatusCode.Internal, ex.Status.StatusCode); + } + + [Test] + public void RequestParsingError_UnaryRequest() + { + helper.UnaryHandler = new UnaryServerMethod((request, context) => + { + return Task.FromResult("RESPONSE"); + }); + + var ex = Assert.Throws(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "UNPARSEABLE_VALUE")); + // Spec doesn't define the behavior. With the current implementation server handler throws exception which results in StatusCode.Unknown. + Assert.AreEqual(StatusCode.Unknown, ex.Status.StatusCode); + } + + [Test] + public async Task RequestParsingError_StreamingRequest() + { + helper.ClientStreamingHandler = new ClientStreamingServerMethod(async (requestStream, context) => + { + Assert.Throws(async () => await requestStream.MoveNext()); + return "RESPONSE"; + }); + + var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall()); + await call.RequestStream.WriteAsync("UNPARSEABLE_VALUE"); + + Assert.AreEqual("RESPONSE", await call); + } + + [Test] + public void RequestSerializationError_BlockingUnary() + { + Assert.Throws(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "UNSERIALIZABLE_VALUE")); + } + + [Test] + public void RequestSerializationError_AsyncUnary() + { + Assert.Throws(async () => await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "UNSERIALIZABLE_VALUE")); + } + + [Test] + public async Task RequestSerializationError_ClientStreaming() + { + helper.ClientStreamingHandler = new ClientStreamingServerMethod(async (requestStream, context) => + { + CollectionAssert.AreEqual(new [] {"A", "B"}, await requestStream.ToListAsync()); + return "RESPONSE"; + }); + var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall()); + await call.RequestStream.WriteAsync("A"); + Assert.Throws(async () => await call.RequestStream.WriteAsync("UNSERIALIZABLE_VALUE")); + await call.RequestStream.WriteAsync("B"); + await call.RequestStream.CompleteAsync(); + + Assert.AreEqual("RESPONSE", await call); + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/MetadataTest.cs b/src/csharp/Grpc.Core.Tests/MetadataTest.cs new file mode 100644 index 00000000..ddeb7d09 --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/MetadataTest.cs @@ -0,0 +1,131 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core; +using Grpc.Core.Internal; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.Core.Tests +{ + public class MetadataTest + { + [Test] + public void AsciiEntry() + { + var entry = new Metadata.Entry("ABC", "XYZ"); + Assert.IsFalse(entry.IsBinary); + Assert.AreEqual("abc", entry.Key); // key is in lowercase. + Assert.AreEqual("XYZ", entry.Value); + CollectionAssert.AreEqual(new[] { (byte)'X', (byte)'Y', (byte)'Z' }, entry.ValueBytes); + + Assert.Throws(typeof(ArgumentException), () => new Metadata.Entry("abc-bin", "xyz")); + + Assert.AreEqual("[Entry: key=abc, value=XYZ]", entry.ToString()); + } + + [Test] + public void BinaryEntry() + { + var bytes = new byte[] { 1, 2, 3 }; + var entry = new Metadata.Entry("ABC-BIN", bytes); + Assert.IsTrue(entry.IsBinary); + Assert.AreEqual("abc-bin", entry.Key); // key is in lowercase. + Assert.Throws(typeof(InvalidOperationException), () => { var v = entry.Value; }); + CollectionAssert.AreEqual(bytes, entry.ValueBytes); + + Assert.Throws(typeof(ArgumentException), () => new Metadata.Entry("abc", bytes)); + + Assert.AreEqual("[Entry: key=abc-bin, valueBytes=System.Byte[]]", entry.ToString()); + } + + [Test] + public void AsciiEntry_KeyValidity() + { + new Metadata.Entry("ABC", "XYZ"); + new Metadata.Entry("0123456789abc", "XYZ"); + new Metadata.Entry("-abc", "XYZ"); + new Metadata.Entry("a_bc_", "XYZ"); + Assert.Throws(typeof(ArgumentException), () => new Metadata.Entry("abc[", "xyz")); + Assert.Throws(typeof(ArgumentException), () => new Metadata.Entry("abc/", "xyz")); + } + + [Test] + public void Entry_ConstructionPreconditions() + { + Assert.Throws(typeof(ArgumentNullException), () => new Metadata.Entry(null, "xyz")); + Assert.Throws(typeof(ArgumentNullException), () => new Metadata.Entry("abc", (string)null)); + Assert.Throws(typeof(ArgumentNullException), () => new Metadata.Entry("abc-bin", (byte[])null)); + } + + [Test] + public void Entry_Immutable() + { + var origBytes = new byte[] { 1, 2, 3 }; + var bytes = new byte[] { 1, 2, 3 }; + var entry = new Metadata.Entry("ABC-BIN", bytes); + bytes[0] = 255; // changing the array passed to constructor should have any effect. + CollectionAssert.AreEqual(origBytes, entry.ValueBytes); + + entry.ValueBytes[0] = 255; + CollectionAssert.AreEqual(origBytes, entry.ValueBytes); + } + + [Test] + public void Entry_CreateUnsafe_Ascii() + { + var bytes = new byte[] { (byte)'X', (byte)'y' }; + var entry = Metadata.Entry.CreateUnsafe("abc", bytes); + Assert.IsFalse(entry.IsBinary); + Assert.AreEqual("abc", entry.Key); + Assert.AreEqual("Xy", entry.Value); + CollectionAssert.AreEqual(bytes, entry.ValueBytes); + } + + [Test] + public void Entry_CreateUnsafe_Binary() + { + var bytes = new byte[] { 1, 2, 3 }; + var entry = Metadata.Entry.CreateUnsafe("abc-bin", bytes); + Assert.IsTrue(entry.IsBinary); + Assert.AreEqual("abc-bin", entry.Key); + Assert.Throws(typeof(InvalidOperationException), () => { var v = entry.Value; }); + CollectionAssert.AreEqual(bytes, entry.ValueBytes); + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs b/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs new file mode 100644 index 00000000..765732c7 --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs @@ -0,0 +1,250 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core; +using Grpc.Core.Internal; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.Core.Tests +{ + /// + /// Allows setting up a mock service in the client-server tests easily. + /// + public class MockServiceHelper + { + public const string ServiceName = "tests.Test"; + + readonly string host; + readonly ServerServiceDefinition serviceDefinition; + + readonly Method unaryMethod; + readonly Method clientStreamingMethod; + readonly Method serverStreamingMethod; + readonly Method duplexStreamingMethod; + + UnaryServerMethod unaryHandler; + ClientStreamingServerMethod clientStreamingHandler; + ServerStreamingServerMethod serverStreamingHandler; + DuplexStreamingServerMethod duplexStreamingHandler; + + Server server; + Channel channel; + + public MockServiceHelper(string host = null, Marshaller marshaller = null) + { + this.host = host ?? "localhost"; + marshaller = marshaller ?? Marshallers.StringMarshaller; + + unaryMethod = new Method( + MethodType.Unary, + ServiceName, + "Unary", + marshaller, + marshaller); + + clientStreamingMethod = new Method( + MethodType.ClientStreaming, + ServiceName, + "ClientStreaming", + marshaller, + marshaller); + + serverStreamingMethod = new Method( + MethodType.ServerStreaming, + ServiceName, + "ServerStreaming", + marshaller, + marshaller); + + duplexStreamingMethod = new Method( + MethodType.DuplexStreaming, + ServiceName, + "DuplexStreaming", + marshaller, + marshaller); + + serviceDefinition = ServerServiceDefinition.CreateBuilder(ServiceName) + .AddMethod(unaryMethod, (request, context) => unaryHandler(request, context)) + .AddMethod(clientStreamingMethod, (requestStream, context) => clientStreamingHandler(requestStream, context)) + .AddMethod(serverStreamingMethod, (request, responseStream, context) => serverStreamingHandler(request, responseStream, context)) + .AddMethod(duplexStreamingMethod, (requestStream, responseStream, context) => duplexStreamingHandler(requestStream, responseStream, context)) + .Build(); + + var defaultStatus = new Status(StatusCode.Unknown, "Default mock implementation. Please provide your own."); + + unaryHandler = new UnaryServerMethod(async (request, context) => + { + context.Status = defaultStatus; + return ""; + }); + + clientStreamingHandler = new ClientStreamingServerMethod(async (requestStream, context) => + { + context.Status = defaultStatus; + return ""; + }); + + serverStreamingHandler = new ServerStreamingServerMethod(async (request, responseStream, context) => + { + context.Status = defaultStatus; + }); + + duplexStreamingHandler = new DuplexStreamingServerMethod(async (requestStream, responseStream, context) => + { + context.Status = defaultStatus; + }); + } + + /// + /// Returns the default server for this service and creates one if not yet created. + /// + public Server GetServer() + { + if (server == null) + { + server = new Server + { + Services = { serviceDefinition }, + Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } } + }; + } + return server; + } + + /// + /// Returns the default channel for this service and creates one if not yet created. + /// + public Channel GetChannel() + { + if (channel == null) + { + channel = new Channel(Host, GetServer().Ports.Single().BoundPort, Credentials.Insecure); + } + return channel; + } + + public CallInvocationDetails CreateUnaryCall(CallOptions options = default(CallOptions)) + { + return new CallInvocationDetails(channel, unaryMethod, options); + } + + public CallInvocationDetails CreateClientStreamingCall(CallOptions options = default(CallOptions)) + { + return new CallInvocationDetails(channel, clientStreamingMethod, options); + } + + public CallInvocationDetails CreateServerStreamingCall(CallOptions options = default(CallOptions)) + { + return new CallInvocationDetails(channel, serverStreamingMethod, options); + } + + public CallInvocationDetails CreateDuplexStreamingCall(CallOptions options = default(CallOptions)) + { + return new CallInvocationDetails(channel, duplexStreamingMethod, options); + } + + public string Host + { + get + { + return this.host; + } + } + + public ServerServiceDefinition ServiceDefinition + { + get + { + return this.serviceDefinition; + } + } + + public UnaryServerMethod UnaryHandler + { + get + { + return this.unaryHandler; + } + + set + { + unaryHandler = value; + } + } + + public ClientStreamingServerMethod ClientStreamingHandler + { + get + { + return this.clientStreamingHandler; + } + + set + { + clientStreamingHandler = value; + } + } + + public ServerStreamingServerMethod ServerStreamingHandler + { + get + { + return this.serverStreamingHandler; + } + + set + { + serverStreamingHandler = value; + } + } + + public DuplexStreamingServerMethod DuplexStreamingHandler + { + get + { + return this.duplexStreamingHandler; + } + + set + { + duplexStreamingHandler = value; + } + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/NUnitVersionTest.cs b/src/csharp/Grpc.Core.Tests/NUnitVersionTest.cs new file mode 100644 index 00000000..3fa6ad09 --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/NUnitVersionTest.cs @@ -0,0 +1,77 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core; +using Grpc.Core.Internal; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.Core.Tests +{ + /// + /// Tests if the version of nunit-console used is sufficient to run async tests. + /// + public class NUnitVersionTest + { + private int testRunCount = 0; + + [TestFixtureTearDown] + public void Cleanup() + { + if (testRunCount != 2) + { + Console.Error.WriteLine("You are using and old version of NUnit that doesn't support async tests and skips them instead. " + + "This test has failed to indicate that."); + Console.Error.Flush(); + Environment.Exit(1); + } + } + + [Test] + public void NUnitVersionTest1() + { + testRunCount++; + } + + // Old version of NUnit will skip this test + [Test] + public async Task NUnitVersionTest2() + { + testRunCount++; + await Task.Delay(10); + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/PInvokeTest.cs b/src/csharp/Grpc.Core.Tests/PInvokeTest.cs new file mode 100644 index 00000000..714c2f74 --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/PInvokeTest.cs @@ -0,0 +1,130 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core; +using Grpc.Core.Internal; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.Core.Tests +{ + public class PInvokeTest + { + int counter; + + [DllImport("grpc_csharp_ext.dll")] + static extern GRPCCallError grpcsharp_test_callback([MarshalAs(UnmanagedType.FunctionPtr)] OpCompletionDelegate callback); + + [DllImport("grpc_csharp_ext.dll")] + static extern IntPtr grpcsharp_test_nop(IntPtr ptr); + + /// + /// (~1.26us .NET Windows) + /// + [Test] + public void CompletionQueueCreateDestroyBenchmark() + { + BenchmarkUtil.RunBenchmark( + 100000, 1000000, + () => + { + CompletionQueueSafeHandle cq = CompletionQueueSafeHandle.Create(); + cq.Dispose(); + }); + } + + /// + /// Approximate results: + /// (~80ns Mono Linux) + /// (~110ns .NET Windows) + /// + [Test] + public void NativeCallbackBenchmark() + { + OpCompletionDelegate handler = Handler; + + counter = 0; + BenchmarkUtil.RunBenchmark( + 1000000, 10000000, + () => + { + grpcsharp_test_callback(handler); + }); + Assert.AreNotEqual(0, counter); + } + + /// + /// Creating a new native-to-managed callback has significant overhead + /// compared to using an existing one. We need to be aware of this. + /// (~50us on Mono Linux!!!) + /// (~1.1us on .NET Windows) + /// + [Test] + public void NewNativeCallbackBenchmark() + { + counter = 0; + BenchmarkUtil.RunBenchmark( + 10000, 10000, + () => + { + grpcsharp_test_callback(new OpCompletionDelegate(Handler)); + }); + Assert.AreNotEqual(0, counter); + } + + /// + /// Tests overhead of a simple PInvoke call. + /// (~46ns .NET Windows) + /// + [Test] + public void NopPInvokeBenchmark() + { + BenchmarkUtil.RunBenchmark( + 1000000, 100000000, + () => + { + grpcsharp_test_nop(IntPtr.Zero); + }); + } + + private void Handler(bool success) + { + counter++; + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/Properties/AssemblyInfo.cs b/src/csharp/Grpc.Core.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..c2e5e81e --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,11 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyTitle("Grpc.Core.Tests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("Google Inc. All rights reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] diff --git a/src/csharp/Grpc.Core.Tests/ResponseHeadersTest.cs b/src/csharp/Grpc.Core.Tests/ResponseHeadersTest.cs new file mode 100644 index 00000000..a1648f36 --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/ResponseHeadersTest.cs @@ -0,0 +1,207 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +using Grpc.Core; +using Grpc.Core.Internal; +using Grpc.Core.Utils; + +using NUnit.Framework; + +namespace Grpc.Core.Tests +{ + /// + /// Tests for response headers support. + /// + public class ResponseHeadersTest + { + MockServiceHelper helper; + Server server; + Channel channel; + + Metadata headers; + + [SetUp] + public void Init() + { + helper = new MockServiceHelper(); + + server = helper.GetServer(); + server.Start(); + channel = helper.GetChannel(); + + headers = new Metadata { { "ascii-header", "abcdefg" } }; + } + + [TearDown] + public void Cleanup() + { + channel.ShutdownAsync().Wait(); + server.ShutdownAsync().Wait(); + } + + [Test] + public async Task ResponseHeadersAsync_UnaryCall() + { + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => + { + await context.WriteResponseHeadersAsync(headers); + return "PASS"; + }); + + var call = Calls.AsyncUnaryCall(helper.CreateUnaryCall(), ""); + var responseHeaders = await call.ResponseHeadersAsync; + + Assert.AreEqual(headers.Count, responseHeaders.Count); + Assert.AreEqual("ascii-header", responseHeaders[0].Key); + Assert.AreEqual("abcdefg", responseHeaders[0].Value); + + Assert.AreEqual("PASS", await call.ResponseAsync); + } + + [Test] + public async Task ResponseHeadersAsync_ClientStreamingCall() + { + helper.ClientStreamingHandler = new ClientStreamingServerMethod(async (requestStream, context) => + { + await context.WriteResponseHeadersAsync(headers); + return "PASS"; + }); + + var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall()); + await call.RequestStream.CompleteAsync(); + var responseHeaders = await call.ResponseHeadersAsync; + + Assert.AreEqual("ascii-header", responseHeaders[0].Key); + Assert.AreEqual("PASS", await call.ResponseAsync); + } + + [Test] + public async Task ResponseHeadersAsync_ServerStreamingCall() + { + helper.ServerStreamingHandler = new ServerStreamingServerMethod(async (request, responseStream, context) => + { + await context.WriteResponseHeadersAsync(headers); + await responseStream.WriteAsync("PASS"); + }); + + var call = Calls.AsyncServerStreamingCall(helper.CreateServerStreamingCall(), ""); + var responseHeaders = await call.ResponseHeadersAsync; + + Assert.AreEqual("ascii-header", responseHeaders[0].Key); + CollectionAssert.AreEqual(new[] { "PASS" }, await call.ResponseStream.ToListAsync()); + } + + [Test] + public async Task ResponseHeadersAsync_DuplexStreamingCall() + { + helper.DuplexStreamingHandler = new DuplexStreamingServerMethod(async (requestStream, responseStream, context) => + { + await context.WriteResponseHeadersAsync(headers); + while (await requestStream.MoveNext()) + { + await responseStream.WriteAsync(requestStream.Current); + } + }); + + var call = Calls.AsyncDuplexStreamingCall(helper.CreateDuplexStreamingCall()); + var responseHeaders = await call.ResponseHeadersAsync; + + var messages = new[] { "PASS" }; + await call.RequestStream.WriteAllAsync(messages); + + Assert.AreEqual("ascii-header", responseHeaders[0].Key); + CollectionAssert.AreEqual(messages, await call.ResponseStream.ToListAsync()); + } + + [Test] + public void WriteResponseHeaders_NullNotAllowed() + { + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => + { + Assert.Throws(typeof(ArgumentNullException), async () => await context.WriteResponseHeadersAsync(null)); + return "PASS"; + }); + + Assert.AreEqual("PASS", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "")); + } + + [Test] + public void WriteResponseHeaders_AllowedOnlyOnce() + { + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => + { + await context.WriteResponseHeadersAsync(headers); + try + { + await context.WriteResponseHeadersAsync(headers); + Assert.Fail(); + } + catch (InvalidOperationException expected) + { + } + return "PASS"; + }); + + Assert.AreEqual("PASS", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "")); + } + + [Test] + public async Task WriteResponseHeaders_NotAllowedAfterWrite() + { + helper.ServerStreamingHandler = new ServerStreamingServerMethod(async (request, responseStream, context) => + { + await responseStream.WriteAsync("A"); + try + { + await context.WriteResponseHeadersAsync(headers); + Assert.Fail(); + } + catch (InvalidOperationException expected) + { + } + await responseStream.WriteAsync("B"); + }); + + var call = Calls.AsyncServerStreamingCall(helper.CreateServerStreamingCall(), ""); + var responses = await call.ResponseStream.ToListAsync(); + CollectionAssert.AreEqual(new[] { "A", "B" }, responses); + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/ServerTest.cs b/src/csharp/Grpc.Core.Tests/ServerTest.cs new file mode 100644 index 00000000..e7193c84 --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/ServerTest.cs @@ -0,0 +1,86 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Linq; +using Grpc.Core; +using Grpc.Core.Internal; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.Core.Tests +{ + public class ServerTest + { + [Test] + public void StartAndShutdownServer() + { + Server server = new Server + { + Ports = { new ServerPort("localhost", ServerPort.PickUnused, ServerCredentials.Insecure) } + }; + server.Start(); + server.ShutdownAsync().Wait(); + } + + [Test] + public void PickUnusedPort() + { + Server server = new Server + { + Ports = { new ServerPort("localhost", ServerPort.PickUnused, ServerCredentials.Insecure) } + }; + + var boundPort = server.Ports.Single(); + Assert.AreEqual(0, boundPort.Port); + Assert.Greater(boundPort.BoundPort, 0); + + server.Start(); + server.ShutdownAsync().Wait(); + } + + [Test] + public void CannotModifyAfterStarted() + { + Server server = new Server + { + Ports = { new ServerPort("localhost", ServerPort.PickUnused, ServerCredentials.Insecure) } + }; + server.Start(); + Assert.Throws(typeof(InvalidOperationException), () => server.Ports.Add("localhost", 9999, ServerCredentials.Insecure)); + Assert.Throws(typeof(InvalidOperationException), () => server.Services.Add(ServerServiceDefinition.CreateBuilder("serviceName").Build())); + + server.ShutdownAsync().Wait(); + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/ShutdownTest.cs b/src/csharp/Grpc.Core.Tests/ShutdownTest.cs new file mode 100644 index 00000000..a2be7ddd --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/ShutdownTest.cs @@ -0,0 +1,77 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core; +using Grpc.Core.Internal; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.Core.Tests +{ + public class ShutdownTest + { + const string Host = "127.0.0.1"; + + MockServiceHelper helper; + Server server; + Channel channel; + + [SetUp] + public void Init() + { + helper = new MockServiceHelper(Host); + server = helper.GetServer(); + server.Start(); + channel = helper.GetChannel(); + } + + [Test] + public async Task AbandonedCall() + { + helper.DuplexStreamingHandler = new DuplexStreamingServerMethod(async (requestStream, responseStream, context) => + { + await requestStream.ToListAsync(); + }); + + var call = Calls.AsyncDuplexStreamingCall(helper.CreateDuplexStreamingCall(new CallOptions(deadline: DateTime.UtcNow.AddMilliseconds(1)))); + + channel.ShutdownAsync().Wait(); + server.ShutdownAsync().Wait(); + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/TestResult.xml b/src/csharp/Grpc.Core.Tests/TestResult.xml new file mode 100644 index 00000000..13da8073 --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/TestResult.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs new file mode 100644 index 00000000..41f661f6 --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs @@ -0,0 +1,154 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core; +using Grpc.Core.Internal; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.Core.Tests +{ + /// + /// Tests for Deadline support. + /// + public class TimeoutsTest + { + MockServiceHelper helper; + Server server; + Channel channel; + + [SetUp] + public void Init() + { + helper = new MockServiceHelper(); + + server = helper.GetServer(); + server.Start(); + channel = helper.GetChannel(); + } + + [TearDown] + public void Cleanup() + { + channel.ShutdownAsync().Wait(); + server.ShutdownAsync().Wait(); + } + + [Test] + public void InfiniteDeadline() + { + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => + { + Assert.AreEqual(DateTime.MaxValue, context.Deadline); + return "PASS"; + }); + + // no deadline specified, check server sees infinite deadline + Assert.AreEqual("PASS", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc")); + + // DateTime.MaxValue deadline specified, check server sees infinite deadline + Assert.AreEqual("PASS", Calls.BlockingUnaryCall(helper.CreateUnaryCall(new CallOptions(deadline: DateTime.MaxValue)), "abc")); + } + + [Test] + public void DeadlineTransferredToServer() + { + var clientDeadline = DateTime.UtcNow + TimeSpan.FromDays(7); + + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => + { + // A fairly relaxed check that the deadline set by client and deadline seen by server + // are in agreement. C core takes care of the work with transferring deadline over the wire, + // so we don't need an exact check here. + Assert.IsTrue(Math.Abs((clientDeadline - context.Deadline).TotalMilliseconds) < 5000); + return "PASS"; + }); + Calls.BlockingUnaryCall(helper.CreateUnaryCall(new CallOptions(deadline: clientDeadline)), "abc"); + } + + [Test] + public void DeadlineInThePast() + { + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => + { + await Task.Delay(60000); + return "FAIL"; + }); + + var ex = Assert.Throws(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(new CallOptions(deadline: DateTime.MinValue)), "abc")); + // We can't guarantee the status code always DeadlineExceeded. See issue #2685. + Assert.Contains(ex.Status.StatusCode, new[] { StatusCode.DeadlineExceeded, StatusCode.Internal }); + } + + [Test] + public void DeadlineExceededStatusOnTimeout() + { + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => + { + await Task.Delay(60000); + return "FAIL"; + }); + + var ex = Assert.Throws(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(new CallOptions(deadline: DateTime.UtcNow.Add(TimeSpan.FromSeconds(5)))), "abc")); + // We can't guarantee the status code always DeadlineExceeded. See issue #2685. + Assert.Contains(ex.Status.StatusCode, new[] { StatusCode.DeadlineExceeded, StatusCode.Internal }); + } + + [Test] + public async Task ServerReceivesCancellationOnTimeout() + { + var serverReceivedCancellationTcs = new TaskCompletionSource(); + + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => + { + // wait until cancellation token is fired. + var tcs = new TaskCompletionSource(); + context.CancellationToken.Register(() => { tcs.SetResult(null); }); + await tcs.Task; + serverReceivedCancellationTcs.SetResult(true); + return ""; + }); + + var ex = Assert.Throws(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(new CallOptions(deadline: DateTime.UtcNow.Add(TimeSpan.FromSeconds(5)))), "abc")); + // We can't guarantee the status code always DeadlineExceeded. See issue #2685. + Assert.Contains(ex.Status.StatusCode, new[] { StatusCode.DeadlineExceeded, StatusCode.Internal }); + + Assert.IsTrue(await serverReceivedCancellationTcs.Task); + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/packages.config b/src/csharp/Grpc.Core.Tests/packages.config new file mode 100644 index 00000000..62077f41 --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/csharp/Grpc.Core/.gitignore b/src/csharp/Grpc.Core/.gitignore new file mode 100644 index 00000000..1746e326 --- /dev/null +++ b/src/csharp/Grpc.Core/.gitignore @@ -0,0 +1,2 @@ +bin +obj diff --git a/src/csharp/Grpc.Core/AsyncClientStreamingCall.cs b/src/csharp/Grpc.Core/AsyncClientStreamingCall.cs new file mode 100644 index 00000000..5646fed3 --- /dev/null +++ b/src/csharp/Grpc.Core/AsyncClientStreamingCall.cs @@ -0,0 +1,135 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; + +namespace Grpc.Core +{ + /// + /// Return type for client streaming calls. + /// + /// Request message type for this call. + /// Response message type for this call. + public sealed class AsyncClientStreamingCall : IDisposable + { + readonly IClientStreamWriter requestStream; + readonly Task responseAsync; + readonly Task responseHeadersAsync; + readonly Func getStatusFunc; + readonly Func getTrailersFunc; + readonly Action disposeAction; + + internal AsyncClientStreamingCall(IClientStreamWriter requestStream, Task responseAsync, Task responseHeadersAsync, Func getStatusFunc, Func getTrailersFunc, Action disposeAction) + { + this.requestStream = requestStream; + this.responseAsync = responseAsync; + this.responseHeadersAsync = responseHeadersAsync; + this.getStatusFunc = getStatusFunc; + this.getTrailersFunc = getTrailersFunc; + this.disposeAction = disposeAction; + } + + /// + /// Asynchronous call result. + /// + public Task ResponseAsync + { + get + { + return this.responseAsync; + } + } + + /// + /// Asynchronous access to response headers. + /// + public Task ResponseHeadersAsync + { + get + { + return this.responseHeadersAsync; + } + } + + /// + /// Async stream to send streaming requests. + /// + public IClientStreamWriter RequestStream + { + get + { + return requestStream; + } + } + + /// + /// Allows awaiting this object directly. + /// + /// + public TaskAwaiter GetAwaiter() + { + return responseAsync.GetAwaiter(); + } + + /// + /// Gets the call status if the call has already finished. + /// Throws InvalidOperationException otherwise. + /// + public Status GetStatus() + { + return getStatusFunc(); + } + + /// + /// Gets the call trailing metadata if the call has already finished. + /// Throws InvalidOperationException otherwise. + /// + public Metadata GetTrailers() + { + return getTrailersFunc(); + } + + /// + /// Provides means to cleanup after the call. + /// If the call has already finished normally (request stream has been completed and call result has been received), doesn't do anything. + /// Otherwise, requests cancellation of the call which should terminate all pending async operations associated with the call. + /// As a result, all resources being used by the call should be released eventually. + /// + public void Dispose() + { + disposeAction.Invoke(); + } + } +} diff --git a/src/csharp/Grpc.Core/AsyncDuplexStreamingCall.cs b/src/csharp/Grpc.Core/AsyncDuplexStreamingCall.cs new file mode 100644 index 00000000..e75108c7 --- /dev/null +++ b/src/csharp/Grpc.Core/AsyncDuplexStreamingCall.cs @@ -0,0 +1,125 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Threading.Tasks; + +namespace Grpc.Core +{ + /// + /// Return type for bidirectional streaming calls. + /// + /// Request message type for this call. + /// Response message type for this call. + public sealed class AsyncDuplexStreamingCall : IDisposable + { + readonly IClientStreamWriter requestStream; + readonly IAsyncStreamReader responseStream; + readonly Task responseHeadersAsync; + readonly Func getStatusFunc; + readonly Func getTrailersFunc; + readonly Action disposeAction; + + internal AsyncDuplexStreamingCall(IClientStreamWriter requestStream, IAsyncStreamReader responseStream, Task responseHeadersAsync, Func getStatusFunc, Func getTrailersFunc, Action disposeAction) + { + this.requestStream = requestStream; + this.responseStream = responseStream; + this.responseHeadersAsync = responseHeadersAsync; + this.getStatusFunc = getStatusFunc; + this.getTrailersFunc = getTrailersFunc; + this.disposeAction = disposeAction; + } + + /// + /// Async stream to read streaming responses. + /// + public IAsyncStreamReader ResponseStream + { + get + { + return responseStream; + } + } + + /// + /// Async stream to send streaming requests. + /// + public IClientStreamWriter RequestStream + { + get + { + return requestStream; + } + } + + /// + /// Asynchronous access to response headers. + /// + public Task ResponseHeadersAsync + { + get + { + return this.responseHeadersAsync; + } + } + + /// + /// Gets the call status if the call has already finished. + /// Throws InvalidOperationException otherwise. + /// + public Status GetStatus() + { + return getStatusFunc(); + } + + /// + /// Gets the call trailing metadata if the call has already finished. + /// Throws InvalidOperationException otherwise. + /// + public Metadata GetTrailers() + { + return getTrailersFunc(); + } + + /// + /// Provides means to cleanup after the call. + /// If the call has already finished normally (request stream has been completed and response stream has been fully read), doesn't do anything. + /// Otherwise, requests cancellation of the call which should terminate all pending async operations associated with the call. + /// As a result, all resources being used by the call should be released eventually. + /// + public void Dispose() + { + disposeAction.Invoke(); + } + } +} diff --git a/src/csharp/Grpc.Core/AsyncServerStreamingCall.cs b/src/csharp/Grpc.Core/AsyncServerStreamingCall.cs new file mode 100644 index 00000000..f9530919 --- /dev/null +++ b/src/csharp/Grpc.Core/AsyncServerStreamingCall.cs @@ -0,0 +1,111 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Threading.Tasks; + +namespace Grpc.Core +{ + /// + /// Return type for server streaming calls. + /// + /// Response message type for this call. + public sealed class AsyncServerStreamingCall : IDisposable + { + readonly IAsyncStreamReader responseStream; + readonly Task responseHeadersAsync; + readonly Func getStatusFunc; + readonly Func getTrailersFunc; + readonly Action disposeAction; + + internal AsyncServerStreamingCall(IAsyncStreamReader responseStream, Task responseHeadersAsync, Func getStatusFunc, Func getTrailersFunc, Action disposeAction) + { + this.responseStream = responseStream; + this.responseHeadersAsync = responseHeadersAsync; + this.getStatusFunc = getStatusFunc; + this.getTrailersFunc = getTrailersFunc; + this.disposeAction = disposeAction; + } + + /// + /// Async stream to read streaming responses. + /// + public IAsyncStreamReader ResponseStream + { + get + { + return responseStream; + } + } + + /// + /// Asynchronous access to response headers. + /// + public Task ResponseHeadersAsync + { + get + { + return this.responseHeadersAsync; + } + } + + /// + /// Gets the call status if the call has already finished. + /// Throws InvalidOperationException otherwise. + /// + public Status GetStatus() + { + return getStatusFunc(); + } + + /// + /// Gets the call trailing metadata if the call has already finished. + /// Throws InvalidOperationException otherwise. + /// + public Metadata GetTrailers() + { + return getTrailersFunc(); + } + + /// + /// Provides means to cleanup after the call. + /// If the call has already finished normally (response stream has been fully read), doesn't do anything. + /// Otherwise, requests cancellation of the call which should terminate all pending async operations associated with the call. + /// As a result, all resources being used by the call should be released eventually. + /// + public void Dispose() + { + disposeAction.Invoke(); + } + } +} diff --git a/src/csharp/Grpc.Core/AsyncUnaryCall.cs b/src/csharp/Grpc.Core/AsyncUnaryCall.cs new file mode 100644 index 00000000..97df8f5e --- /dev/null +++ b/src/csharp/Grpc.Core/AsyncUnaryCall.cs @@ -0,0 +1,120 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; + +namespace Grpc.Core +{ + /// + /// Return type for single request - single response call. + /// + /// Response message type for this call. + public sealed class AsyncUnaryCall : IDisposable + { + readonly Task responseAsync; + readonly Task responseHeadersAsync; + readonly Func getStatusFunc; + readonly Func getTrailersFunc; + readonly Action disposeAction; + + internal AsyncUnaryCall(Task responseAsync, Task responseHeadersAsync, Func getStatusFunc, Func getTrailersFunc, Action disposeAction) + { + this.responseAsync = responseAsync; + this.responseHeadersAsync = responseHeadersAsync; + this.getStatusFunc = getStatusFunc; + this.getTrailersFunc = getTrailersFunc; + this.disposeAction = disposeAction; + } + + /// + /// Asynchronous call result. + /// + public Task ResponseAsync + { + get + { + return this.responseAsync; + } + } + + /// + /// Asynchronous access to response headers. + /// + public Task ResponseHeadersAsync + { + get + { + return this.responseHeadersAsync; + } + } + + /// + /// Allows awaiting this object directly. + /// + public TaskAwaiter GetAwaiter() + { + return responseAsync.GetAwaiter(); + } + + /// + /// Gets the call status if the call has already finished. + /// Throws InvalidOperationException otherwise. + /// + public Status GetStatus() + { + return getStatusFunc(); + } + + /// + /// Gets the call trailing metadata if the call has already finished. + /// Throws InvalidOperationException otherwise. + /// + public Metadata GetTrailers() + { + return getTrailersFunc(); + } + + /// + /// Provides means to cleanup after the call. + /// If the call has already finished normally (request stream has been completed and call result has been received), doesn't do anything. + /// Otherwise, requests cancellation of the call which should terminate all pending async operations associated with the call. + /// As a result, all resources being used by the call should be released eventually. + /// + public void Dispose() + { + disposeAction.Invoke(); + } + } +} diff --git a/src/csharp/Grpc.Core/CallInvocationDetails.cs b/src/csharp/Grpc.Core/CallInvocationDetails.cs new file mode 100644 index 00000000..8228b8f3 --- /dev/null +++ b/src/csharp/Grpc.Core/CallInvocationDetails.cs @@ -0,0 +1,173 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using Grpc.Core.Internal; +using Grpc.Core.Utils; + +namespace Grpc.Core +{ + /// + /// Details about a client-side call to be invoked. + /// + /// Request message type for the call. + /// Response message type for the call. + public struct CallInvocationDetails + { + readonly Channel channel; + readonly string method; + readonly string host; + readonly Marshaller requestMarshaller; + readonly Marshaller responseMarshaller; + CallOptions options; + + /// + /// Initializes a new instance of the struct. + /// + /// Channel to use for this call. + /// Method to call. + /// Call options. + public CallInvocationDetails(Channel channel, Method method, CallOptions options) : + this(channel, method, null, options) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// Channel to use for this call. + /// Method to call. + /// Host that contains the method. if null, default host will be used. + /// Call options. + public CallInvocationDetails(Channel channel, Method method, string host, CallOptions options) : + this(channel, method.FullName, host, method.RequestMarshaller, method.ResponseMarshaller, options) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// Channel to use for this call. + /// Qualified method name. + /// Host that contains the method. + /// Request marshaller. + /// Response marshaller. + /// Call options. + public CallInvocationDetails(Channel channel, string method, string host, Marshaller requestMarshaller, Marshaller responseMarshaller, CallOptions options) + { + this.channel = Preconditions.CheckNotNull(channel, "channel"); + this.method = Preconditions.CheckNotNull(method, "method"); + this.host = host; + this.requestMarshaller = Preconditions.CheckNotNull(requestMarshaller, "requestMarshaller"); + this.responseMarshaller = Preconditions.CheckNotNull(responseMarshaller, "responseMarshaller"); + this.options = options; + } + + /// + /// Get channel associated with this call. + /// + public Channel Channel + { + get + { + return this.channel; + } + } + + /// + /// Gets name of method to be called. + /// + public string Method + { + get + { + return this.method; + } + } + + /// + /// Get name of host. + /// + public string Host + { + get + { + return this.host; + } + } + + /// + /// Gets marshaller used to serialize requests. + /// + public Marshaller RequestMarshaller + { + get + { + return this.requestMarshaller; + } + } + + /// + /// Gets marshaller used to deserialized responses. + /// + public Marshaller ResponseMarshaller + { + get + { + return this.responseMarshaller; + } + } + + /// + /// Gets the call options. + /// + public CallOptions Options + { + get + { + return options; + } + } + + /// + /// Returns new instance of with + /// Options set to the value provided. Values of all other fields are preserved. + /// + public CallInvocationDetails WithOptions(CallOptions options) + { + var newDetails = this; + newDetails.options = options; + return newDetails; + } + } +} diff --git a/src/csharp/Grpc.Core/CallOptions.cs b/src/csharp/Grpc.Core/CallOptions.cs new file mode 100644 index 00000000..c3bc9c31 --- /dev/null +++ b/src/csharp/Grpc.Core/CallOptions.cs @@ -0,0 +1,181 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Threading; + +using Grpc.Core.Internal; +using Grpc.Core.Utils; + +namespace Grpc.Core +{ + /// + /// Options for calls made by client. + /// + public struct CallOptions + { + Metadata headers; + DateTime? deadline; + CancellationToken cancellationToken; + WriteOptions writeOptions; + ContextPropagationToken propagationToken; + + /// + /// Creates a new instance of CallOptions struct. + /// + /// Headers to be sent with the call. + /// Deadline for the call to finish. null means no deadline. + /// Can be used to request cancellation of the call. + /// Write options that will be used for this call. + /// Context propagation token obtained from . + public CallOptions(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken), + WriteOptions writeOptions = null, ContextPropagationToken propagationToken = null) + { + this.headers = headers; + this.deadline = deadline; + this.cancellationToken = cancellationToken; + this.writeOptions = writeOptions; + this.propagationToken = propagationToken; + } + + /// + /// Headers to send at the beginning of the call. + /// + public Metadata Headers + { + get { return headers; } + } + + /// + /// Call deadline. + /// + public DateTime? Deadline + { + get { return deadline; } + } + + /// + /// Token that can be used for cancelling the call. + /// + public CancellationToken CancellationToken + { + get { return cancellationToken; } + } + + /// + /// Write options that will be used for this call. + /// + public WriteOptions WriteOptions + { + get + { + return this.writeOptions; + } + } + + /// + /// Token for propagating parent call context. + /// + public ContextPropagationToken PropagationToken + { + get + { + return this.propagationToken; + } + } + + /// + /// Returns new instance of with + /// Headers set to the value provided. Values of all other fields are preserved. + /// + /// The headers. + public CallOptions WithHeaders(Metadata headers) + { + var newOptions = this; + newOptions.headers = headers; + return newOptions; + } + + /// + /// Returns new instance of with + /// Deadline set to the value provided. Values of all other fields are preserved. + /// + /// The deadline. + public CallOptions WithDeadline(DateTime deadline) + { + var newOptions = this; + newOptions.deadline = deadline; + return newOptions; + } + + /// + /// Returns new instance of with + /// CancellationToken set to the value provided. Values of all other fields are preserved. + /// + /// The cancellation token. + public CallOptions WithCancellationToken(CancellationToken cancellationToken) + { + var newOptions = this; + newOptions.cancellationToken = cancellationToken; + return newOptions; + } + + /// + /// Returns a new instance of with + /// all previously unset values set to their defaults and deadline and cancellation + /// token propagated when appropriate. + /// + internal CallOptions Normalize() + { + var newOptions = this; + if (propagationToken != null) + { + if (propagationToken.Options.IsPropagateDeadline) + { + Preconditions.CheckArgument(!newOptions.deadline.HasValue, + "Cannot propagate deadline from parent call. The deadline has already been set explicitly."); + newOptions.deadline = propagationToken.ParentDeadline; + } + if (propagationToken.Options.IsPropagateCancellation) + { + Preconditions.CheckArgument(!newOptions.cancellationToken.CanBeCanceled, + "Cannot propagate cancellation token from parent call. The cancellation token has already been set to a non-default value."); + } + } + + newOptions.headers = newOptions.headers ?? Metadata.Empty; + newOptions.deadline = newOptions.deadline ?? DateTime.MaxValue; + return newOptions; + } + } +} diff --git a/src/csharp/Grpc.Core/Calls.cs b/src/csharp/Grpc.Core/Calls.cs new file mode 100644 index 00000000..94b3c2fe --- /dev/null +++ b/src/csharp/Grpc.Core/Calls.cs @@ -0,0 +1,137 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System.Threading.Tasks; +using Grpc.Core.Internal; + +namespace Grpc.Core +{ + /// + /// Helper methods for generated clients to make RPC calls. + /// Most users will use this class only indirectly and will be + /// making calls using client object generated from protocol + /// buffer definition files. + /// + public static class Calls + { + /// + /// Invokes a simple remote call in a blocking fashion. + /// + /// The response. + /// The call defintion. + /// Request message. + /// Type of request message. + /// The of response message. + public static TResponse BlockingUnaryCall(CallInvocationDetails call, TRequest req) + where TRequest : class + where TResponse : class + { + var asyncCall = new AsyncCall(call); + return asyncCall.UnaryCall(req); + } + + /// + /// Invokes a simple remote call asynchronously. + /// + /// An awaitable call object providing access to the response. + /// The call defintion. + /// Request message. + /// Type of request message. + /// The of response message. + public static AsyncUnaryCall AsyncUnaryCall(CallInvocationDetails call, TRequest req) + where TRequest : class + where TResponse : class + { + var asyncCall = new AsyncCall(call); + var asyncResult = asyncCall.UnaryCallAsync(req); + return new AsyncUnaryCall(asyncResult, asyncCall.ResponseHeadersAsync, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel); + } + + /// + /// Invokes a server streaming call asynchronously. + /// In server streaming scenario, client sends on request and server responds with a stream of responses. + /// + /// A call object providing access to the asynchronous response stream. + /// The call defintion. + /// Request message. + /// Type of request message. + /// The of response messages. + public static AsyncServerStreamingCall AsyncServerStreamingCall(CallInvocationDetails call, TRequest req) + where TRequest : class + where TResponse : class + { + var asyncCall = new AsyncCall(call); + asyncCall.StartServerStreamingCall(req); + var responseStream = new ClientResponseStream(asyncCall); + return new AsyncServerStreamingCall(responseStream, asyncCall.ResponseHeadersAsync, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel); + } + + /// + /// Invokes a client streaming call asynchronously. + /// In client streaming scenario, client sends a stream of requests and server responds with a single response. + /// + /// The call defintion. + /// An awaitable call object providing access to the response. + /// Type of request messages. + /// The of response message. + public static AsyncClientStreamingCall AsyncClientStreamingCall(CallInvocationDetails call) + where TRequest : class + where TResponse : class + { + var asyncCall = new AsyncCall(call); + var resultTask = asyncCall.ClientStreamingCallAsync(); + var requestStream = new ClientRequestStream(asyncCall); + return new AsyncClientStreamingCall(requestStream, resultTask, asyncCall.ResponseHeadersAsync, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel); + } + + /// + /// Invokes a duplex streaming call asynchronously. + /// In duplex streaming scenario, client sends a stream of requests and server responds with a stream of responses. + /// The response stream is completely independent and both side can be sending messages at the same time. + /// + /// A call object providing access to the asynchronous request and response streams. + /// The call definition. + /// Type of request messages. + /// Type of reponse messages. + public static AsyncDuplexStreamingCall AsyncDuplexStreamingCall(CallInvocationDetails call) + where TRequest : class + where TResponse : class + { + var asyncCall = new AsyncCall(call); + asyncCall.StartDuplexStreamingCall(); + var requestStream = new ClientRequestStream(asyncCall); + var responseStream = new ClientResponseStream(asyncCall); + return new AsyncDuplexStreamingCall(requestStream, responseStream, asyncCall.ResponseHeadersAsync, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel); + } + } +} diff --git a/src/csharp/Grpc.Core/Channel.cs b/src/csharp/Grpc.Core/Channel.cs new file mode 100644 index 00000000..f1942727 --- /dev/null +++ b/src/csharp/Grpc.Core/Channel.cs @@ -0,0 +1,250 @@ +#region Copyright notice and license +// Copyright 2015, 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. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; + +using Grpc.Core.Internal; +using Grpc.Core.Logging; +using Grpc.Core.Utils; + +namespace Grpc.Core +{ + /// + /// Represents a gRPC channel. Channels are an abstraction of long-lived connections to remote servers. + /// More client objects can reuse the same channel. Creating a channel is an expensive operation compared to invoking + /// a remote call so in general you should reuse a single channel for as many calls as possible. + /// + public class Channel + { + static readonly ILogger Logger = GrpcEnvironment.Logger.ForType(); + + readonly object myLock = new object(); + readonly AtomicCounter activeCallCounter = new AtomicCounter(); + + readonly string target; + readonly GrpcEnvironment environment; + readonly ChannelSafeHandle handle; + readonly List options; + + bool shutdownRequested; + + /// + /// Creates a channel that connects to a specific host. + /// Port will default to 80 for an unsecure channel and to 443 for a secure channel. + /// + /// Target of the channel. + /// Credentials to secure the channel. + /// Channel options. + public Channel(string target, Credentials credentials, IEnumerable options = null) + { + this.target = Preconditions.CheckNotNull(target, "target"); + this.environment = GrpcEnvironment.AddRef(); + this.options = options != null ? new List(options) : new List(); + + EnsureUserAgentChannelOption(this.options); + using (CredentialsSafeHandle nativeCredentials = credentials.ToNativeCredentials()) + using (ChannelArgsSafeHandle nativeChannelArgs = ChannelOptions.CreateChannelArgs(this.options)) + { + if (nativeCredentials != null) + { + this.handle = ChannelSafeHandle.CreateSecure(nativeCredentials, target, nativeChannelArgs); + } + else + { + this.handle = ChannelSafeHandle.CreateInsecure(target, nativeChannelArgs); + } + } + } + + /// + /// Creates a channel that connects to a specific host and port. + /// + /// The name or IP address of the host. + /// The port. + /// Credentials to secure the channel. + /// Channel options. + public Channel(string host, int port, Credentials credentials, IEnumerable options = null) : + this(string.Format("{0}:{1}", host, port), credentials, options) + { + } + + /// + /// Gets current connectivity state of this channel. + /// + public ChannelState State + { + get + { + return handle.CheckConnectivityState(false); + } + } + + /// + /// Returned tasks completes once channel state has become different from + /// given lastObservedState. + /// If deadline is reached or and error occurs, returned task is cancelled. + /// + public Task WaitForStateChangedAsync(ChannelState lastObservedState, DateTime? deadline = null) + { + Preconditions.CheckArgument(lastObservedState != ChannelState.FatalFailure, + "FatalFailure is a terminal state. No further state changes can occur."); + var tcs = new TaskCompletionSource(); + var deadlineTimespec = deadline.HasValue ? Timespec.FromDateTime(deadline.Value) : Timespec.InfFuture; + var handler = new BatchCompletionDelegate((success, ctx) => + { + if (success) + { + tcs.SetResult(null); + } + else + { + tcs.SetCanceled(); + } + }); + handle.WatchConnectivityState(lastObservedState, deadlineTimespec, environment.CompletionQueue, environment.CompletionRegistry, handler); + return tcs.Task; + } + + /// Resolved address of the remote endpoint in URI format. + public string ResolvedTarget + { + get + { + return handle.GetTarget(); + } + } + + /// The original target used to create the channel. + public string Target + { + get + { + return this.target; + } + } + + /// + /// Allows explicitly requesting channel to connect without starting an RPC. + /// Returned task completes once state Ready was seen. If the deadline is reached, + /// or channel enters the FatalFailure state, the task is cancelled. + /// There is no need to call this explicitly unless your use case requires that. + /// Starting an RPC on a new channel will request connection implicitly. + /// + /// The deadline. null indicates no deadline. + public async Task ConnectAsync(DateTime? deadline = null) + { + var currentState = handle.CheckConnectivityState(true); + while (currentState != ChannelState.Ready) + { + if (currentState == ChannelState.FatalFailure) + { + throw new OperationCanceledException("Channel has reached FatalFailure state."); + } + await WaitForStateChangedAsync(currentState, deadline); + currentState = handle.CheckConnectivityState(false); + } + } + + /// + /// Waits until there are no more active calls for this channel and then cleans up + /// resources used by this channel. + /// + public async Task ShutdownAsync() + { + lock (myLock) + { + Preconditions.CheckState(!shutdownRequested); + shutdownRequested = true; + } + + var activeCallCount = activeCallCounter.Count; + if (activeCallCount > 0) + { + Logger.Warning("Channel shutdown was called but there are still {0} active calls for that channel.", activeCallCount); + } + + handle.Dispose(); + + await Task.Run(() => GrpcEnvironment.Release()); + } + + internal ChannelSafeHandle Handle + { + get + { + return this.handle; + } + } + + internal GrpcEnvironment Environment + { + get + { + return this.environment; + } + } + + internal void AddCallReference(object call) + { + activeCallCounter.Increment(); + + bool success = false; + handle.DangerousAddRef(ref success); + Preconditions.CheckState(success); + } + + internal void RemoveCallReference(object call) + { + handle.DangerousRelease(); + + activeCallCounter.Decrement(); + } + + private static void EnsureUserAgentChannelOption(List options) + { + if (!options.Any((option) => option.Name == ChannelOptions.PrimaryUserAgentString)) + { + options.Add(new ChannelOption(ChannelOptions.PrimaryUserAgentString, GetUserAgentString())); + } + } + + private static string GetUserAgentString() + { + // TODO(jtattermusch): it would be useful to also provide .NET/mono version. + return string.Format("grpc-csharp/{0}", VersionInfo.CurrentVersion); + } + } +} diff --git a/src/csharp/Grpc.Core/ChannelOptions.cs b/src/csharp/Grpc.Core/ChannelOptions.cs new file mode 100644 index 00000000..f5ef63af --- /dev/null +++ b/src/csharp/Grpc.Core/ChannelOptions.cs @@ -0,0 +1,210 @@ +#region Copyright notice and license +// Copyright 2015, 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. +#endregion +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core.Internal; +using Grpc.Core.Utils; + +namespace Grpc.Core +{ + /// + /// Channel option specified when creating a channel. + /// Corresponds to grpc_channel_args from grpc/grpc.h. + /// + public sealed class ChannelOption + { + /// + /// Type of ChannelOption. + /// + public enum OptionType + { + /// + /// Channel option with integer value. + /// + Integer, + + /// + /// Channel option with string value. + /// + String + } + + private readonly OptionType type; + private readonly string name; + private readonly int intValue; + private readonly string stringValue; + + /// + /// Creates a channel option with a string value. + /// + /// Name. + /// String value. + public ChannelOption(string name, string stringValue) + { + this.type = OptionType.String; + this.name = Preconditions.CheckNotNull(name, "name"); + this.stringValue = Preconditions.CheckNotNull(stringValue, "stringValue"); + } + + /// + /// Creates a channel option with an integer value. + /// + /// Name. + /// Integer value. + public ChannelOption(string name, int intValue) + { + this.type = OptionType.Integer; + this.name = Preconditions.CheckNotNull(name, "name"); + this.intValue = intValue; + } + + /// + /// Gets the type of the ChannelOption. + /// + public OptionType Type + { + get + { + return type; + } + } + + /// + /// Gets the name of the ChannelOption. + /// + public string Name + { + get + { + return name; + } + } + + /// + /// Gets the integer value the ChannelOption. + /// + public int IntValue + { + get + { + Preconditions.CheckState(type == OptionType.Integer); + return intValue; + } + } + + /// + /// Gets the string value the ChannelOption. + /// + public string StringValue + { + get + { + Preconditions.CheckState(type == OptionType.String); + return stringValue; + } + } + } + + /// + /// Defines names of supported channel options. + /// + public static class ChannelOptions + { + /// Override SSL target check. Only to be used for testing. + public const string SslTargetNameOverride = "grpc.ssl_target_name_override"; + + /// Enable census for tracing and stats collection + public const string Census = "grpc.census"; + + /// Maximum number of concurrent incoming streams to allow on a http2 connection + public const string MaxConcurrentStreams = "grpc.max_concurrent_streams"; + + /// Maximum message length that the channel can receive + public const string MaxMessageLength = "grpc.max_message_length"; + + /// Initial sequence number for http2 transports + public const string Http2InitialSequenceNumber = "grpc.http2.initial_sequence_number"; + + /// Default authority for calls. + public const string DefaultAuthority = "grpc.default_authority"; + + /// Primary user agent: goes at the start of the user-agent metadata + public const string PrimaryUserAgentString = "grpc.primary_user_agent"; + + /// Secondary user agent: goes at the end of the user-agent metadata + public const string SecondaryUserAgentString = "grpc.secondary_user_agent"; + + /// + /// Creates native object for a collection of channel options. + /// + /// The native channel arguments. + internal static ChannelArgsSafeHandle CreateChannelArgs(List options) + { + if (options == null || options.Count == 0) + { + return ChannelArgsSafeHandle.CreateNull(); + } + ChannelArgsSafeHandle nativeArgs = null; + try + { + nativeArgs = ChannelArgsSafeHandle.Create(options.Count); + for (int i = 0; i < options.Count; i++) + { + var option = options[i]; + if (option.Type == ChannelOption.OptionType.Integer) + { + nativeArgs.SetInteger(i, option.Name, option.IntValue); + } + else if (option.Type == ChannelOption.OptionType.String) + { + nativeArgs.SetString(i, option.Name, option.StringValue); + } + else + { + throw new InvalidOperationException("Unknown option type"); + } + } + return nativeArgs; + } + catch (Exception) + { + if (nativeArgs != null) + { + nativeArgs.Dispose(); + } + throw; + } + } + } +} diff --git a/src/csharp/Grpc.Core/ChannelState.cs b/src/csharp/Grpc.Core/ChannelState.cs new file mode 100644 index 00000000..d293b98f --- /dev/null +++ b/src/csharp/Grpc.Core/ChannelState.cs @@ -0,0 +1,69 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; + +namespace Grpc.Core +{ + /// + /// Connectivity state of a channel. + /// Based on grpc_connectivity_state from grpc/grpc.h + /// + public enum ChannelState + { + /// + /// Channel is idle + /// + Idle, + + /// + /// Channel is connecting + /// + Connecting, + + /// + /// Channel is ready for work + /// + Ready, + + /// + /// Channel has seen a failure but expects to recover + /// + TransientFailure, + + /// + /// Channel has seen a failure that it cannot recover from + /// + FatalFailure + } +} diff --git a/src/csharp/Grpc.Core/ClientBase.cs b/src/csharp/Grpc.Core/ClientBase.cs new file mode 100644 index 00000000..f4533e73 --- /dev/null +++ b/src/csharp/Grpc.Core/ClientBase.cs @@ -0,0 +1,138 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace Grpc.Core +{ + /// + /// Interceptor for call headers. + /// + public delegate void HeaderInterceptor(IMethod method, string authUri, Metadata metadata); + + /// + /// Base class for client-side stubs. + /// + public abstract class ClientBase + { + // Regex for removal of the optional DNS scheme, trailing port, and trailing backslash + static readonly Regex ChannelTargetPattern = new Regex(@"^(dns:\/{3})?([^:\/]+)(:\d+)?\/?$"); + + readonly Channel channel; + readonly string authUriBase; + + /// + /// Initializes a new instance of ClientBase class. + /// + /// The channel to use for remote call invocation. + public ClientBase(Channel channel) + { + this.channel = channel; + this.authUriBase = GetAuthUriBase(channel.Target); + } + + /// + /// Can be used to register a custom header (request metadata) interceptor. + /// The interceptor is invoked each time a new call on this client is started. + /// + public HeaderInterceptor HeaderInterceptor + { + get; + set; + } + + /// + /// gRPC supports multiple "hosts" being served by a single server. + /// This property can be used to set the target host explicitly. + /// By default, this will be set to null with the meaning + /// "use default host". + /// + public string Host + { + get; + set; + } + + /// + /// Channel associated with this client. + /// + public Channel Channel + { + get + { + return this.channel; + } + } + + /// + /// Creates a new call to given method. + /// + /// The method to invoke. + /// The call options. + /// Request message type. + /// Response message type. + /// The call invocation details. + protected CallInvocationDetails CreateCall(Method method, CallOptions options) + where TRequest : class + where TResponse : class + { + var interceptor = HeaderInterceptor; + if (interceptor != null) + { + if (options.Headers == null) + { + options = options.WithHeaders(new Metadata()); + } + var authUri = authUriBase != null ? authUriBase + method.ServiceName : null; + interceptor(method, authUri, options.Headers); + } + return new CallInvocationDetails(channel, method, Host, options); + } + + /// + /// Creates Auth URI base from channel's target (the one passed at channel creation). + /// Fully-qualified service name is to be appended to this. + /// + internal static string GetAuthUriBase(string target) + { + var match = ChannelTargetPattern.Match(target); + if (!match.Success) + { + return null; + } + return "https://" + match.Groups[2].Value + "/"; + } + } +} diff --git a/src/csharp/Grpc.Core/CompressionLevel.cs b/src/csharp/Grpc.Core/CompressionLevel.cs new file mode 100644 index 00000000..399652b8 --- /dev/null +++ b/src/csharp/Grpc.Core/CompressionLevel.cs @@ -0,0 +1,63 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; + +namespace Grpc.Core +{ + /// + /// Compression level based on grpc_compression_level from grpc/compression.h + /// + public enum CompressionLevel + { + /// + /// No compression. + /// + None = 0, + + /// + /// Low compression. + /// + Low, + + /// + /// Medium compression. + /// + Medium, + + /// + /// High compression. + /// + High, + } +} diff --git a/src/csharp/Grpc.Core/ContextPropagationToken.cs b/src/csharp/Grpc.Core/ContextPropagationToken.cs new file mode 100644 index 00000000..1d899b97 --- /dev/null +++ b/src/csharp/Grpc.Core/ContextPropagationToken.cs @@ -0,0 +1,170 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Threading; + +using Grpc.Core.Internal; +using Grpc.Core.Utils; + +namespace Grpc.Core +{ + /// + /// Token for propagating context of server side handlers to child calls. + /// In situations when a backend is making calls to another backend, + /// it makes sense to propagate properties like deadline and cancellation + /// token of the server call to the child call. + /// The gRPC native layer provides some other contexts (like tracing context) that + /// are not accessible to explicitly C# layer, but this token still allows propagating them. + /// + public class ContextPropagationToken + { + /// + /// Default propagation mask used by C core. + /// + private const ContextPropagationFlags DefaultCoreMask = (ContextPropagationFlags)0xffff; + + /// + /// Default propagation mask used by C# - we want to propagate deadline + /// and cancellation token by our own means. + /// + internal const ContextPropagationFlags DefaultMask = DefaultCoreMask + & ~ContextPropagationFlags.Deadline & ~ContextPropagationFlags.Cancellation; + + readonly CallSafeHandle parentCall; + readonly DateTime deadline; + readonly CancellationToken cancellationToken; + readonly ContextPropagationOptions options; + + internal ContextPropagationToken(CallSafeHandle parentCall, DateTime deadline, CancellationToken cancellationToken, ContextPropagationOptions options) + { + this.parentCall = Preconditions.CheckNotNull(parentCall); + this.deadline = deadline; + this.cancellationToken = cancellationToken; + this.options = options ?? ContextPropagationOptions.Default; + } + + /// + /// Gets the native handle of the parent call. + /// + internal CallSafeHandle ParentCall + { + get + { + return this.parentCall; + } + } + + /// + /// Gets the parent call's deadline. + /// + internal DateTime ParentDeadline + { + get + { + return this.deadline; + } + } + + /// + /// Gets the parent call's cancellation token. + /// + internal CancellationToken ParentCancellationToken + { + get + { + return this.cancellationToken; + } + } + + /// + /// Get the context propagation options. + /// + internal ContextPropagationOptions Options + { + get + { + return this.options; + } + } + } + + /// + /// Options for . + /// + public class ContextPropagationOptions + { + /// + /// The context propagation options that will be used by default. + /// + public static readonly ContextPropagationOptions Default = new ContextPropagationOptions(); + + bool propagateDeadline; + bool propagateCancellation; + + /// + /// Creates new context propagation options. + /// + /// If set to true parent call's deadline will be propagated to the child call. + /// If set to true parent call's cancellation token will be propagated to the child call. + public ContextPropagationOptions(bool propagateDeadline = true, bool propagateCancellation = true) + { + this.propagateDeadline = propagateDeadline; + this.propagateCancellation = propagateCancellation; + } + + /// true if parent call's deadline should be propagated to the child call. + public bool IsPropagateDeadline + { + get { return this.propagateDeadline; } + } + + /// true if parent call's cancellation token should be propagated to the child call. + public bool IsPropagateCancellation + { + get { return this.propagateCancellation; } + } + } + + /// + /// Context propagation flags from grpc/grpc.h. + /// + [Flags] + internal enum ContextPropagationFlags + { + Deadline = 1, + CensusStatsContext = 2, + CensusTracingContext = 4, + Cancellation = 8 + } +} diff --git a/src/csharp/Grpc.Core/Credentials.cs b/src/csharp/Grpc.Core/Credentials.cs new file mode 100644 index 00000000..4fcac0c4 --- /dev/null +++ b/src/csharp/Grpc.Core/Credentials.cs @@ -0,0 +1,138 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using Grpc.Core.Internal; + +namespace Grpc.Core +{ + /// + /// Client-side credentials. Used for creation of a secure channel. + /// + public abstract class Credentials + { + static readonly Credentials InsecureInstance = new InsecureCredentialsImpl(); + + /// + /// Returns instance of credential that provides no security and + /// will result in creating an unsecure channel with no encryption whatsoever. + /// + public static Credentials Insecure + { + get + { + return InsecureInstance; + } + } + + /// + /// Creates native object for the credentials. May return null if insecure channel + /// should be created. + /// + /// The native credentials. + internal abstract CredentialsSafeHandle ToNativeCredentials(); + + private sealed class InsecureCredentialsImpl : Credentials + { + internal override CredentialsSafeHandle ToNativeCredentials() + { + return null; + } + } + } + + /// + /// Client-side SSL credentials. + /// + public sealed class SslCredentials : Credentials + { + readonly string rootCertificates; + readonly KeyCertificatePair keyCertificatePair; + + /// + /// Creates client-side SSL credentials loaded from + /// disk file pointed to by the GRPC_DEFAULT_SSL_ROOTS_FILE_PATH environment variable. + /// If that fails, gets the roots certificates from a well known place on disk. + /// + public SslCredentials() : this(null, null) + { + } + + /// + /// Creates client-side SSL credentials from + /// a string containing PEM encoded root certificates. + /// + public SslCredentials(string rootCertificates) : this(rootCertificates, null) + { + } + + /// + /// Creates client-side SSL credentials. + /// + /// string containing PEM encoded server root certificates. + /// a key certificate pair. + public SslCredentials(string rootCertificates, KeyCertificatePair keyCertificatePair) + { + this.rootCertificates = rootCertificates; + this.keyCertificatePair = keyCertificatePair; + } + + /// + /// PEM encoding of the server root certificates. + /// + public string RootCertificates + { + get + { + return this.rootCertificates; + } + } + + /// + /// Client side key and certificate pair. + /// If null, client will not use key and certificate pair. + /// + public KeyCertificatePair KeyCertificatePair + { + get + { + return this.keyCertificatePair; + } + } + + internal override CredentialsSafeHandle ToNativeCredentials() + { + return CredentialsSafeHandle.CreateSslCredentials(rootCertificates, keyCertificatePair); + } + } +} diff --git a/src/csharp/Grpc.Core/Grpc.Core.csproj b/src/csharp/Grpc.Core/Grpc.Core.csproj new file mode 100644 index 00000000..ad2af17b --- /dev/null +++ b/src/csharp/Grpc.Core/Grpc.Core.csproj @@ -0,0 +1,151 @@ + + + + + + Debug + AnyCPU + {CCC4440E-49F7-4790-B0AF-FEABB0837AE7} + Library + Grpc.Core + Grpc.Core + v4.5 + 8bb563fb + bin\$(Configuration)\Grpc.Core.Xml + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + + + pdbonly + true + bin\Release + prompt + 4 + + + pdbonly + true + bin\ReleaseSigned + SIGNED + prompt + 4 + True + C:\keys\Grpc.snk + + + + + ..\packages\Ix-Async.1.2.3\lib\net45\System.Interactive.Async.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PreserveNewest + + + + + + + + + + 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}. + + + + + + + + + + \ No newline at end of file diff --git a/src/csharp/Grpc.Core/Grpc.Core.nuspec b/src/csharp/Grpc.Core/Grpc.Core.nuspec new file mode 100644 index 00000000..06de55c8 --- /dev/null +++ b/src/csharp/Grpc.Core/Grpc.Core.nuspec @@ -0,0 +1,29 @@ + + + + Grpc.Core + gRPC C# Core + Core C# implementation of gRPC - an RPC library and framework + Core C# implementation of gRPC - an RPC library and framework. See project site for more info. + $version$ + Google Inc. + grpc-packages + https://github.com/grpc/grpc/blob/master/LICENSE + https://github.com/grpc/grpc + false + Release $version$ of gRPC C# + Copyright 2015, Google Inc. + gRPC RPC Protocol HTTP/2 + + + + + + + + + + + + + diff --git a/src/csharp/Grpc.Core/GrpcEnvironment.cs b/src/csharp/Grpc.Core/GrpcEnvironment.cs new file mode 100644 index 00000000..e7c04185 --- /dev/null +++ b/src/csharp/Grpc.Core/GrpcEnvironment.cs @@ -0,0 +1,214 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using Grpc.Core.Internal; +using Grpc.Core.Logging; +using Grpc.Core.Utils; + +namespace Grpc.Core +{ + /// + /// Encapsulates initialization and shutdown of gRPC library. + /// + public class GrpcEnvironment + { + const int THREAD_POOL_SIZE = 4; + + [DllImport("grpc_csharp_ext.dll")] + static extern void grpcsharp_init(); + + [DllImport("grpc_csharp_ext.dll")] + static extern void grpcsharp_shutdown(); + + [DllImport("grpc_csharp_ext.dll")] + static extern IntPtr grpcsharp_version_string(); // returns not-owned const char* + + static object staticLock = new object(); + static GrpcEnvironment instance; + static int refCount; + + static ILogger logger = new ConsoleLogger(); + + readonly GrpcThreadPool threadPool; + readonly CompletionRegistry completionRegistry; + readonly DebugStats debugStats = new DebugStats(); + bool isClosed; + + /// + /// Returns a reference-counted instance of initialized gRPC environment. + /// Subsequent invocations return the same instance unless reference count has dropped to zero previously. + /// + internal static GrpcEnvironment AddRef() + { + lock (staticLock) + { + refCount++; + if (instance == null) + { + instance = new GrpcEnvironment(); + } + return instance; + } + } + + /// + /// Decrements the reference count for currently active environment and shuts down the gRPC environment if reference count drops to zero. + /// (and blocks until the environment has been fully shutdown). + /// + internal static void Release() + { + lock (staticLock) + { + Preconditions.CheckState(refCount > 0); + refCount--; + if (refCount == 0) + { + instance.Close(); + instance = null; + } + } + } + + internal static int GetRefCount() + { + lock (staticLock) + { + return refCount; + } + } + + /// + /// Gets application-wide logger used by gRPC. + /// + /// The logger. + public static ILogger Logger + { + get + { + return logger; + } + } + + /// + /// Sets the application-wide logger that should be used by gRPC. + /// + public static void SetLogger(ILogger customLogger) + { + Preconditions.CheckNotNull(customLogger, "customLogger"); + logger = customLogger; + } + + /// + /// Creates gRPC environment. + /// + private GrpcEnvironment() + { + NativeLogRedirector.Redirect(); + GrpcNativeInit(); + completionRegistry = new CompletionRegistry(this); + threadPool = new GrpcThreadPool(this, THREAD_POOL_SIZE); + threadPool.Start(); + } + + /// + /// Gets the completion registry used by this gRPC environment. + /// + internal CompletionRegistry CompletionRegistry + { + get + { + return this.completionRegistry; + } + } + + /// + /// Gets the completion queue used by this gRPC environment. + /// + internal CompletionQueueSafeHandle CompletionQueue + { + get + { + return this.threadPool.CompletionQueue; + } + } + + /// + /// Gets the completion queue used by this gRPC environment. + /// + internal DebugStats DebugStats + { + get + { + return this.debugStats; + } + } + + /// + /// Gets version of gRPC C core. + /// + internal static string GetCoreVersionString() + { + var ptr = grpcsharp_version_string(); // the pointer is not owned + return Marshal.PtrToStringAnsi(ptr); + } + + internal static void GrpcNativeInit() + { + grpcsharp_init(); + } + + internal static void GrpcNativeShutdown() + { + grpcsharp_shutdown(); + } + + /// + /// Shuts down this environment. + /// + private void Close() + { + if (isClosed) + { + throw new InvalidOperationException("Close has already been called"); + } + threadPool.Stop(); + GrpcNativeShutdown(); + isClosed = true; + + debugStats.CheckOK(); + } + } +} diff --git a/src/csharp/Grpc.Core/IAsyncStreamReader.cs b/src/csharp/Grpc.Core/IAsyncStreamReader.cs new file mode 100644 index 00000000..49e1ea78 --- /dev/null +++ b/src/csharp/Grpc.Core/IAsyncStreamReader.cs @@ -0,0 +1,50 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Grpc.Core +{ + /// + /// A stream of messages to be read. + /// + /// The message type. + public interface IAsyncStreamReader : IAsyncEnumerator + { + // TODO(jtattermusch): consider just using IAsyncEnumerator instead of this interface. + } +} diff --git a/src/csharp/Grpc.Core/IAsyncStreamWriter.cs b/src/csharp/Grpc.Core/IAsyncStreamWriter.cs new file mode 100644 index 00000000..9c0d2d31 --- /dev/null +++ b/src/csharp/Grpc.Core/IAsyncStreamWriter.cs @@ -0,0 +1,62 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Grpc.Core +{ + /// + /// A writable stream of messages. + /// + /// The message type. + public interface IAsyncStreamWriter + { + /// + /// Writes a single asynchronously. Only one write can be pending at a time. + /// + /// the message to be written. Cannot be null. + Task WriteAsync(T message); + + /// + /// Write options that will be used for the next write. + /// If null, default options will be used. + /// Once set, this property maintains its value across subsequent + /// writes. + /// + WriteOptions WriteOptions { get; set; } + } +} diff --git a/src/csharp/Grpc.Core/IClientStreamWriter.cs b/src/csharp/Grpc.Core/IClientStreamWriter.cs new file mode 100644 index 00000000..3fd0774d --- /dev/null +++ b/src/csharp/Grpc.Core/IClientStreamWriter.cs @@ -0,0 +1,53 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Grpc.Core +{ + /// + /// Client-side writable stream of messages with Close capability. + /// + /// The message type. + public interface IClientStreamWriter : IAsyncStreamWriter + { + /// + /// Completes/closes the stream. Can only be called once there is no pending write. No writes should follow calling this. + /// + Task CompleteAsync(); + } +} diff --git a/src/csharp/Grpc.Core/IServerStreamWriter.cs b/src/csharp/Grpc.Core/IServerStreamWriter.cs new file mode 100644 index 00000000..9f3af591 --- /dev/null +++ b/src/csharp/Grpc.Core/IServerStreamWriter.cs @@ -0,0 +1,49 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Grpc.Core +{ + /// + /// A writable stream of messages that is used in server-side handlers. + /// + public interface IServerStreamWriter : IAsyncStreamWriter + { + // TODO(jtattermusch): consider just using IAsyncStreamWriter instead of this interface. + } +} diff --git a/src/csharp/Grpc.Core/Internal/AsyncCall.cs b/src/csharp/Grpc.Core/Internal/AsyncCall.cs new file mode 100644 index 00000000..e3b00781 --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/AsyncCall.cs @@ -0,0 +1,437 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core.Internal; +using Grpc.Core.Logging; +using Grpc.Core.Utils; + +namespace Grpc.Core.Internal +{ + /// + /// Manages client side native call lifecycle. + /// + internal class AsyncCall : AsyncCallBase + { + static readonly ILogger Logger = GrpcEnvironment.Logger.ForType>(); + + readonly CallInvocationDetails details; + readonly INativeCall injectedNativeCall; // for testing + + // Completion of a pending unary response if not null. + TaskCompletionSource unaryResponseTcs; + + // Indicates that steaming call has finished. + TaskCompletionSource streamingCallFinishedTcs = new TaskCompletionSource(); + + // Response headers set here once received. + TaskCompletionSource responseHeadersTcs = new TaskCompletionSource(); + + // Set after status is received. Used for both unary and streaming response calls. + ClientSideStatus? finishedStatus; + + public AsyncCall(CallInvocationDetails callDetails) + : base(callDetails.RequestMarshaller.Serializer, callDetails.ResponseMarshaller.Deserializer, callDetails.Channel.Environment) + { + this.details = callDetails.WithOptions(callDetails.Options.Normalize()); + this.initialMetadataSent = true; // we always send metadata at the very beginning of the call. + } + + /// + /// This constructor should only be used for testing. + /// + public AsyncCall(CallInvocationDetails callDetails, INativeCall injectedNativeCall) : this(callDetails) + { + this.injectedNativeCall = injectedNativeCall; + } + + // TODO: this method is not Async, so it shouldn't be in AsyncCall class, but + // it is reusing fair amount of code in this class, so we are leaving it here. + /// + /// Blocking unary request - unary response call. + /// + public TResponse UnaryCall(TRequest msg) + { + using (CompletionQueueSafeHandle cq = CompletionQueueSafeHandle.Create()) + { + byte[] payload = UnsafeSerialize(msg); + + unaryResponseTcs = new TaskCompletionSource(); + + lock (myLock) + { + Preconditions.CheckState(!started); + started = true; + Initialize(cq); + + halfcloseRequested = true; + readingDone = true; + } + + using (var metadataArray = MetadataArraySafeHandle.Create(details.Options.Headers)) + { + using (var ctx = BatchContextSafeHandle.Create()) + { + call.StartUnary(ctx, payload, metadataArray, GetWriteFlagsForCall()); + var ev = cq.Pluck(ctx.Handle); + + bool success = (ev.success != 0); + try + { + HandleUnaryResponse(success, ctx.GetReceivedStatusOnClient(), ctx.GetReceivedMessage(), ctx.GetReceivedInitialMetadata()); + } + catch (Exception e) + { + Logger.Error(e, "Exception occured while invoking completion delegate."); + } + } + } + + // Once the blocking call returns, the result should be available synchronously. + // Note that GetAwaiter().GetResult() doesn't wrap exceptions in AggregateException. + return unaryResponseTcs.Task.GetAwaiter().GetResult(); + } + } + + /// + /// Starts a unary request - unary response call. + /// + public Task UnaryCallAsync(TRequest msg) + { + lock (myLock) + { + Preconditions.CheckState(!started); + started = true; + + Initialize(environment.CompletionQueue); + + halfcloseRequested = true; + readingDone = true; + + byte[] payload = UnsafeSerialize(msg); + + unaryResponseTcs = new TaskCompletionSource(); + using (var metadataArray = MetadataArraySafeHandle.Create(details.Options.Headers)) + { + call.StartUnary(HandleUnaryResponse, payload, metadataArray, GetWriteFlagsForCall()); + } + return unaryResponseTcs.Task; + } + } + + /// + /// Starts a streamed request - unary response call. + /// Use StartSendMessage and StartSendCloseFromClient to stream requests. + /// + public Task ClientStreamingCallAsync() + { + lock (myLock) + { + Preconditions.CheckState(!started); + started = true; + + Initialize(environment.CompletionQueue); + + readingDone = true; + + unaryResponseTcs = new TaskCompletionSource(); + using (var metadataArray = MetadataArraySafeHandle.Create(details.Options.Headers)) + { + call.StartClientStreaming(HandleUnaryResponse, metadataArray); + } + + return unaryResponseTcs.Task; + } + } + + /// + /// Starts a unary request - streamed response call. + /// + public void StartServerStreamingCall(TRequest msg) + { + lock (myLock) + { + Preconditions.CheckState(!started); + started = true; + + Initialize(environment.CompletionQueue); + + halfcloseRequested = true; + + byte[] payload = UnsafeSerialize(msg); + + using (var metadataArray = MetadataArraySafeHandle.Create(details.Options.Headers)) + { + call.StartServerStreaming(HandleFinished, payload, metadataArray, GetWriteFlagsForCall()); + } + call.StartReceiveInitialMetadata(HandleReceivedResponseHeaders); + } + } + + /// + /// Starts a streaming request - streaming response call. + /// Use StartSendMessage and StartSendCloseFromClient to stream requests. + /// + public void StartDuplexStreamingCall() + { + lock (myLock) + { + Preconditions.CheckState(!started); + started = true; + + Initialize(environment.CompletionQueue); + + using (var metadataArray = MetadataArraySafeHandle.Create(details.Options.Headers)) + { + call.StartDuplexStreaming(HandleFinished, metadataArray); + } + call.StartReceiveInitialMetadata(HandleReceivedResponseHeaders); + } + } + + /// + /// Sends a streaming request. Only one pending send action is allowed at any given time. + /// completionDelegate is called when the operation finishes. + /// + public void StartSendMessage(TRequest msg, WriteFlags writeFlags, AsyncCompletionDelegate completionDelegate) + { + StartSendMessageInternal(msg, writeFlags, completionDelegate); + } + + /// + /// Receives a streaming response. Only one pending read action is allowed at any given time. + /// completionDelegate is called when the operation finishes. + /// + public void StartReadMessage(AsyncCompletionDelegate completionDelegate) + { + StartReadMessageInternal(completionDelegate); + } + + /// + /// Sends halfclose, indicating client is done with streaming requests. + /// Only one pending send action is allowed at any given time. + /// completionDelegate is called when the operation finishes. + /// + public void StartSendCloseFromClient(AsyncCompletionDelegate completionDelegate) + { + lock (myLock) + { + Preconditions.CheckNotNull(completionDelegate, "Completion delegate cannot be null"); + CheckSendingAllowed(); + + call.StartSendCloseFromClient(HandleHalfclosed); + + halfcloseRequested = true; + sendCompletionDelegate = completionDelegate; + } + } + + /// + /// Get the task that completes once if streaming call finishes with ok status and throws RpcException with given status otherwise. + /// + public Task StreamingCallFinishedTask + { + get + { + return streamingCallFinishedTcs.Task; + } + } + + /// + /// Get the task that completes once response headers are received. + /// + public Task ResponseHeadersAsync + { + get + { + return responseHeadersTcs.Task; + } + } + + /// + /// Gets the resulting status if the call has already finished. + /// Throws InvalidOperationException otherwise. + /// + public Status GetStatus() + { + lock (myLock) + { + Preconditions.CheckState(finishedStatus.HasValue, "Status can only be accessed once the call has finished."); + return finishedStatus.Value.Status; + } + } + + /// + /// Gets the trailing metadata if the call has already finished. + /// Throws InvalidOperationException otherwise. + /// + public Metadata GetTrailers() + { + lock (myLock) + { + Preconditions.CheckState(finishedStatus.HasValue, "Trailers can only be accessed once the call has finished."); + return finishedStatus.Value.Trailers; + } + } + + public CallInvocationDetails Details + { + get + { + return this.details; + } + } + + protected override void OnAfterReleaseResources() + { + details.Channel.RemoveCallReference(this); + } + + protected override bool IsClient + { + get { return true; } + } + + private void Initialize(CompletionQueueSafeHandle cq) + { + var call = CreateNativeCall(cq); + details.Channel.AddCallReference(this); + InitializeInternal(call); + RegisterCancellationCallback(); + } + + private INativeCall CreateNativeCall(CompletionQueueSafeHandle cq) + { + if (injectedNativeCall != null) + { + return injectedNativeCall; // allows injecting a mock INativeCall in tests. + } + + var parentCall = details.Options.PropagationToken != null ? details.Options.PropagationToken.ParentCall : CallSafeHandle.NullInstance; + + return details.Channel.Handle.CreateCall(environment.CompletionRegistry, + parentCall, ContextPropagationToken.DefaultMask, cq, + details.Method, details.Host, Timespec.FromDateTime(details.Options.Deadline.Value)); + } + + // Make sure that once cancellationToken for this call is cancelled, Cancel() will be called. + private void RegisterCancellationCallback() + { + var token = details.Options.CancellationToken; + if (token.CanBeCanceled) + { + token.Register(() => this.Cancel()); + } + } + + /// + /// Gets WriteFlags set in callDetails.Options.WriteOptions + /// + private WriteFlags GetWriteFlagsForCall() + { + var writeOptions = details.Options.WriteOptions; + return writeOptions != null ? writeOptions.Flags : default(WriteFlags); + } + + /// + /// Handles receive status completion for calls with streaming response. + /// + private void HandleReceivedResponseHeaders(bool success, Metadata responseHeaders) + { + responseHeadersTcs.SetResult(responseHeaders); + } + + /// + /// Handler for unary response completion. + /// + private void HandleUnaryResponse(bool success, ClientSideStatus receivedStatus, byte[] receivedMessage, Metadata responseHeaders) + { + TResponse msg = default(TResponse); + var deserializeException = success ? TryDeserialize(receivedMessage, out msg) : null; + + lock (myLock) + { + finished = true; + + if (deserializeException != null && receivedStatus.Status.StatusCode == StatusCode.OK) + { + receivedStatus = new ClientSideStatus(DeserializeResponseFailureStatus, receivedStatus.Trailers); + } + finishedStatus = receivedStatus; + + ReleaseResourcesIfPossible(); + } + + responseHeadersTcs.SetResult(responseHeaders); + + var status = receivedStatus.Status; + + if (!success || status.StatusCode != StatusCode.OK) + { + unaryResponseTcs.SetException(new RpcException(status)); + return; + } + + unaryResponseTcs.SetResult(msg); + } + + /// + /// Handles receive status completion for calls with streaming response. + /// + private void HandleFinished(bool success, ClientSideStatus receivedStatus) + { + lock (myLock) + { + finished = true; + finishedStatus = receivedStatus; + + ReleaseResourcesIfPossible(); + } + + var status = receivedStatus.Status; + + if (!success || status.StatusCode != StatusCode.OK) + { + streamingCallFinishedTcs.SetException(new RpcException(status)); + return; + } + + streamingCallFinishedTcs.SetResult(null); + } + } +} \ No newline at end of file diff --git a/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs b/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs new file mode 100644 index 00000000..3e2c57c9 --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs @@ -0,0 +1,361 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; + +using Grpc.Core.Internal; +using Grpc.Core.Logging; +using Grpc.Core.Utils; + +namespace Grpc.Core.Internal +{ + /// + /// Base for handling both client side and server side calls. + /// Manages native call lifecycle and provides convenience methods. + /// + internal abstract class AsyncCallBase + { + static readonly ILogger Logger = GrpcEnvironment.Logger.ForType>(); + protected static readonly Status DeserializeResponseFailureStatus = new Status(StatusCode.Internal, "Failed to deserialize response message."); + + readonly Func serializer; + readonly Func deserializer; + + protected readonly GrpcEnvironment environment; + protected readonly object myLock = new object(); + + protected INativeCall call; + protected bool disposed; + + protected bool started; + protected bool cancelRequested; + + protected AsyncCompletionDelegate sendCompletionDelegate; // Completion of a pending send or sendclose if not null. + protected AsyncCompletionDelegate readCompletionDelegate; // Completion of a pending send or sendclose if not null. + + protected bool readingDone; // True if last read (i.e. read with null payload) was already received. + protected bool halfcloseRequested; // True if send close have been initiated. + protected bool finished; // True if close has been received from the peer. + + protected bool initialMetadataSent; + protected long streamingWritesCounter; // Number of streaming send operations started so far. + + public AsyncCallBase(Func serializer, Func deserializer, GrpcEnvironment environment) + { + this.serializer = Preconditions.CheckNotNull(serializer); + this.deserializer = Preconditions.CheckNotNull(deserializer); + this.environment = Preconditions.CheckNotNull(environment); + } + + /// + /// Requests cancelling the call. + /// + public void Cancel() + { + lock (myLock) + { + Preconditions.CheckState(started); + cancelRequested = true; + + if (!disposed) + { + call.Cancel(); + } + } + } + + /// + /// Requests cancelling the call with given status. + /// + protected void CancelWithStatus(Status status) + { + lock (myLock) + { + cancelRequested = true; + + if (!disposed) + { + call.CancelWithStatus(status); + } + } + } + + protected void InitializeInternal(INativeCall call) + { + lock (myLock) + { + this.call = call; + } + } + + /// + /// Initiates sending a message. Only one send operation can be active at a time. + /// completionDelegate is invoked upon completion. + /// + protected void StartSendMessageInternal(TWrite msg, WriteFlags writeFlags, AsyncCompletionDelegate completionDelegate) + { + byte[] payload = UnsafeSerialize(msg); + + lock (myLock) + { + Preconditions.CheckNotNull(completionDelegate, "Completion delegate cannot be null"); + CheckSendingAllowed(); + + call.StartSendMessage(HandleSendFinished, payload, writeFlags, !initialMetadataSent); + + sendCompletionDelegate = completionDelegate; + initialMetadataSent = true; + streamingWritesCounter++; + } + } + + /// + /// Initiates reading a message. Only one read operation can be active at a time. + /// completionDelegate is invoked upon completion. + /// + protected void StartReadMessageInternal(AsyncCompletionDelegate completionDelegate) + { + lock (myLock) + { + Preconditions.CheckNotNull(completionDelegate, "Completion delegate cannot be null"); + CheckReadingAllowed(); + + call.StartReceiveMessage(HandleReadFinished); + readCompletionDelegate = completionDelegate; + } + } + + /// + /// If there are no more pending actions and no new actions can be started, releases + /// the underlying native resources. + /// + protected bool ReleaseResourcesIfPossible() + { + if (!disposed && call != null) + { + bool noMoreSendCompletions = sendCompletionDelegate == null && (halfcloseRequested || cancelRequested || finished); + if (noMoreSendCompletions && readingDone && finished) + { + ReleaseResources(); + return true; + } + } + return false; + } + + protected abstract bool IsClient + { + get; + } + + private void ReleaseResources() + { + if (call != null) + { + call.Dispose(); + } + disposed = true; + OnAfterReleaseResources(); + } + + protected virtual void OnAfterReleaseResources() + { + } + + protected void CheckSendingAllowed() + { + Preconditions.CheckState(started); + CheckNotCancelled(); + Preconditions.CheckState(!disposed); + + Preconditions.CheckState(!halfcloseRequested, "Already halfclosed."); + Preconditions.CheckState(!finished, "Already finished."); + Preconditions.CheckState(sendCompletionDelegate == null, "Only one write can be pending at a time"); + } + + protected virtual void CheckReadingAllowed() + { + Preconditions.CheckState(started); + Preconditions.CheckState(!disposed); + + Preconditions.CheckState(!readingDone, "Stream has already been closed."); + Preconditions.CheckState(readCompletionDelegate == null, "Only one read can be pending at a time"); + } + + protected void CheckNotCancelled() + { + if (cancelRequested) + { + throw new OperationCanceledException("Remote call has been cancelled."); + } + } + + protected byte[] UnsafeSerialize(TWrite msg) + { + return serializer(msg); + } + + protected Exception TrySerialize(TWrite msg, out byte[] payload) + { + try + { + payload = serializer(msg); + return null; + } + catch (Exception e) + { + payload = null; + return e; + } + } + + protected Exception TryDeserialize(byte[] payload, out TRead msg) + { + try + { + msg = deserializer(payload); + return null; + } + catch (Exception e) + { + msg = default(TRead); + return e; + } + } + + protected void FireCompletion(AsyncCompletionDelegate completionDelegate, T value, Exception error) + { + try + { + completionDelegate(value, error); + } + catch (Exception e) + { + Logger.Error(e, "Exception occured while invoking completion delegate."); + } + } + + /// + /// Handles send completion. + /// + protected void HandleSendFinished(bool success) + { + AsyncCompletionDelegate origCompletionDelegate = null; + lock (myLock) + { + origCompletionDelegate = sendCompletionDelegate; + sendCompletionDelegate = null; + + ReleaseResourcesIfPossible(); + } + + if (!success) + { + FireCompletion(origCompletionDelegate, null, new InvalidOperationException("Send failed")); + } + else + { + FireCompletion(origCompletionDelegate, null, null); + } + } + + /// + /// Handles halfclose completion. + /// + protected void HandleHalfclosed(bool success) + { + AsyncCompletionDelegate origCompletionDelegate = null; + lock (myLock) + { + origCompletionDelegate = sendCompletionDelegate; + sendCompletionDelegate = null; + + ReleaseResourcesIfPossible(); + } + + if (!success) + { + FireCompletion(origCompletionDelegate, null, new InvalidOperationException("Halfclose failed")); + } + else + { + FireCompletion(origCompletionDelegate, null, null); + } + } + + /// + /// Handles streaming read completion. + /// + protected void HandleReadFinished(bool success, byte[] receivedMessage) + { + TRead msg = default(TRead); + var deserializeException = (success && receivedMessage != null) ? TryDeserialize(receivedMessage, out msg) : null; + + AsyncCompletionDelegate origCompletionDelegate = null; + lock (myLock) + { + origCompletionDelegate = readCompletionDelegate; + readCompletionDelegate = null; + + if (receivedMessage == null) + { + // This was the last read. + readingDone = true; + } + + if (deserializeException != null && IsClient) + { + readingDone = true; + CancelWithStatus(DeserializeResponseFailureStatus); + } + + ReleaseResourcesIfPossible(); + } + + // TODO: handle the case when success==false + + if (deserializeException != null && !IsClient) + { + FireCompletion(origCompletionDelegate, default(TRead), new IOException("Failed to deserialize request message.", deserializeException)); + return; + } + FireCompletion(origCompletionDelegate, msg, null); + } + } +} \ No newline at end of file diff --git a/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs b/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs new file mode 100644 index 00000000..46ca4593 --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs @@ -0,0 +1,218 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core.Internal; +using Grpc.Core.Utils; + +namespace Grpc.Core.Internal +{ + /// + /// Manages server side native call lifecycle. + /// + internal class AsyncCallServer : AsyncCallBase + { + readonly TaskCompletionSource finishedServersideTcs = new TaskCompletionSource(); + readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); + readonly Server server; + + public AsyncCallServer(Func serializer, Func deserializer, GrpcEnvironment environment, Server server) : base(serializer, deserializer, environment) + { + this.server = Preconditions.CheckNotNull(server); + } + + public void Initialize(CallSafeHandle call) + { + call.SetCompletionRegistry(environment.CompletionRegistry); + + server.AddCallReference(this); + InitializeInternal(call); + } + + /// + /// Starts a server side call. + /// + public Task ServerSideCallAsync() + { + lock (myLock) + { + Preconditions.CheckNotNull(call); + + started = true; + + call.StartServerSide(HandleFinishedServerside); + return finishedServersideTcs.Task; + } + } + + /// + /// Sends a streaming response. Only one pending send action is allowed at any given time. + /// completionDelegate is called when the operation finishes. + /// + public void StartSendMessage(TResponse msg, WriteFlags writeFlags, AsyncCompletionDelegate completionDelegate) + { + StartSendMessageInternal(msg, writeFlags, completionDelegate); + } + + /// + /// Receives a streaming request. Only one pending read action is allowed at any given time. + /// completionDelegate is called when the operation finishes. + /// + public void StartReadMessage(AsyncCompletionDelegate completionDelegate) + { + StartReadMessageInternal(completionDelegate); + } + + /// + /// Initiates sending a initial metadata. + /// Even though C-core allows sending metadata in parallel to sending messages, we will treat sending metadata as a send message operation + /// to make things simpler. + /// completionDelegate is invoked upon completion. + /// + public void StartSendInitialMetadata(Metadata headers, AsyncCompletionDelegate completionDelegate) + { + lock (myLock) + { + Preconditions.CheckNotNull(headers, "metadata"); + Preconditions.CheckNotNull(completionDelegate, "Completion delegate cannot be null"); + + Preconditions.CheckState(!initialMetadataSent, "Response headers can only be sent once per call."); + Preconditions.CheckState(streamingWritesCounter == 0, "Response headers can only be sent before the first write starts."); + CheckSendingAllowed(); + + Preconditions.CheckNotNull(completionDelegate, "Completion delegate cannot be null"); + + using (var metadataArray = MetadataArraySafeHandle.Create(headers)) + { + call.StartSendInitialMetadata(HandleSendFinished, metadataArray); + } + + this.initialMetadataSent = true; + sendCompletionDelegate = completionDelegate; + } + } + + /// + /// Sends call result status, also indicating server is done with streaming responses. + /// Only one pending send action is allowed at any given time. + /// completionDelegate is called when the operation finishes. + /// + public void StartSendStatusFromServer(Status status, Metadata trailers, AsyncCompletionDelegate completionDelegate) + { + lock (myLock) + { + Preconditions.CheckNotNull(completionDelegate, "Completion delegate cannot be null"); + CheckSendingAllowed(); + + using (var metadataArray = MetadataArraySafeHandle.Create(trailers)) + { + call.StartSendStatusFromServer(HandleHalfclosed, status, metadataArray, !initialMetadataSent); + } + halfcloseRequested = true; + readingDone = true; + sendCompletionDelegate = completionDelegate; + } + } + + /// + /// Gets cancellation token that gets cancelled once close completion + /// is received and the cancelled flag is set. + /// + public CancellationToken CancellationToken + { + get + { + return cancellationTokenSource.Token; + } + } + + public string Peer + { + get + { + return call.GetPeer(); + } + } + + protected override bool IsClient + { + get { return false; } + } + + protected override void CheckReadingAllowed() + { + base.CheckReadingAllowed(); + Preconditions.CheckArgument(!cancelRequested); + } + + protected override void OnAfterReleaseResources() + { + server.RemoveCallReference(this); + } + + /// + /// Handles the server side close completion. + /// + private void HandleFinishedServerside(bool success, bool cancelled) + { + lock (myLock) + { + finished = true; + + if (cancelled) + { + // Once we cancel, we don't have to care that much + // about reads and writes. + + // TODO(jtattermusch): is this still necessary? + Cancel(); + } + + ReleaseResourcesIfPossible(); + } + // TODO(jtattermusch): handle error + + if (cancelled) + { + cancellationTokenSource.Cancel(); + } + + finishedServersideTcs.SetResult(null); + } + } +} \ No newline at end of file diff --git a/src/csharp/Grpc.Core/Internal/AsyncCompletion.cs b/src/csharp/Grpc.Core/Internal/AsyncCompletion.cs new file mode 100644 index 00000000..c88cae98 --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/AsyncCompletion.cs @@ -0,0 +1,94 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core.Internal; +using Grpc.Core.Utils; + +namespace Grpc.Core.Internal +{ + /// + /// If error != null, there's been an error or operation has been cancelled. + /// + internal delegate void AsyncCompletionDelegate(T result, Exception error); + + /// + /// Helper for transforming AsyncCompletionDelegate into full-fledged Task. + /// + internal class AsyncCompletionTaskSource + { + readonly TaskCompletionSource tcs = new TaskCompletionSource(); + readonly AsyncCompletionDelegate completionDelegate; + + public AsyncCompletionTaskSource() + { + completionDelegate = new AsyncCompletionDelegate(HandleCompletion); + } + + public Task Task + { + get + { + return tcs.Task; + } + } + + public AsyncCompletionDelegate CompletionDelegate + { + get + { + return completionDelegate; + } + } + + private void HandleCompletion(T value, Exception error) + { + if (error == null) + { + tcs.SetResult(value); + return; + } + if (error is OperationCanceledException) + { + tcs.SetCanceled(); + return; + } + tcs.SetException(error); + } + } +} \ No newline at end of file diff --git a/src/csharp/Grpc.Core/Internal/AtomicCounter.cs b/src/csharp/Grpc.Core/Internal/AtomicCounter.cs new file mode 100644 index 00000000..7ccda225 --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/AtomicCounter.cs @@ -0,0 +1,61 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Threading; + +namespace Grpc.Core.Internal +{ + internal class AtomicCounter + { + long counter = 0; + + public void Increment() + { + Interlocked.Increment(ref counter); + } + + public void Decrement() + { + Interlocked.Decrement(ref counter); + } + + public long Count + { + get + { + return counter; + } + } + } +} diff --git a/src/csharp/Grpc.Core/Internal/BatchContextSafeHandle.cs b/src/csharp/Grpc.Core/Internal/BatchContextSafeHandle.cs new file mode 100644 index 00000000..3a96414b --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/BatchContextSafeHandle.cs @@ -0,0 +1,266 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Runtime.InteropServices; +using Grpc.Core; + +namespace Grpc.Core.Internal +{ + /// + /// grpcsharp_batch_context + /// + internal class BatchContextSafeHandle : SafeHandleZeroIsInvalid + { + [DllImport("grpc_csharp_ext.dll")] + static extern BatchContextSafeHandle grpcsharp_batch_context_create(); + + [DllImport("grpc_csharp_ext.dll")] + static extern IntPtr grpcsharp_batch_context_recv_initial_metadata(BatchContextSafeHandle ctx); + + [DllImport("grpc_csharp_ext.dll")] + static extern IntPtr grpcsharp_batch_context_recv_message_length(BatchContextSafeHandle ctx); + + [DllImport("grpc_csharp_ext.dll")] + static extern void grpcsharp_batch_context_recv_message_to_buffer(BatchContextSafeHandle ctx, byte[] buffer, UIntPtr bufferLen); + + [DllImport("grpc_csharp_ext.dll")] + static extern StatusCode grpcsharp_batch_context_recv_status_on_client_status(BatchContextSafeHandle ctx); + + [DllImport("grpc_csharp_ext.dll")] + static extern IntPtr grpcsharp_batch_context_recv_status_on_client_details(BatchContextSafeHandle ctx); // returns const char* + + [DllImport("grpc_csharp_ext.dll")] + static extern IntPtr grpcsharp_batch_context_recv_status_on_client_trailing_metadata(BatchContextSafeHandle ctx); + + [DllImport("grpc_csharp_ext.dll")] + static extern CallSafeHandle grpcsharp_batch_context_server_rpc_new_call(BatchContextSafeHandle ctx); + + [DllImport("grpc_csharp_ext.dll")] + static extern IntPtr grpcsharp_batch_context_server_rpc_new_method(BatchContextSafeHandle ctx); // returns const char* + + [DllImport("grpc_csharp_ext.dll")] + static extern IntPtr grpcsharp_batch_context_server_rpc_new_host(BatchContextSafeHandle ctx); // returns const char* + + [DllImport("grpc_csharp_ext.dll")] + static extern Timespec grpcsharp_batch_context_server_rpc_new_deadline(BatchContextSafeHandle ctx); + + [DllImport("grpc_csharp_ext.dll")] + static extern IntPtr grpcsharp_batch_context_server_rpc_new_request_metadata(BatchContextSafeHandle ctx); + + [DllImport("grpc_csharp_ext.dll")] + static extern int grpcsharp_batch_context_recv_close_on_server_cancelled(BatchContextSafeHandle ctx); + + [DllImport("grpc_csharp_ext.dll")] + static extern void grpcsharp_batch_context_destroy(IntPtr ctx); + + private BatchContextSafeHandle() + { + } + + public static BatchContextSafeHandle Create() + { + return grpcsharp_batch_context_create(); + } + + public IntPtr Handle + { + get + { + return handle; + } + } + + // Gets data of recv_initial_metadata completion. + public Metadata GetReceivedInitialMetadata() + { + IntPtr metadataArrayPtr = grpcsharp_batch_context_recv_initial_metadata(this); + return MetadataArraySafeHandle.ReadMetadataFromPtrUnsafe(metadataArrayPtr); + } + + // Gets data of recv_status_on_client completion. + public ClientSideStatus GetReceivedStatusOnClient() + { + string details = Marshal.PtrToStringAnsi(grpcsharp_batch_context_recv_status_on_client_details(this)); + var status = new Status(grpcsharp_batch_context_recv_status_on_client_status(this), details); + + IntPtr metadataArrayPtr = grpcsharp_batch_context_recv_status_on_client_trailing_metadata(this); + var metadata = MetadataArraySafeHandle.ReadMetadataFromPtrUnsafe(metadataArrayPtr); + + return new ClientSideStatus(status, metadata); + } + + // Gets data of recv_message completion. + public byte[] GetReceivedMessage() + { + IntPtr len = grpcsharp_batch_context_recv_message_length(this); + if (len == new IntPtr(-1)) + { + return null; + } + byte[] data = new byte[(int)len]; + grpcsharp_batch_context_recv_message_to_buffer(this, data, new UIntPtr((ulong)data.Length)); + return data; + } + + // Gets data of server_rpc_new completion. + public ServerRpcNew GetServerRpcNew(Server server) + { + var call = grpcsharp_batch_context_server_rpc_new_call(this); + + var method = Marshal.PtrToStringAnsi(grpcsharp_batch_context_server_rpc_new_method(this)); + var host = Marshal.PtrToStringAnsi(grpcsharp_batch_context_server_rpc_new_host(this)); + var deadline = grpcsharp_batch_context_server_rpc_new_deadline(this); + + IntPtr metadataArrayPtr = grpcsharp_batch_context_server_rpc_new_request_metadata(this); + var metadata = MetadataArraySafeHandle.ReadMetadataFromPtrUnsafe(metadataArrayPtr); + + return new ServerRpcNew(server, call, method, host, deadline, metadata); + } + + // Gets data of receive_close_on_server completion. + public bool GetReceivedCloseOnServerCancelled() + { + return grpcsharp_batch_context_recv_close_on_server_cancelled(this) != 0; + } + + protected override bool ReleaseHandle() + { + grpcsharp_batch_context_destroy(handle); + return true; + } + } + + /// + /// Status + metadata received on client side when call finishes. + /// (when receive_status_on_client operation finishes). + /// + internal struct ClientSideStatus + { + readonly Status status; + readonly Metadata trailers; + + public ClientSideStatus(Status status, Metadata trailers) + { + this.status = status; + this.trailers = trailers; + } + + public Status Status + { + get + { + return this.status; + } + } + + public Metadata Trailers + { + get + { + return this.trailers; + } + } + } + + /// + /// Details of a newly received RPC. + /// + internal struct ServerRpcNew + { + readonly Server server; + readonly CallSafeHandle call; + readonly string method; + readonly string host; + readonly Timespec deadline; + readonly Metadata requestMetadata; + + public ServerRpcNew(Server server, CallSafeHandle call, string method, string host, Timespec deadline, Metadata requestMetadata) + { + this.server = server; + this.call = call; + this.method = method; + this.host = host; + this.deadline = deadline; + this.requestMetadata = requestMetadata; + } + + public Server Server + { + get + { + return this.server; + } + } + + public CallSafeHandle Call + { + get + { + return this.call; + } + } + + public string Method + { + get + { + return this.method; + } + } + + public string Host + { + get + { + return this.host; + } + } + + public Timespec Deadline + { + get + { + return this.deadline; + } + } + + public Metadata RequestMetadata + { + get + { + return this.requestMetadata; + } + } + } +} \ No newline at end of file diff --git a/src/csharp/Grpc.Core/Internal/CStringSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CStringSafeHandle.cs new file mode 100644 index 00000000..92fbe8cf --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/CStringSafeHandle.cs @@ -0,0 +1,60 @@ +#region Copyright notice and license +// Copyright 2015, 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. +#endregion +using System; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +namespace Grpc.Core.Internal +{ + /// + /// Owned char* object. + /// + internal class CStringSafeHandle : SafeHandleZeroIsInvalid + { + [DllImport("grpc_csharp_ext.dll")] + static extern void gprsharp_free(IntPtr ptr); + + private CStringSafeHandle() + { + } + + public string GetValue() + { + return Marshal.PtrToStringAnsi(handle); + } + + protected override bool ReleaseHandle() + { + gprsharp_free(handle); + return true; + } + } +} diff --git a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs new file mode 100644 index 00000000..c3611a77 --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs @@ -0,0 +1,229 @@ +#region Copyright notice and license +// Copyright 2015, 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. + +#endregion +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using Grpc.Core; +using Grpc.Core.Utils; + +namespace Grpc.Core.Internal +{ + /// + /// grpc_call from grpc/grpc.h + /// + internal class CallSafeHandle : SafeHandleZeroIsInvalid, INativeCall + { + public static readonly CallSafeHandle NullInstance = new CallSafeHandle(); + + const uint GRPC_WRITE_BUFFER_HINT = 1; + CompletionRegistry completionRegistry; + + [DllImport("grpc_csharp_ext.dll")] + static extern GRPCCallError grpcsharp_call_cancel(CallSafeHandle call); + + [DllImport("grpc_csharp_ext.dll")] + static extern GRPCCallError grpcsharp_call_cancel_with_status(CallSafeHandle call, StatusCode status, string description); + + [DllImport("grpc_csharp_ext.dll")] + static extern GRPCCallError grpcsharp_call_start_unary(CallSafeHandle call, + BatchContextSafeHandle ctx, byte[] send_buffer, UIntPtr send_buffer_len, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags); + + [DllImport("grpc_csharp_ext.dll")] + static extern GRPCCallError grpcsharp_call_start_client_streaming(CallSafeHandle call, + BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray); + + [DllImport("grpc_csharp_ext.dll")] + static extern GRPCCallError grpcsharp_call_start_server_streaming(CallSafeHandle call, + BatchContextSafeHandle ctx, byte[] send_buffer, UIntPtr send_buffer_len, + MetadataArraySafeHandle metadataArray, WriteFlags writeFlags); + + [DllImport("grpc_csharp_ext.dll")] + static extern GRPCCallError grpcsharp_call_start_duplex_streaming(CallSafeHandle call, + BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray); + + [DllImport("grpc_csharp_ext.dll")] + static extern GRPCCallError grpcsharp_call_send_message(CallSafeHandle call, + BatchContextSafeHandle ctx, byte[] send_buffer, UIntPtr send_buffer_len, WriteFlags writeFlags, bool sendEmptyInitialMetadata); + + [DllImport("grpc_csharp_ext.dll")] + static extern GRPCCallError grpcsharp_call_send_close_from_client(CallSafeHandle call, + BatchContextSafeHandle ctx); + + [DllImport("grpc_csharp_ext.dll")] + static extern GRPCCallError grpcsharp_call_send_status_from_server(CallSafeHandle call, + BatchContextSafeHandle ctx, StatusCode statusCode, string statusMessage, MetadataArraySafeHandle metadataArray, bool sendEmptyInitialMetadata); + + [DllImport("grpc_csharp_ext.dll")] + static extern GRPCCallError grpcsharp_call_recv_message(CallSafeHandle call, + BatchContextSafeHandle ctx); + + [DllImport("grpc_csharp_ext.dll")] + static extern GRPCCallError grpcsharp_call_recv_initial_metadata(CallSafeHandle call, + BatchContextSafeHandle ctx); + + [DllImport("grpc_csharp_ext.dll")] + static extern GRPCCallError grpcsharp_call_start_serverside(CallSafeHandle call, + BatchContextSafeHandle ctx); + + [DllImport("grpc_csharp_ext.dll")] + static extern GRPCCallError grpcsharp_call_send_initial_metadata(CallSafeHandle call, + BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray); + + [DllImport("grpc_csharp_ext.dll")] + static extern CStringSafeHandle grpcsharp_call_get_peer(CallSafeHandle call); + + [DllImport("grpc_csharp_ext.dll")] + static extern void grpcsharp_call_destroy(IntPtr call); + + private CallSafeHandle() + { + } + + public void SetCompletionRegistry(CompletionRegistry completionRegistry) + { + this.completionRegistry = completionRegistry; + } + + public void StartUnary(UnaryResponseClientHandler callback, byte[] payload, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags) + { + var ctx = BatchContextSafeHandle.Create(); + completionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success, context.GetReceivedStatusOnClient(), context.GetReceivedMessage(), context.GetReceivedInitialMetadata())); + grpcsharp_call_start_unary(this, ctx, payload, new UIntPtr((ulong)payload.Length), metadataArray, writeFlags) + .CheckOk(); + } + + public void StartUnary(BatchContextSafeHandle ctx, byte[] payload, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags) + { + grpcsharp_call_start_unary(this, ctx, payload, new UIntPtr((ulong)payload.Length), metadataArray, writeFlags) + .CheckOk(); + } + + public void StartClientStreaming(UnaryResponseClientHandler callback, MetadataArraySafeHandle metadataArray) + { + var ctx = BatchContextSafeHandle.Create(); + completionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success, context.GetReceivedStatusOnClient(), context.GetReceivedMessage(), context.GetReceivedInitialMetadata())); + grpcsharp_call_start_client_streaming(this, ctx, metadataArray).CheckOk(); + } + + public void StartServerStreaming(ReceivedStatusOnClientHandler callback, byte[] payload, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags) + { + var ctx = BatchContextSafeHandle.Create(); + completionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success, context.GetReceivedStatusOnClient())); + grpcsharp_call_start_server_streaming(this, ctx, payload, new UIntPtr((ulong)payload.Length), metadataArray, writeFlags).CheckOk(); + } + + public void StartDuplexStreaming(ReceivedStatusOnClientHandler callback, MetadataArraySafeHandle metadataArray) + { + var ctx = BatchContextSafeHandle.Create(); + completionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success, context.GetReceivedStatusOnClient())); + grpcsharp_call_start_duplex_streaming(this, ctx, metadataArray).CheckOk(); + } + + public void StartSendMessage(SendCompletionHandler callback, byte[] payload, WriteFlags writeFlags, bool sendEmptyInitialMetadata) + { + var ctx = BatchContextSafeHandle.Create(); + completionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success)); + grpcsharp_call_send_message(this, ctx, payload, new UIntPtr((ulong)payload.Length), writeFlags, sendEmptyInitialMetadata).CheckOk(); + } + + public void StartSendCloseFromClient(SendCompletionHandler callback) + { + var ctx = BatchContextSafeHandle.Create(); + completionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success)); + grpcsharp_call_send_close_from_client(this, ctx).CheckOk(); + } + + public void StartSendStatusFromServer(SendCompletionHandler callback, Status status, MetadataArraySafeHandle metadataArray, bool sendEmptyInitialMetadata) + { + var ctx = BatchContextSafeHandle.Create(); + completionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success)); + grpcsharp_call_send_status_from_server(this, ctx, status.StatusCode, status.Detail, metadataArray, sendEmptyInitialMetadata).CheckOk(); + } + + public void StartReceiveMessage(ReceivedMessageHandler callback) + { + var ctx = BatchContextSafeHandle.Create(); + completionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success, context.GetReceivedMessage())); + grpcsharp_call_recv_message(this, ctx).CheckOk(); + } + + public void StartReceiveInitialMetadata(ReceivedResponseHeadersHandler callback) + { + var ctx = BatchContextSafeHandle.Create(); + completionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success, context.GetReceivedInitialMetadata())); + grpcsharp_call_recv_initial_metadata(this, ctx).CheckOk(); + } + + public void StartServerSide(ReceivedCloseOnServerHandler callback) + { + var ctx = BatchContextSafeHandle.Create(); + completionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success, context.GetReceivedCloseOnServerCancelled())); + grpcsharp_call_start_serverside(this, ctx).CheckOk(); + } + + public void StartSendInitialMetadata(SendCompletionHandler callback, MetadataArraySafeHandle metadataArray) + { + var ctx = BatchContextSafeHandle.Create(); + completionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success)); + grpcsharp_call_send_initial_metadata(this, ctx, metadataArray).CheckOk(); + } + + public void Cancel() + { + grpcsharp_call_cancel(this).CheckOk(); + } + + public void CancelWithStatus(Status status) + { + grpcsharp_call_cancel_with_status(this, status.StatusCode, status.Detail).CheckOk(); + } + + public string GetPeer() + { + using (var cstring = grpcsharp_call_get_peer(this)) + { + return cstring.GetValue(); + } + } + + protected override bool ReleaseHandle() + { + grpcsharp_call_destroy(handle); + return true; + } + + private static uint GetFlags(bool buffered) + { + return buffered ? 0 : GRPC_WRITE_BUFFER_HINT; + } + } +} \ No newline at end of file diff --git a/src/csharp/Grpc.Core/Internal/ChannelArgsSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ChannelArgsSafeHandle.cs new file mode 100644 index 00000000..ea5b5237 --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/ChannelArgsSafeHandle.cs @@ -0,0 +1,84 @@ +#region Copyright notice and license +// Copyright 2015, 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. +#endregion +using System; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +namespace Grpc.Core.Internal +{ + /// + /// grpc_channel_args from grpc/grpc.h + /// + internal class ChannelArgsSafeHandle : SafeHandleZeroIsInvalid + { + [DllImport("grpc_csharp_ext.dll")] + static extern ChannelArgsSafeHandle grpcsharp_channel_args_create(UIntPtr numArgs); + + [DllImport("grpc_csharp_ext.dll", CharSet = CharSet.Ansi)] + static extern void grpcsharp_channel_args_set_string(ChannelArgsSafeHandle args, UIntPtr index, string key, string value); + + [DllImport("grpc_csharp_ext.dll", CharSet = CharSet.Ansi)] + static extern void grpcsharp_channel_args_set_integer(ChannelArgsSafeHandle args, UIntPtr index, string key, int value); + + [DllImport("grpc_csharp_ext.dll")] + static extern void grpcsharp_channel_args_destroy(IntPtr args); + + private ChannelArgsSafeHandle() + { + } + + public static ChannelArgsSafeHandle CreateNull() + { + return new ChannelArgsSafeHandle(); + } + + public static ChannelArgsSafeHandle Create(int size) + { + return grpcsharp_channel_args_create(new UIntPtr((uint)size)); + } + + public void SetString(int index, string key, string value) + { + grpcsharp_channel_args_set_string(this, new UIntPtr((uint)index), key, value); + } + + public void SetInteger(int index, string key, int value) + { + grpcsharp_channel_args_set_integer(this, new UIntPtr((uint)index), key, value); + } + + protected override bool ReleaseHandle() + { + grpcsharp_channel_args_destroy(handle); + return true; + } + } +} diff --git a/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs new file mode 100644 index 00000000..7a1c6e3d --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs @@ -0,0 +1,120 @@ +#region Copyright notice and license +// Copyright 2015, 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. +#endregion +using System; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; + +namespace Grpc.Core.Internal +{ + /// + /// grpc_channel from grpc/grpc.h + /// + internal class ChannelSafeHandle : SafeHandleZeroIsInvalid + { + [DllImport("grpc_csharp_ext.dll")] + static extern ChannelSafeHandle grpcsharp_insecure_channel_create(string target, ChannelArgsSafeHandle channelArgs); + + [DllImport("grpc_csharp_ext.dll")] + static extern ChannelSafeHandle grpcsharp_secure_channel_create(CredentialsSafeHandle credentials, string target, ChannelArgsSafeHandle channelArgs); + + [DllImport("grpc_csharp_ext.dll")] + static extern CallSafeHandle grpcsharp_channel_create_call(ChannelSafeHandle channel, CallSafeHandle parentCall, ContextPropagationFlags propagationMask, CompletionQueueSafeHandle cq, string method, string host, Timespec deadline); + + [DllImport("grpc_csharp_ext.dll")] + static extern ChannelState grpcsharp_channel_check_connectivity_state(ChannelSafeHandle channel, int tryToConnect); + + [DllImport("grpc_csharp_ext.dll")] + static extern void grpcsharp_channel_watch_connectivity_state(ChannelSafeHandle channel, ChannelState lastObservedState, + Timespec deadline, CompletionQueueSafeHandle cq, BatchContextSafeHandle ctx); + + [DllImport("grpc_csharp_ext.dll")] + static extern CStringSafeHandle grpcsharp_channel_get_target(ChannelSafeHandle call); + + [DllImport("grpc_csharp_ext.dll")] + static extern void grpcsharp_channel_destroy(IntPtr channel); + + private ChannelSafeHandle() + { + } + + public static ChannelSafeHandle CreateInsecure(string target, ChannelArgsSafeHandle channelArgs) + { + // Increment reference count for the native gRPC environment to make sure we don't do grpc_shutdown() before destroying the server handle. + // Doing so would make object finalizer crash if we end up abandoning the handle. + GrpcEnvironment.GrpcNativeInit(); + return grpcsharp_insecure_channel_create(target, channelArgs); + } + + public static ChannelSafeHandle CreateSecure(CredentialsSafeHandle credentials, string target, ChannelArgsSafeHandle channelArgs) + { + // Increment reference count for the native gRPC environment to make sure we don't do grpc_shutdown() before destroying the server handle. + // Doing so would make object finalizer crash if we end up abandoning the handle. + GrpcEnvironment.GrpcNativeInit(); + return grpcsharp_secure_channel_create(credentials, target, channelArgs); + } + + public CallSafeHandle CreateCall(CompletionRegistry registry, CallSafeHandle parentCall, ContextPropagationFlags propagationMask, CompletionQueueSafeHandle cq, string method, string host, Timespec deadline) + { + var result = grpcsharp_channel_create_call(this, parentCall, propagationMask, cq, method, host, deadline); + result.SetCompletionRegistry(registry); + return result; + } + + public ChannelState CheckConnectivityState(bool tryToConnect) + { + return grpcsharp_channel_check_connectivity_state(this, tryToConnect ? 1 : 0); + } + + public void WatchConnectivityState(ChannelState lastObservedState, Timespec deadline, CompletionQueueSafeHandle cq, + CompletionRegistry completionRegistry, BatchCompletionDelegate callback) + { + var ctx = BatchContextSafeHandle.Create(); + completionRegistry.RegisterBatchCompletion(ctx, callback); + grpcsharp_channel_watch_connectivity_state(this, lastObservedState, deadline, cq, ctx); + } + + public string GetTarget() + { + using (var cstring = grpcsharp_channel_get_target(this)) + { + return cstring.GetValue(); + } + } + + protected override bool ReleaseHandle() + { + grpcsharp_channel_destroy(handle); + GrpcEnvironment.GrpcNativeShutdown(); + return true; + } + } +} diff --git a/src/csharp/Grpc.Core/Internal/ClientRequestStream.cs b/src/csharp/Grpc.Core/Internal/ClientRequestStream.cs new file mode 100644 index 00000000..013f00ff --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/ClientRequestStream.cs @@ -0,0 +1,84 @@ +#region Copyright notice and license +// Copyright 2015, 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. +#endregion +using System; +using System.Threading.Tasks; +using Grpc.Core.Internal; + +namespace Grpc.Core.Internal +{ + /// + /// Writes requests asynchronously to an underlying AsyncCall object. + /// + internal class ClientRequestStream : IClientStreamWriter + { + readonly AsyncCall call; + WriteOptions writeOptions; + + public ClientRequestStream(AsyncCall call) + { + this.call = call; + this.writeOptions = call.Details.Options.WriteOptions; + } + + public Task WriteAsync(TRequest message) + { + var taskSource = new AsyncCompletionTaskSource(); + call.StartSendMessage(message, GetWriteFlags(), taskSource.CompletionDelegate); + return taskSource.Task; + } + + public Task CompleteAsync() + { + var taskSource = new AsyncCompletionTaskSource(); + call.StartSendCloseFromClient(taskSource.CompletionDelegate); + return taskSource.Task; + } + + public WriteOptions WriteOptions + { + get + { + return this.writeOptions; + } + + set + { + writeOptions = value; + } + } + + private WriteFlags GetWriteFlags() + { + var options = writeOptions; + return options != null ? options.Flags : default(WriteFlags); + } + } +} diff --git a/src/csharp/Grpc.Core/Internal/ClientResponseStream.cs b/src/csharp/Grpc.Core/Internal/ClientResponseStream.cs new file mode 100644 index 00000000..b4a7335c --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/ClientResponseStream.cs @@ -0,0 +1,89 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Grpc.Core.Internal +{ + internal class ClientResponseStream : IAsyncStreamReader + where TRequest : class + where TResponse : class + { + readonly AsyncCall call; + TResponse current; + + public ClientResponseStream(AsyncCall call) + { + this.call = call; + } + + public TResponse Current + { + get + { + if (current == null) + { + throw new InvalidOperationException("No current element is available."); + } + return current; + } + } + + public async Task MoveNext(CancellationToken token) + { + if (token != CancellationToken.None) + { + throw new InvalidOperationException("Cancellation of individual reads is not supported."); + } + var taskSource = new AsyncCompletionTaskSource(); + call.StartReadMessage(taskSource.CompletionDelegate); + var result = await taskSource.Task; + this.current = result; + + if (result == null) + { + await call.StreamingCallFinishedTask; + return false; + } + return true; + } + + public void Dispose() + { + // TODO(jtattermusch): implement the semantics of stream disposal. + } + } +} diff --git a/src/csharp/Grpc.Core/Internal/CompletionQueueEvent.cs b/src/csharp/Grpc.Core/Internal/CompletionQueueEvent.cs new file mode 100644 index 00000000..3f517514 --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/CompletionQueueEvent.cs @@ -0,0 +1,60 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Runtime.InteropServices; + +namespace Grpc.Core.Internal +{ + /// + /// grpc_event from grpc/grpc.h + /// + [StructLayout(LayoutKind.Sequential)] + internal struct CompletionQueueEvent + { + [DllImport("grpc_csharp_ext.dll")] + static extern int grpcsharp_sizeof_grpc_event(); + + public GRPCCompletionType type; + public int success; + public IntPtr tag; + + internal static int NativeSize + { + get + { + return grpcsharp_sizeof_grpc_event(); + } + } + } +} diff --git a/src/csharp/Grpc.Core/Internal/CompletionQueueSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CompletionQueueSafeHandle.cs new file mode 100644 index 00000000..f7a3471b --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/CompletionQueueSafeHandle.cs @@ -0,0 +1,87 @@ +#region Copyright notice and license +// Copyright 2015, 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. +#endregion +using System; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +namespace Grpc.Core.Internal +{ + /// + /// grpc_completion_queue from grpc/grpc.h + /// + internal class CompletionQueueSafeHandle : SafeHandleZeroIsInvalid + { + [DllImport("grpc_csharp_ext.dll")] + static extern CompletionQueueSafeHandle grpcsharp_completion_queue_create(); + + [DllImport("grpc_csharp_ext.dll")] + static extern void grpcsharp_completion_queue_shutdown(CompletionQueueSafeHandle cq); + + [DllImport("grpc_csharp_ext.dll")] + static extern CompletionQueueEvent grpcsharp_completion_queue_next(CompletionQueueSafeHandle cq); + + [DllImport("grpc_csharp_ext.dll")] + static extern CompletionQueueEvent grpcsharp_completion_queue_pluck(CompletionQueueSafeHandle cq, IntPtr tag); + + [DllImport("grpc_csharp_ext.dll")] + static extern void grpcsharp_completion_queue_destroy(IntPtr cq); + + private CompletionQueueSafeHandle() + { + } + + public static CompletionQueueSafeHandle Create() + { + return grpcsharp_completion_queue_create(); + } + + public CompletionQueueEvent Next() + { + return grpcsharp_completion_queue_next(this); + } + + public CompletionQueueEvent Pluck(IntPtr tag) + { + return grpcsharp_completion_queue_pluck(this, tag); + } + + public void Shutdown() + { + grpcsharp_completion_queue_shutdown(this); + } + + protected override bool ReleaseHandle() + { + grpcsharp_completion_queue_destroy(handle); + return true; + } + } +} diff --git a/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs b/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs new file mode 100644 index 00000000..2796c959 --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs @@ -0,0 +1,98 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using Grpc.Core.Logging; +using Grpc.Core.Utils; + +namespace Grpc.Core.Internal +{ + internal delegate void OpCompletionDelegate(bool success); + + internal delegate void BatchCompletionDelegate(bool success, BatchContextSafeHandle ctx); + + internal class CompletionRegistry + { + static readonly ILogger Logger = GrpcEnvironment.Logger.ForType(); + + readonly GrpcEnvironment environment; + readonly ConcurrentDictionary dict = new ConcurrentDictionary(); + + public CompletionRegistry(GrpcEnvironment environment) + { + this.environment = environment; + } + + public void Register(IntPtr key, OpCompletionDelegate callback) + { + environment.DebugStats.PendingBatchCompletions.Increment(); + Preconditions.CheckState(dict.TryAdd(key, callback)); + } + + public void RegisterBatchCompletion(BatchContextSafeHandle ctx, BatchCompletionDelegate callback) + { + OpCompletionDelegate opCallback = ((success) => HandleBatchCompletion(success, ctx, callback)); + Register(ctx.Handle, opCallback); + } + + public OpCompletionDelegate Extract(IntPtr key) + { + OpCompletionDelegate value; + Preconditions.CheckState(dict.TryRemove(key, out value)); + environment.DebugStats.PendingBatchCompletions.Decrement(); + return value; + } + + private static void HandleBatchCompletion(bool success, BatchContextSafeHandle ctx, BatchCompletionDelegate callback) + { + try + { + callback(success, ctx); + } + catch (Exception e) + { + Logger.Error(e, "Exception occured while invoking completion delegate."); + } + finally + { + if (ctx != null) + { + ctx.Dispose(); + } + } + } + } +} diff --git a/src/csharp/Grpc.Core/Internal/CredentialsSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CredentialsSafeHandle.cs new file mode 100644 index 00000000..feed3353 --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/CredentialsSafeHandle.cs @@ -0,0 +1,78 @@ +#region Copyright notice and license +// Copyright 2015, 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. +#endregion +using System; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; + +namespace Grpc.Core.Internal +{ + /// + /// grpc_credentials from grpc/grpc_security.h + /// + internal class CredentialsSafeHandle : SafeHandleZeroIsInvalid + { + [DllImport("grpc_csharp_ext.dll", CharSet = CharSet.Ansi)] + static extern CredentialsSafeHandle grpcsharp_ssl_credentials_create(string pemRootCerts, string keyCertPairCertChain, string keyCertPairPrivateKey); + + [DllImport("grpc_csharp_ext.dll")] + static extern void grpcsharp_credentials_release(IntPtr credentials); + + private CredentialsSafeHandle() + { + } + + public static CredentialsSafeHandle CreateNullCredentials() + { + var creds = new CredentialsSafeHandle(); + creds.SetHandle(IntPtr.Zero); + return creds; + } + + public static CredentialsSafeHandle CreateSslCredentials(string pemRootCerts, KeyCertificatePair keyCertPair) + { + if (keyCertPair != null) + { + return grpcsharp_ssl_credentials_create(pemRootCerts, keyCertPair.CertificateChain, keyCertPair.PrivateKey); + } + else + { + return grpcsharp_ssl_credentials_create(pemRootCerts, null, null); + } + } + + protected override bool ReleaseHandle() + { + grpcsharp_credentials_release(handle); + return true; + } + } +} diff --git a/src/csharp/Grpc.Core/Internal/DebugStats.cs b/src/csharp/Grpc.Core/Internal/DebugStats.cs new file mode 100644 index 00000000..1bea1adf --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/DebugStats.cs @@ -0,0 +1,60 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Threading; + +namespace Grpc.Core.Internal +{ + internal class DebugStats + { + public readonly AtomicCounter PendingBatchCompletions = new AtomicCounter(); + + /// + /// Checks the debug stats and take action for any inconsistency found. + /// + public void CheckOK() + { + var pendingBatchCompletions = PendingBatchCompletions.Count; + if (pendingBatchCompletions != 0) + { + DebugWarning(string.Format("Detected {0} pending batch completions.", pendingBatchCompletions)); + } + } + + private void DebugWarning(string message) + { + throw new Exception("Shutdown check: " + message); + } + } +} diff --git a/src/csharp/Grpc.Core/Internal/Enums.cs b/src/csharp/Grpc.Core/Internal/Enums.cs new file mode 100644 index 00000000..18509816 --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/Enums.cs @@ -0,0 +1,108 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Runtime.InteropServices; +using Grpc.Core.Utils; + +namespace Grpc.Core.Internal +{ + /// + /// grpc_call_error from grpc/grpc.h + /// + internal enum GRPCCallError + { + /* everything went ok */ + OK = 0, + /* something failed, we don't know what */ + Error, + /* this method is not available on the server */ + NotOnServer, + /* this method is not available on the client */ + NotOnClient, + /* this method must be called before server_accept */ + AlreadyAccepted, + /* this method must be called before invoke */ + AlreadyInvoked, + /* this method must be called after invoke */ + NotInvoked, + /* this call is already finished + (writes_done or write_status has already been called) */ + AlreadyFinished, + /* there is already an outstanding read/write operation on the call */ + TooManyOperations, + /* the flags value was illegal for this call */ + InvalidFlags + } + + internal static class CallErrorExtensions + { + /// + /// Checks the call API invocation's result is OK. + /// + public static void CheckOk(this GRPCCallError callError) + { + Preconditions.CheckState(callError == GRPCCallError.OK, "Call error: " + callError); + } + } + + /// + /// grpc_completion_type from grpc/grpc.h + /// + internal enum GRPCCompletionType + { + /* Shutting down */ + Shutdown, + + /* No event before timeout */ + Timeout, + + /* operation completion */ + OpComplete + } + + /// + /// gpr_clock_type from grpc/support/time.h + /// + internal enum GPRClockType + { + /* Monotonic clock */ + Monotonic, + + /* Realtime clock */ + Realtime, + + /* Timespan - the distance between two time points */ + Timespan + } +} diff --git a/src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs b/src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs new file mode 100644 index 00000000..4b7124ee --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs @@ -0,0 +1,139 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core.Logging; + +namespace Grpc.Core.Internal +{ + /// + /// Pool of threads polling on the same completion queue. + /// + internal class GrpcThreadPool + { + static readonly ILogger Logger = GrpcEnvironment.Logger.ForType(); + + readonly GrpcEnvironment environment; + readonly object myLock = new object(); + readonly List threads = new List(); + readonly int poolSize; + + CompletionQueueSafeHandle cq; + + public GrpcThreadPool(GrpcEnvironment environment, int poolSize) + { + this.environment = environment; + this.poolSize = poolSize; + } + + public void Start() + { + lock (myLock) + { + if (cq != null) + { + throw new InvalidOperationException("Already started."); + } + + cq = CompletionQueueSafeHandle.Create(); + + for (int i = 0; i < poolSize; i++) + { + threads.Add(CreateAndStartThread(i)); + } + } + } + + public void Stop() + { + lock (myLock) + { + cq.Shutdown(); + foreach (var thread in threads) + { + thread.Join(); + } + + cq.Dispose(); + } + } + + internal CompletionQueueSafeHandle CompletionQueue + { + get + { + return cq; + } + } + + private Thread CreateAndStartThread(int i) + { + var thread = new Thread(new ThreadStart(RunHandlerLoop)); + thread.IsBackground = false; + thread.Start(); + thread.Name = "grpc " + i; + return thread; + } + + /// + /// Body of the polling thread. + /// + private void RunHandlerLoop() + { + CompletionQueueEvent ev; + do + { + ev = cq.Next(); + if (ev.type == GRPCCompletionType.OpComplete) + { + bool success = (ev.success != 0); + IntPtr tag = ev.tag; + try + { + var callback = environment.CompletionRegistry.Extract(tag); + callback(success); + } + catch (Exception e) + { + Logger.Error(e, "Exception occured while invoking completion delegate"); + } + } + } + while (ev.type != GRPCCompletionType.Shutdown); + } + } +} diff --git a/src/csharp/Grpc.Core/Internal/INativeCall.cs b/src/csharp/Grpc.Core/Internal/INativeCall.cs new file mode 100644 index 00000000..cbef5991 --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/INativeCall.cs @@ -0,0 +1,85 @@ +#region Copyright notice and license +// Copyright 2015, 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. + +#endregion + +using System; + +namespace Grpc.Core.Internal +{ + internal delegate void UnaryResponseClientHandler(bool success, ClientSideStatus receivedStatus, byte[] receivedMessage, Metadata responseHeaders); + + // Received status for streaming response calls. + internal delegate void ReceivedStatusOnClientHandler(bool success, ClientSideStatus receivedStatus); + + internal delegate void ReceivedMessageHandler(bool success, byte[] receivedMessage); + + internal delegate void ReceivedResponseHeadersHandler(bool success, Metadata responseHeaders); + + internal delegate void SendCompletionHandler(bool success); + + internal delegate void ReceivedCloseOnServerHandler(bool success, bool cancelled); + + /// + /// Abstraction of a native call object. + /// + internal interface INativeCall : IDisposable + { + void Cancel(); + + void CancelWithStatus(Grpc.Core.Status status); + + string GetPeer(); + + void StartUnary(UnaryResponseClientHandler callback, byte[] payload, MetadataArraySafeHandle metadataArray, Grpc.Core.WriteFlags writeFlags); + + void StartUnary(BatchContextSafeHandle ctx, byte[] payload, MetadataArraySafeHandle metadataArray, Grpc.Core.WriteFlags writeFlags); + + void StartClientStreaming(UnaryResponseClientHandler callback, MetadataArraySafeHandle metadataArray); + + void StartServerStreaming(ReceivedStatusOnClientHandler callback, byte[] payload, MetadataArraySafeHandle metadataArray, Grpc.Core.WriteFlags writeFlags); + + void StartDuplexStreaming(ReceivedStatusOnClientHandler callback, MetadataArraySafeHandle metadataArray); + + void StartReceiveMessage(ReceivedMessageHandler callback); + + void StartReceiveInitialMetadata(ReceivedResponseHeadersHandler callback); + + void StartSendInitialMetadata(SendCompletionHandler callback, MetadataArraySafeHandle metadataArray); + + void StartSendMessage(SendCompletionHandler callback, byte[] payload, Grpc.Core.WriteFlags writeFlags, bool sendEmptyInitialMetadata); + + void StartSendCloseFromClient(SendCompletionHandler callback); + + void StartSendStatusFromServer(SendCompletionHandler callback, Grpc.Core.Status status, MetadataArraySafeHandle metadataArray, bool sendEmptyInitialMetadata); + + void StartServerSide(ReceivedCloseOnServerHandler callback); + } +} diff --git a/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs b/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs new file mode 100644 index 00000000..31b834c9 --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs @@ -0,0 +1,117 @@ +#region Copyright notice and license +// Copyright 2015, 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. +#endregion +using System; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +namespace Grpc.Core.Internal +{ + /// + /// grpc_metadata_array from grpc/grpc.h + /// + internal class MetadataArraySafeHandle : SafeHandleZeroIsInvalid + { + [DllImport("grpc_csharp_ext.dll")] + static extern MetadataArraySafeHandle grpcsharp_metadata_array_create(UIntPtr capacity); + + [DllImport("grpc_csharp_ext.dll", CharSet = CharSet.Ansi)] + static extern void grpcsharp_metadata_array_add(MetadataArraySafeHandle array, string key, byte[] value, UIntPtr valueLength); + + [DllImport("grpc_csharp_ext.dll")] + static extern UIntPtr grpcsharp_metadata_array_count(IntPtr metadataArray); + + [DllImport("grpc_csharp_ext.dll")] + static extern IntPtr grpcsharp_metadata_array_get_key(IntPtr metadataArray, UIntPtr index); + + [DllImport("grpc_csharp_ext.dll")] + static extern IntPtr grpcsharp_metadata_array_get_value(IntPtr metadataArray, UIntPtr index); + + [DllImport("grpc_csharp_ext.dll")] + static extern UIntPtr grpcsharp_metadata_array_get_value_length(IntPtr metadataArray, UIntPtr index); + + [DllImport("grpc_csharp_ext.dll")] + static extern void grpcsharp_metadata_array_destroy_full(IntPtr array); + + private MetadataArraySafeHandle() + { + } + + public static MetadataArraySafeHandle Create(Metadata metadata) + { + // TODO(jtattermusch): we might wanna check that the metadata is readonly + var metadataArray = grpcsharp_metadata_array_create(new UIntPtr((ulong)metadata.Count)); + for (int i = 0; i < metadata.Count; i++) + { + var valueBytes = metadata[i].GetSerializedValueUnsafe(); + grpcsharp_metadata_array_add(metadataArray, metadata[i].Key, valueBytes, new UIntPtr((ulong)valueBytes.Length)); + } + return metadataArray; + } + + /// + /// Reads metadata from pointer to grpc_metadata_array + /// + public static Metadata ReadMetadataFromPtrUnsafe(IntPtr metadataArray) + { + if (metadataArray == IntPtr.Zero) + { + return null; + } + + ulong count = grpcsharp_metadata_array_count(metadataArray).ToUInt64(); + + var metadata = new Metadata(); + for (ulong i = 0; i < count; i++) + { + var index = new UIntPtr(i); + string key = Marshal.PtrToStringAnsi(grpcsharp_metadata_array_get_key(metadataArray, index)); + var bytes = new byte[grpcsharp_metadata_array_get_value_length(metadataArray, index).ToUInt64()]; + Marshal.Copy(grpcsharp_metadata_array_get_value(metadataArray, index), bytes, 0, bytes.Length); + metadata.Add(Metadata.Entry.CreateUnsafe(key, bytes)); + } + return metadata; + } + + internal IntPtr Handle + { + get + { + return handle; + } + } + + protected override bool ReleaseHandle() + { + grpcsharp_metadata_array_destroy_full(handle); + return true; + } + } +} diff --git a/src/csharp/Grpc.Core/Internal/NativeLogRedirector.cs b/src/csharp/Grpc.Core/Internal/NativeLogRedirector.cs new file mode 100644 index 00000000..b8a55c5f --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/NativeLogRedirector.cs @@ -0,0 +1,107 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Collections.Concurrent; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Threading; + +namespace Grpc.Core.Internal +{ + internal delegate void GprLogDelegate(IntPtr fileStringPtr, int line, ulong threadId, IntPtr severityStringPtr, IntPtr msgPtr); + + /// + /// Logs from gRPC C core library can get lost if your application is not a console app. + /// This class allows redirection of logs to gRPC logger. + /// + internal static class NativeLogRedirector + { + static object staticLock = new object(); + static GprLogDelegate writeCallback; + + [DllImport("grpc_csharp_ext.dll")] + static extern void grpcsharp_redirect_log(GprLogDelegate callback); + + /// + /// Redirects logs from native gRPC C core library to a general logger. + /// + public static void Redirect() + { + lock (staticLock) + { + if (writeCallback == null) + { + writeCallback = new GprLogDelegate(HandleWrite); + grpcsharp_redirect_log(writeCallback); + } + } + } + + private static void HandleWrite(IntPtr fileStringPtr, int line, ulong threadId, IntPtr severityStringPtr, IntPtr msgPtr) + { + try + { + var logger = GrpcEnvironment.Logger; + string severityString = Marshal.PtrToStringAnsi(severityStringPtr); + string message = string.Format("{0} {1}:{2}: {3}", + threadId, + Marshal.PtrToStringAnsi(fileStringPtr), + line, + Marshal.PtrToStringAnsi(msgPtr)); + + switch (severityString) + { + case "D": + logger.Debug(message); + break; + case "I": + logger.Info(message); + break; + case "E": + logger.Error(message); + break; + default: + // severity not recognized, default to error. + logger.Error(message); + break; + } + } + catch (Exception e) + { + Console.WriteLine("Caught exception in native callback " + e); + } + } + } +} diff --git a/src/csharp/Grpc.Core/Internal/SafeHandleZeroIsInvalid.cs b/src/csharp/Grpc.Core/Internal/SafeHandleZeroIsInvalid.cs new file mode 100644 index 00000000..702aea28 --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/SafeHandleZeroIsInvalid.cs @@ -0,0 +1,66 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Runtime.InteropServices; + +namespace Grpc.Core.Internal +{ + /// + /// Safe handle to wrap native objects. + /// + internal abstract class SafeHandleZeroIsInvalid : SafeHandle + { + public SafeHandleZeroIsInvalid() : base(IntPtr.Zero, true) + { + } + + public SafeHandleZeroIsInvalid(bool ownsHandle) : base(IntPtr.Zero, ownsHandle) + { + } + + public override bool IsInvalid + { + get + { + return handle == IntPtr.Zero; + } + } + + protected override bool ReleaseHandle() + { + // handle is not owned. + return true; + } + } +} diff --git a/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs b/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs new file mode 100644 index 00000000..59f4c572 --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs @@ -0,0 +1,317 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core.Internal; +using Grpc.Core.Logging; +using Grpc.Core.Utils; + +namespace Grpc.Core.Internal +{ + internal interface IServerCallHandler + { + Task HandleCall(ServerRpcNew newRpc, GrpcEnvironment environment); + } + + internal class UnaryServerCallHandler : IServerCallHandler + where TRequest : class + where TResponse : class + { + static readonly ILogger Logger = GrpcEnvironment.Logger.ForType>(); + + readonly Method method; + readonly UnaryServerMethod handler; + + public UnaryServerCallHandler(Method method, UnaryServerMethod handler) + { + this.method = method; + this.handler = handler; + } + + public async Task HandleCall(ServerRpcNew newRpc, GrpcEnvironment environment) + { + var asyncCall = new AsyncCallServer( + method.ResponseMarshaller.Serializer, + method.RequestMarshaller.Deserializer, + environment, newRpc.Server); + + asyncCall.Initialize(newRpc.Call); + var finishedTask = asyncCall.ServerSideCallAsync(); + var requestStream = new ServerRequestStream(asyncCall); + var responseStream = new ServerResponseStream(asyncCall); + + Status status; + var context = HandlerUtils.NewContext(newRpc, asyncCall.Peer, responseStream, asyncCall.CancellationToken); + try + { + Preconditions.CheckArgument(await requestStream.MoveNext()); + var request = requestStream.Current; + // TODO(jtattermusch): we need to read the full stream so that native callhandle gets deallocated. + Preconditions.CheckArgument(!await requestStream.MoveNext()); + var result = await handler(request, context); + status = context.Status; + await responseStream.WriteAsync(result); + } + catch (Exception e) + { + Logger.Error(e, "Exception occured in handler."); + status = HandlerUtils.StatusFromException(e); + } + try + { + await responseStream.WriteStatusAsync(status, context.ResponseTrailers); + } + catch (OperationCanceledException) + { + // Call has been already cancelled. + } + await finishedTask; + } + } + + internal class ServerStreamingServerCallHandler : IServerCallHandler + where TRequest : class + where TResponse : class + { + static readonly ILogger Logger = GrpcEnvironment.Logger.ForType>(); + + readonly Method method; + readonly ServerStreamingServerMethod handler; + + public ServerStreamingServerCallHandler(Method method, ServerStreamingServerMethod handler) + { + this.method = method; + this.handler = handler; + } + + public async Task HandleCall(ServerRpcNew newRpc, GrpcEnvironment environment) + { + var asyncCall = new AsyncCallServer( + method.ResponseMarshaller.Serializer, + method.RequestMarshaller.Deserializer, + environment, newRpc.Server); + + asyncCall.Initialize(newRpc.Call); + var finishedTask = asyncCall.ServerSideCallAsync(); + var requestStream = new ServerRequestStream(asyncCall); + var responseStream = new ServerResponseStream(asyncCall); + + Status status; + var context = HandlerUtils.NewContext(newRpc, asyncCall.Peer, responseStream, asyncCall.CancellationToken); + try + { + Preconditions.CheckArgument(await requestStream.MoveNext()); + var request = requestStream.Current; + // TODO(jtattermusch): we need to read the full stream so that native callhandle gets deallocated. + Preconditions.CheckArgument(!await requestStream.MoveNext()); + await handler(request, responseStream, context); + status = context.Status; + } + catch (Exception e) + { + Logger.Error(e, "Exception occured in handler."); + status = HandlerUtils.StatusFromException(e); + } + + try + { + await responseStream.WriteStatusAsync(status, context.ResponseTrailers); + } + catch (OperationCanceledException) + { + // Call has been already cancelled. + } + await finishedTask; + } + } + + internal class ClientStreamingServerCallHandler : IServerCallHandler + where TRequest : class + where TResponse : class + { + static readonly ILogger Logger = GrpcEnvironment.Logger.ForType>(); + + readonly Method method; + readonly ClientStreamingServerMethod handler; + + public ClientStreamingServerCallHandler(Method method, ClientStreamingServerMethod handler) + { + this.method = method; + this.handler = handler; + } + + public async Task HandleCall(ServerRpcNew newRpc, GrpcEnvironment environment) + { + var asyncCall = new AsyncCallServer( + method.ResponseMarshaller.Serializer, + method.RequestMarshaller.Deserializer, + environment, newRpc.Server); + + asyncCall.Initialize(newRpc.Call); + var finishedTask = asyncCall.ServerSideCallAsync(); + var requestStream = new ServerRequestStream(asyncCall); + var responseStream = new ServerResponseStream(asyncCall); + + Status status; + var context = HandlerUtils.NewContext(newRpc, asyncCall.Peer, responseStream, asyncCall.CancellationToken); + try + { + var result = await handler(requestStream, context); + status = context.Status; + try + { + await responseStream.WriteAsync(result); + } + catch (OperationCanceledException) + { + status = Status.DefaultCancelled; + } + } + catch (Exception e) + { + Logger.Error(e, "Exception occured in handler."); + status = HandlerUtils.StatusFromException(e); + } + + try + { + await responseStream.WriteStatusAsync(status, context.ResponseTrailers); + } + catch (OperationCanceledException) + { + // Call has been already cancelled. + } + await finishedTask; + } + } + + internal class DuplexStreamingServerCallHandler : IServerCallHandler + where TRequest : class + where TResponse : class + { + static readonly ILogger Logger = GrpcEnvironment.Logger.ForType>(); + + readonly Method method; + readonly DuplexStreamingServerMethod handler; + + public DuplexStreamingServerCallHandler(Method method, DuplexStreamingServerMethod handler) + { + this.method = method; + this.handler = handler; + } + + public async Task HandleCall(ServerRpcNew newRpc, GrpcEnvironment environment) + { + var asyncCall = new AsyncCallServer( + method.ResponseMarshaller.Serializer, + method.RequestMarshaller.Deserializer, + environment, newRpc.Server); + + asyncCall.Initialize(newRpc.Call); + var finishedTask = asyncCall.ServerSideCallAsync(); + var requestStream = new ServerRequestStream(asyncCall); + var responseStream = new ServerResponseStream(asyncCall); + + Status status; + var context = HandlerUtils.NewContext(newRpc, asyncCall.Peer, responseStream, asyncCall.CancellationToken); + try + { + await handler(requestStream, responseStream, context); + status = context.Status; + } + catch (Exception e) + { + Logger.Error(e, "Exception occured in handler."); + status = HandlerUtils.StatusFromException(e); + } + try + { + await responseStream.WriteStatusAsync(status, context.ResponseTrailers); + } + catch (OperationCanceledException) + { + // Call has been already cancelled. + } + await finishedTask; + } + } + + internal class NoSuchMethodCallHandler : IServerCallHandler + { + public static readonly NoSuchMethodCallHandler Instance = new NoSuchMethodCallHandler(); + + public async Task HandleCall(ServerRpcNew newRpc, GrpcEnvironment environment) + { + // We don't care about the payload type here. + var asyncCall = new AsyncCallServer( + (payload) => payload, (payload) => payload, environment, newRpc.Server); + + asyncCall.Initialize(newRpc.Call); + var finishedTask = asyncCall.ServerSideCallAsync(); + var responseStream = new ServerResponseStream(asyncCall); + + await responseStream.WriteStatusAsync(new Status(StatusCode.Unimplemented, "No such method."), Metadata.Empty); + await finishedTask; + } + } + + internal static class HandlerUtils + { + public static Status StatusFromException(Exception e) + { + var rpcException = e as RpcException; + if (rpcException != null) + { + // use the status thrown by handler. + return rpcException.Status; + } + + // TODO(jtattermusch): what is the right status code here? + return new Status(StatusCode.Unknown, "Exception was thrown by handler."); + } + + public static ServerCallContext NewContext(ServerRpcNew newRpc, string peer, ServerResponseStream serverResponseStream, CancellationToken cancellationToken) + where TRequest : class + where TResponse : class + { + DateTime realtimeDeadline = newRpc.Deadline.ToClockType(GPRClockType.Realtime).ToDateTime(); + + return new ServerCallContext(newRpc.Call, newRpc.Method, newRpc.Host, peer, realtimeDeadline, + newRpc.RequestMetadata, cancellationToken, serverResponseStream.WriteResponseHeadersAsync, serverResponseStream); + } + } +} diff --git a/src/csharp/Grpc.Core/Internal/ServerCalls.cs b/src/csharp/Grpc.Core/Internal/ServerCalls.cs new file mode 100644 index 00000000..81279678 --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/ServerCalls.cs @@ -0,0 +1,71 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core; + +namespace Grpc.Core.Internal +{ + internal static class ServerCalls + { + public static IServerCallHandler UnaryCall(Method method, UnaryServerMethod handler) + where TRequest : class + where TResponse : class + { + return new UnaryServerCallHandler(method, handler); + } + + public static IServerCallHandler ClientStreamingCall(Method method, ClientStreamingServerMethod handler) + where TRequest : class + where TResponse : class + { + return new ClientStreamingServerCallHandler(method, handler); + } + + public static IServerCallHandler ServerStreamingCall(Method method, ServerStreamingServerMethod handler) + where TRequest : class + where TResponse : class + { + return new ServerStreamingServerCallHandler(method, handler); + } + + public static IServerCallHandler DuplexStreamingCall(Method method, DuplexStreamingServerMethod handler) + where TRequest : class + where TResponse : class + { + return new DuplexStreamingServerCallHandler(method, handler); + } + } +} diff --git a/src/csharp/Grpc.Core/Internal/ServerCredentialsSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ServerCredentialsSafeHandle.cs new file mode 100644 index 00000000..51e352a1 --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/ServerCredentialsSafeHandle.cs @@ -0,0 +1,69 @@ +#region Copyright notice and license +// Copyright 2015, 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. +#endregion +using System; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core.Utils; + +namespace Grpc.Core.Internal +{ + /// + /// grpc_server_credentials from grpc/grpc_security.h + /// + internal class ServerCredentialsSafeHandle : SafeHandleZeroIsInvalid + { + [DllImport("grpc_csharp_ext.dll", CharSet = CharSet.Ansi)] + static extern ServerCredentialsSafeHandle grpcsharp_ssl_server_credentials_create(string pemRootCerts, string[] keyCertPairCertChainArray, string[] keyCertPairPrivateKeyArray, UIntPtr numKeyCertPairs, bool forceClientAuth); + + [DllImport("grpc_csharp_ext.dll")] + static extern void grpcsharp_server_credentials_release(IntPtr credentials); + + private ServerCredentialsSafeHandle() + { + } + + public static ServerCredentialsSafeHandle CreateSslCredentials(string pemRootCerts, string[] keyCertPairCertChainArray, string[] keyCertPairPrivateKeyArray, bool forceClientAuth) + { + Preconditions.CheckArgument(keyCertPairCertChainArray.Length == keyCertPairPrivateKeyArray.Length); + return grpcsharp_ssl_server_credentials_create(pemRootCerts, + keyCertPairCertChainArray, keyCertPairPrivateKeyArray, + new UIntPtr((ulong)keyCertPairCertChainArray.Length), + forceClientAuth); + } + + protected override bool ReleaseHandle() + { + grpcsharp_server_credentials_release(handle); + return true; + } + } +} diff --git a/src/csharp/Grpc.Core/Internal/ServerRequestStream.cs b/src/csharp/Grpc.Core/Internal/ServerRequestStream.cs new file mode 100644 index 00000000..3fccb88a --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/ServerRequestStream.cs @@ -0,0 +1,83 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Grpc.Core.Internal +{ + internal class ServerRequestStream : IAsyncStreamReader + where TRequest : class + where TResponse : class + { + readonly AsyncCallServer call; + TRequest current; + + public ServerRequestStream(AsyncCallServer call) + { + this.call = call; + } + + public TRequest Current + { + get + { + if (current == null) + { + throw new InvalidOperationException("No current element is available."); + } + return current; + } + } + + public async Task MoveNext(CancellationToken token) + { + if (token != CancellationToken.None) + { + throw new InvalidOperationException("Cancellation of individual reads is not supported."); + } + var taskSource = new AsyncCompletionTaskSource(); + call.StartReadMessage(taskSource.CompletionDelegate); + var result = await taskSource.Task; + this.current = result; + return result != null; + } + + public void Dispose() + { + // TODO(jtattermusch): implement the semantics of stream disposal. + } + } +} diff --git a/src/csharp/Grpc.Core/Internal/ServerResponseStream.cs b/src/csharp/Grpc.Core/Internal/ServerResponseStream.cs new file mode 100644 index 00000000..03e39efc --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/ServerResponseStream.cs @@ -0,0 +1,93 @@ +#region Copyright notice and license +// Copyright 2015, 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. +#endregion + +using System; +using System.Threading.Tasks; +using Grpc.Core.Internal; + +namespace Grpc.Core.Internal +{ + /// + /// Writes responses asynchronously to an underlying AsyncCallServer object. + /// + internal class ServerResponseStream : IServerStreamWriter, IHasWriteOptions + where TRequest : class + where TResponse : class + { + readonly AsyncCallServer call; + WriteOptions writeOptions; + + public ServerResponseStream(AsyncCallServer call) + { + this.call = call; + } + + public Task WriteAsync(TResponse message) + { + var taskSource = new AsyncCompletionTaskSource(); + call.StartSendMessage(message, GetWriteFlags(), taskSource.CompletionDelegate); + return taskSource.Task; + } + + public Task WriteStatusAsync(Status status, Metadata trailers) + { + var taskSource = new AsyncCompletionTaskSource(); + call.StartSendStatusFromServer(status, trailers, taskSource.CompletionDelegate); + return taskSource.Task; + } + + public Task WriteResponseHeadersAsync(Metadata responseHeaders) + { + var taskSource = new AsyncCompletionTaskSource(); + call.StartSendInitialMetadata(responseHeaders, taskSource.CompletionDelegate); + return taskSource.Task; + } + + public WriteOptions WriteOptions + { + get + { + return writeOptions; + } + + set + { + writeOptions = value; + } + } + + private WriteFlags GetWriteFlags() + { + var options = writeOptions; + return options != null ? options.Flags : default(WriteFlags); + } + } +} diff --git a/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs new file mode 100644 index 00000000..5ee7ac14 --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs @@ -0,0 +1,125 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Collections.Concurrent; +using System.Diagnostics; +using System.Runtime.InteropServices; +using Grpc.Core.Utils; + +namespace Grpc.Core.Internal +{ + /// + /// grpc_server from grpc/grpc.h + /// + internal sealed class ServerSafeHandle : SafeHandleZeroIsInvalid + { + [DllImport("grpc_csharp_ext.dll")] + static extern ServerSafeHandle grpcsharp_server_create(CompletionQueueSafeHandle cq, ChannelArgsSafeHandle args); + + [DllImport("grpc_csharp_ext.dll")] + static extern int grpcsharp_server_add_insecure_http2_port(ServerSafeHandle server, string addr); + + [DllImport("grpc_csharp_ext.dll")] + static extern int grpcsharp_server_add_secure_http2_port(ServerSafeHandle server, string addr, ServerCredentialsSafeHandle creds); + + [DllImport("grpc_csharp_ext.dll")] + static extern void grpcsharp_server_start(ServerSafeHandle server); + + [DllImport("grpc_csharp_ext.dll")] + static extern GRPCCallError grpcsharp_server_request_call(ServerSafeHandle server, CompletionQueueSafeHandle cq, BatchContextSafeHandle ctx); + + [DllImport("grpc_csharp_ext.dll")] + static extern void grpcsharp_server_cancel_all_calls(ServerSafeHandle server); + + [DllImport("grpc_csharp_ext.dll")] + static extern void grpcsharp_server_shutdown_and_notify_callback(ServerSafeHandle server, CompletionQueueSafeHandle cq, BatchContextSafeHandle ctx); + + [DllImport("grpc_csharp_ext.dll")] + static extern void grpcsharp_server_destroy(IntPtr server); + + private ServerSafeHandle() + { + } + + public static ServerSafeHandle NewServer(CompletionQueueSafeHandle cq, ChannelArgsSafeHandle args) + { + // Increment reference count for the native gRPC environment to make sure we don't do grpc_shutdown() before destroying the server handle. + // Doing so would make object finalizer crash if we end up abandoning the handle. + GrpcEnvironment.GrpcNativeInit(); + return grpcsharp_server_create(cq, args); + } + + public int AddInsecurePort(string addr) + { + return grpcsharp_server_add_insecure_http2_port(this, addr); + } + + public int AddSecurePort(string addr, ServerCredentialsSafeHandle credentials) + { + return grpcsharp_server_add_secure_http2_port(this, addr, credentials); + } + + public void Start() + { + grpcsharp_server_start(this); + } + + public void ShutdownAndNotify(BatchCompletionDelegate callback, GrpcEnvironment environment) + { + var ctx = BatchContextSafeHandle.Create(); + environment.CompletionRegistry.RegisterBatchCompletion(ctx, callback); + grpcsharp_server_shutdown_and_notify_callback(this, environment.CompletionQueue, ctx); + } + + public void RequestCall(BatchCompletionDelegate callback, GrpcEnvironment environment) + { + var ctx = BatchContextSafeHandle.Create(); + environment.CompletionRegistry.RegisterBatchCompletion(ctx, callback); + grpcsharp_server_request_call(this, environment.CompletionQueue, ctx).CheckOk(); + } + + protected override bool ReleaseHandle() + { + grpcsharp_server_destroy(handle); + GrpcEnvironment.GrpcNativeShutdown(); + return true; + } + + // Only to be called after ShutdownAndNotify. + public void CancelAllCalls() + { + grpcsharp_server_cancel_all_calls(this); + } + } +} diff --git a/src/csharp/Grpc.Core/Internal/Timespec.cs b/src/csharp/Grpc.Core/Internal/Timespec.cs new file mode 100644 index 00000000..daf85d5f --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/Timespec.cs @@ -0,0 +1,250 @@ +#region Copyright notice and license +// Copyright 2015, 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. +#endregion +using System; +using System.Runtime.InteropServices; +using System.Threading; + +using Grpc.Core.Utils; + +namespace Grpc.Core.Internal +{ + /// + /// gpr_timespec from grpc/support/time.h + /// + [StructLayout(LayoutKind.Sequential)] + internal struct Timespec + { + const long NanosPerSecond = 1000 * 1000 * 1000; + const long NanosPerTick = 100; + const long TicksPerSecond = NanosPerSecond / NanosPerTick; + + static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + + [DllImport("grpc_csharp_ext.dll")] + static extern Timespec gprsharp_now(GPRClockType clockType); + + [DllImport("grpc_csharp_ext.dll")] + static extern Timespec gprsharp_inf_future(GPRClockType clockType); + + [DllImport("grpc_csharp_ext.dll")] + static extern Timespec gprsharp_inf_past(GPRClockType clockType); + + [DllImport("grpc_csharp_ext.dll")] + static extern Timespec gprsharp_convert_clock_type(Timespec t, GPRClockType targetClock); + + [DllImport("grpc_csharp_ext.dll")] + static extern int gprsharp_sizeof_timespec(); + + public Timespec(IntPtr tv_sec, int tv_nsec) : this(tv_sec, tv_nsec, GPRClockType.Realtime) + { + } + + public Timespec(IntPtr tv_sec, int tv_nsec, GPRClockType clock_type) + { + this.tv_sec = tv_sec; + this.tv_nsec = tv_nsec; + this.clock_type = clock_type; + } + + // NOTE: on linux 64bit sizeof(gpr_timespec) = 16, on windows 32bit sizeof(gpr_timespec) = 8 + // so IntPtr seems to have the right size to work on both. + private System.IntPtr tv_sec; + private int tv_nsec; + private GPRClockType clock_type; + + /// + /// Timespec a long time in the future. + /// + public static Timespec InfFuture + { + get + { + return gprsharp_inf_future(GPRClockType.Realtime); + } + } + + /// + /// Timespec a long time in the past. + /// + public static Timespec InfPast + { + get + { + return gprsharp_inf_past(GPRClockType.Realtime); + } + } + + /// + /// Return Timespec representing the current time. + /// + public static Timespec Now + { + get + { + return gprsharp_now(GPRClockType.Realtime); + } + } + + /// + /// Seconds since unix epoch. + /// + public IntPtr TimevalSeconds + { + get + { + return tv_sec; + } + } + + /// + /// The nanoseconds part of timeval. + /// + public int TimevalNanos + { + get + { + return tv_nsec; + } + } + + /// + /// Converts the timespec to desired clock type. + /// + public Timespec ToClockType(GPRClockType targetClock) + { + return gprsharp_convert_clock_type(this, targetClock); + } + + /// + /// Converts Timespec to DateTime. + /// Timespec needs to be of type GPRClockType.Realtime and needs to represent a legal value. + /// DateTime has lower resolution (100ns), so rounding can occurs. + /// Value are always rounded up to the nearest DateTime value in the future. + /// + /// For Timespec.InfFuture or if timespec is after the largest representable DateTime, DateTime.MaxValue is returned. + /// For Timespec.InfPast or if timespec is before the lowest representable DateTime, DateTime.MinValue is returned. + /// + /// Unless DateTime.MaxValue or DateTime.MinValue is returned, the resulting DateTime is always in UTC + /// (DateTimeKind.Utc) + /// + public DateTime ToDateTime() + { + Preconditions.CheckState(tv_nsec >= 0 && tv_nsec < NanosPerSecond); + Preconditions.CheckState(clock_type == GPRClockType.Realtime); + + // fast path for InfFuture + if (this.Equals(InfFuture)) + { + return DateTime.MaxValue; + } + + // fast path for InfPast + if (this.Equals(InfPast)) + { + return DateTime.MinValue; + } + + try + { + // convert nanos to ticks, round up to the nearest tick + long ticksFromNanos = tv_nsec / NanosPerTick + ((tv_nsec % NanosPerTick != 0) ? 1 : 0); + long ticksTotal = checked(tv_sec.ToInt64() * TicksPerSecond + ticksFromNanos); + return UnixEpoch.AddTicks(ticksTotal); + } + catch (OverflowException) + { + // ticks out of long range + return tv_sec.ToInt64() > 0 ? DateTime.MaxValue : DateTime.MinValue; + } + catch (ArgumentOutOfRangeException) + { + // resulting date time would be larger than MaxValue + return tv_sec.ToInt64() > 0 ? DateTime.MaxValue : DateTime.MinValue; + } + } + + /// + /// Creates DateTime to Timespec. + /// DateTime has to be in UTC (DateTimeKind.Utc) unless it's DateTime.MaxValue or DateTime.MinValue. + /// For DateTime.MaxValue of date time after the largest representable Timespec, Timespec.InfFuture is returned. + /// For DateTime.MinValue of date time before the lowest representable Timespec, Timespec.InfPast is returned. + /// + /// The date time. + /// Date time. + public static Timespec FromDateTime(DateTime dateTime) + { + if (dateTime == DateTime.MaxValue) + { + return Timespec.InfFuture; + } + + if (dateTime == DateTime.MinValue) + { + return Timespec.InfPast; + } + + Preconditions.CheckArgument(dateTime.Kind == DateTimeKind.Utc, "dateTime needs of kind DateTimeKind.Utc or be equal to DateTime.MaxValue or DateTime.MinValue."); + + try + { + TimeSpan timeSpan = dateTime - UnixEpoch; + long ticks = timeSpan.Ticks; + + long seconds = ticks / TicksPerSecond; + int nanos = (int)((ticks % TicksPerSecond) * NanosPerTick); + if (nanos < 0) + { + // correct the result based on C# modulo semantics for negative dividend + seconds--; + nanos += (int)NanosPerSecond; + } + // new IntPtr possibly throws OverflowException + return new Timespec(new IntPtr(seconds), nanos); + } + catch (OverflowException) + { + return dateTime > UnixEpoch ? Timespec.InfFuture : Timespec.InfPast; + } + catch (ArgumentOutOfRangeException) + { + return dateTime > UnixEpoch ? Timespec.InfFuture : Timespec.InfPast; + } + } + + internal static int NativeSize + { + get + { + return gprsharp_sizeof_timespec(); + } + } + } +} diff --git a/src/csharp/Grpc.Core/KeyCertificatePair.cs b/src/csharp/Grpc.Core/KeyCertificatePair.cs new file mode 100644 index 00000000..6f691975 --- /dev/null +++ b/src/csharp/Grpc.Core/KeyCertificatePair.cs @@ -0,0 +1,83 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Collections.Generic; + +using Grpc.Core.Internal; +using Grpc.Core.Utils; + +namespace Grpc.Core +{ + /// + /// Key certificate pair (in PEM encoding). + /// + public sealed class KeyCertificatePair + { + readonly string certificateChain; + readonly string privateKey; + + /// + /// Creates a new certificate chain - private key pair. + /// + /// PEM encoded certificate chain. + /// PEM encoded private key. + public KeyCertificatePair(string certificateChain, string privateKey) + { + this.certificateChain = Preconditions.CheckNotNull(certificateChain, "certificateChain"); + this.privateKey = Preconditions.CheckNotNull(privateKey, "privateKey"); + } + + /// + /// PEM encoded certificate chain. + /// + public string CertificateChain + { + get + { + return certificateChain; + } + } + + /// + /// PEM encoded private key. + /// + public string PrivateKey + { + get + { + return privateKey; + } + } + } +} diff --git a/src/csharp/Grpc.Core/Logging/ConsoleLogger.cs b/src/csharp/Grpc.Core/Logging/ConsoleLogger.cs new file mode 100644 index 00000000..5c5b8021 --- /dev/null +++ b/src/csharp/Grpc.Core/Logging/ConsoleLogger.cs @@ -0,0 +1,150 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Collections.Generic; + +namespace Grpc.Core.Logging +{ + /// Logger that logs to System.Console. + public class ConsoleLogger : ILogger + { + readonly Type forType; + readonly string forTypeString; + + /// Creates a console logger not associated to any specific type. + public ConsoleLogger() : this(null) + { + } + + /// Creates a console logger that logs messsage specific for given type. + private ConsoleLogger(Type forType) + { + this.forType = forType; + if (forType != null) + { + var namespaceStr = forType.Namespace ?? ""; + if (namespaceStr.Length > 0) + { + namespaceStr += "."; + } + this.forTypeString = namespaceStr + forType.Name + " "; + } + else + { + this.forTypeString = ""; + } + } + + /// + /// Returns a logger associated with the specified type. + /// + public ILogger ForType() + { + if (typeof(T) == forType) + { + return this; + } + return new ConsoleLogger(typeof(T)); + } + + /// Logs a message with severity Debug. + public void Debug(string message) + { + Log("D", message); + } + + /// Logs a formatted message with severity Debug. + public void Debug(string format, params object[] formatArgs) + { + Debug(string.Format(format, formatArgs)); + } + + /// Logs a message with severity Info. + public void Info(string message) + { + Log("I", message); + } + + /// Logs a formatted message with severity Info. + public void Info(string format, params object[] formatArgs) + { + Info(string.Format(format, formatArgs)); + } + + /// Logs a message with severity Warning. + public void Warning(string message) + { + Log("W", message); + } + + /// Logs a formatted message with severity Warning. + public void Warning(string format, params object[] formatArgs) + { + Warning(string.Format(format, formatArgs)); + } + + /// Logs a message and an associated exception with severity Warning. + public void Warning(Exception exception, string message) + { + Warning(message + " " + exception); + } + + /// Logs a message with severity Error. + public void Error(string message) + { + Log("E", message); + } + + /// Logs a formatted message with severity Error. + public void Error(string format, params object[] formatArgs) + { + Error(string.Format(format, formatArgs)); + } + + /// Logs a message and an associated exception with severity Error. + public void Error(Exception exception, string message) + { + Error(message + " " + exception); + } + + private void Log(string severityString, string message) + { + Console.Error.WriteLine("{0}{1} {2}{3}", + severityString, + DateTime.Now, + forTypeString, + message); + } + } +} diff --git a/src/csharp/Grpc.Core/Logging/ILogger.cs b/src/csharp/Grpc.Core/Logging/ILogger.cs new file mode 100644 index 00000000..7c032642 --- /dev/null +++ b/src/csharp/Grpc.Core/Logging/ILogger.cs @@ -0,0 +1,75 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Collections.Generic; + +namespace Grpc.Core.Logging +{ + /// For logging messages. + public interface ILogger + { + /// Returns a logger associated with the specified type. + ILogger ForType(); + + /// Logs a message with severity Debug. + void Debug(string message); + + /// Logs a formatted message with severity Debug. + void Debug(string format, params object[] formatArgs); + + /// Logs a message with severity Info. + void Info(string message); + + /// Logs a formatted message with severity Info. + void Info(string format, params object[] formatArgs); + + /// Logs a message with severity Warning. + void Warning(string message); + + /// Logs a formatted message with severity Warning. + void Warning(string format, params object[] formatArgs); + + /// Logs a message and an associated exception with severity Warning. + void Warning(Exception exception, string message); + + /// Logs a message with severity Error. + void Error(string message); + + /// Logs a formatted message with severity Error. + void Error(string format, params object[] formatArgs); + + /// Logs a message and an associated exception with severity Error. + void Error(Exception exception, string message); + } +} diff --git a/src/csharp/Grpc.Core/Marshaller.cs b/src/csharp/Grpc.Core/Marshaller.cs new file mode 100644 index 00000000..3493d2d3 --- /dev/null +++ b/src/csharp/Grpc.Core/Marshaller.cs @@ -0,0 +1,106 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using Grpc.Core.Utils; + +namespace Grpc.Core +{ + /// + /// Encapsulates the logic for serializing and deserializing messages. + /// + public class Marshaller + { + readonly Func serializer; + readonly Func deserializer; + + /// + /// Initializes a new marshaller. + /// + /// Function that will be used to serialize messages. + /// Function that will be used to deserialize messages. + public Marshaller(Func serializer, Func deserializer) + { + this.serializer = Preconditions.CheckNotNull(serializer, "serializer"); + this.deserializer = Preconditions.CheckNotNull(deserializer, "deserializer"); + } + + /// + /// Gets the serializer function. + /// + public Func Serializer + { + get + { + return this.serializer; + } + } + + /// + /// Gets the deserializer function. + /// + public Func Deserializer + { + get + { + return this.deserializer; + } + } + } + + /// + /// Utilities for creating marshallers. + /// + public static class Marshallers + { + /// + /// Creates a marshaller from specified serializer and deserializer. + /// + public static Marshaller Create(Func serializer, Func deserializer) + { + return new Marshaller(serializer, deserializer); + } + + /// + /// Returns a marshaller for string type. This is useful for testing. + /// + public static Marshaller StringMarshaller + { + get + { + return new Marshaller(System.Text.Encoding.UTF8.GetBytes, + System.Text.Encoding.UTF8.GetString); + } + } + } +} diff --git a/src/csharp/Grpc.Core/Metadata.cs b/src/csharp/Grpc.Core/Metadata.cs new file mode 100644 index 00000000..21bdf4f1 --- /dev/null +++ b/src/csharp/Grpc.Core/Metadata.cs @@ -0,0 +1,333 @@ +#region Copyright notice and license +// Copyright 2015, 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. +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Text; +using System.Text.RegularExpressions; + +using Grpc.Core.Utils; + +namespace Grpc.Core +{ + /// + /// A collection of metadata entries that can be exchanged during a call. + /// gRPC supports these types of metadata: + /// + /// Request headersare sent by the client at the beginning of a remote call before any request messages are sent. + /// Response headersare sent by the server at the beginning of a remote call handler before any response messages are sent. + /// Response trailersare sent by the server at the end of a remote call along with resulting call status. + /// + /// + public sealed class Metadata : IList + { + /// + /// All binary headers should have this suffix. + /// + public const string BinaryHeaderSuffix = "-bin"; + + /// + /// An read-only instance of metadata containing no entries. + /// + public static readonly Metadata Empty = new Metadata().Freeze(); + + readonly List entries; + bool readOnly; + + /// + /// Initializes a new instance of Metadata. + /// + public Metadata() + { + this.entries = new List(); + } + + /// + /// Makes this object read-only. + /// + /// this object + internal Metadata Freeze() + { + this.readOnly = true; + return this; + } + + // TODO: add support for access by key + + #region IList members + + public int IndexOf(Metadata.Entry item) + { + return entries.IndexOf(item); + } + + public void Insert(int index, Metadata.Entry item) + { + CheckWriteable(); + entries.Insert(index, item); + } + + public void RemoveAt(int index) + { + CheckWriteable(); + entries.RemoveAt(index); + } + + public Metadata.Entry this[int index] + { + get + { + return entries[index]; + } + + set + { + CheckWriteable(); + entries[index] = value; + } + } + + public void Add(Metadata.Entry item) + { + CheckWriteable(); + entries.Add(item); + } + + public void Add(string key, string value) + { + Add(new Entry(key, value)); + } + + public void Add(string key, byte[] valueBytes) + { + Add(new Entry(key, valueBytes)); + } + + public void Clear() + { + CheckWriteable(); + entries.Clear(); + } + + public bool Contains(Metadata.Entry item) + { + return entries.Contains(item); + } + + public void CopyTo(Metadata.Entry[] array, int arrayIndex) + { + entries.CopyTo(array, arrayIndex); + } + + public int Count + { + get { return entries.Count; } + } + + public bool IsReadOnly + { + get { return readOnly; } + } + + public bool Remove(Metadata.Entry item) + { + CheckWriteable(); + return entries.Remove(item); + } + + public IEnumerator GetEnumerator() + { + return entries.GetEnumerator(); + } + + IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return entries.GetEnumerator(); + } + + private void CheckWriteable() + { + Preconditions.CheckState(!readOnly, "Object is read only"); + } + + #endregion + + /// + /// Metadata entry + /// + public struct Entry + { + private static readonly Encoding Encoding = Encoding.ASCII; + private static readonly Regex ValidKeyRegex = new Regex("^[a-z0-9_-]+$"); + + readonly string key; + readonly string value; + readonly byte[] valueBytes; + + private Entry(string key, string value, byte[] valueBytes) + { + this.key = key; + this.value = value; + this.valueBytes = valueBytes; + } + + /// + /// Initializes a new instance of the struct with a binary value. + /// + /// Metadata key, needs to have suffix indicating a binary valued metadata entry. + /// Value bytes. + public Entry(string key, byte[] valueBytes) + { + this.key = NormalizeKey(key); + Preconditions.CheckArgument(this.key.EndsWith(BinaryHeaderSuffix), + "Key for binary valued metadata entry needs to have suffix indicating binary value."); + this.value = null; + Preconditions.CheckNotNull(valueBytes, "valueBytes"); + this.valueBytes = new byte[valueBytes.Length]; + Buffer.BlockCopy(valueBytes, 0, this.valueBytes, 0, valueBytes.Length); // defensive copy to guarantee immutability + } + + /// + /// Initializes a new instance of the struct holding an ASCII value. + /// + /// Metadata key, must not use suffix indicating a binary valued metadata entry. + /// Value string. Only ASCII characters are allowed. + public Entry(string key, string value) + { + this.key = NormalizeKey(key); + Preconditions.CheckArgument(!this.key.EndsWith(BinaryHeaderSuffix), + "Key for ASCII valued metadata entry cannot have suffix indicating binary value."); + this.value = Preconditions.CheckNotNull(value, "value"); + this.valueBytes = null; + } + + /// + /// Gets the metadata entry key. + /// + public string Key + { + get + { + return this.key; + } + } + + /// + /// Gets the binary value of this metadata entry. + /// + public byte[] ValueBytes + { + get + { + if (valueBytes == null) + { + return Encoding.GetBytes(value); + } + + // defensive copy to guarantee immutability + var bytes = new byte[valueBytes.Length]; + Buffer.BlockCopy(valueBytes, 0, bytes, 0, valueBytes.Length); + return bytes; + } + } + + /// + /// Gets the string value of this metadata entry. + /// + public string Value + { + get + { + Preconditions.CheckState(!IsBinary, "Cannot access string value of a binary metadata entry"); + return value ?? Encoding.GetString(valueBytes); + } + } + + /// + /// Returns true if this entry is a binary-value entry. + /// + public bool IsBinary + { + get + { + return value == null; + } + } + + /// + /// Returns a that represents the current . + /// + public override string ToString() + { + if (IsBinary) + { + return string.Format("[Entry: key={0}, valueBytes={1}]", key, valueBytes); + } + + return string.Format("[Entry: key={0}, value={1}]", key, value); + } + + /// + /// Gets the serialized value for this entry. For binary metadata entries, this leaks + /// the internal valueBytes byte array and caller must not change contents of it. + /// + internal byte[] GetSerializedValueUnsafe() + { + return valueBytes ?? Encoding.GetBytes(value); + } + + /// + /// Creates a binary value or ascii value metadata entry from data received from the native layer. + /// We trust C core to give us well-formed data, so we don't perform any checks or defensive copying. + /// + internal static Entry CreateUnsafe(string key, byte[] valueBytes) + { + if (key.EndsWith(BinaryHeaderSuffix)) + { + return new Entry(key, null, valueBytes); + } + return new Entry(key, Encoding.GetString(valueBytes), null); + } + + private static string NormalizeKey(string key) + { + var normalized = Preconditions.CheckNotNull(key, "key").ToLower(CultureInfo.InvariantCulture); + Preconditions.CheckArgument(ValidKeyRegex.IsMatch(normalized), + "Metadata entry key not valid. Keys can only contain lowercase alphanumeric characters, underscores and hyphens."); + return normalized; + } + } + } +} diff --git a/src/csharp/Grpc.Core/Method.cs b/src/csharp/Grpc.Core/Method.cs new file mode 100644 index 00000000..99162a7d --- /dev/null +++ b/src/csharp/Grpc.Core/Method.cs @@ -0,0 +1,191 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using Grpc.Core.Utils; + +namespace Grpc.Core +{ + /// + /// Method types supported by gRPC. + /// + public enum MethodType + { + /// Single request sent from client, single response received from server. + Unary, + + /// Stream of request sent from client, single response received from server. + ClientStreaming, + + /// Single request sent from client, stream of responses received from server. + ServerStreaming, + + /// Both server and client can stream arbitrary number of requests and responses simultaneously. + DuplexStreaming + } + + /// + /// A non-generic representation of a remote method. + /// + public interface IMethod + { + /// + /// Gets the type of the method. + /// + MethodType Type { get; } + + /// + /// Gets the name of the service to which this method belongs. + /// + string ServiceName { get; } + + /// + /// Gets the unqualified name of the method. + /// + string Name { get; } + + /// + /// Gets the fully qualified name of the method. On the server side, methods are dispatched + /// based on this name. + /// + string FullName { get; } + } + + /// + /// A description of a remote method. + /// + /// Request message type for this method. + /// Response message type for this method. + public class Method : IMethod + { + readonly MethodType type; + readonly string serviceName; + readonly string name; + readonly Marshaller requestMarshaller; + readonly Marshaller responseMarshaller; + readonly string fullName; + + /// + /// Initializes a new instance of the Method class. + /// + /// Type of method. + /// Name of service this method belongs to. + /// Unqualified name of the method. + /// Marshaller used for request messages. + /// Marshaller used for response messages. + public Method(MethodType type, string serviceName, string name, Marshaller requestMarshaller, Marshaller responseMarshaller) + { + this.type = type; + this.serviceName = Preconditions.CheckNotNull(serviceName, "serviceName"); + this.name = Preconditions.CheckNotNull(name, "name"); + this.requestMarshaller = Preconditions.CheckNotNull(requestMarshaller, "requestMarshaller"); + this.responseMarshaller = Preconditions.CheckNotNull(responseMarshaller, "responseMarshaller"); + this.fullName = GetFullName(serviceName, name); + } + + /// + /// Gets the type of the method. + /// + public MethodType Type + { + get + { + return this.type; + } + } + + /// + /// Gets the name of the service to which this method belongs. + /// + public string ServiceName + { + get + { + return this.serviceName; + } + } + + /// + /// Gets the unqualified name of the method. + /// + public string Name + { + get + { + return this.name; + } + } + + /// + /// Gets the marshaller used for request messages. + /// + public Marshaller RequestMarshaller + { + get + { + return this.requestMarshaller; + } + } + + /// + /// Gets the marshaller used for response messages. + /// + public Marshaller ResponseMarshaller + { + get + { + return this.responseMarshaller; + } + } + + /// + /// Gets the fully qualified name of the method. On the server side, methods are dispatched + /// based on this name. + /// + public string FullName + { + get + { + return this.fullName; + } + } + + /// + /// Gets full name of the method including the service name. + /// + internal static string GetFullName(string serviceName, string methodName) + { + return "/" + serviceName + "/" + methodName; + } + } +} diff --git a/src/csharp/Grpc.Core/Properties/AssemblyInfo.cs b/src/csharp/Grpc.Core/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..29db85d7 --- /dev/null +++ b/src/csharp/Grpc.Core/Properties/AssemblyInfo.cs @@ -0,0 +1,21 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyTitle("Grpc.Core")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("Google Inc. All rights reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +#if SIGNED +[assembly: InternalsVisibleTo("Grpc.Core.Tests,PublicKey=" + + "00240000048000009400000006020000002400005253413100040000010001002f5797a92c6fcde81bd4098f43" + + "0442bb8e12768722de0b0cb1b15e955b32a11352740ee59f2c94c48edc8e177d1052536b8ac651bce11ce5da3a" + + "27fc95aff3dc604a6971417453f9483c7b5e836756d5b271bf8f2403fe186e31956148c03d804487cf642f8cc0" + + "71394ee9672dfe5b55ea0f95dfd5a7f77d22c962ccf51320d3")] +#else +[assembly: InternalsVisibleTo("Grpc.Core.Tests")] +#endif \ No newline at end of file diff --git a/src/csharp/Grpc.Core/RpcException.cs b/src/csharp/Grpc.Core/RpcException.cs new file mode 100644 index 00000000..cac417e6 --- /dev/null +++ b/src/csharp/Grpc.Core/RpcException.cs @@ -0,0 +1,75 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; + +namespace Grpc.Core +{ + /// + /// Thrown when remote procedure call fails. Every RpcException is associated with a resulting of the call. + /// + public class RpcException : Exception + { + private readonly Status status; + + /// + /// Creates a new RpcException associated with given status. + /// + /// Resulting status of a call. + public RpcException(Status status) : base(status.ToString()) + { + this.status = status; + } + + /// + /// Creates a new RpcException associated with given status and message. + /// + /// Resulting status of a call. + /// The exception message. + public RpcException(Status status, string message) : base(message) + { + this.status = status; + } + + /// + /// Resulting status of the call. + /// + public Status Status + { + get + { + return status; + } + } + } +} diff --git a/src/csharp/Grpc.Core/Server.cs b/src/csharp/Grpc.Core/Server.cs new file mode 100644 index 00000000..7c94d215 --- /dev/null +++ b/src/csharp/Grpc.Core/Server.cs @@ -0,0 +1,389 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using Grpc.Core.Internal; +using Grpc.Core.Logging; +using Grpc.Core.Utils; + +namespace Grpc.Core +{ + /// + /// gRPC server. A single server can server arbitrary number of services and can listen on more than one ports. + /// + public class Server + { + static readonly ILogger Logger = GrpcEnvironment.Logger.ForType(); + + readonly AtomicCounter activeCallCounter = new AtomicCounter(); + + readonly ServiceDefinitionCollection serviceDefinitions; + readonly ServerPortCollection ports; + readonly GrpcEnvironment environment; + readonly List options; + readonly ServerSafeHandle handle; + readonly object myLock = new object(); + + readonly List serviceDefinitionsList = new List(); + readonly List serverPortList = new List(); + readonly Dictionary callHandlers = new Dictionary(); + readonly TaskCompletionSource shutdownTcs = new TaskCompletionSource(); + + bool startRequested; + bool shutdownRequested; + + /// + /// Create a new server. + /// + /// Channel options. + public Server(IEnumerable options = null) + { + this.serviceDefinitions = new ServiceDefinitionCollection(this); + this.ports = new ServerPortCollection(this); + this.environment = GrpcEnvironment.AddRef(); + this.options = options != null ? new List(options) : new List(); + using (var channelArgs = ChannelOptions.CreateChannelArgs(this.options)) + { + this.handle = ServerSafeHandle.NewServer(environment.CompletionQueue, channelArgs); + } + } + + /// + /// Services that will be exported by the server once started. Register a service with this + /// server by adding its definition to this collection. + /// + public ServiceDefinitionCollection Services + { + get + { + return serviceDefinitions; + } + } + + /// + /// Ports on which the server will listen once started. Register a port with this + /// server by adding its definition to this collection. + /// + public ServerPortCollection Ports + { + get + { + return ports; + } + } + + /// + /// To allow awaiting termination of the server. + /// + public Task ShutdownTask + { + get + { + return shutdownTcs.Task; + } + } + + /// + /// Starts the server. + /// + public void Start() + { + lock (myLock) + { + Preconditions.CheckState(!startRequested); + startRequested = true; + + handle.Start(); + AllowOneRpc(); + } + } + + /// + /// Requests server shutdown and when there are no more calls being serviced, + /// cleans up used resources. The returned task finishes when shutdown procedure + /// is complete. + /// + public async Task ShutdownAsync() + { + lock (myLock) + { + Preconditions.CheckState(startRequested); + Preconditions.CheckState(!shutdownRequested); + shutdownRequested = true; + } + + handle.ShutdownAndNotify(HandleServerShutdown, environment); + await shutdownTcs.Task; + DisposeHandle(); + + await Task.Run(() => GrpcEnvironment.Release()); + } + + /// + /// Requests server shutdown while cancelling all the in-progress calls. + /// The returned task finishes when shutdown procedure is complete. + /// + public async Task KillAsync() + { + lock (myLock) + { + Preconditions.CheckState(startRequested); + Preconditions.CheckState(!shutdownRequested); + shutdownRequested = true; + } + + handle.ShutdownAndNotify(HandleServerShutdown, environment); + handle.CancelAllCalls(); + await shutdownTcs.Task; + DisposeHandle(); + } + + internal void AddCallReference(object call) + { + activeCallCounter.Increment(); + + bool success = false; + handle.DangerousAddRef(ref success); + Preconditions.CheckState(success); + } + + internal void RemoveCallReference(object call) + { + handle.DangerousRelease(); + activeCallCounter.Decrement(); + } + + /// + /// Adds a service definition. + /// + private void AddServiceDefinitionInternal(ServerServiceDefinition serviceDefinition) + { + lock (myLock) + { + Preconditions.CheckState(!startRequested); + foreach (var entry in serviceDefinition.CallHandlers) + { + callHandlers.Add(entry.Key, entry.Value); + } + serviceDefinitionsList.Add(serviceDefinition); + } + } + + /// + /// Adds a listening port. + /// + private int AddPortInternal(ServerPort serverPort) + { + lock (myLock) + { + Preconditions.CheckNotNull(serverPort.Credentials, "serverPort"); + Preconditions.CheckState(!startRequested); + var address = string.Format("{0}:{1}", serverPort.Host, serverPort.Port); + int boundPort; + using (var nativeCredentials = serverPort.Credentials.ToNativeCredentials()) + { + if (nativeCredentials != null) + { + boundPort = handle.AddSecurePort(address, nativeCredentials); + } + else + { + boundPort = handle.AddInsecurePort(address); + } + } + var newServerPort = new ServerPort(serverPort, boundPort); + this.serverPortList.Add(newServerPort); + return boundPort; + } + } + + /// + /// Allows one new RPC call to be received by server. + /// + private void AllowOneRpc() + { + lock (myLock) + { + if (!shutdownRequested) + { + handle.RequestCall(HandleNewServerRpc, environment); + } + } + } + + private void DisposeHandle() + { + var activeCallCount = activeCallCounter.Count; + if (activeCallCount > 0) + { + Logger.Warning("Server shutdown has finished but there are still {0} active calls for that server.", activeCallCount); + } + handle.Dispose(); + } + + /// + /// Selects corresponding handler for given call and handles the call. + /// + private async Task HandleCallAsync(ServerRpcNew newRpc) + { + try + { + IServerCallHandler callHandler; + if (!callHandlers.TryGetValue(newRpc.Method, out callHandler)) + { + callHandler = NoSuchMethodCallHandler.Instance; + } + await callHandler.HandleCall(newRpc, environment); + } + catch (Exception e) + { + Logger.Warning(e, "Exception while handling RPC."); + } + } + + /// + /// Handles the native callback. + /// + private void HandleNewServerRpc(bool success, BatchContextSafeHandle ctx) + { + if (success) + { + ServerRpcNew newRpc = ctx.GetServerRpcNew(this); + + // after server shutdown, the callback returns with null call + if (!newRpc.Call.IsInvalid) + { + Task.Run(async () => await HandleCallAsync(newRpc)); + } + } + + AllowOneRpc(); + } + + /// + /// Handles native callback. + /// + private void HandleServerShutdown(bool success, BatchContextSafeHandle ctx) + { + shutdownTcs.SetResult(null); + } + + /// + /// Collection of service definitions. + /// + public class ServiceDefinitionCollection : IEnumerable + { + readonly Server server; + + internal ServiceDefinitionCollection(Server server) + { + this.server = server; + } + + /// + /// Adds a service definition to the server. This is how you register + /// handlers for a service with the server. Only call this before Start(). + /// + public void Add(ServerServiceDefinition serviceDefinition) + { + server.AddServiceDefinitionInternal(serviceDefinition); + } + + /// + /// Gets enumerator for this collection. + /// + public IEnumerator GetEnumerator() + { + return server.serviceDefinitionsList.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return server.serviceDefinitionsList.GetEnumerator(); + } + } + + /// + /// Collection of server ports. + /// + public class ServerPortCollection : IEnumerable + { + readonly Server server; + + internal ServerPortCollection(Server server) + { + this.server = server; + } + + /// + /// Adds a new port on which server should listen. + /// Only call this before Start(). + /// The port on which server will be listening. + /// + public int Add(ServerPort serverPort) + { + return server.AddPortInternal(serverPort); + } + + /// + /// Adds a new port on which server should listen. + /// The port on which server will be listening. + /// + /// the host + /// the port. If zero, an unused port is chosen automatically. + /// credentials to use to secure this port. + public int Add(string host, int port, ServerCredentials credentials) + { + return Add(new ServerPort(host, port, credentials)); + } + + /// + /// Gets enumerator for this collection. + /// + public IEnumerator GetEnumerator() + { + return server.serverPortList.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return server.serverPortList.GetEnumerator(); + } + } + } +} diff --git a/src/csharp/Grpc.Core/ServerCallContext.cs b/src/csharp/Grpc.Core/ServerCallContext.cs new file mode 100644 index 00000000..09a6b882 --- /dev/null +++ b/src/csharp/Grpc.Core/ServerCallContext.cs @@ -0,0 +1,201 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; + +using Grpc.Core.Internal; + +namespace Grpc.Core +{ + /// + /// Context for a server-side call. + /// + public class ServerCallContext + { + private readonly CallSafeHandle callHandle; + private readonly string method; + private readonly string host; + private readonly string peer; + private readonly DateTime deadline; + private readonly Metadata requestHeaders; + private readonly CancellationToken cancellationToken; + private readonly Metadata responseTrailers = new Metadata(); + + private Status status = Status.DefaultSuccess; + private Func writeHeadersFunc; + private IHasWriteOptions writeOptionsHolder; + + internal ServerCallContext(CallSafeHandle callHandle, string method, string host, string peer, DateTime deadline, Metadata requestHeaders, CancellationToken cancellationToken, + Func writeHeadersFunc, IHasWriteOptions writeOptionsHolder) + { + this.callHandle = callHandle; + this.method = method; + this.host = host; + this.peer = peer; + this.deadline = deadline; + this.requestHeaders = requestHeaders; + this.cancellationToken = cancellationToken; + this.writeHeadersFunc = writeHeadersFunc; + this.writeOptionsHolder = writeOptionsHolder; + } + + /// + /// Asynchronously sends response headers for the current call to the client. This method may only be invoked once for each call and needs to be invoked + /// before any response messages are written. Writing the first response message implicitly sends empty response headers if WriteResponseHeadersAsync haven't + /// been called yet. + /// + /// The response headers to send. + /// The task that finished once response headers have been written. + public Task WriteResponseHeadersAsync(Metadata responseHeaders) + { + return writeHeadersFunc(responseHeaders); + } + + /// + /// Creates a propagation token to be used to propagate call context to a child call. + /// + public ContextPropagationToken CreatePropagationToken(ContextPropagationOptions options = null) + { + return new ContextPropagationToken(callHandle, deadline, cancellationToken, options); + } + + /// Name of method called in this RPC. + public string Method + { + get + { + return this.method; + } + } + + /// Name of host called in this RPC. + public string Host + { + get + { + return this.host; + } + } + + /// Address of the remote endpoint in URI format. + public string Peer + { + get + { + return this.peer; + } + } + + /// Deadline for this RPC. + public DateTime Deadline + { + get + { + return this.deadline; + } + } + + /// Initial metadata sent by client. + public Metadata RequestHeaders + { + get + { + return this.requestHeaders; + } + } + + /// Cancellation token signals when call is cancelled. + public CancellationToken CancellationToken + { + get + { + return this.cancellationToken; + } + } + + /// Trailers to send back to client after RPC finishes. + public Metadata ResponseTrailers + { + get + { + return this.responseTrailers; + } + } + + /// Status to send back to client after RPC finishes. + public Status Status + { + get + { + return this.status; + } + + set + { + status = value; + } + } + + /// + /// Allows setting write options for the following write. + /// For streaming response calls, this property is also exposed as on IServerStreamWriter for convenience. + /// Both properties are backed by the same underlying value. + /// + public WriteOptions WriteOptions + { + get + { + return writeOptionsHolder.WriteOptions; + } + + set + { + writeOptionsHolder.WriteOptions = value; + } + } + } + + /// + /// Allows sharing write options between ServerCallContext and other objects. + /// + public interface IHasWriteOptions + { + /// + /// Gets or sets the write options. + /// + WriteOptions WriteOptions { get; set; } + } +} diff --git a/src/csharp/Grpc.Core/ServerCredentials.cs b/src/csharp/Grpc.Core/ServerCredentials.cs new file mode 100644 index 00000000..3c6703d3 --- /dev/null +++ b/src/csharp/Grpc.Core/ServerCredentials.cs @@ -0,0 +1,160 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Collections.Generic; +using Grpc.Core.Internal; +using Grpc.Core.Utils; + +namespace Grpc.Core +{ + /// + /// Server side credentials. + /// + public abstract class ServerCredentials + { + static readonly ServerCredentials InsecureInstance = new InsecureServerCredentialsImpl(); + + /// + /// Returns instance of credential that provides no security and + /// will result in creating an unsecure server port with no encryption whatsoever. + /// + public static ServerCredentials Insecure + { + get + { + return InsecureInstance; + } + } + + /// + /// Creates native object for the credentials. + /// + /// The native credentials. + internal abstract ServerCredentialsSafeHandle ToNativeCredentials(); + + private sealed class InsecureServerCredentialsImpl : ServerCredentials + { + internal override ServerCredentialsSafeHandle ToNativeCredentials() + { + return null; + } + } + } + + /// + /// Server-side SSL credentials. + /// + public class SslServerCredentials : ServerCredentials + { + readonly IList keyCertificatePairs; + readonly string rootCertificates; + readonly bool forceClientAuth; + + /// + /// Creates server-side SSL credentials. + /// + /// Key-certificates to use. + /// PEM encoded client root certificates used to authenticate client. + /// If true, client will be rejected unless it proves its unthenticity using against rootCertificates. + public SslServerCredentials(IEnumerable keyCertificatePairs, string rootCertificates, bool forceClientAuth) + { + this.keyCertificatePairs = new List(keyCertificatePairs).AsReadOnly(); + Preconditions.CheckArgument(this.keyCertificatePairs.Count > 0, + "At least one KeyCertificatePair needs to be provided."); + if (forceClientAuth) + { + Preconditions.CheckNotNull(rootCertificates, + "Cannot force client authentication unless you provide rootCertificates."); + } + this.rootCertificates = rootCertificates; + this.forceClientAuth = forceClientAuth; + } + + /// + /// Creates server-side SSL credentials. + /// This constructor should be use if you do not wish to autheticate client + /// using client root certificates. + /// + /// Key-certificates to use. + public SslServerCredentials(IEnumerable keyCertificatePairs) : this(keyCertificatePairs, null, false) + { + } + + /// + /// Key-certificate pairs. + /// + public IList KeyCertificatePairs + { + get + { + return this.keyCertificatePairs; + } + } + + /// + /// PEM encoded client root certificates. + /// + public string RootCertificates + { + get + { + return this.rootCertificates; + } + } + + /// + /// If true, the authenticity of client check will be enforced. + /// + public bool ForceClientAuthentication + { + get + { + return this.forceClientAuth; + } + } + + internal override ServerCredentialsSafeHandle ToNativeCredentials() + { + int count = keyCertificatePairs.Count; + string[] certChains = new string[count]; + string[] keys = new string[count]; + for (int i = 0; i < count; i++) + { + certChains[i] = keyCertificatePairs[i].CertificateChain; + keys[i] = keyCertificatePairs[i].PrivateKey; + } + return ServerCredentialsSafeHandle.CreateSslCredentials(rootCertificates, certChains, keys, forceClientAuth); + } + } +} diff --git a/src/csharp/Grpc.Core/ServerMethods.cs b/src/csharp/Grpc.Core/ServerMethods.cs new file mode 100644 index 00000000..728f77cd --- /dev/null +++ b/src/csharp/Grpc.Core/ServerMethods.cs @@ -0,0 +1,73 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System.Threading.Tasks; + +namespace Grpc.Core +{ + /// + /// Server-side handler for unary call. + /// + /// Request message type for this method. + /// Response message type for this method. + public delegate Task UnaryServerMethod(TRequest request, ServerCallContext context) + where TRequest : class + where TResponse : class; + + /// + /// Server-side handler for client streaming call. + /// + /// Request message type for this method. + /// Response message type for this method. + public delegate Task ClientStreamingServerMethod(IAsyncStreamReader requestStream, ServerCallContext context) + where TRequest : class + where TResponse : class; + + /// + /// Server-side handler for server streaming call. + /// + /// Request message type for this method. + /// Response message type for this method. + public delegate Task ServerStreamingServerMethod(TRequest request, IServerStreamWriter responseStream, ServerCallContext context) + where TRequest : class + where TResponse : class; + + /// + /// Server-side handler for bidi streaming call. + /// + /// Request message type for this method. + /// Response message type for this method. + public delegate Task DuplexStreamingServerMethod(IAsyncStreamReader requestStream, IServerStreamWriter responseStream, ServerCallContext context) + where TRequest : class + where TResponse : class; +} diff --git a/src/csharp/Grpc.Core/ServerPort.cs b/src/csharp/Grpc.Core/ServerPort.cs new file mode 100644 index 00000000..598404d0 --- /dev/null +++ b/src/csharp/Grpc.Core/ServerPort.cs @@ -0,0 +1,120 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; + +using Grpc.Core.Utils; + +namespace Grpc.Core +{ + /// + /// A port exposed by a server. + /// + public class ServerPort + { + /// + /// Pass this value as port to have the server choose an unused listening port for you. + /// Ports added to a server will contain the bound port in their property. + /// + public const int PickUnused = 0; + + readonly string host; + readonly int port; + readonly ServerCredentials credentials; + readonly int boundPort; + + /// + /// Creates a new port on which server should listen. + /// + /// The port on which server will be listening. + /// the host + /// the port. If zero, an unused port is chosen automatically. + /// credentials to use to secure this port. + public ServerPort(string host, int port, ServerCredentials credentials) + { + this.host = Preconditions.CheckNotNull(host, "host"); + this.port = port; + this.credentials = Preconditions.CheckNotNull(credentials, "credentials"); + } + + /// + /// Creates a port from an existing ServerPort instance and boundPort value. + /// + internal ServerPort(ServerPort serverPort, int boundPort) + { + this.host = serverPort.host; + this.port = serverPort.port; + this.credentials = serverPort.credentials; + this.boundPort = boundPort; + } + + /// The host. + public string Host + { + get + { + return host; + } + } + + /// The port. + public int Port + { + get + { + return port; + } + } + + /// The server credentials. + public ServerCredentials Credentials + { + get + { + return credentials; + } + } + + /// + /// The port actually bound by the server. This is useful if you let server + /// pick port automatically. + /// + public int BoundPort + { + get + { + return boundPort; + } + } + } +} diff --git a/src/csharp/Grpc.Core/ServerServiceDefinition.cs b/src/csharp/Grpc.Core/ServerServiceDefinition.cs new file mode 100644 index 00000000..deb1431c --- /dev/null +++ b/src/csharp/Grpc.Core/ServerServiceDefinition.cs @@ -0,0 +1,172 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using Grpc.Core.Internal; + +namespace Grpc.Core +{ + /// + /// Mapping of method names to server call handlers. + /// Normally, the ServerServiceDefinition objects will be created by the BindService factory method + /// that is part of the autogenerated code for a protocol buffers service definition. + /// + public class ServerServiceDefinition + { + readonly ReadOnlyDictionary callHandlers; + + private ServerServiceDefinition(Dictionary callHandlers) + { + this.callHandlers = new ReadOnlyDictionary(callHandlers); + } + + internal IDictionary CallHandlers + { + get + { + return this.callHandlers; + } + } + + /// + /// Creates a new builder object for ServerServiceDefinition. + /// + /// The service name. + /// The builder object. + public static Builder CreateBuilder(string serviceName) + { + return new Builder(serviceName); + } + + /// + /// Builder class for . + /// + public class Builder + { + readonly string serviceName; + readonly Dictionary callHandlers = new Dictionary(); + + /// + /// Creates a new instance of builder. + /// + /// The service name. + public Builder(string serviceName) + { + this.serviceName = serviceName; + } + + /// + /// Adds a definitions for a single request - single response method. + /// + /// The request message class. + /// The response message class. + /// The method. + /// The method handler. + /// This builder instance. + public Builder AddMethod( + Method method, + UnaryServerMethod handler) + where TRequest : class + where TResponse : class + { + callHandlers.Add(method.FullName, ServerCalls.UnaryCall(method, handler)); + return this; + } + + /// + /// Adds a definitions for a client streaming method. + /// + /// The request message class. + /// The response message class. + /// The method. + /// The method handler. + /// This builder instance. + public Builder AddMethod( + Method method, + ClientStreamingServerMethod handler) + where TRequest : class + where TResponse : class + { + callHandlers.Add(method.FullName, ServerCalls.ClientStreamingCall(method, handler)); + return this; + } + + /// + /// Adds a definitions for a server streaming method. + /// + /// The request message class. + /// The response message class. + /// The method. + /// The method handler. + /// This builder instance. + public Builder AddMethod( + Method method, + ServerStreamingServerMethod handler) + where TRequest : class + where TResponse : class + { + callHandlers.Add(method.FullName, ServerCalls.ServerStreamingCall(method, handler)); + return this; + } + + /// + /// Adds a definitions for a bidirectional streaming method. + /// + /// The request message class. + /// The response message class. + /// The method. + /// The method handler. + /// This builder instance. + public Builder AddMethod( + Method method, + DuplexStreamingServerMethod handler) + where TRequest : class + where TResponse : class + { + callHandlers.Add(method.FullName, ServerCalls.DuplexStreamingCall(method, handler)); + return this; + } + + /// + /// Creates an immutable ServerServiceDefinition from this builder. + /// + /// The ServerServiceDefinition object. + public ServerServiceDefinition Build() + { + return new ServerServiceDefinition(callHandlers); + } + } + } +} diff --git a/src/csharp/Grpc.Core/Status.cs b/src/csharp/Grpc.Core/Status.cs new file mode 100644 index 00000000..6bd8dc82 --- /dev/null +++ b/src/csharp/Grpc.Core/Status.cs @@ -0,0 +1,95 @@ +#region Copyright notice and license +// Copyright 2015, 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. +#endregion + +using Grpc.Core.Utils; + +namespace Grpc.Core +{ + /// + /// Represents RPC result, which consists of and an optional detail string. + /// + public struct Status + { + /// + /// Default result of a successful RPC. StatusCode=OK, empty details message. + /// + public static readonly Status DefaultSuccess = new Status(StatusCode.OK, ""); + + /// + /// Default result of a cancelled RPC. StatusCode=Cancelled, empty details message. + /// + public static readonly Status DefaultCancelled = new Status(StatusCode.Cancelled, ""); + + readonly StatusCode statusCode; + readonly string detail; + + /// + /// Creates a new instance of Status. + /// + /// Status code. + /// Detail. + public Status(StatusCode statusCode, string detail) + { + this.statusCode = statusCode; + this.detail = detail; + } + + /// + /// Gets the gRPC status code. OK indicates success, all other values indicate an error. + /// + public StatusCode StatusCode + { + get + { + return statusCode; + } + } + + /// + /// Gets the detail. + /// + public string Detail + { + get + { + return detail; + } + } + + /// + /// Returns a that represents the current . + /// + public override string ToString() + { + return string.Format("Status(StatusCode={0}, Detail=\"{1}\")", statusCode, detail); + } + } +} diff --git a/src/csharp/Grpc.Core/StatusCode.cs b/src/csharp/Grpc.Core/StatusCode.cs new file mode 100644 index 00000000..90606955 --- /dev/null +++ b/src/csharp/Grpc.Core/StatusCode.cs @@ -0,0 +1,139 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +namespace Grpc.Core +{ + /// + /// Result of a remote procedure call. + /// Based on grpc_status_code from grpc/status.h + /// + public enum StatusCode + { + /// Not an error; returned on success. + OK = 0, + + /// The operation was cancelled (typically by the caller). + Cancelled = 1, + + /// + /// Unknown error. An example of where this error may be returned is + /// if a Status value received from another address space belongs to + /// an error-space that is not known in this address space. Also + /// errors raised by APIs that do not return enough error information + /// may be converted to this error. + /// + Unknown = 2, + + /// + /// Client specified an invalid argument. Note that this differs + /// from FAILED_PRECONDITION. INVALID_ARGUMENT indicates arguments + /// that are problematic regardless of the state of the system + /// (e.g., a malformed file name). + /// + InvalidArgument = 3, + + /// + /// Deadline expired before operation could complete. For operations + /// that change the state of the system, this error may be returned + /// even if the operation has completed successfully. For example, a + /// successful response from a server could have been delayed long + /// enough for the deadline to expire. + /// + DeadlineExceeded = 4, + + /// Some requested entity (e.g., file or directory) was not found. + NotFound = 5, + + /// Some entity that we attempted to create (e.g., file or directory) already exists. + AlreadyExists = 6, + + /// + /// The caller does not have permission to execute the specified + /// operation. PERMISSION_DENIED must not be used for rejections + /// caused by exhausting some resource (use RESOURCE_EXHAUSTED + /// instead for those errors). PERMISSION_DENIED must not be + /// used if the caller can not be identified (use UNAUTHENTICATED + /// instead for those errors). + /// + PermissionDenied = 7, + + /// The request does not have valid authentication credentials for the operation. + Unauthenticated = 16, + + /// + /// Some resource has been exhausted, perhaps a per-user quota, or + /// perhaps the entire file system is out of space. + /// + ResourceExhausted = 8, + + /// + /// Operation was rejected because the system is not in a state + /// required for the operation's execution. For example, directory + /// to be deleted may be non-empty, an rmdir operation is applied to + /// a non-directory, etc. + /// + FailedPrecondition = 9, + + /// + /// The operation was aborted, typically due to a concurrency issue + /// like sequencer check failures, transaction aborts, etc. + /// + Aborted = 10, + + /// + /// Operation was attempted past the valid range. E.g., seeking or + /// reading past end of file. + /// + OutOfRange = 11, + + /// Operation is not implemented or not supported/enabled in this service. + Unimplemented = 12, + + /// + /// Internal errors. Means some invariants expected by underlying + /// system has been broken. If you see one of these errors, + /// something is very broken. + /// + Internal = 13, + + /// + /// The service is currently unavailable. This is a most likely a + /// transient condition and may be corrected by retrying with + /// a backoff. + /// + Unavailable = 14, + + /// Unrecoverable data loss or corruption. + DataLoss = 15 + } +} diff --git a/src/csharp/Grpc.Core/Utils/AsyncStreamExtensions.cs b/src/csharp/Grpc.Core/Utils/AsyncStreamExtensions.cs new file mode 100644 index 00000000..cdf1e510 --- /dev/null +++ b/src/csharp/Grpc.Core/Utils/AsyncStreamExtensions.cs @@ -0,0 +1,100 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Grpc.Core.Utils +{ + /// + /// Extension methods that simplify work with gRPC streaming calls. + /// + public static class AsyncStreamExtensions + { + /// + /// Reads the entire stream and executes an async action for each element. + /// + public static async Task ForEachAsync(this IAsyncStreamReader streamReader, Func asyncAction) + where T : class + { + while (await streamReader.MoveNext()) + { + await asyncAction(streamReader.Current); + } + } + + /// + /// Reads the entire stream and creates a list containing all the elements read. + /// + public static async Task> ToListAsync(this IAsyncStreamReader streamReader) + where T : class + { + var result = new List(); + while (await streamReader.MoveNext()) + { + result.Add(streamReader.Current); + } + return result; + } + + /// + /// Writes all elements from given enumerable to the stream. + /// Completes the stream afterwards unless close = false. + /// + public static async Task WriteAllAsync(this IClientStreamWriter streamWriter, IEnumerable elements, bool complete = true) + where T : class + { + foreach (var element in elements) + { + await streamWriter.WriteAsync(element); + } + if (complete) + { + await streamWriter.CompleteAsync(); + } + } + + /// + /// Writes all elements from given enumerable to the stream. + /// + public static async Task WriteAllAsync(this IServerStreamWriter streamWriter, IEnumerable elements) + where T : class + { + foreach (var element in elements) + { + await streamWriter.WriteAsync(element); + } + } + } +} diff --git a/src/csharp/Grpc.Core/Utils/BenchmarkUtil.cs b/src/csharp/Grpc.Core/Utils/BenchmarkUtil.cs new file mode 100644 index 00000000..eb3a5b16 --- /dev/null +++ b/src/csharp/Grpc.Core/Utils/BenchmarkUtil.cs @@ -0,0 +1,72 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading.Tasks; + +namespace Grpc.Core.Utils +{ + /// + /// Utility methods to run microbenchmarks. + /// + public static class BenchmarkUtil + { + /// + /// Runs a simple benchmark preceded by warmup phase. + /// + public static void RunBenchmark(int warmupIterations, int benchmarkIterations, Action action) + { + var logger = GrpcEnvironment.Logger; + + logger.Info("Warmup iterations: {0}", warmupIterations); + for (int i = 0; i < warmupIterations; i++) + { + action(); + } + + logger.Info("Benchmark iterations: {0}", benchmarkIterations); + var stopwatch = new Stopwatch(); + stopwatch.Start(); + for (int i = 0; i < benchmarkIterations; i++) + { + action(); + } + stopwatch.Stop(); + logger.Info("Elapsed time: {0}ms", stopwatch.ElapsedMilliseconds); + logger.Info("Ops per second: {0}", (int)((double)benchmarkIterations * 1000 / stopwatch.ElapsedMilliseconds)); + } + } +} diff --git a/src/csharp/Grpc.Core/Utils/Preconditions.cs b/src/csharp/Grpc.Core/Utils/Preconditions.cs new file mode 100644 index 00000000..a8ab6033 --- /dev/null +++ b/src/csharp/Grpc.Core/Utils/Preconditions.cs @@ -0,0 +1,120 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; + +namespace Grpc.Core.Utils +{ + /// + /// Utility methods to simplify checking preconditions in the code. + /// + public static class Preconditions + { + /// + /// Throws if condition is false. + /// + /// The condition. + public static void CheckArgument(bool condition) + { + if (!condition) + { + throw new ArgumentException(); + } + } + + /// + /// Throws with given message if condition is false. + /// + /// The condition. + /// The error message. + public static void CheckArgument(bool condition, string errorMessage) + { + if (!condition) + { + throw new ArgumentException(errorMessage); + } + } + + /// + /// Throws if reference is null. + /// + /// The reference. + public static T CheckNotNull(T reference) + { + if (reference == null) + { + throw new ArgumentNullException(); + } + return reference; + } + + /// + /// Throws if reference is null. + /// + /// The reference. + /// The parameter name. + public static T CheckNotNull(T reference, string paramName) + { + if (reference == null) + { + throw new ArgumentNullException(paramName); + } + return reference; + } + + /// + /// Throws if condition is false. + /// + /// The condition. + public static void CheckState(bool condition) + { + if (!condition) + { + throw new InvalidOperationException(); + } + } + + /// + /// Throws with given message if condition is false. + /// + /// The condition. + /// The error message. + public static void CheckState(bool condition, string errorMessage) + { + if (!condition) + { + throw new InvalidOperationException(errorMessage); + } + } + } +} diff --git a/src/csharp/Grpc.Core/Version.cs b/src/csharp/Grpc.Core/Version.cs new file mode 100644 index 00000000..d02b301c --- /dev/null +++ b/src/csharp/Grpc.Core/Version.cs @@ -0,0 +1,37 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System.Reflection; + +// The current version of gRPC C#. +[assembly: AssemblyVersion(Grpc.Core.VersionInfo.CurrentVersion + ".0")] diff --git a/src/csharp/Grpc.Core/VersionInfo.cs b/src/csharp/Grpc.Core/VersionInfo.cs new file mode 100644 index 00000000..e9ec30e9 --- /dev/null +++ b/src/csharp/Grpc.Core/VersionInfo.cs @@ -0,0 +1,46 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +namespace Grpc.Core +{ + /// + /// Provides info about current version of gRPC. + /// + public static class VersionInfo + { + /// + /// Current version of gRPC C# + /// + public const string CurrentVersion = "0.7.1"; + } +} diff --git a/src/csharp/Grpc.Core/WriteOptions.cs b/src/csharp/Grpc.Core/WriteOptions.cs new file mode 100644 index 00000000..7523ada8 --- /dev/null +++ b/src/csharp/Grpc.Core/WriteOptions.cs @@ -0,0 +1,89 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; + +namespace Grpc.Core +{ + /// + /// Flags for write operations. + /// + [Flags] + public enum WriteFlags + { + /// + /// Hint that the write may be buffered and need not go out on the wire immediately. + /// gRPC is free to buffer the message until the next non-buffered + /// write, or until write stream completion, but it need not buffer completely or at all. + /// + BufferHint = 0x1, + + /// + /// Force compression to be disabled for a particular write. + /// + NoCompress = 0x2 + } + + /// + /// Options for write operations. + /// + public class WriteOptions + { + /// + /// Default write options. + /// + public static readonly WriteOptions Default = new WriteOptions(); + + private WriteFlags flags; + + /// + /// Initializes a new instance of WriteOptions class. + /// + /// The write flags. + public WriteOptions(WriteFlags flags = default(WriteFlags)) + { + this.flags = flags; + } + + /// + /// Gets the write flags. + /// + public WriteFlags Flags + { + get + { + return this.flags; + } + } + } +} diff --git a/src/csharp/Grpc.Core/packages.config b/src/csharp/Grpc.Core/packages.config new file mode 100644 index 00000000..9b12b9b0 --- /dev/null +++ b/src/csharp/Grpc.Core/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/csharp/Grpc.Examples.MathClient/.gitignore b/src/csharp/Grpc.Examples.MathClient/.gitignore new file mode 100644 index 00000000..1746e326 --- /dev/null +++ b/src/csharp/Grpc.Examples.MathClient/.gitignore @@ -0,0 +1,2 @@ +bin +obj diff --git a/src/csharp/Grpc.Examples.MathClient/Grpc.Examples.MathClient.csproj b/src/csharp/Grpc.Examples.MathClient/Grpc.Examples.MathClient.csproj new file mode 100644 index 00000000..b603e3af --- /dev/null +++ b/src/csharp/Grpc.Examples.MathClient/Grpc.Examples.MathClient.csproj @@ -0,0 +1,60 @@ + + + + Debug + AnyCPU + 10.0.0 + 2.0 + {61ECB8EE-0C96-4F8E-B187-8E4D227417C0} + Exe + math + MathClient + v4.5 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + + + pdbonly + true + bin\Release + prompt + 4 + + + pdbonly + true + bin\ReleaseSigned + prompt + 4 + True + C:\keys\Grpc.snk + + + + + + + Version.cs + + + + + + + + {CCC4440E-49F7-4790-B0AF-FEABB0837AE7} + Grpc.Core + + + {7DC1433E-3225-42C7-B7EA-546D56E27A4B} + Grpc.Examples + + + \ No newline at end of file diff --git a/src/csharp/Grpc.Examples.MathClient/MathClient.cs b/src/csharp/Grpc.Examples.MathClient/MathClient.cs new file mode 100644 index 00000000..01e4a80b --- /dev/null +++ b/src/csharp/Grpc.Examples.MathClient/MathClient.cs @@ -0,0 +1,59 @@ +#region Copyright notice and license +// Copyright 2015, 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. +#endregion +using System; +using System.Runtime.InteropServices; +using System.Threading; +using Grpc.Core; + +namespace Math +{ + class MathClient + { + public static void Main(string[] args) + { + var channel = new Channel("127.0.0.1", 23456, Credentials.Insecure); + Math.IMathClient client = new Math.MathClient(channel); + MathExamples.DivExample(client); + + MathExamples.DivAsyncExample(client).Wait(); + + MathExamples.FibExample(client).Wait(); + + MathExamples.SumExample(client).Wait(); + + MathExamples.DivManyExample(client).Wait(); + + MathExamples.DependendRequestsExample(client).Wait(); + + channel.ShutdownAsync().Wait(); + } + } +} diff --git a/src/csharp/Grpc.Examples.MathClient/Properties/AssemblyInfo.cs b/src/csharp/Grpc.Examples.MathClient/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..0fb0dbd5 --- /dev/null +++ b/src/csharp/Grpc.Examples.MathClient/Properties/AssemblyInfo.cs @@ -0,0 +1,11 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyTitle("Grpc.Examples.MathClient")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("Google Inc. All rights reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] diff --git a/src/csharp/Grpc.Examples.MathServer/.gitignore b/src/csharp/Grpc.Examples.MathServer/.gitignore new file mode 100644 index 00000000..1746e326 --- /dev/null +++ b/src/csharp/Grpc.Examples.MathServer/.gitignore @@ -0,0 +1,2 @@ +bin +obj diff --git a/src/csharp/Grpc.Examples.MathServer/Grpc.Examples.MathServer.csproj b/src/csharp/Grpc.Examples.MathServer/Grpc.Examples.MathServer.csproj new file mode 100644 index 00000000..5f74b587 --- /dev/null +++ b/src/csharp/Grpc.Examples.MathServer/Grpc.Examples.MathServer.csproj @@ -0,0 +1,60 @@ + + + + Debug + AnyCPU + 10.0.0 + 2.0 + {BF62FE08-373A-43D6-9D73-41CAA38B7011} + Exe + Grpc.Examples.MathServer + MathServer + v4.5 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + + + pdbonly + true + bin\Release + prompt + 4 + + + pdbonly + true + bin\ReleaseSigned + prompt + 4 + True + C:\keys\Grpc.snk + + + + + + + Version.cs + + + + + + + + {CCC4440E-49F7-4790-B0AF-FEABB0837AE7} + Grpc.Core + + + {7DC1433E-3225-42C7-B7EA-546D56E27A4B} + Grpc.Examples + + + \ No newline at end of file diff --git a/src/csharp/Grpc.Examples.MathServer/MathServer.cs b/src/csharp/Grpc.Examples.MathServer/MathServer.cs new file mode 100644 index 00000000..6e974a08 --- /dev/null +++ b/src/csharp/Grpc.Examples.MathServer/MathServer.cs @@ -0,0 +1,61 @@ +#region Copyright notice and license +// Copyright 2015, 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. +#endregion + +using System; +using System.Runtime.InteropServices; +using System.Threading; +using Grpc.Core; + +namespace Math +{ + class MainClass + { + const string Host = "0.0.0.0"; + const int Port = 23456; + + public static void Main(string[] args) + { + Server server = new Server + { + Services = { Math.BindService(new MathServiceImpl()) }, + Ports = { { Host, Port, ServerCredentials.Insecure } } + }; + server.Start(); + + Console.WriteLine("MathServer listening on port " + Port); + + Console.WriteLine("Press any key to stop the server..."); + Console.ReadKey(); + + server.ShutdownAsync().Wait(); + } + } +} diff --git a/src/csharp/Grpc.Examples.MathServer/Properties/AssemblyInfo.cs b/src/csharp/Grpc.Examples.MathServer/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..63035b6c --- /dev/null +++ b/src/csharp/Grpc.Examples.MathServer/Properties/AssemblyInfo.cs @@ -0,0 +1,11 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyTitle("Grpc.Examples.MathServer")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("Google Inc. All rights reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] diff --git a/src/csharp/Grpc.Examples.Tests/.gitignore b/src/csharp/Grpc.Examples.Tests/.gitignore new file mode 100644 index 00000000..4795a95b --- /dev/null +++ b/src/csharp/Grpc.Examples.Tests/.gitignore @@ -0,0 +1,3 @@ +test-results +bin +obj diff --git a/src/csharp/Grpc.Examples.Tests/Grpc.Examples.Tests.csproj b/src/csharp/Grpc.Examples.Tests/Grpc.Examples.Tests.csproj new file mode 100644 index 00000000..c4c1ee6d --- /dev/null +++ b/src/csharp/Grpc.Examples.Tests/Grpc.Examples.Tests.csproj @@ -0,0 +1,77 @@ + + + + Debug + AnyCPU + 10.0.0 + 2.0 + {143B1C29-C442-4BE0-BF3F-A8F92288AC9F} + Library + Grpc.Examples.Tests + Grpc.Examples.Tests + v4.5 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + + + pdbonly + true + bin\Release + prompt + 4 + + + pdbonly + true + bin\ReleaseSigned + prompt + 4 + True + C:\keys\Grpc.snk + + + + False + ..\packages\Google.Protobuf.3.0.0-alpha4\lib\portable-net45+netcore45+wpa81+wp8\Google.Protobuf.dll + + + ..\packages\NUnit.2.6.4\lib\nunit.framework.dll + + + + False + ..\packages\Ix-Async.1.2.3\lib\net45\System.Interactive.Async.dll + + + + + Version.cs + + + + + + + + {CCC4440E-49F7-4790-B0AF-FEABB0837AE7} + Grpc.Core + + + {7DC1433E-3225-42C7-B7EA-546D56E27A4B} + Grpc.Examples + + + + + + + + + \ No newline at end of file diff --git a/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs b/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs new file mode 100644 index 00000000..e2975b5d --- /dev/null +++ b/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs @@ -0,0 +1,193 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Math.Tests +{ + /// + /// Math client talks to local math server. + /// + public class MathClientServerTest + { + const string Host = "localhost"; + Server server; + Channel channel; + Math.MathClient client; + + [TestFixtureSetUp] + public void Init() + { + server = new Server + { + Services = { Math.BindService(new MathServiceImpl()) }, + Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } } + }; + server.Start(); + channel = new Channel(Host, server.Ports.Single().BoundPort, Credentials.Insecure); + client = Math.NewClient(channel); + } + + [TestFixtureTearDown] + public void Cleanup() + { + channel.ShutdownAsync().Wait(); + server.ShutdownAsync().Wait(); + } + + [Test] + public void Div1() + { + DivReply response = client.Div(new DivArgs { Dividend = 10, Divisor = 3 }); + Assert.AreEqual(3, response.Quotient); + Assert.AreEqual(1, response.Remainder); + } + + [Test] + public void Div2() + { + DivReply response = client.Div(new DivArgs { Dividend = 0, Divisor = 1 }); + Assert.AreEqual(0, response.Quotient); + Assert.AreEqual(0, response.Remainder); + } + + [Test] + public void DivByZero() + { + var ex = Assert.Throws(() => client.Div(new DivArgs { Dividend = 0, Divisor = 0 })); + Assert.AreEqual(StatusCode.Unknown, ex.Status.StatusCode); + } + + [Test] + public async Task DivAsync() + { + DivReply response = await client.DivAsync(new DivArgs { Dividend = 10, Divisor = 3 }); + Assert.AreEqual(3, response.Quotient); + Assert.AreEqual(1, response.Remainder); + } + + [Test] + public async Task Fib() + { + using (var call = client.Fib(new FibArgs { Limit = 6 })) + { + var responses = await call.ResponseStream.ToListAsync(); + CollectionAssert.AreEqual(new List { 1, 1, 2, 3, 5, 8 }, + responses.ConvertAll((n) => n.Num_)); + } + } + + [Test] + public async Task FibWithCancel() + { + var cts = new CancellationTokenSource(); + + using (var call = client.Fib(new FibArgs { Limit = 0 }, cancellationToken: cts.Token)) + { + List responses = new List(); + + try + { + while (await call.ResponseStream.MoveNext()) + { + if (responses.Count == 0) + { + cts.CancelAfter(500); // make sure we cancel soon + } + responses.Add(call.ResponseStream.Current.Num_); + } + Assert.Fail(); + } + catch (RpcException e) + { + Assert.IsTrue(responses.Count > 0); + Assert.AreEqual(StatusCode.Cancelled, e.Status.StatusCode); + } + } + } + + [Test] + public async Task FibWithDeadline() + { + using (var call = client.Fib(new FibArgs { Limit = 0 }, + deadline: DateTime.UtcNow.AddMilliseconds(500))) + { + var ex = Assert.Throws(async () => await call.ResponseStream.ToListAsync()); + + // We can't guarantee the status code always DeadlineExceeded. See issue #2685. + Assert.Contains(ex.Status.StatusCode, new[] { StatusCode.DeadlineExceeded, StatusCode.Internal }); + } + } + + // TODO: test Fib with limit=0 and cancellation + [Test] + public async Task Sum() + { + using (var call = client.Sum()) + { + var numbers = new List { 10, 20, 30 }.ConvertAll(n => new Num { Num_ = n }); + + await call.RequestStream.WriteAllAsync(numbers); + var result = await call.ResponseAsync; + Assert.AreEqual(60, result.Num_); + } + } + + [Test] + public async Task DivMany() + { + var divArgsList = new List + { + new DivArgs { Dividend = 10, Divisor = 3 }, + new DivArgs { Dividend = 100, Divisor = 21 }, + new DivArgs { Dividend = 7, Divisor = 2 } + }; + + using (var call = client.DivMany()) + { + await call.RequestStream.WriteAllAsync(divArgsList); + var result = await call.ResponseStream.ToListAsync(); + + CollectionAssert.AreEqual(new long[] { 3, 4, 3 }, result.ConvertAll((divReply) => divReply.Quotient)); + CollectionAssert.AreEqual(new long[] { 1, 16, 1 }, result.ConvertAll((divReply) => divReply.Remainder)); + } + } + } +} diff --git a/src/csharp/Grpc.Examples.Tests/Properties/AssemblyInfo.cs b/src/csharp/Grpc.Examples.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..846afb46 --- /dev/null +++ b/src/csharp/Grpc.Examples.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,11 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyTitle("Grpc.Examples.Tests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("Google Inc. All rights reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] diff --git a/src/csharp/Grpc.Examples.Tests/packages.config b/src/csharp/Grpc.Examples.Tests/packages.config new file mode 100644 index 00000000..7266fa17 --- /dev/null +++ b/src/csharp/Grpc.Examples.Tests/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/csharp/Grpc.Examples/.gitignore b/src/csharp/Grpc.Examples/.gitignore new file mode 100644 index 00000000..4795a95b --- /dev/null +++ b/src/csharp/Grpc.Examples/.gitignore @@ -0,0 +1,3 @@ +test-results +bin +obj diff --git a/src/csharp/Grpc.Examples/Grpc.Examples.csproj b/src/csharp/Grpc.Examples/Grpc.Examples.csproj new file mode 100644 index 00000000..55462e02 --- /dev/null +++ b/src/csharp/Grpc.Examples/Grpc.Examples.csproj @@ -0,0 +1,71 @@ + + + + Debug + AnyCPU + 10.0.0 + 2.0 + {7DC1433E-3225-42C7-B7EA-546D56E27A4B} + Library + Grpc.Examples + Grpc.Examples + v4.5 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + + + pdbonly + true + bin\Release + prompt + 4 + + + pdbonly + true + bin\ReleaseSigned + prompt + 4 + True + C:\keys\Grpc.snk + + + + False + ..\packages\Google.Protobuf.3.0.0-alpha4\lib\portable-net45+netcore45+wpa81+wp8\Google.Protobuf.dll + + + + + ..\packages\Ix-Async.1.2.3\lib\net45\System.Interactive.Async.dll + + + + + Version.cs + + + + + + + + + + + {CCC4440E-49F7-4790-B0AF-FEABB0837AE7} + Grpc.Core + + + + + + + \ No newline at end of file diff --git a/src/csharp/Grpc.Examples/Math.cs b/src/csharp/Grpc.Examples/Math.cs new file mode 100644 index 00000000..d0e1ee8a --- /dev/null +++ b/src/csharp/Grpc.Examples/Math.cs @@ -0,0 +1,616 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: math.proto +#pragma warning disable 1591, 0612, 3021 +#region Designer generated code + +using pb = global::Google.Protobuf; +using pbc = global::Google.Protobuf.Collections; +using pbr = global::Google.Protobuf.Reflection; +using scg = global::System.Collections.Generic; +namespace Math { + + namespace Proto { + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public static partial class Math { + + #region Descriptor + public static pbr::FileDescriptor Descriptor { + get { return descriptor; } + } + private static pbr::FileDescriptor descriptor; + + static Math() { + byte[] descriptorData = global::System.Convert.FromBase64String( + string.Concat( + "CgptYXRoLnByb3RvEgRtYXRoIiwKB0RpdkFyZ3MSEAoIZGl2aWRlbmQYASAB", + "KAMSDwoHZGl2aXNvchgCIAEoAyIvCghEaXZSZXBseRIQCghxdW90aWVudBgB", + "IAEoAxIRCglyZW1haW5kZXIYAiABKAMiGAoHRmliQXJncxINCgVsaW1pdBgB", + "IAEoAyISCgNOdW0SCwoDbnVtGAEgASgDIhkKCEZpYlJlcGx5Eg0KBWNvdW50", + "GAEgASgDMqQBCgRNYXRoEiYKA0RpdhINLm1hdGguRGl2QXJncxoOLm1hdGgu", + "RGl2UmVwbHkiABIuCgdEaXZNYW55Eg0ubWF0aC5EaXZBcmdzGg4ubWF0aC5E", + "aXZSZXBseSIAKAEwARIjCgNGaWISDS5tYXRoLkZpYkFyZ3MaCS5tYXRoLk51", + "bSIAMAESHwoDU3VtEgkubWF0aC5OdW0aCS5tYXRoLk51bSIAKAFiBnByb3Rv", + "Mw==")); + descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData, + new pbr::FileDescriptor[] { }, + new pbr::GeneratedCodeInfo(null, new pbr::GeneratedCodeInfo[] { + new pbr::GeneratedCodeInfo(typeof(global::Math.DivArgs), new[]{ "Dividend", "Divisor" }, null, null, null), + new pbr::GeneratedCodeInfo(typeof(global::Math.DivReply), new[]{ "Quotient", "Remainder" }, null, null, null), + new pbr::GeneratedCodeInfo(typeof(global::Math.FibArgs), new[]{ "Limit" }, null, null, null), + new pbr::GeneratedCodeInfo(typeof(global::Math.Num), new[]{ "Num_" }, null, null, null), + new pbr::GeneratedCodeInfo(typeof(global::Math.FibReply), new[]{ "Count" }, null, null, null) + })); + } + #endregion + + } + } + #region Messages + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class DivArgs : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new DivArgs()); + public static pb::MessageParser Parser { get { return _parser; } } + + public static pbr::MessageDescriptor Descriptor { + get { return global::Math.Proto.Math.Descriptor.MessageTypes[0]; } + } + + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + public DivArgs() { + OnConstruction(); + } + + partial void OnConstruction(); + + public DivArgs(DivArgs other) : this() { + dividend_ = other.dividend_; + divisor_ = other.divisor_; + } + + public DivArgs Clone() { + return new DivArgs(this); + } + + public const int DividendFieldNumber = 1; + private long dividend_; + public long Dividend { + get { return dividend_; } + set { + dividend_ = value; + } + } + + public const int DivisorFieldNumber = 2; + private long divisor_; + public long Divisor { + get { return divisor_; } + set { + divisor_ = value; + } + } + + public override bool Equals(object other) { + return Equals(other as DivArgs); + } + + public bool Equals(DivArgs other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Dividend != other.Dividend) return false; + if (Divisor != other.Divisor) return false; + return true; + } + + public override int GetHashCode() { + int hash = 1; + if (Dividend != 0L) hash ^= Dividend.GetHashCode(); + if (Divisor != 0L) hash ^= Divisor.GetHashCode(); + return hash; + } + + public override string ToString() { + return pb::JsonFormatter.Default.Format(this); + } + + public void WriteTo(pb::CodedOutputStream output) { + if (Dividend != 0L) { + output.WriteRawTag(8); + output.WriteInt64(Dividend); + } + if (Divisor != 0L) { + output.WriteRawTag(16); + output.WriteInt64(Divisor); + } + } + + public int CalculateSize() { + int size = 0; + if (Dividend != 0L) { + size += 1 + pb::CodedOutputStream.ComputeInt64Size(Dividend); + } + if (Divisor != 0L) { + size += 1 + pb::CodedOutputStream.ComputeInt64Size(Divisor); + } + return size; + } + + public void MergeFrom(DivArgs other) { + if (other == null) { + return; + } + if (other.Dividend != 0L) { + Dividend = other.Dividend; + } + if (other.Divisor != 0L) { + Divisor = other.Divisor; + } + } + + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + input.SkipLastField(); + break; + case 8: { + Dividend = input.ReadInt64(); + break; + } + case 16: { + Divisor = input.ReadInt64(); + break; + } + } + } + } + + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class DivReply : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new DivReply()); + public static pb::MessageParser Parser { get { return _parser; } } + + public static pbr::MessageDescriptor Descriptor { + get { return global::Math.Proto.Math.Descriptor.MessageTypes[1]; } + } + + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + public DivReply() { + OnConstruction(); + } + + partial void OnConstruction(); + + public DivReply(DivReply other) : this() { + quotient_ = other.quotient_; + remainder_ = other.remainder_; + } + + public DivReply Clone() { + return new DivReply(this); + } + + public const int QuotientFieldNumber = 1; + private long quotient_; + public long Quotient { + get { return quotient_; } + set { + quotient_ = value; + } + } + + public const int RemainderFieldNumber = 2; + private long remainder_; + public long Remainder { + get { return remainder_; } + set { + remainder_ = value; + } + } + + public override bool Equals(object other) { + return Equals(other as DivReply); + } + + public bool Equals(DivReply other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Quotient != other.Quotient) return false; + if (Remainder != other.Remainder) return false; + return true; + } + + public override int GetHashCode() { + int hash = 1; + if (Quotient != 0L) hash ^= Quotient.GetHashCode(); + if (Remainder != 0L) hash ^= Remainder.GetHashCode(); + return hash; + } + + public override string ToString() { + return pb::JsonFormatter.Default.Format(this); + } + + public void WriteTo(pb::CodedOutputStream output) { + if (Quotient != 0L) { + output.WriteRawTag(8); + output.WriteInt64(Quotient); + } + if (Remainder != 0L) { + output.WriteRawTag(16); + output.WriteInt64(Remainder); + } + } + + public int CalculateSize() { + int size = 0; + if (Quotient != 0L) { + size += 1 + pb::CodedOutputStream.ComputeInt64Size(Quotient); + } + if (Remainder != 0L) { + size += 1 + pb::CodedOutputStream.ComputeInt64Size(Remainder); + } + return size; + } + + public void MergeFrom(DivReply other) { + if (other == null) { + return; + } + if (other.Quotient != 0L) { + Quotient = other.Quotient; + } + if (other.Remainder != 0L) { + Remainder = other.Remainder; + } + } + + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + input.SkipLastField(); + break; + case 8: { + Quotient = input.ReadInt64(); + break; + } + case 16: { + Remainder = input.ReadInt64(); + break; + } + } + } + } + + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class FibArgs : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new FibArgs()); + public static pb::MessageParser Parser { get { return _parser; } } + + public static pbr::MessageDescriptor Descriptor { + get { return global::Math.Proto.Math.Descriptor.MessageTypes[2]; } + } + + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + public FibArgs() { + OnConstruction(); + } + + partial void OnConstruction(); + + public FibArgs(FibArgs other) : this() { + limit_ = other.limit_; + } + + public FibArgs Clone() { + return new FibArgs(this); + } + + public const int LimitFieldNumber = 1; + private long limit_; + public long Limit { + get { return limit_; } + set { + limit_ = value; + } + } + + public override bool Equals(object other) { + return Equals(other as FibArgs); + } + + public bool Equals(FibArgs other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Limit != other.Limit) return false; + return true; + } + + public override int GetHashCode() { + int hash = 1; + if (Limit != 0L) hash ^= Limit.GetHashCode(); + return hash; + } + + public override string ToString() { + return pb::JsonFormatter.Default.Format(this); + } + + public void WriteTo(pb::CodedOutputStream output) { + if (Limit != 0L) { + output.WriteRawTag(8); + output.WriteInt64(Limit); + } + } + + public int CalculateSize() { + int size = 0; + if (Limit != 0L) { + size += 1 + pb::CodedOutputStream.ComputeInt64Size(Limit); + } + return size; + } + + public void MergeFrom(FibArgs other) { + if (other == null) { + return; + } + if (other.Limit != 0L) { + Limit = other.Limit; + } + } + + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + input.SkipLastField(); + break; + case 8: { + Limit = input.ReadInt64(); + break; + } + } + } + } + + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class Num : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new Num()); + public static pb::MessageParser Parser { get { return _parser; } } + + public static pbr::MessageDescriptor Descriptor { + get { return global::Math.Proto.Math.Descriptor.MessageTypes[3]; } + } + + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + public Num() { + OnConstruction(); + } + + partial void OnConstruction(); + + public Num(Num other) : this() { + num_ = other.num_; + } + + public Num Clone() { + return new Num(this); + } + + public const int Num_FieldNumber = 1; + private long num_; + public long Num_ { + get { return num_; } + set { + num_ = value; + } + } + + public override bool Equals(object other) { + return Equals(other as Num); + } + + public bool Equals(Num other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Num_ != other.Num_) return false; + return true; + } + + public override int GetHashCode() { + int hash = 1; + if (Num_ != 0L) hash ^= Num_.GetHashCode(); + return hash; + } + + public override string ToString() { + return pb::JsonFormatter.Default.Format(this); + } + + public void WriteTo(pb::CodedOutputStream output) { + if (Num_ != 0L) { + output.WriteRawTag(8); + output.WriteInt64(Num_); + } + } + + public int CalculateSize() { + int size = 0; + if (Num_ != 0L) { + size += 1 + pb::CodedOutputStream.ComputeInt64Size(Num_); + } + return size; + } + + public void MergeFrom(Num other) { + if (other == null) { + return; + } + if (other.Num_ != 0L) { + Num_ = other.Num_; + } + } + + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + input.SkipLastField(); + break; + case 8: { + Num_ = input.ReadInt64(); + break; + } + } + } + } + + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class FibReply : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new FibReply()); + public static pb::MessageParser Parser { get { return _parser; } } + + public static pbr::MessageDescriptor Descriptor { + get { return global::Math.Proto.Math.Descriptor.MessageTypes[4]; } + } + + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + public FibReply() { + OnConstruction(); + } + + partial void OnConstruction(); + + public FibReply(FibReply other) : this() { + count_ = other.count_; + } + + public FibReply Clone() { + return new FibReply(this); + } + + public const int CountFieldNumber = 1; + private long count_; + public long Count { + get { return count_; } + set { + count_ = value; + } + } + + public override bool Equals(object other) { + return Equals(other as FibReply); + } + + public bool Equals(FibReply other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Count != other.Count) return false; + return true; + } + + public override int GetHashCode() { + int hash = 1; + if (Count != 0L) hash ^= Count.GetHashCode(); + return hash; + } + + public override string ToString() { + return pb::JsonFormatter.Default.Format(this); + } + + public void WriteTo(pb::CodedOutputStream output) { + if (Count != 0L) { + output.WriteRawTag(8); + output.WriteInt64(Count); + } + } + + public int CalculateSize() { + int size = 0; + if (Count != 0L) { + size += 1 + pb::CodedOutputStream.ComputeInt64Size(Count); + } + return size; + } + + public void MergeFrom(FibReply other) { + if (other == null) { + return; + } + if (other.Count != 0L) { + Count = other.Count; + } + } + + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + input.SkipLastField(); + break; + case 8: { + Count = input.ReadInt64(); + break; + } + } + } + } + + } + + #endregion + +} + +#endregion Designer generated code diff --git a/src/csharp/Grpc.Examples/MathExamples.cs b/src/csharp/Grpc.Examples/MathExamples.cs new file mode 100644 index 00000000..8009ccbb --- /dev/null +++ b/src/csharp/Grpc.Examples/MathExamples.cs @@ -0,0 +1,113 @@ +#region Copyright notice and license +// Copyright 2015, 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. +#endregion + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Grpc.Core.Utils; + +namespace Math +{ + public static class MathExamples + { + public static void DivExample(Math.IMathClient client) + { + DivReply result = client.Div(new DivArgs { Dividend = 10, Divisor = 3 }); + Console.WriteLine("Div Result: " + result); + } + + public static async Task DivAsyncExample(Math.IMathClient client) + { + DivReply result = await client.DivAsync(new DivArgs { Dividend = 4, Divisor = 5 }); + Console.WriteLine("DivAsync Result: " + result); + } + + public static async Task FibExample(Math.IMathClient client) + { + using (var call = client.Fib(new FibArgs { Limit = 5 })) + { + List result = await call.ResponseStream.ToListAsync(); + Console.WriteLine("Fib Result: " + string.Join("|", result)); + } + } + + public static async Task SumExample(Math.IMathClient client) + { + var numbers = new List + { + new Num { Num_ = 1 }, + new Num { Num_ = 2 }, + new Num { Num_ = 3 } + }; + + using (var call = client.Sum()) + { + await call.RequestStream.WriteAllAsync(numbers); + Console.WriteLine("Sum Result: " + await call.ResponseAsync); + } + } + + public static async Task DivManyExample(Math.IMathClient client) + { + var divArgsList = new List + { + new DivArgs { Dividend = 10, Divisor = 3 }, + new DivArgs { Dividend = 100, Divisor = 21 }, + new DivArgs { Dividend = 7, Divisor = 2 } + }; + using (var call = client.DivMany()) + { + await call.RequestStream.WriteAllAsync(divArgsList); + Console.WriteLine("DivMany Result: " + string.Join("|", await call.ResponseStream.ToListAsync())); + } + } + + public static async Task DependendRequestsExample(Math.IMathClient client) + { + var numbers = new List + { + new Num { Num_ = 1 }, + new Num { Num_ = 2 }, + new Num { Num_ = 3 } + }; + + Num sum; + using (var sumCall = client.Sum()) + { + await sumCall.RequestStream.WriteAllAsync(numbers); + sum = await sumCall.ResponseAsync; + } + + DivReply result = await client.DivAsync(new DivArgs { Dividend = sum.Num_, Divisor = numbers.Count }); + Console.WriteLine("Avg Result: " + result); + } + } +} diff --git a/src/csharp/Grpc.Examples/MathGrpc.cs b/src/csharp/Grpc.Examples/MathGrpc.cs new file mode 100644 index 00000000..175d110f --- /dev/null +++ b/src/csharp/Grpc.Examples/MathGrpc.cs @@ -0,0 +1,154 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: math.proto +#region Designer generated code + +using System; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core; + +namespace Math { + public static class Math + { + static readonly string __ServiceName = "math.Math"; + + static readonly Marshaller __Marshaller_DivArgs = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Math.DivArgs.Parser.ParseFrom); + static readonly Marshaller __Marshaller_DivReply = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Math.DivReply.Parser.ParseFrom); + static readonly Marshaller __Marshaller_FibArgs = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Math.FibArgs.Parser.ParseFrom); + static readonly Marshaller __Marshaller_Num = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Math.Num.Parser.ParseFrom); + + static readonly Method __Method_Div = new Method( + MethodType.Unary, + __ServiceName, + "Div", + __Marshaller_DivArgs, + __Marshaller_DivReply); + + static readonly Method __Method_DivMany = new Method( + MethodType.DuplexStreaming, + __ServiceName, + "DivMany", + __Marshaller_DivArgs, + __Marshaller_DivReply); + + static readonly Method __Method_Fib = new Method( + MethodType.ServerStreaming, + __ServiceName, + "Fib", + __Marshaller_FibArgs, + __Marshaller_Num); + + static readonly Method __Method_Sum = new Method( + MethodType.ClientStreaming, + __ServiceName, + "Sum", + __Marshaller_Num, + __Marshaller_Num); + + // service descriptor + public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor + { + get { return global::Math.Proto.Math.Descriptor.Services[0]; } + } + + // client interface + public interface IMathClient + { + global::Math.DivReply Div(global::Math.DivArgs request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + global::Math.DivReply Div(global::Math.DivArgs request, CallOptions options); + AsyncUnaryCall DivAsync(global::Math.DivArgs request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + AsyncUnaryCall DivAsync(global::Math.DivArgs request, CallOptions options); + AsyncDuplexStreamingCall DivMany(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + AsyncDuplexStreamingCall DivMany(CallOptions options); + AsyncServerStreamingCall Fib(global::Math.FibArgs request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + AsyncServerStreamingCall Fib(global::Math.FibArgs request, CallOptions options); + AsyncClientStreamingCall Sum(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + AsyncClientStreamingCall Sum(CallOptions options); + } + + // server-side interface + public interface IMath + { + Task Div(global::Math.DivArgs request, ServerCallContext context); + Task DivMany(IAsyncStreamReader requestStream, IServerStreamWriter responseStream, ServerCallContext context); + Task Fib(global::Math.FibArgs request, IServerStreamWriter responseStream, ServerCallContext context); + Task Sum(IAsyncStreamReader requestStream, ServerCallContext context); + } + + // client stub + public class MathClient : ClientBase, IMathClient + { + public MathClient(Channel channel) : base(channel) + { + } + public global::Math.DivReply Div(global::Math.DivArgs request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var call = CreateCall(__Method_Div, new CallOptions(headers, deadline, cancellationToken)); + return Calls.BlockingUnaryCall(call, request); + } + public global::Math.DivReply Div(global::Math.DivArgs request, CallOptions options) + { + var call = CreateCall(__Method_Div, options); + return Calls.BlockingUnaryCall(call, request); + } + public AsyncUnaryCall DivAsync(global::Math.DivArgs request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var call = CreateCall(__Method_Div, new CallOptions(headers, deadline, cancellationToken)); + return Calls.AsyncUnaryCall(call, request); + } + public AsyncUnaryCall DivAsync(global::Math.DivArgs request, CallOptions options) + { + var call = CreateCall(__Method_Div, options); + return Calls.AsyncUnaryCall(call, request); + } + public AsyncDuplexStreamingCall DivMany(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var call = CreateCall(__Method_DivMany, new CallOptions(headers, deadline, cancellationToken)); + return Calls.AsyncDuplexStreamingCall(call); + } + public AsyncDuplexStreamingCall DivMany(CallOptions options) + { + var call = CreateCall(__Method_DivMany, options); + return Calls.AsyncDuplexStreamingCall(call); + } + public AsyncServerStreamingCall Fib(global::Math.FibArgs request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var call = CreateCall(__Method_Fib, new CallOptions(headers, deadline, cancellationToken)); + return Calls.AsyncServerStreamingCall(call, request); + } + public AsyncServerStreamingCall Fib(global::Math.FibArgs request, CallOptions options) + { + var call = CreateCall(__Method_Fib, options); + return Calls.AsyncServerStreamingCall(call, request); + } + public AsyncClientStreamingCall Sum(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var call = CreateCall(__Method_Sum, new CallOptions(headers, deadline, cancellationToken)); + return Calls.AsyncClientStreamingCall(call); + } + public AsyncClientStreamingCall Sum(CallOptions options) + { + var call = CreateCall(__Method_Sum, options); + return Calls.AsyncClientStreamingCall(call); + } + } + + // creates service definition that can be registered with a server + public static ServerServiceDefinition BindService(IMath serviceImpl) + { + return ServerServiceDefinition.CreateBuilder(__ServiceName) + .AddMethod(__Method_Div, serviceImpl.Div) + .AddMethod(__Method_DivMany, serviceImpl.DivMany) + .AddMethod(__Method_Fib, serviceImpl.Fib) + .AddMethod(__Method_Sum, serviceImpl.Sum).Build(); + } + + // creates a new client + public static MathClient NewClient(Channel channel) + { + return new MathClient(channel); + } + + } +} +#endregion diff --git a/src/csharp/Grpc.Examples/MathServiceImpl.cs b/src/csharp/Grpc.Examples/MathServiceImpl.cs new file mode 100644 index 00000000..71dc655e --- /dev/null +++ b/src/csharp/Grpc.Examples/MathServiceImpl.cs @@ -0,0 +1,112 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core; +using Grpc.Core.Utils; + +namespace Math +{ + /// + /// Implementation of MathService server + /// + public class MathServiceImpl : Math.IMath + { + public Task Div(DivArgs request, ServerCallContext context) + { + return Task.FromResult(DivInternal(request)); + } + + public async Task Fib(FibArgs request, IServerStreamWriter responseStream, ServerCallContext context) + { + if (request.Limit <= 0) + { + // keep streaming the sequence until cancelled. + IEnumerator fibEnumerator = FibInternal(long.MaxValue).GetEnumerator(); + while (!context.CancellationToken.IsCancellationRequested && fibEnumerator.MoveNext()) + { + await responseStream.WriteAsync(fibEnumerator.Current); + await Task.Delay(100); + } + } + + if (request.Limit > 0) + { + foreach (var num in FibInternal(request.Limit)) + { + await responseStream.WriteAsync(num); + } + } + } + + public async Task Sum(IAsyncStreamReader requestStream, ServerCallContext context) + { + long sum = 0; + await requestStream.ForEachAsync(async num => + { + sum += num.Num_; + }); + return new Num { Num_ = sum }; + } + + public async Task DivMany(IAsyncStreamReader requestStream, IServerStreamWriter responseStream, ServerCallContext context) + { + await requestStream.ForEachAsync(async divArgs => await responseStream.WriteAsync(DivInternal(divArgs))); + } + + static DivReply DivInternal(DivArgs args) + { + long quotient = args.Dividend / args.Divisor; + long remainder = args.Dividend % args.Divisor; + return new DivReply { Quotient = quotient, Remainder = remainder }; + } + + static IEnumerable FibInternal(long n) + { + long a = 1; + yield return new Num { Num_ = a }; + + long b = 1; + for (long i = 0; i < n - 1; i++) + { + long temp = a; + a = b; + b = temp + b; + yield return new Num { Num_ = a }; + } + } + } +} diff --git a/src/csharp/Grpc.Examples/Properties/AssemblyInfo.cs b/src/csharp/Grpc.Examples/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..92111389 --- /dev/null +++ b/src/csharp/Grpc.Examples/Properties/AssemblyInfo.cs @@ -0,0 +1,11 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyTitle("Grpc.Examples")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("Google Inc. All rights reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] diff --git a/src/csharp/Grpc.Examples/Settings.StyleCop b/src/csharp/Grpc.Examples/Settings.StyleCop new file mode 100644 index 00000000..e9b6e717 --- /dev/null +++ b/src/csharp/Grpc.Examples/Settings.StyleCop @@ -0,0 +1,10 @@ + + + Math.cs + + + False + + + + \ No newline at end of file diff --git a/src/csharp/Grpc.Examples/packages.config b/src/csharp/Grpc.Examples/packages.config new file mode 100644 index 00000000..adf8da23 --- /dev/null +++ b/src/csharp/Grpc.Examples/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/csharp/Grpc.Examples/proto/math.proto b/src/csharp/Grpc.Examples/proto/math.proto new file mode 100644 index 00000000..311e148c --- /dev/null +++ b/src/csharp/Grpc.Examples/proto/math.proto @@ -0,0 +1,80 @@ + +// Copyright 2015, 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. + +syntax = "proto3"; + +package math; + +message DivArgs { + int64 dividend = 1; + int64 divisor = 2; +} + +message DivReply { + int64 quotient = 1; + int64 remainder = 2; +} + +message FibArgs { + int64 limit = 1; +} + +message Num { + int64 num = 1; +} + +message FibReply { + int64 count = 1; +} + +service Math { + // Div divides args.dividend by args.divisor and returns the quotient and + // remainder. + rpc Div (DivArgs) returns (DivReply) { + } + + // DivMany accepts an arbitrary number of division args from the client stream + // and sends back the results in the reply stream. The stream continues until + // the client closes its end; the server does the same after sending all the + // replies. The stream ends immediately if either end aborts. + rpc DivMany (stream DivArgs) returns (stream DivReply) { + } + + // Fib generates numbers in the Fibonacci sequence. If args.limit > 0, Fib + // generates up to limit numbers; otherwise it continues until the call is + // canceled. Unlike Fib above, Fib has no final FibReply. + rpc Fib (FibArgs) returns (stream Num) { + } + + // Sum sums a stream of numbers, returning the final result once the stream + // is closed. + rpc Sum (stream Num) returns (Num) { + } +} diff --git a/src/csharp/Grpc.HealthCheck.Tests/.gitignore b/src/csharp/Grpc.HealthCheck.Tests/.gitignore new file mode 100644 index 00000000..1746e326 --- /dev/null +++ b/src/csharp/Grpc.HealthCheck.Tests/.gitignore @@ -0,0 +1,2 @@ +bin +obj diff --git a/src/csharp/Grpc.HealthCheck.Tests/Grpc.HealthCheck.Tests.csproj b/src/csharp/Grpc.HealthCheck.Tests/Grpc.HealthCheck.Tests.csproj new file mode 100644 index 00000000..396dc43a --- /dev/null +++ b/src/csharp/Grpc.HealthCheck.Tests/Grpc.HealthCheck.Tests.csproj @@ -0,0 +1,87 @@ + + + + + Debug + AnyCPU + {F8C6D937-C44B-4EE3-A431-B0FBAEACE47D} + Library + Properties + Grpc.HealthCheck.Tests + Grpc.HealthCheck.Tests + v4.5 + 512 + + + true + full + false + bin\Debug\ + prompt + 4 + + + pdbonly + true + bin\Release\ + prompt + 4 + + + pdbonly + true + bin\ReleaseSigned + prompt + 4 + True + C:\keys\Grpc.snk + + + + False + ..\packages\Google.Protobuf.3.0.0-alpha4\lib\portable-net45+netcore45+wpa81+wp8\Google.Protobuf.dll + + + ..\packages\NUnit.2.6.4\lib\nunit.framework.dll + + + + + + + + + + + + Version.cs + + + + + + + + {ccc4440e-49f7-4790-b0af-feabb0837ae7} + Grpc.Core + + + {aa5e328a-8835-49d7-98ed-c29f2b3049f0} + Grpc.HealthCheck + + + + + + + + + + + \ No newline at end of file diff --git a/src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs b/src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs new file mode 100644 index 00000000..6c3a53be --- /dev/null +++ b/src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs @@ -0,0 +1,96 @@ +#region Copyright notice and license +// Copyright 2015, 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. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Grpc.Core; +using Grpc.Health.V1Alpha; +using NUnit.Framework; + +namespace Grpc.HealthCheck.Tests +{ + /// + /// Health client talks to health server. + /// + public class HealthClientServerTest + { + const string Host = "localhost"; + Server server; + Channel channel; + Grpc.Health.V1Alpha.Health.IHealthClient client; + Grpc.HealthCheck.HealthServiceImpl serviceImpl; + + [TestFixtureSetUp] + public void Init() + { + serviceImpl = new HealthServiceImpl(); + + server = new Server + { + Services = { Grpc.Health.V1Alpha.Health.BindService(serviceImpl) }, + Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } } + }; + server.Start(); + channel = new Channel(Host, server.Ports.Single().BoundPort, Credentials.Insecure); + + client = Grpc.Health.V1Alpha.Health.NewClient(channel); + } + + [TestFixtureTearDown] + public void Cleanup() + { + channel.ShutdownAsync().Wait(); + + server.ShutdownAsync().Wait(); + } + + [Test] + public void ServiceIsRunning() + { + serviceImpl.SetStatus("", "", HealthCheckResponse.Types.ServingStatus.SERVING); + + var response = client.Check(new HealthCheckRequest { Host = "", Service = "" }); + Assert.AreEqual(HealthCheckResponse.Types.ServingStatus.SERVING, response.Status); + } + + [Test] + public void ServiceDoesntExist() + { + Assert.Throws(Is.TypeOf(typeof(RpcException)).And.Property("Status").Property("StatusCode").EqualTo(StatusCode.NotFound), () => client.Check(new HealthCheckRequest { Host = "", Service = "nonexistent.service" })); + } + + // TODO(jtattermusch): add test with timeout once timeouts are supported + } +} \ No newline at end of file diff --git a/src/csharp/Grpc.HealthCheck.Tests/HealthServiceImplTest.cs b/src/csharp/Grpc.HealthCheck.Tests/HealthServiceImplTest.cs new file mode 100644 index 00000000..2097c0dc --- /dev/null +++ b/src/csharp/Grpc.HealthCheck.Tests/HealthServiceImplTest.cs @@ -0,0 +1,107 @@ +#region Copyright notice and license +// Copyright 2015, 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. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Grpc.Core; +using Grpc.Health.V1Alpha; +using NUnit.Framework; + +namespace Grpc.HealthCheck.Tests +{ + /// + /// Tests for HealthCheckServiceImpl + /// + public class HealthServiceImplTest + { + [Test] + public void SetStatus() + { + var impl = new HealthServiceImpl(); + impl.SetStatus("", "", HealthCheckResponse.Types.ServingStatus.SERVING); + Assert.AreEqual(HealthCheckResponse.Types.ServingStatus.SERVING, GetStatusHelper(impl, "", "")); + + impl.SetStatus("", "", HealthCheckResponse.Types.ServingStatus.NOT_SERVING); + Assert.AreEqual(HealthCheckResponse.Types.ServingStatus.NOT_SERVING, GetStatusHelper(impl, "", "")); + + impl.SetStatus("virtual-host", "", HealthCheckResponse.Types.ServingStatus.UNKNOWN); + Assert.AreEqual(HealthCheckResponse.Types.ServingStatus.UNKNOWN, GetStatusHelper(impl, "virtual-host", "")); + + impl.SetStatus("virtual-host", "grpc.test.TestService", HealthCheckResponse.Types.ServingStatus.SERVING); + Assert.AreEqual(HealthCheckResponse.Types.ServingStatus.SERVING, GetStatusHelper(impl, "virtual-host", "grpc.test.TestService")); + } + + [Test] + public void ClearStatus() + { + var impl = new HealthServiceImpl(); + impl.SetStatus("", "", HealthCheckResponse.Types.ServingStatus.SERVING); + impl.SetStatus("virtual-host", "", HealthCheckResponse.Types.ServingStatus.UNKNOWN); + + impl.ClearStatus("", ""); + + Assert.Throws(Is.TypeOf(typeof(RpcException)).And.Property("Status").Property("StatusCode").EqualTo(StatusCode.NotFound), () => GetStatusHelper(impl, "", "")); + Assert.AreEqual(HealthCheckResponse.Types.ServingStatus.UNKNOWN, GetStatusHelper(impl, "virtual-host", "")); + } + + [Test] + public void ClearAll() + { + var impl = new HealthServiceImpl(); + impl.SetStatus("", "", HealthCheckResponse.Types.ServingStatus.SERVING); + impl.SetStatus("virtual-host", "", HealthCheckResponse.Types.ServingStatus.UNKNOWN); + + impl.ClearAll(); + Assert.Throws(typeof(RpcException), () => GetStatusHelper(impl, "", "")); + Assert.Throws(typeof(RpcException), () => GetStatusHelper(impl, "virtual-host", "")); + } + + [Test] + public void NullsRejected() + { + var impl = new HealthServiceImpl(); + Assert.Throws(typeof(ArgumentNullException), () => impl.SetStatus(null, "", HealthCheckResponse.Types.ServingStatus.SERVING)); + Assert.Throws(typeof(ArgumentNullException), () => impl.SetStatus("", null, HealthCheckResponse.Types.ServingStatus.SERVING)); + + Assert.Throws(typeof(ArgumentNullException), () => impl.ClearStatus(null, "")); + Assert.Throws(typeof(ArgumentNullException), () => impl.ClearStatus("", null)); + } + + private static HealthCheckResponse.Types.ServingStatus GetStatusHelper(HealthServiceImpl impl, string host, string service) + { + return impl.Check(new HealthCheckRequest { Host = host, Service = service }, null).Result.Status; + } + } +} diff --git a/src/csharp/Grpc.HealthCheck.Tests/Properties/AssemblyInfo.cs b/src/csharp/Grpc.HealthCheck.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..d5660305 --- /dev/null +++ b/src/csharp/Grpc.HealthCheck.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,11 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyTitle("Grpc.HealthCheck.Tests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("Google Inc. All rights reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] diff --git a/src/csharp/Grpc.HealthCheck.Tests/packages.config b/src/csharp/Grpc.HealthCheck.Tests/packages.config new file mode 100644 index 00000000..40ffb852 --- /dev/null +++ b/src/csharp/Grpc.HealthCheck.Tests/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/csharp/Grpc.HealthCheck/.gitignore b/src/csharp/Grpc.HealthCheck/.gitignore new file mode 100644 index 00000000..1746e326 --- /dev/null +++ b/src/csharp/Grpc.HealthCheck/.gitignore @@ -0,0 +1,2 @@ +bin +obj diff --git a/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.csproj b/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.csproj new file mode 100644 index 00000000..8fce5d39 --- /dev/null +++ b/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.csproj @@ -0,0 +1,84 @@ + + + + + Debug + AnyCPU + {AA5E328A-8835-49D7-98ED-C29F2B3049F0} + Library + Properties + Grpc.HealthCheck + Grpc.HealthCheck + v4.5 + 512 + bin\$(Configuration)\Grpc.HealthCheck.Xml + + + true + full + false + bin\Debug\ + prompt + 4 + + + pdbonly + true + bin\Release\ + prompt + 4 + + + pdbonly + true + bin\ReleaseSigned + prompt + 4 + True + C:\keys\Grpc.snk + + + + False + ..\packages\Google.Protobuf.3.0.0-alpha4\lib\portable-net45+netcore45+wpa81+wp8\Google.Protobuf.dll + + + + + ..\packages\Ix-Async.1.2.3\lib\net45\System.Interactive.Async.dll + + + + + + + + + + Version.cs + + + + + + + + + + + + + + {ccc4440e-49f7-4790-b0af-feabb0837ae7} + Grpc.Core + + + + + \ No newline at end of file diff --git a/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.nuspec b/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.nuspec new file mode 100644 index 00000000..66386288 --- /dev/null +++ b/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.nuspec @@ -0,0 +1,28 @@ + + + + Grpc.HealthCheck + gRPC C# Healthchecking + Implementation of gRPC health service + Example implementation of grpc.health.v1alpha service that can be used for health-checking. + $version$ + Google Inc. + grpc-packages + https://github.com/grpc/grpc/blob/master/LICENSE + https://github.com/grpc/grpc + false + Copyright 2015, Google Inc. + gRPC health check + + + + + + + + + + + + + diff --git a/src/csharp/Grpc.HealthCheck/Health.cs b/src/csharp/Grpc.HealthCheck/Health.cs new file mode 100644 index 00000000..570e2745 --- /dev/null +++ b/src/csharp/Grpc.HealthCheck/Health.cs @@ -0,0 +1,293 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: health.proto +#pragma warning disable 1591, 0612, 3021 +#region Designer generated code + +using pb = global::Google.Protobuf; +using pbc = global::Google.Protobuf.Collections; +using pbr = global::Google.Protobuf.Reflection; +using scg = global::System.Collections.Generic; +namespace Grpc.Health.V1Alpha { + + namespace Proto { + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public static partial class Health { + + #region Descriptor + public static pbr::FileDescriptor Descriptor { + get { return descriptor; } + } + private static pbr::FileDescriptor descriptor; + + static Health() { + byte[] descriptorData = global::System.Convert.FromBase64String( + string.Concat( + "CgxoZWFsdGgucHJvdG8SE2dycGMuaGVhbHRoLnYxYWxwaGEiMwoSSGVhbHRo", + "Q2hlY2tSZXF1ZXN0EgwKBGhvc3QYASABKAkSDwoHc2VydmljZRgCIAEoCSKZ", + "AQoTSGVhbHRoQ2hlY2tSZXNwb25zZRJGCgZzdGF0dXMYASABKA4yNi5ncnBj", + "LmhlYWx0aC52MWFscGhhLkhlYWx0aENoZWNrUmVzcG9uc2UuU2VydmluZ1N0", + "YXR1cyI6Cg1TZXJ2aW5nU3RhdHVzEgsKB1VOS05PV04QABILCgdTRVJWSU5H", + "EAESDwoLTk9UX1NFUlZJTkcQAjJkCgZIZWFsdGgSWgoFQ2hlY2sSJy5ncnBj", + "LmhlYWx0aC52MWFscGhhLkhlYWx0aENoZWNrUmVxdWVzdBooLmdycGMuaGVh", + "bHRoLnYxYWxwaGEuSGVhbHRoQ2hlY2tSZXNwb25zZUIWqgITR3JwYy5IZWFs", + "dGguVjFBbHBoYWIGcHJvdG8z")); + descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData, + new pbr::FileDescriptor[] { }, + new pbr::GeneratedCodeInfo(null, new pbr::GeneratedCodeInfo[] { + new pbr::GeneratedCodeInfo(typeof(global::Grpc.Health.V1Alpha.HealthCheckRequest), new[]{ "Host", "Service" }, null, null, null), + new pbr::GeneratedCodeInfo(typeof(global::Grpc.Health.V1Alpha.HealthCheckResponse), new[]{ "Status" }, null, new[]{ typeof(global::Grpc.Health.V1Alpha.HealthCheckResponse.Types.ServingStatus) }, null) + })); + } + #endregion + + } + } + #region Messages + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class HealthCheckRequest : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new HealthCheckRequest()); + public static pb::MessageParser Parser { get { return _parser; } } + + public static pbr::MessageDescriptor Descriptor { + get { return global::Grpc.Health.V1Alpha.Proto.Health.Descriptor.MessageTypes[0]; } + } + + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + public HealthCheckRequest() { + OnConstruction(); + } + + partial void OnConstruction(); + + public HealthCheckRequest(HealthCheckRequest other) : this() { + host_ = other.host_; + service_ = other.service_; + } + + public HealthCheckRequest Clone() { + return new HealthCheckRequest(this); + } + + public const int HostFieldNumber = 1; + private string host_ = ""; + public string Host { + get { return host_; } + set { + host_ = pb::Preconditions.CheckNotNull(value, "value"); + } + } + + public const int ServiceFieldNumber = 2; + private string service_ = ""; + public string Service { + get { return service_; } + set { + service_ = pb::Preconditions.CheckNotNull(value, "value"); + } + } + + public override bool Equals(object other) { + return Equals(other as HealthCheckRequest); + } + + public bool Equals(HealthCheckRequest other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Host != other.Host) return false; + if (Service != other.Service) return false; + return true; + } + + public override int GetHashCode() { + int hash = 1; + if (Host.Length != 0) hash ^= Host.GetHashCode(); + if (Service.Length != 0) hash ^= Service.GetHashCode(); + return hash; + } + + public override string ToString() { + return pb::JsonFormatter.Default.Format(this); + } + + public void WriteTo(pb::CodedOutputStream output) { + if (Host.Length != 0) { + output.WriteRawTag(10); + output.WriteString(Host); + } + if (Service.Length != 0) { + output.WriteRawTag(18); + output.WriteString(Service); + } + } + + public int CalculateSize() { + int size = 0; + if (Host.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Host); + } + if (Service.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Service); + } + return size; + } + + public void MergeFrom(HealthCheckRequest other) { + if (other == null) { + return; + } + if (other.Host.Length != 0) { + Host = other.Host; + } + if (other.Service.Length != 0) { + Service = other.Service; + } + } + + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + input.SkipLastField(); + break; + case 10: { + Host = input.ReadString(); + break; + } + case 18: { + Service = input.ReadString(); + break; + } + } + } + } + + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class HealthCheckResponse : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new HealthCheckResponse()); + public static pb::MessageParser Parser { get { return _parser; } } + + public static pbr::MessageDescriptor Descriptor { + get { return global::Grpc.Health.V1Alpha.Proto.Health.Descriptor.MessageTypes[1]; } + } + + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + public HealthCheckResponse() { + OnConstruction(); + } + + partial void OnConstruction(); + + public HealthCheckResponse(HealthCheckResponse other) : this() { + status_ = other.status_; + } + + public HealthCheckResponse Clone() { + return new HealthCheckResponse(this); + } + + public const int StatusFieldNumber = 1; + private global::Grpc.Health.V1Alpha.HealthCheckResponse.Types.ServingStatus status_ = global::Grpc.Health.V1Alpha.HealthCheckResponse.Types.ServingStatus.UNKNOWN; + public global::Grpc.Health.V1Alpha.HealthCheckResponse.Types.ServingStatus Status { + get { return status_; } + set { + status_ = value; + } + } + + public override bool Equals(object other) { + return Equals(other as HealthCheckResponse); + } + + public bool Equals(HealthCheckResponse other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Status != other.Status) return false; + return true; + } + + public override int GetHashCode() { + int hash = 1; + if (Status != global::Grpc.Health.V1Alpha.HealthCheckResponse.Types.ServingStatus.UNKNOWN) hash ^= Status.GetHashCode(); + return hash; + } + + public override string ToString() { + return pb::JsonFormatter.Default.Format(this); + } + + public void WriteTo(pb::CodedOutputStream output) { + if (Status != global::Grpc.Health.V1Alpha.HealthCheckResponse.Types.ServingStatus.UNKNOWN) { + output.WriteRawTag(8); + output.WriteEnum((int) Status); + } + } + + public int CalculateSize() { + int size = 0; + if (Status != global::Grpc.Health.V1Alpha.HealthCheckResponse.Types.ServingStatus.UNKNOWN) { + size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Status); + } + return size; + } + + public void MergeFrom(HealthCheckResponse other) { + if (other == null) { + return; + } + if (other.Status != global::Grpc.Health.V1Alpha.HealthCheckResponse.Types.ServingStatus.UNKNOWN) { + Status = other.Status; + } + } + + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + input.SkipLastField(); + break; + case 8: { + status_ = (global::Grpc.Health.V1Alpha.HealthCheckResponse.Types.ServingStatus) input.ReadEnum(); + break; + } + } + } + } + + #region Nested types + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public static partial class Types { + public enum ServingStatus { + UNKNOWN = 0, + SERVING = 1, + NOT_SERVING = 2, + } + + } + #endregion + + } + + #endregion + +} + +#endregion Designer generated code diff --git a/src/csharp/Grpc.HealthCheck/HealthGrpc.cs b/src/csharp/Grpc.HealthCheck/HealthGrpc.cs new file mode 100644 index 00000000..da721ce5 --- /dev/null +++ b/src/csharp/Grpc.HealthCheck/HealthGrpc.cs @@ -0,0 +1,89 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: health.proto +#region Designer generated code + +using System; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core; + +namespace Grpc.Health.V1Alpha { + public static class Health + { + static readonly string __ServiceName = "grpc.health.v1alpha.Health"; + + static readonly Marshaller __Marshaller_HealthCheckRequest = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Grpc.Health.V1Alpha.HealthCheckRequest.Parser.ParseFrom); + static readonly Marshaller __Marshaller_HealthCheckResponse = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Grpc.Health.V1Alpha.HealthCheckResponse.Parser.ParseFrom); + + static readonly Method __Method_Check = new Method( + MethodType.Unary, + __ServiceName, + "Check", + __Marshaller_HealthCheckRequest, + __Marshaller_HealthCheckResponse); + + // service descriptor + public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor + { + get { return global::Grpc.Health.V1Alpha.Proto.Health.Descriptor.Services[0]; } + } + + // client interface + public interface IHealthClient + { + global::Grpc.Health.V1Alpha.HealthCheckResponse Check(global::Grpc.Health.V1Alpha.HealthCheckRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + global::Grpc.Health.V1Alpha.HealthCheckResponse Check(global::Grpc.Health.V1Alpha.HealthCheckRequest request, CallOptions options); + AsyncUnaryCall CheckAsync(global::Grpc.Health.V1Alpha.HealthCheckRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + AsyncUnaryCall CheckAsync(global::Grpc.Health.V1Alpha.HealthCheckRequest request, CallOptions options); + } + + // server-side interface + public interface IHealth + { + Task Check(global::Grpc.Health.V1Alpha.HealthCheckRequest request, ServerCallContext context); + } + + // client stub + public class HealthClient : ClientBase, IHealthClient + { + public HealthClient(Channel channel) : base(channel) + { + } + public global::Grpc.Health.V1Alpha.HealthCheckResponse Check(global::Grpc.Health.V1Alpha.HealthCheckRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var call = CreateCall(__Method_Check, new CallOptions(headers, deadline, cancellationToken)); + return Calls.BlockingUnaryCall(call, request); + } + public global::Grpc.Health.V1Alpha.HealthCheckResponse Check(global::Grpc.Health.V1Alpha.HealthCheckRequest request, CallOptions options) + { + var call = CreateCall(__Method_Check, options); + return Calls.BlockingUnaryCall(call, request); + } + public AsyncUnaryCall CheckAsync(global::Grpc.Health.V1Alpha.HealthCheckRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var call = CreateCall(__Method_Check, new CallOptions(headers, deadline, cancellationToken)); + return Calls.AsyncUnaryCall(call, request); + } + public AsyncUnaryCall CheckAsync(global::Grpc.Health.V1Alpha.HealthCheckRequest request, CallOptions options) + { + var call = CreateCall(__Method_Check, options); + return Calls.AsyncUnaryCall(call, request); + } + } + + // creates service definition that can be registered with a server + public static ServerServiceDefinition BindService(IHealth serviceImpl) + { + return ServerServiceDefinition.CreateBuilder(__ServiceName) + .AddMethod(__Method_Check, serviceImpl.Check).Build(); + } + + // creates a new client + public static HealthClient NewClient(Channel channel) + { + return new HealthClient(channel); + } + + } +} +#endregion diff --git a/src/csharp/Grpc.HealthCheck/HealthServiceImpl.cs b/src/csharp/Grpc.HealthCheck/HealthServiceImpl.cs new file mode 100644 index 00000000..26c6445c --- /dev/null +++ b/src/csharp/Grpc.HealthCheck/HealthServiceImpl.cs @@ -0,0 +1,138 @@ +#region Copyright notice and license +// Copyright 2015, 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. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Grpc.Core; +using Grpc.Core.Utils; +using Grpc.Health.V1Alpha; + +namespace Grpc.HealthCheck +{ + /// + /// Implementation of a simple Health service. Useful for health checking. + /// + /// Registering service with a server: + /// + /// var serviceImpl = new HealthServiceImpl(); + /// server = new Server(); + /// server.AddServiceDefinition(Grpc.Health.V1Alpha.Health.BindService(serviceImpl)); + /// + /// + public class HealthServiceImpl : Grpc.Health.V1Alpha.Health.IHealth + { + private readonly object myLock = new object(); + private readonly Dictionary statusMap = + new Dictionary(); + + /// + /// Sets the health status for given host and service. + /// + /// The host. Cannot be null. + /// The service. Cannot be null. + /// the health status + public void SetStatus(string host, string service, HealthCheckResponse.Types.ServingStatus status) + { + lock (myLock) + { + statusMap[CreateKey(host, service)] = status; + } + } + + /// + /// Clears health status for given host and service. + /// + /// The host. Cannot be null. + /// The service. Cannot be null. + public void ClearStatus(string host, string service) + { + lock (myLock) + { + statusMap.Remove(CreateKey(host, service)); + } + } + + /// + /// Clears statuses for all hosts and services. + /// + public void ClearAll() + { + lock (myLock) + { + statusMap.Clear(); + } + } + + /// + /// Performs a health status check. + /// + /// The check request. + /// The call context. + /// The asynchronous response. + public Task Check(HealthCheckRequest request, ServerCallContext context) + { + lock (myLock) + { + var host = request.Host; + var service = request.Service; + + HealthCheckResponse.Types.ServingStatus status; + if (!statusMap.TryGetValue(CreateKey(host, service), out status)) + { + // TODO(jtattermusch): returning specific status from server handler is not supported yet. + throw new RpcException(new Status(StatusCode.NotFound, "")); + } + return Task.FromResult(new HealthCheckResponse { Status = status }); + } + } + + private static Key CreateKey(string host, string service) + { + return new Key(host, service); + } + + private struct Key + { + public Key(string host, string service) + { + this.Host = Preconditions.CheckNotNull(host); + this.Service = Preconditions.CheckNotNull(service); + } + + readonly string Host; + readonly string Service; + } + } +} diff --git a/src/csharp/Grpc.HealthCheck/Properties/AssemblyInfo.cs b/src/csharp/Grpc.HealthCheck/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..41a54a98 --- /dev/null +++ b/src/csharp/Grpc.HealthCheck/Properties/AssemblyInfo.cs @@ -0,0 +1,11 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyTitle("Grpc.HealthCheck")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("Google Inc. All rights reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] \ No newline at end of file diff --git a/src/csharp/Grpc.HealthCheck/Settings.StyleCop b/src/csharp/Grpc.HealthCheck/Settings.StyleCop new file mode 100644 index 00000000..2942add9 --- /dev/null +++ b/src/csharp/Grpc.HealthCheck/Settings.StyleCop @@ -0,0 +1,10 @@ + + + Health.cs + + + False + + + + diff --git a/src/csharp/Grpc.HealthCheck/packages.config b/src/csharp/Grpc.HealthCheck/packages.config new file mode 100644 index 00000000..cafff612 --- /dev/null +++ b/src/csharp/Grpc.HealthCheck/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/csharp/Grpc.HealthCheck/proto/health.proto b/src/csharp/Grpc.HealthCheck/proto/health.proto new file mode 100644 index 00000000..01aa3fcf --- /dev/null +++ b/src/csharp/Grpc.HealthCheck/proto/health.proto @@ -0,0 +1,52 @@ +// Copyright 2015, 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. + +// TODO(jtattermusch): switch to proto3 once C# supports that. +syntax = "proto3"; + +package grpc.health.v1alpha; +option csharp_namespace = "Grpc.Health.V1Alpha"; + +message HealthCheckRequest { + string host = 1; + string service = 2; +} + +message HealthCheckResponse { + enum ServingStatus { + UNKNOWN = 0; + SERVING = 1; + NOT_SERVING = 2; + } + ServingStatus status = 1; +} + +service Health { + rpc Check(HealthCheckRequest) returns (HealthCheckResponse); +} \ No newline at end of file diff --git a/src/csharp/Grpc.IntegrationTesting.Client/.gitignore b/src/csharp/Grpc.IntegrationTesting.Client/.gitignore new file mode 100644 index 00000000..a382af22 --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting.Client/.gitignore @@ -0,0 +1,3 @@ +bin +obj + diff --git a/src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj b/src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj new file mode 100644 index 00000000..2c38c964 --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj @@ -0,0 +1,64 @@ + + + + Debug + AnyCPU + {3D166931-BA2D-416E-95A3-D36E8F6E90B9} + Exe + Grpc.IntegrationTesting.Client + Grpc.IntegrationTesting.Client + Grpc.IntegrationTesting.Client.Program + v4.5 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + AnyCPU + + + pdbonly + true + bin\Release + prompt + 4 + AnyCPU + + + pdbonly + true + bin\ReleaseSigned + prompt + 4 + True + C:\keys\Grpc.snk + + + + + + + Version.cs + + + + + + + + {C61154BA-DD4A-4838-8420-0162A28925E0} + Grpc.IntegrationTesting + + + {CCC4440E-49F7-4790-B0AF-FEABB0837AE7} + Grpc.Core + + + + + + \ No newline at end of file diff --git a/src/csharp/Grpc.IntegrationTesting.Client/Program.cs b/src/csharp/Grpc.IntegrationTesting.Client/Program.cs new file mode 100644 index 00000000..2e1c9aaa --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting.Client/Program.cs @@ -0,0 +1,46 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using Grpc.IntegrationTesting; + +namespace Grpc.IntegrationTesting.Client +{ + class Program + { + public static void Main(string[] args) + { + InteropClient.Run(args); + } + } +} diff --git a/src/csharp/Grpc.IntegrationTesting.Client/Properties/AssemblyInfo.cs b/src/csharp/Grpc.IntegrationTesting.Client/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..f51f2796 --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting.Client/Properties/AssemblyInfo.cs @@ -0,0 +1,11 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyTitle("Grpc.IntegrationTesting.Client")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("Google Inc. All rights reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] diff --git a/src/csharp/Grpc.IntegrationTesting.Client/app.config b/src/csharp/Grpc.IntegrationTesting.Client/app.config new file mode 100644 index 00000000..84d7534d --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting.Client/app.config @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/csharp/Grpc.IntegrationTesting.Server/.gitignore b/src/csharp/Grpc.IntegrationTesting.Server/.gitignore new file mode 100644 index 00000000..a382af22 --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting.Server/.gitignore @@ -0,0 +1,3 @@ +bin +obj + diff --git a/src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj b/src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj new file mode 100644 index 00000000..949ad613 --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj @@ -0,0 +1,64 @@ + + + + Debug + AnyCPU + {A654F3B8-E859-4E6A-B30D-227527DBEF0D} + Exe + Grpc.IntegrationTesting.Server + Grpc.IntegrationTesting.Server + Grpc.IntegrationTesting.Server.Program + v4.5 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + AnyCPU + + + pdbonly + true + bin\Release + prompt + 4 + AnyCPU + + + pdbonly + true + bin\ReleaseSigned + prompt + 4 + True + C:\keys\Grpc.snk + + + + + + + Version.cs + + + + + + + + {C61154BA-DD4A-4838-8420-0162A28925E0} + Grpc.IntegrationTesting + + + {CCC4440E-49F7-4790-B0AF-FEABB0837AE7} + Grpc.Core + + + + + + \ No newline at end of file diff --git a/src/csharp/Grpc.IntegrationTesting.Server/Program.cs b/src/csharp/Grpc.IntegrationTesting.Server/Program.cs new file mode 100644 index 00000000..01bcc6e3 --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting.Server/Program.cs @@ -0,0 +1,45 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; + +namespace Grpc.IntegrationTesting.Server +{ + class Program + { + public static void Main(string[] args) + { + InteropServer.Run(args); + } + } +} diff --git a/src/csharp/Grpc.IntegrationTesting.Server/Properties/AssemblyInfo.cs b/src/csharp/Grpc.IntegrationTesting.Server/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..f68d9a3d --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting.Server/Properties/AssemblyInfo.cs @@ -0,0 +1,11 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyTitle("Grpc.IntegrationTesting.Server")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("Google Inc. All rights reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] diff --git a/src/csharp/Grpc.IntegrationTesting.Server/app.config b/src/csharp/Grpc.IntegrationTesting.Server/app.config new file mode 100644 index 00000000..84d7534d --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting.Server/app.config @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/csharp/Grpc.IntegrationTesting/.gitignore b/src/csharp/Grpc.IntegrationTesting/.gitignore new file mode 100644 index 00000000..8d4a6c08 --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting/.gitignore @@ -0,0 +1,2 @@ +bin +obj \ No newline at end of file diff --git a/src/csharp/Grpc.IntegrationTesting/Empty.cs b/src/csharp/Grpc.IntegrationTesting/Empty.cs new file mode 100644 index 00000000..28c28c9a --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting/Empty.cs @@ -0,0 +1,118 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: empty.proto +#pragma warning disable 1591, 0612, 3021 +#region Designer generated code + +using pb = global::Google.Protobuf; +using pbc = global::Google.Protobuf.Collections; +using pbr = global::Google.Protobuf.Reflection; +using scg = global::System.Collections.Generic; +namespace Grpc.Testing { + + namespace Proto { + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public static partial class Empty { + + #region Descriptor + public static pbr::FileDescriptor Descriptor { + get { return descriptor; } + } + private static pbr::FileDescriptor descriptor; + + static Empty() { + byte[] descriptorData = global::System.Convert.FromBase64String( + string.Concat( + "CgtlbXB0eS5wcm90bxIMZ3JwYy50ZXN0aW5nIgcKBUVtcHR5YgZwcm90bzM=")); + descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData, + new pbr::FileDescriptor[] { }, + new pbr::GeneratedCodeInfo(null, new pbr::GeneratedCodeInfo[] { + new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.Empty), null, null, null, null) + })); + } + #endregion + + } + } + #region Messages + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class Empty : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new Empty()); + public static pb::MessageParser Parser { get { return _parser; } } + + public static pbr::MessageDescriptor Descriptor { + get { return global::Grpc.Testing.Proto.Empty.Descriptor.MessageTypes[0]; } + } + + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + public Empty() { + OnConstruction(); + } + + partial void OnConstruction(); + + public Empty(Empty other) : this() { + } + + public Empty Clone() { + return new Empty(this); + } + + public override bool Equals(object other) { + return Equals(other as Empty); + } + + public bool Equals(Empty other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + return true; + } + + public override int GetHashCode() { + int hash = 1; + return hash; + } + + public override string ToString() { + return pb::JsonFormatter.Default.Format(this); + } + + public void WriteTo(pb::CodedOutputStream output) { + } + + public int CalculateSize() { + int size = 0; + return size; + } + + public void MergeFrom(Empty other) { + if (other == null) { + return; + } + } + + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + input.SkipLastField(); + break; + } + } + } + + } + + #endregion + +} + +#endregion Designer generated code diff --git a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj new file mode 100644 index 00000000..a0bcf431 --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj @@ -0,0 +1,147 @@ + + + + Debug + AnyCPU + {C61154BA-DD4A-4838-8420-0162A28925E0} + Library + Grpc.IntegrationTesting + Grpc.IntegrationTesting + v4.5 + 6566287f + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + AnyCPU + + + pdbonly + true + bin\Release + prompt + 4 + AnyCPU + + + pdbonly + true + bin\ReleaseSigned + prompt + 4 + True + C:\keys\Grpc.snk + + + + False + ..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll + + + ..\packages\CommandLineParser.1.9.71\lib\net45\CommandLine.dll + + + False + ..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.dll + + + False + ..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.PlatformServices.dll + + + False + ..\packages\Google.Apis.Core.1.9.3\lib\portable-net40+sl50+win+wpa81+wp80\Google.Apis.Core.dll + + + False + ..\packages\Google.Protobuf.3.0.0-alpha4\lib\portable-net45+netcore45+wpa81+wp8\Google.Protobuf.dll + + + False + ..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll + + + False + ..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll + + + False + ..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll + + + False + ..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll + + + ..\packages\NUnit.2.6.4\lib\nunit.framework.dll + + + + ..\packages\Ix-Async.1.2.3\lib\net45\System.Interactive.Async.dll + + + + + + + + Version.cs + + + + + + + + + + + + + + + + + {CCC4440E-49F7-4790-B0AF-FEABB0837AE7} + Grpc.Core + + + {AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA} + Grpc.Auth + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + + + + + 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}. + + + + \ No newline at end of file diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs new file mode 100644 index 00000000..616093d4 --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs @@ -0,0 +1,491 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; + +using CommandLine; +using Google.Apis.Auth.OAuth2; +using Google.Protobuf; +using Grpc.Auth; +using Grpc.Core; +using Grpc.Core.Utils; +using Grpc.Testing; +using NUnit.Framework; +using CommandLine.Text; +using System.IO; + +namespace Grpc.IntegrationTesting +{ + public class InteropClient + { + private class ClientOptions + { + [Option("server_host", DefaultValue = "127.0.0.1")] + public string ServerHost { get; set; } + + [Option("server_host_override", DefaultValue = TestCredentials.DefaultHostOverride)] + public string ServerHostOverride { get; set; } + + [Option("server_port", Required = true)] + public int ServerPort { get; set; } + + [Option("test_case", DefaultValue = "large_unary")] + public string TestCase { get; set; } + + [Option("use_tls")] + public bool UseTls { get; set; } + + [Option("use_test_ca")] + public bool UseTestCa { get; set; } + + [Option("default_service_account", Required = false)] + public string DefaultServiceAccount { get; set; } + + [Option("oauth_scope", Required = false)] + public string OAuthScope { get; set; } + + [Option("service_account_key_file", Required = false)] + public string ServiceAccountKeyFile { get; set; } + + [HelpOption] + public string GetUsage() + { + var help = new HelpText + { + Heading = "gRPC C# interop testing client", + AddDashesToOption = true + }; + help.AddPreOptionsLine("Usage:"); + help.AddOptions(this); + return help; + } + } + + ClientOptions options; + + private InteropClient(ClientOptions options) + { + this.options = options; + } + + public static void Run(string[] args) + { + var options = new ClientOptions(); + if (!Parser.Default.ParseArguments(args, options)) + { + Environment.Exit(1); + } + + var interopClient = new InteropClient(options); + interopClient.Run().Wait(); + } + + private async Task Run() + { + var credentials = options.UseTls ? TestCredentials.CreateTestClientCredentials(options.UseTestCa) : Credentials.Insecure; + + List channelOptions = null; + if (!string.IsNullOrEmpty(options.ServerHostOverride)) + { + channelOptions = new List + { + new ChannelOption(ChannelOptions.SslTargetNameOverride, options.ServerHostOverride) + }; + } + Console.WriteLine(options.ServerHost); + Console.WriteLine(options.ServerPort); + var channel = new Channel(options.ServerHost, options.ServerPort, credentials, channelOptions); + TestService.TestServiceClient client = new TestService.TestServiceClient(channel); + await RunTestCaseAsync(client, options); + await channel.ShutdownAsync(); + } + + private async Task RunTestCaseAsync(TestService.TestServiceClient client, ClientOptions options) + { + switch (options.TestCase) + { + case "empty_unary": + RunEmptyUnary(client); + break; + case "large_unary": + RunLargeUnary(client); + break; + case "client_streaming": + await RunClientStreamingAsync(client); + break; + case "server_streaming": + await RunServerStreamingAsync(client); + break; + case "ping_pong": + await RunPingPongAsync(client); + break; + case "empty_stream": + await RunEmptyStreamAsync(client); + break; + case "compute_engine_creds": + await RunComputeEngineCredsAsync(client, options.DefaultServiceAccount, options.OAuthScope); + break; + case "jwt_token_creds": + await RunJwtTokenCredsAsync(client, options.DefaultServiceAccount); + break; + case "oauth2_auth_token": + await RunOAuth2AuthTokenAsync(client, options.DefaultServiceAccount, options.OAuthScope); + break; + case "per_rpc_creds": + await RunPerRpcCredsAsync(client, options.DefaultServiceAccount, options.OAuthScope); + break; + case "cancel_after_begin": + await RunCancelAfterBeginAsync(client); + break; + case "cancel_after_first_response": + await RunCancelAfterFirstResponseAsync(client); + break; + case "timeout_on_sleeping_server": + await RunTimeoutOnSleepingServerAsync(client); + break; + case "benchmark_empty_unary": + RunBenchmarkEmptyUnary(client); + break; + default: + throw new ArgumentException("Unknown test case " + options.TestCase); + } + } + + public static void RunEmptyUnary(TestService.ITestServiceClient client) + { + Console.WriteLine("running empty_unary"); + var response = client.EmptyCall(new Empty()); + Assert.IsNotNull(response); + Console.WriteLine("Passed!"); + } + + public static void RunLargeUnary(TestService.ITestServiceClient client) + { + Console.WriteLine("running large_unary"); + var request = new SimpleRequest + { + ResponseType = PayloadType.COMPRESSABLE, + ResponseSize = 314159, + Payload = CreateZerosPayload(271828) + }; + + var response = client.UnaryCall(request); + + Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type); + Assert.AreEqual(314159, response.Payload.Body.Length); + Console.WriteLine("Passed!"); + } + + public static async Task RunClientStreamingAsync(TestService.ITestServiceClient client) + { + Console.WriteLine("running client_streaming"); + + var bodySizes = new List { 27182, 8, 1828, 45904 }.ConvertAll((size) => new StreamingInputCallRequest { Payload = CreateZerosPayload(size) }); + + using (var call = client.StreamingInputCall()) + { + await call.RequestStream.WriteAllAsync(bodySizes); + + var response = await call.ResponseAsync; + Assert.AreEqual(74922, response.AggregatedPayloadSize); + } + Console.WriteLine("Passed!"); + } + + public static async Task RunServerStreamingAsync(TestService.ITestServiceClient client) + { + Console.WriteLine("running server_streaming"); + + var bodySizes = new List { 31415, 9, 2653, 58979 }; + + var request = new StreamingOutputCallRequest + { + ResponseType = PayloadType.COMPRESSABLE, + ResponseParameters = { bodySizes.ConvertAll((size) => new ResponseParameters { Size = size }) } + }; + + using (var call = client.StreamingOutputCall(request)) + { + var responseList = await call.ResponseStream.ToListAsync(); + foreach (var res in responseList) + { + Assert.AreEqual(PayloadType.COMPRESSABLE, res.Payload.Type); + } + CollectionAssert.AreEqual(bodySizes, responseList.ConvertAll((item) => item.Payload.Body.Length)); + } + Console.WriteLine("Passed!"); + } + + public static async Task RunPingPongAsync(TestService.ITestServiceClient client) + { + Console.WriteLine("running ping_pong"); + + using (var call = client.FullDuplexCall()) + { + await call.RequestStream.WriteAsync(new StreamingOutputCallRequest + { + ResponseType = PayloadType.COMPRESSABLE, + ResponseParameters = { new ResponseParameters { Size = 31415 } }, + Payload = CreateZerosPayload(27182) + }); + + Assert.IsTrue(await call.ResponseStream.MoveNext()); + Assert.AreEqual(PayloadType.COMPRESSABLE, call.ResponseStream.Current.Payload.Type); + Assert.AreEqual(31415, call.ResponseStream.Current.Payload.Body.Length); + + await call.RequestStream.WriteAsync(new StreamingOutputCallRequest + { + ResponseType = PayloadType.COMPRESSABLE, + ResponseParameters = { new ResponseParameters { Size = 9 } }, + Payload = CreateZerosPayload(8) + }); + + Assert.IsTrue(await call.ResponseStream.MoveNext()); + Assert.AreEqual(PayloadType.COMPRESSABLE, call.ResponseStream.Current.Payload.Type); + Assert.AreEqual(9, call.ResponseStream.Current.Payload.Body.Length); + + await call.RequestStream.WriteAsync(new StreamingOutputCallRequest + { + ResponseType = PayloadType.COMPRESSABLE, + ResponseParameters = { new ResponseParameters { Size = 2653 } }, + Payload = CreateZerosPayload(1828) + }); + + Assert.IsTrue(await call.ResponseStream.MoveNext()); + Assert.AreEqual(PayloadType.COMPRESSABLE, call.ResponseStream.Current.Payload.Type); + Assert.AreEqual(2653, call.ResponseStream.Current.Payload.Body.Length); + + await call.RequestStream.WriteAsync(new StreamingOutputCallRequest + { + ResponseType = PayloadType.COMPRESSABLE, + ResponseParameters = { new ResponseParameters { Size = 58979 } }, + Payload = CreateZerosPayload(45904) + }); + + Assert.IsTrue(await call.ResponseStream.MoveNext()); + Assert.AreEqual(PayloadType.COMPRESSABLE, call.ResponseStream.Current.Payload.Type); + Assert.AreEqual(58979, call.ResponseStream.Current.Payload.Body.Length); + + await call.RequestStream.CompleteAsync(); + + Assert.IsFalse(await call.ResponseStream.MoveNext()); + } + Console.WriteLine("Passed!"); + } + + public static async Task RunEmptyStreamAsync(TestService.ITestServiceClient client) + { + Console.WriteLine("running empty_stream"); + using (var call = client.FullDuplexCall()) + { + await call.RequestStream.CompleteAsync(); + + var responseList = await call.ResponseStream.ToListAsync(); + Assert.AreEqual(0, responseList.Count); + } + Console.WriteLine("Passed!"); + } + + public static async Task RunComputeEngineCredsAsync(TestService.TestServiceClient client, string defaultServiceAccount, string oauthScope) + { + Console.WriteLine("running compute_engine_creds"); + var credential = await GoogleCredential.GetApplicationDefaultAsync(); + Assert.IsFalse(credential.IsCreateScopedRequired); + client.HeaderInterceptor = AuthInterceptors.FromCredential(credential); + + var request = new SimpleRequest + { + ResponseType = PayloadType.COMPRESSABLE, + ResponseSize = 314159, + Payload = CreateZerosPayload(271828), + FillUsername = true, + FillOauthScope = true + }; + + var response = client.UnaryCall(request); + + Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type); + Assert.AreEqual(314159, response.Payload.Body.Length); + Assert.False(string.IsNullOrEmpty(response.OauthScope)); + Assert.True(oauthScope.Contains(response.OauthScope)); + Assert.AreEqual(defaultServiceAccount, response.Username); + Console.WriteLine("Passed!"); + } + + public static async Task RunJwtTokenCredsAsync(TestService.TestServiceClient client, string defaultServiceAccount) + { + Console.WriteLine("running jwt_token_creds"); + var credential = await GoogleCredential.GetApplicationDefaultAsync(); + Assert.IsTrue(credential.IsCreateScopedRequired); + client.HeaderInterceptor = AuthInterceptors.FromCredential(credential); + + var request = new SimpleRequest + { + ResponseType = PayloadType.COMPRESSABLE, + ResponseSize = 314159, + Payload = CreateZerosPayload(271828), + FillUsername = true, + }; + + var response = client.UnaryCall(request); + + Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type); + Assert.AreEqual(314159, response.Payload.Body.Length); + Assert.AreEqual(defaultServiceAccount, response.Username); + Console.WriteLine("Passed!"); + } + + public static async Task RunOAuth2AuthTokenAsync(TestService.TestServiceClient client, string defaultServiceAccount, string oauthScope) + { + Console.WriteLine("running oauth2_auth_token"); + ITokenAccess credential = (await GoogleCredential.GetApplicationDefaultAsync()).CreateScoped(new[] { oauthScope }); + string oauth2Token = await credential.GetAccessTokenForRequestAsync(); + + client.HeaderInterceptor = AuthInterceptors.FromAccessToken(oauth2Token); + + var request = new SimpleRequest + { + FillUsername = true, + FillOauthScope = true + }; + + var response = client.UnaryCall(request); + + Assert.False(string.IsNullOrEmpty(response.OauthScope)); + Assert.True(oauthScope.Contains(response.OauthScope)); + Assert.AreEqual(defaultServiceAccount, response.Username); + Console.WriteLine("Passed!"); + } + + public static async Task RunPerRpcCredsAsync(TestService.TestServiceClient client, string defaultServiceAccount, string oauthScope) + { + Console.WriteLine("running per_rpc_creds"); + ITokenAccess credential = (await GoogleCredential.GetApplicationDefaultAsync()).CreateScoped(new[] { oauthScope }); + string accessToken = await credential.GetAccessTokenForRequestAsync(); + var headerInterceptor = AuthInterceptors.FromAccessToken(accessToken); + + var request = new SimpleRequest + { + FillUsername = true, + }; + + var headers = new Metadata(); + headerInterceptor(null, "", headers); + var response = client.UnaryCall(request, headers: headers); + + Assert.AreEqual(defaultServiceAccount, response.Username); + Console.WriteLine("Passed!"); + } + + public static async Task RunCancelAfterBeginAsync(TestService.ITestServiceClient client) + { + Console.WriteLine("running cancel_after_begin"); + + var cts = new CancellationTokenSource(); + using (var call = client.StreamingInputCall(cancellationToken: cts.Token)) + { + // TODO(jtattermusch): we need this to ensure call has been initiated once we cancel it. + await Task.Delay(1000); + cts.Cancel(); + + var ex = Assert.Throws(async () => await call.ResponseAsync); + Assert.AreEqual(StatusCode.Cancelled, ex.Status.StatusCode); + } + Console.WriteLine("Passed!"); + } + + public static async Task RunCancelAfterFirstResponseAsync(TestService.ITestServiceClient client) + { + Console.WriteLine("running cancel_after_first_response"); + + var cts = new CancellationTokenSource(); + using (var call = client.FullDuplexCall(cancellationToken: cts.Token)) + { + await call.RequestStream.WriteAsync(new StreamingOutputCallRequest + { + ResponseType = PayloadType.COMPRESSABLE, + ResponseParameters = { new ResponseParameters { Size = 31415 } }, + Payload = CreateZerosPayload(27182) + }); + + Assert.IsTrue(await call.ResponseStream.MoveNext()); + Assert.AreEqual(PayloadType.COMPRESSABLE, call.ResponseStream.Current.Payload.Type); + Assert.AreEqual(31415, call.ResponseStream.Current.Payload.Body.Length); + + cts.Cancel(); + + var ex = Assert.Throws(async () => await call.ResponseStream.MoveNext()); + Assert.AreEqual(StatusCode.Cancelled, ex.Status.StatusCode); + } + Console.WriteLine("Passed!"); + } + + public static async Task RunTimeoutOnSleepingServerAsync(TestService.ITestServiceClient client) + { + Console.WriteLine("running timeout_on_sleeping_server"); + + var deadline = DateTime.UtcNow.AddMilliseconds(1); + using (var call = client.FullDuplexCall(deadline: deadline)) + { + try + { + await call.RequestStream.WriteAsync(new StreamingOutputCallRequest { Payload = CreateZerosPayload(27182) }); + } + catch (InvalidOperationException) + { + // Deadline was reached before write has started. Eat the exception and continue. + } + + var ex = Assert.Throws(async () => await call.ResponseStream.MoveNext()); + Assert.AreEqual(StatusCode.DeadlineExceeded, ex.Status.StatusCode); + } + Console.WriteLine("Passed!"); + } + + // This is not an official interop test, but it's useful. + public static void RunBenchmarkEmptyUnary(TestService.ITestServiceClient client) + { + BenchmarkUtil.RunBenchmark(10000, 10000, + () => { client.EmptyCall(new Empty()); }); + } + + private static Payload CreateZerosPayload(int size) + { + return new Payload { Body = ByteString.CopyFrom(new byte[size]) }; + } + } +} diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs b/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs new file mode 100644 index 00000000..7bc17a20 --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs @@ -0,0 +1,136 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core; +using Grpc.Core.Utils; +using Grpc.Testing; +using NUnit.Framework; + +namespace Grpc.IntegrationTesting +{ + /// + /// Runs interop tests in-process. + /// + public class InteropClientServerTest + { + const string Host = "localhost"; + Server server; + Channel channel; + TestService.ITestServiceClient client; + + [TestFixtureSetUp] + public void Init() + { + server = new Server + { + Services = { TestService.BindService(new TestServiceImpl()) }, + Ports = { { Host, ServerPort.PickUnused, TestCredentials.CreateTestServerCredentials() } } + }; + server.Start(); + + var options = new List + { + new ChannelOption(ChannelOptions.SslTargetNameOverride, TestCredentials.DefaultHostOverride) + }; + int port = server.Ports.Single().BoundPort; + channel = new Channel(Host, port, TestCredentials.CreateTestClientCredentials(true), options); + client = TestService.NewClient(channel); + } + + [TestFixtureTearDown] + public void Cleanup() + { + channel.ShutdownAsync().Wait(); + server.ShutdownAsync().Wait(); + } + + [Test] + public void EmptyUnary() + { + InteropClient.RunEmptyUnary(client); + } + + [Test] + public void LargeUnary() + { + InteropClient.RunLargeUnary(client); + } + + [Test] + public async Task ClientStreaming() + { + await InteropClient.RunClientStreamingAsync(client); + } + + [Test] + public async Task ServerStreaming() + { + await InteropClient.RunServerStreamingAsync(client); + } + + [Test] + public async Task PingPong() + { + await InteropClient.RunPingPongAsync(client); + } + + [Test] + public async Task EmptyStream() + { + await InteropClient.RunEmptyStreamAsync(client); + } + + [Test] + public async Task CancelAfterBegin() + { + await InteropClient.RunCancelAfterBeginAsync(client); + } + + [Test] + public async Task CancelAfterFirstResponse() + { + await InteropClient.RunCancelAfterFirstResponseAsync(client); + } + + [Test] + public async Task TimeoutOnSleepingServerAsync() + { + await InteropClient.RunTimeoutOnSleepingServerAsync(client); + } + } +} diff --git a/src/csharp/Grpc.IntegrationTesting/InteropServer.cs b/src/csharp/Grpc.IntegrationTesting/InteropServer.cs new file mode 100644 index 00000000..513f8722 --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting/InteropServer.cs @@ -0,0 +1,116 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +using CommandLine; +using CommandLine.Text; +using Grpc.Core; +using Grpc.Core.Utils; +using Grpc.Testing; +using NUnit.Framework; + +namespace Grpc.IntegrationTesting +{ + public class InteropServer + { + private class ServerOptions + { + [Option("port", DefaultValue = 8070)] + public int Port { get; set; } + + [Option("use_tls")] + public bool UseTls { get; set; } + + [HelpOption] + public string GetUsage() + { + var help = new HelpText + { + Heading = "gRPC C# interop testing server", + AddDashesToOption = true + }; + help.AddPreOptionsLine("Usage:"); + help.AddOptions(this); + return help; + } + } + + ServerOptions options; + + private InteropServer(ServerOptions options) + { + this.options = options; + } + + public static void Run(string[] args) + { + var options = new ServerOptions(); + if (!Parser.Default.ParseArguments(args, options)) + { + Environment.Exit(1); + } + + var interopServer = new InteropServer(options); + interopServer.Run(); + } + + private void Run() + { + var server = new Server + { + Services = { TestService.BindService(new TestServiceImpl()) } + }; + + string host = "0.0.0.0"; + int port = options.Port; + if (options.UseTls) + { + server.Ports.Add(host, port, TestCredentials.CreateTestServerCredentials()); + } + else + { + server.Ports.Add(host, options.Port, ServerCredentials.Insecure); + } + Console.WriteLine("Running server on " + string.Format("{0}:{1}", host, port)); + server.Start(); + + server.ShutdownTask.Wait(); + } + } +} diff --git a/src/csharp/Grpc.IntegrationTesting/Messages.cs b/src/csharp/Grpc.IntegrationTesting/Messages.cs new file mode 100644 index 00000000..a3cbb7d7 --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting/Messages.cs @@ -0,0 +1,1172 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: messages.proto +#pragma warning disable 1591, 0612, 3021 +#region Designer generated code + +using pb = global::Google.Protobuf; +using pbc = global::Google.Protobuf.Collections; +using pbr = global::Google.Protobuf.Reflection; +using scg = global::System.Collections.Generic; +namespace Grpc.Testing { + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public static partial class Messages { + + #region Descriptor + public static pbr::FileDescriptor Descriptor { + get { return descriptor; } + } + private static pbr::FileDescriptor descriptor; + + static Messages() { + byte[] descriptorData = global::System.Convert.FromBase64String( + string.Concat( + "Cg5tZXNzYWdlcy5wcm90bxIMZ3JwYy50ZXN0aW5nIkAKB1BheWxvYWQSJwoE", + "dHlwZRgBIAEoDjIZLmdycGMudGVzdGluZy5QYXlsb2FkVHlwZRIMCgRib2R5", + "GAIgASgMIrEBCg1TaW1wbGVSZXF1ZXN0EjAKDXJlc3BvbnNlX3R5cGUYASAB", + "KA4yGS5ncnBjLnRlc3RpbmcuUGF5bG9hZFR5cGUSFQoNcmVzcG9uc2Vfc2l6", + "ZRgCIAEoBRImCgdwYXlsb2FkGAMgASgLMhUuZ3JwYy50ZXN0aW5nLlBheWxv", + "YWQSFQoNZmlsbF91c2VybmFtZRgEIAEoCBIYChBmaWxsX29hdXRoX3Njb3Bl", + "GAUgASgIIl8KDlNpbXBsZVJlc3BvbnNlEiYKB3BheWxvYWQYASABKAsyFS5n", + "cnBjLnRlc3RpbmcuUGF5bG9hZBIQCgh1c2VybmFtZRgCIAEoCRITCgtvYXV0", + "aF9zY29wZRgDIAEoCSJDChlTdHJlYW1pbmdJbnB1dENhbGxSZXF1ZXN0EiYK", + "B3BheWxvYWQYASABKAsyFS5ncnBjLnRlc3RpbmcuUGF5bG9hZCI9ChpTdHJl", + "YW1pbmdJbnB1dENhbGxSZXNwb25zZRIfChdhZ2dyZWdhdGVkX3BheWxvYWRf", + "c2l6ZRgBIAEoBSI3ChJSZXNwb25zZVBhcmFtZXRlcnMSDAoEc2l6ZRgBIAEo", + "BRITCgtpbnRlcnZhbF91cxgCIAEoBSK1AQoaU3RyZWFtaW5nT3V0cHV0Q2Fs", + "bFJlcXVlc3QSMAoNcmVzcG9uc2VfdHlwZRgBIAEoDjIZLmdycGMudGVzdGlu", + "Zy5QYXlsb2FkVHlwZRI9ChNyZXNwb25zZV9wYXJhbWV0ZXJzGAIgAygLMiAu", + "Z3JwYy50ZXN0aW5nLlJlc3BvbnNlUGFyYW1ldGVycxImCgdwYXlsb2FkGAMg", + "ASgLMhUuZ3JwYy50ZXN0aW5nLlBheWxvYWQiRQobU3RyZWFtaW5nT3V0cHV0", + "Q2FsbFJlc3BvbnNlEiYKB3BheWxvYWQYASABKAsyFS5ncnBjLnRlc3Rpbmcu", + "UGF5bG9hZCo/CgtQYXlsb2FkVHlwZRIQCgxDT01QUkVTU0FCTEUQABISCg5V", + "TkNPTVBSRVNTQUJMRRABEgoKBlJBTkRPTRACYgZwcm90bzM=")); + descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData, + new pbr::FileDescriptor[] { }, + new pbr::GeneratedCodeInfo(new[] {typeof(global::Grpc.Testing.PayloadType), }, new pbr::GeneratedCodeInfo[] { + new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.Payload), new[]{ "Type", "Body" }, null, null, null), + new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.SimpleRequest), new[]{ "ResponseType", "ResponseSize", "Payload", "FillUsername", "FillOauthScope" }, null, null, null), + new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.SimpleResponse), new[]{ "Payload", "Username", "OauthScope" }, null, null, null), + new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.StreamingInputCallRequest), new[]{ "Payload" }, null, null, null), + new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.StreamingInputCallResponse), new[]{ "AggregatedPayloadSize" }, null, null, null), + new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.ResponseParameters), new[]{ "Size", "IntervalUs" }, null, null, null), + new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.StreamingOutputCallRequest), new[]{ "ResponseType", "ResponseParameters", "Payload" }, null, null, null), + new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.StreamingOutputCallResponse), new[]{ "Payload" }, null, null, null) + })); + } + #endregion + + } + #region Enums + public enum PayloadType { + COMPRESSABLE = 0, + UNCOMPRESSABLE = 1, + RANDOM = 2, + } + + #endregion + + #region Messages + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class Payload : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new Payload()); + public static pb::MessageParser Parser { get { return _parser; } } + + public static pbr::MessageDescriptor Descriptor { + get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[0]; } + } + + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + public Payload() { + OnConstruction(); + } + + partial void OnConstruction(); + + public Payload(Payload other) : this() { + type_ = other.type_; + body_ = other.body_; + } + + public Payload Clone() { + return new Payload(this); + } + + public const int TypeFieldNumber = 1; + private global::Grpc.Testing.PayloadType type_ = global::Grpc.Testing.PayloadType.COMPRESSABLE; + public global::Grpc.Testing.PayloadType Type { + get { return type_; } + set { + type_ = value; + } + } + + public const int BodyFieldNumber = 2; + private pb::ByteString body_ = pb::ByteString.Empty; + public pb::ByteString Body { + get { return body_; } + set { + body_ = pb::Preconditions.CheckNotNull(value, "value"); + } + } + + public override bool Equals(object other) { + return Equals(other as Payload); + } + + public bool Equals(Payload other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Type != other.Type) return false; + if (Body != other.Body) return false; + return true; + } + + public override int GetHashCode() { + int hash = 1; + if (Type != global::Grpc.Testing.PayloadType.COMPRESSABLE) hash ^= Type.GetHashCode(); + if (Body.Length != 0) hash ^= Body.GetHashCode(); + return hash; + } + + public override string ToString() { + return pb::JsonFormatter.Default.Format(this); + } + + public void WriteTo(pb::CodedOutputStream output) { + if (Type != global::Grpc.Testing.PayloadType.COMPRESSABLE) { + output.WriteRawTag(8); + output.WriteEnum((int) Type); + } + if (Body.Length != 0) { + output.WriteRawTag(18); + output.WriteBytes(Body); + } + } + + public int CalculateSize() { + int size = 0; + if (Type != global::Grpc.Testing.PayloadType.COMPRESSABLE) { + size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Type); + } + if (Body.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeBytesSize(Body); + } + return size; + } + + public void MergeFrom(Payload other) { + if (other == null) { + return; + } + if (other.Type != global::Grpc.Testing.PayloadType.COMPRESSABLE) { + Type = other.Type; + } + if (other.Body.Length != 0) { + Body = other.Body; + } + } + + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + input.SkipLastField(); + break; + case 8: { + type_ = (global::Grpc.Testing.PayloadType) input.ReadEnum(); + break; + } + case 18: { + Body = input.ReadBytes(); + break; + } + } + } + } + + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class SimpleRequest : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new SimpleRequest()); + public static pb::MessageParser Parser { get { return _parser; } } + + public static pbr::MessageDescriptor Descriptor { + get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[1]; } + } + + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + public SimpleRequest() { + OnConstruction(); + } + + partial void OnConstruction(); + + public SimpleRequest(SimpleRequest other) : this() { + responseType_ = other.responseType_; + responseSize_ = other.responseSize_; + Payload = other.payload_ != null ? other.Payload.Clone() : null; + fillUsername_ = other.fillUsername_; + fillOauthScope_ = other.fillOauthScope_; + } + + public SimpleRequest Clone() { + return new SimpleRequest(this); + } + + public const int ResponseTypeFieldNumber = 1; + private global::Grpc.Testing.PayloadType responseType_ = global::Grpc.Testing.PayloadType.COMPRESSABLE; + public global::Grpc.Testing.PayloadType ResponseType { + get { return responseType_; } + set { + responseType_ = value; + } + } + + public const int ResponseSizeFieldNumber = 2; + private int responseSize_; + public int ResponseSize { + get { return responseSize_; } + set { + responseSize_ = value; + } + } + + public const int PayloadFieldNumber = 3; + private global::Grpc.Testing.Payload payload_; + public global::Grpc.Testing.Payload Payload { + get { return payload_; } + set { + payload_ = value; + } + } + + public const int FillUsernameFieldNumber = 4; + private bool fillUsername_; + public bool FillUsername { + get { return fillUsername_; } + set { + fillUsername_ = value; + } + } + + public const int FillOauthScopeFieldNumber = 5; + private bool fillOauthScope_; + public bool FillOauthScope { + get { return fillOauthScope_; } + set { + fillOauthScope_ = value; + } + } + + public override bool Equals(object other) { + return Equals(other as SimpleRequest); + } + + public bool Equals(SimpleRequest other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (ResponseType != other.ResponseType) return false; + if (ResponseSize != other.ResponseSize) return false; + if (!object.Equals(Payload, other.Payload)) return false; + if (FillUsername != other.FillUsername) return false; + if (FillOauthScope != other.FillOauthScope) return false; + return true; + } + + public override int GetHashCode() { + int hash = 1; + if (ResponseType != global::Grpc.Testing.PayloadType.COMPRESSABLE) hash ^= ResponseType.GetHashCode(); + if (ResponseSize != 0) hash ^= ResponseSize.GetHashCode(); + if (payload_ != null) hash ^= Payload.GetHashCode(); + if (FillUsername != false) hash ^= FillUsername.GetHashCode(); + if (FillOauthScope != false) hash ^= FillOauthScope.GetHashCode(); + return hash; + } + + public override string ToString() { + return pb::JsonFormatter.Default.Format(this); + } + + public void WriteTo(pb::CodedOutputStream output) { + if (ResponseType != global::Grpc.Testing.PayloadType.COMPRESSABLE) { + output.WriteRawTag(8); + output.WriteEnum((int) ResponseType); + } + if (ResponseSize != 0) { + output.WriteRawTag(16); + output.WriteInt32(ResponseSize); + } + if (payload_ != null) { + output.WriteRawTag(26); + output.WriteMessage(Payload); + } + if (FillUsername != false) { + output.WriteRawTag(32); + output.WriteBool(FillUsername); + } + if (FillOauthScope != false) { + output.WriteRawTag(40); + output.WriteBool(FillOauthScope); + } + } + + public int CalculateSize() { + int size = 0; + if (ResponseType != global::Grpc.Testing.PayloadType.COMPRESSABLE) { + size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) ResponseType); + } + if (ResponseSize != 0) { + size += 1 + pb::CodedOutputStream.ComputeInt32Size(ResponseSize); + } + if (payload_ != null) { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(Payload); + } + if (FillUsername != false) { + size += 1 + 1; + } + if (FillOauthScope != false) { + size += 1 + 1; + } + return size; + } + + public void MergeFrom(SimpleRequest other) { + if (other == null) { + return; + } + if (other.ResponseType != global::Grpc.Testing.PayloadType.COMPRESSABLE) { + ResponseType = other.ResponseType; + } + if (other.ResponseSize != 0) { + ResponseSize = other.ResponseSize; + } + if (other.payload_ != null) { + if (payload_ == null) { + payload_ = new global::Grpc.Testing.Payload(); + } + Payload.MergeFrom(other.Payload); + } + if (other.FillUsername != false) { + FillUsername = other.FillUsername; + } + if (other.FillOauthScope != false) { + FillOauthScope = other.FillOauthScope; + } + } + + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + input.SkipLastField(); + break; + case 8: { + responseType_ = (global::Grpc.Testing.PayloadType) input.ReadEnum(); + break; + } + case 16: { + ResponseSize = input.ReadInt32(); + break; + } + case 26: { + if (payload_ == null) { + payload_ = new global::Grpc.Testing.Payload(); + } + input.ReadMessage(payload_); + break; + } + case 32: { + FillUsername = input.ReadBool(); + break; + } + case 40: { + FillOauthScope = input.ReadBool(); + break; + } + } + } + } + + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class SimpleResponse : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new SimpleResponse()); + public static pb::MessageParser Parser { get { return _parser; } } + + public static pbr::MessageDescriptor Descriptor { + get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[2]; } + } + + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + public SimpleResponse() { + OnConstruction(); + } + + partial void OnConstruction(); + + public SimpleResponse(SimpleResponse other) : this() { + Payload = other.payload_ != null ? other.Payload.Clone() : null; + username_ = other.username_; + oauthScope_ = other.oauthScope_; + } + + public SimpleResponse Clone() { + return new SimpleResponse(this); + } + + public const int PayloadFieldNumber = 1; + private global::Grpc.Testing.Payload payload_; + public global::Grpc.Testing.Payload Payload { + get { return payload_; } + set { + payload_ = value; + } + } + + public const int UsernameFieldNumber = 2; + private string username_ = ""; + public string Username { + get { return username_; } + set { + username_ = pb::Preconditions.CheckNotNull(value, "value"); + } + } + + public const int OauthScopeFieldNumber = 3; + private string oauthScope_ = ""; + public string OauthScope { + get { return oauthScope_; } + set { + oauthScope_ = pb::Preconditions.CheckNotNull(value, "value"); + } + } + + public override bool Equals(object other) { + return Equals(other as SimpleResponse); + } + + public bool Equals(SimpleResponse other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (!object.Equals(Payload, other.Payload)) return false; + if (Username != other.Username) return false; + if (OauthScope != other.OauthScope) return false; + return true; + } + + public override int GetHashCode() { + int hash = 1; + if (payload_ != null) hash ^= Payload.GetHashCode(); + if (Username.Length != 0) hash ^= Username.GetHashCode(); + if (OauthScope.Length != 0) hash ^= OauthScope.GetHashCode(); + return hash; + } + + public override string ToString() { + return pb::JsonFormatter.Default.Format(this); + } + + public void WriteTo(pb::CodedOutputStream output) { + if (payload_ != null) { + output.WriteRawTag(10); + output.WriteMessage(Payload); + } + if (Username.Length != 0) { + output.WriteRawTag(18); + output.WriteString(Username); + } + if (OauthScope.Length != 0) { + output.WriteRawTag(26); + output.WriteString(OauthScope); + } + } + + public int CalculateSize() { + int size = 0; + if (payload_ != null) { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(Payload); + } + if (Username.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Username); + } + if (OauthScope.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(OauthScope); + } + return size; + } + + public void MergeFrom(SimpleResponse other) { + if (other == null) { + return; + } + if (other.payload_ != null) { + if (payload_ == null) { + payload_ = new global::Grpc.Testing.Payload(); + } + Payload.MergeFrom(other.Payload); + } + if (other.Username.Length != 0) { + Username = other.Username; + } + if (other.OauthScope.Length != 0) { + OauthScope = other.OauthScope; + } + } + + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + input.SkipLastField(); + break; + case 10: { + if (payload_ == null) { + payload_ = new global::Grpc.Testing.Payload(); + } + input.ReadMessage(payload_); + break; + } + case 18: { + Username = input.ReadString(); + break; + } + case 26: { + OauthScope = input.ReadString(); + break; + } + } + } + } + + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class StreamingInputCallRequest : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new StreamingInputCallRequest()); + public static pb::MessageParser Parser { get { return _parser; } } + + public static pbr::MessageDescriptor Descriptor { + get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[3]; } + } + + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + public StreamingInputCallRequest() { + OnConstruction(); + } + + partial void OnConstruction(); + + public StreamingInputCallRequest(StreamingInputCallRequest other) : this() { + Payload = other.payload_ != null ? other.Payload.Clone() : null; + } + + public StreamingInputCallRequest Clone() { + return new StreamingInputCallRequest(this); + } + + public const int PayloadFieldNumber = 1; + private global::Grpc.Testing.Payload payload_; + public global::Grpc.Testing.Payload Payload { + get { return payload_; } + set { + payload_ = value; + } + } + + public override bool Equals(object other) { + return Equals(other as StreamingInputCallRequest); + } + + public bool Equals(StreamingInputCallRequest other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (!object.Equals(Payload, other.Payload)) return false; + return true; + } + + public override int GetHashCode() { + int hash = 1; + if (payload_ != null) hash ^= Payload.GetHashCode(); + return hash; + } + + public override string ToString() { + return pb::JsonFormatter.Default.Format(this); + } + + public void WriteTo(pb::CodedOutputStream output) { + if (payload_ != null) { + output.WriteRawTag(10); + output.WriteMessage(Payload); + } + } + + public int CalculateSize() { + int size = 0; + if (payload_ != null) { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(Payload); + } + return size; + } + + public void MergeFrom(StreamingInputCallRequest other) { + if (other == null) { + return; + } + if (other.payload_ != null) { + if (payload_ == null) { + payload_ = new global::Grpc.Testing.Payload(); + } + Payload.MergeFrom(other.Payload); + } + } + + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + input.SkipLastField(); + break; + case 10: { + if (payload_ == null) { + payload_ = new global::Grpc.Testing.Payload(); + } + input.ReadMessage(payload_); + break; + } + } + } + } + + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class StreamingInputCallResponse : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new StreamingInputCallResponse()); + public static pb::MessageParser Parser { get { return _parser; } } + + public static pbr::MessageDescriptor Descriptor { + get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[4]; } + } + + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + public StreamingInputCallResponse() { + OnConstruction(); + } + + partial void OnConstruction(); + + public StreamingInputCallResponse(StreamingInputCallResponse other) : this() { + aggregatedPayloadSize_ = other.aggregatedPayloadSize_; + } + + public StreamingInputCallResponse Clone() { + return new StreamingInputCallResponse(this); + } + + public const int AggregatedPayloadSizeFieldNumber = 1; + private int aggregatedPayloadSize_; + public int AggregatedPayloadSize { + get { return aggregatedPayloadSize_; } + set { + aggregatedPayloadSize_ = value; + } + } + + public override bool Equals(object other) { + return Equals(other as StreamingInputCallResponse); + } + + public bool Equals(StreamingInputCallResponse other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (AggregatedPayloadSize != other.AggregatedPayloadSize) return false; + return true; + } + + public override int GetHashCode() { + int hash = 1; + if (AggregatedPayloadSize != 0) hash ^= AggregatedPayloadSize.GetHashCode(); + return hash; + } + + public override string ToString() { + return pb::JsonFormatter.Default.Format(this); + } + + public void WriteTo(pb::CodedOutputStream output) { + if (AggregatedPayloadSize != 0) { + output.WriteRawTag(8); + output.WriteInt32(AggregatedPayloadSize); + } + } + + public int CalculateSize() { + int size = 0; + if (AggregatedPayloadSize != 0) { + size += 1 + pb::CodedOutputStream.ComputeInt32Size(AggregatedPayloadSize); + } + return size; + } + + public void MergeFrom(StreamingInputCallResponse other) { + if (other == null) { + return; + } + if (other.AggregatedPayloadSize != 0) { + AggregatedPayloadSize = other.AggregatedPayloadSize; + } + } + + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + input.SkipLastField(); + break; + case 8: { + AggregatedPayloadSize = input.ReadInt32(); + break; + } + } + } + } + + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class ResponseParameters : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new ResponseParameters()); + public static pb::MessageParser Parser { get { return _parser; } } + + public static pbr::MessageDescriptor Descriptor { + get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[5]; } + } + + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + public ResponseParameters() { + OnConstruction(); + } + + partial void OnConstruction(); + + public ResponseParameters(ResponseParameters other) : this() { + size_ = other.size_; + intervalUs_ = other.intervalUs_; + } + + public ResponseParameters Clone() { + return new ResponseParameters(this); + } + + public const int SizeFieldNumber = 1; + private int size_; + public int Size { + get { return size_; } + set { + size_ = value; + } + } + + public const int IntervalUsFieldNumber = 2; + private int intervalUs_; + public int IntervalUs { + get { return intervalUs_; } + set { + intervalUs_ = value; + } + } + + public override bool Equals(object other) { + return Equals(other as ResponseParameters); + } + + public bool Equals(ResponseParameters other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Size != other.Size) return false; + if (IntervalUs != other.IntervalUs) return false; + return true; + } + + public override int GetHashCode() { + int hash = 1; + if (Size != 0) hash ^= Size.GetHashCode(); + if (IntervalUs != 0) hash ^= IntervalUs.GetHashCode(); + return hash; + } + + public override string ToString() { + return pb::JsonFormatter.Default.Format(this); + } + + public void WriteTo(pb::CodedOutputStream output) { + if (Size != 0) { + output.WriteRawTag(8); + output.WriteInt32(Size); + } + if (IntervalUs != 0) { + output.WriteRawTag(16); + output.WriteInt32(IntervalUs); + } + } + + public int CalculateSize() { + int size = 0; + if (Size != 0) { + size += 1 + pb::CodedOutputStream.ComputeInt32Size(Size); + } + if (IntervalUs != 0) { + size += 1 + pb::CodedOutputStream.ComputeInt32Size(IntervalUs); + } + return size; + } + + public void MergeFrom(ResponseParameters other) { + if (other == null) { + return; + } + if (other.Size != 0) { + Size = other.Size; + } + if (other.IntervalUs != 0) { + IntervalUs = other.IntervalUs; + } + } + + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + input.SkipLastField(); + break; + case 8: { + Size = input.ReadInt32(); + break; + } + case 16: { + IntervalUs = input.ReadInt32(); + break; + } + } + } + } + + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class StreamingOutputCallRequest : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new StreamingOutputCallRequest()); + public static pb::MessageParser Parser { get { return _parser; } } + + public static pbr::MessageDescriptor Descriptor { + get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[6]; } + } + + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + public StreamingOutputCallRequest() { + OnConstruction(); + } + + partial void OnConstruction(); + + public StreamingOutputCallRequest(StreamingOutputCallRequest other) : this() { + responseType_ = other.responseType_; + responseParameters_ = other.responseParameters_.Clone(); + Payload = other.payload_ != null ? other.Payload.Clone() : null; + } + + public StreamingOutputCallRequest Clone() { + return new StreamingOutputCallRequest(this); + } + + public const int ResponseTypeFieldNumber = 1; + private global::Grpc.Testing.PayloadType responseType_ = global::Grpc.Testing.PayloadType.COMPRESSABLE; + public global::Grpc.Testing.PayloadType ResponseType { + get { return responseType_; } + set { + responseType_ = value; + } + } + + public const int ResponseParametersFieldNumber = 2; + private static readonly pb::FieldCodec _repeated_responseParameters_codec + = pb::FieldCodec.ForMessage(18, global::Grpc.Testing.ResponseParameters.Parser); + private readonly pbc::RepeatedField responseParameters_ = new pbc::RepeatedField(); + public pbc::RepeatedField ResponseParameters { + get { return responseParameters_; } + } + + public const int PayloadFieldNumber = 3; + private global::Grpc.Testing.Payload payload_; + public global::Grpc.Testing.Payload Payload { + get { return payload_; } + set { + payload_ = value; + } + } + + public override bool Equals(object other) { + return Equals(other as StreamingOutputCallRequest); + } + + public bool Equals(StreamingOutputCallRequest other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (ResponseType != other.ResponseType) return false; + if(!responseParameters_.Equals(other.responseParameters_)) return false; + if (!object.Equals(Payload, other.Payload)) return false; + return true; + } + + public override int GetHashCode() { + int hash = 1; + if (ResponseType != global::Grpc.Testing.PayloadType.COMPRESSABLE) hash ^= ResponseType.GetHashCode(); + hash ^= responseParameters_.GetHashCode(); + if (payload_ != null) hash ^= Payload.GetHashCode(); + return hash; + } + + public override string ToString() { + return pb::JsonFormatter.Default.Format(this); + } + + public void WriteTo(pb::CodedOutputStream output) { + if (ResponseType != global::Grpc.Testing.PayloadType.COMPRESSABLE) { + output.WriteRawTag(8); + output.WriteEnum((int) ResponseType); + } + responseParameters_.WriteTo(output, _repeated_responseParameters_codec); + if (payload_ != null) { + output.WriteRawTag(26); + output.WriteMessage(Payload); + } + } + + public int CalculateSize() { + int size = 0; + if (ResponseType != global::Grpc.Testing.PayloadType.COMPRESSABLE) { + size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) ResponseType); + } + size += responseParameters_.CalculateSize(_repeated_responseParameters_codec); + if (payload_ != null) { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(Payload); + } + return size; + } + + public void MergeFrom(StreamingOutputCallRequest other) { + if (other == null) { + return; + } + if (other.ResponseType != global::Grpc.Testing.PayloadType.COMPRESSABLE) { + ResponseType = other.ResponseType; + } + responseParameters_.Add(other.responseParameters_); + if (other.payload_ != null) { + if (payload_ == null) { + payload_ = new global::Grpc.Testing.Payload(); + } + Payload.MergeFrom(other.Payload); + } + } + + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + input.SkipLastField(); + break; + case 8: { + responseType_ = (global::Grpc.Testing.PayloadType) input.ReadEnum(); + break; + } + case 18: { + responseParameters_.AddEntriesFrom(input, _repeated_responseParameters_codec); + break; + } + case 26: { + if (payload_ == null) { + payload_ = new global::Grpc.Testing.Payload(); + } + input.ReadMessage(payload_); + break; + } + } + } + } + + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class StreamingOutputCallResponse : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new StreamingOutputCallResponse()); + public static pb::MessageParser Parser { get { return _parser; } } + + public static pbr::MessageDescriptor Descriptor { + get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[7]; } + } + + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + public StreamingOutputCallResponse() { + OnConstruction(); + } + + partial void OnConstruction(); + + public StreamingOutputCallResponse(StreamingOutputCallResponse other) : this() { + Payload = other.payload_ != null ? other.Payload.Clone() : null; + } + + public StreamingOutputCallResponse Clone() { + return new StreamingOutputCallResponse(this); + } + + public const int PayloadFieldNumber = 1; + private global::Grpc.Testing.Payload payload_; + public global::Grpc.Testing.Payload Payload { + get { return payload_; } + set { + payload_ = value; + } + } + + public override bool Equals(object other) { + return Equals(other as StreamingOutputCallResponse); + } + + public bool Equals(StreamingOutputCallResponse other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (!object.Equals(Payload, other.Payload)) return false; + return true; + } + + public override int GetHashCode() { + int hash = 1; + if (payload_ != null) hash ^= Payload.GetHashCode(); + return hash; + } + + public override string ToString() { + return pb::JsonFormatter.Default.Format(this); + } + + public void WriteTo(pb::CodedOutputStream output) { + if (payload_ != null) { + output.WriteRawTag(10); + output.WriteMessage(Payload); + } + } + + public int CalculateSize() { + int size = 0; + if (payload_ != null) { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(Payload); + } + return size; + } + + public void MergeFrom(StreamingOutputCallResponse other) { + if (other == null) { + return; + } + if (other.payload_ != null) { + if (payload_ == null) { + payload_ = new global::Grpc.Testing.Payload(); + } + Payload.MergeFrom(other.Payload); + } + } + + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + input.SkipLastField(); + break; + case 10: { + if (payload_ == null) { + payload_ = new global::Grpc.Testing.Payload(); + } + input.ReadMessage(payload_); + break; + } + } + } + } + + } + + #endregion + +} + +#endregion Designer generated code diff --git a/src/csharp/Grpc.IntegrationTesting/Properties/AssemblyInfo.cs b/src/csharp/Grpc.IntegrationTesting/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..1beb0bbb --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting/Properties/AssemblyInfo.cs @@ -0,0 +1,11 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyTitle("Grpc.IntegrationTesting")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("Google Inc. All rights reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] diff --git a/src/csharp/Grpc.IntegrationTesting/Settings.StyleCop b/src/csharp/Grpc.IntegrationTesting/Settings.StyleCop new file mode 100644 index 00000000..fb99cd4a --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting/Settings.StyleCop @@ -0,0 +1,11 @@ + + + Messages.cs + Empty.cs + + + False + + + + \ No newline at end of file diff --git a/src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs b/src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs new file mode 100644 index 00000000..37b2518c --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs @@ -0,0 +1,99 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core; +using Grpc.Core.Utils; +using Grpc.Testing; +using NUnit.Framework; + +namespace Grpc.IntegrationTesting +{ + /// + /// Test SSL credentials where server authenticates client + /// and client authenticates the server. + /// + public class SslCredentialsTest + { + const string Host = "localhost"; + Server server; + Channel channel; + TestService.ITestServiceClient client; + + [TestFixtureSetUp] + public void Init() + { + var rootCert = File.ReadAllText(TestCredentials.ClientCertAuthorityPath); + var keyCertPair = new KeyCertificatePair( + File.ReadAllText(TestCredentials.ServerCertChainPath), + File.ReadAllText(TestCredentials.ServerPrivateKeyPath)); + + var serverCredentials = new SslServerCredentials(new[] { keyCertPair }, rootCert, true); + var clientCredentials = new SslCredentials(rootCert, keyCertPair); + + server = new Server + { + Services = { TestService.BindService(new TestServiceImpl()) }, + Ports = { { Host, ServerPort.PickUnused, serverCredentials } } + }; + server.Start(); + + var options = new List + { + new ChannelOption(ChannelOptions.SslTargetNameOverride, TestCredentials.DefaultHostOverride) + }; + + channel = new Channel(Host, server.Ports.Single().BoundPort, clientCredentials, options); + client = TestService.NewClient(channel); + } + + [TestFixtureTearDown] + public void Cleanup() + { + channel.ShutdownAsync().Wait(); + server.ShutdownAsync().Wait(); + } + + [Test] + public void AuthenticatedClientAndServer() + { + var response = client.UnaryCall(new SimpleRequest { ResponseSize = 10 }); + Assert.AreEqual(10, response.Payload.Body.Length); + } + } +} diff --git a/src/csharp/Grpc.IntegrationTesting/Test.cs b/src/csharp/Grpc.IntegrationTesting/Test.cs new file mode 100644 index 00000000..466ec57d --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting/Test.cs @@ -0,0 +1,48 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: test.proto +#pragma warning disable 1591, 0612, 3021 +#region Designer generated code + +using pb = global::Google.Protobuf; +using pbc = global::Google.Protobuf.Collections; +using pbr = global::Google.Protobuf.Reflection; +using scg = global::System.Collections.Generic; +namespace Grpc.Testing { + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public static partial class Test { + + #region Descriptor + public static pbr::FileDescriptor Descriptor { + get { return descriptor; } + } + private static pbr::FileDescriptor descriptor; + + static Test() { + byte[] descriptorData = global::System.Convert.FromBase64String( + string.Concat( + "Cgp0ZXN0LnByb3RvEgxncnBjLnRlc3RpbmcaC2VtcHR5LnByb3RvGg5tZXNz", + "YWdlcy5wcm90bzK7BAoLVGVzdFNlcnZpY2USNQoJRW1wdHlDYWxsEhMuZ3Jw", + "Yy50ZXN0aW5nLkVtcHR5GhMuZ3JwYy50ZXN0aW5nLkVtcHR5EkYKCVVuYXJ5", + "Q2FsbBIbLmdycGMudGVzdGluZy5TaW1wbGVSZXF1ZXN0GhwuZ3JwYy50ZXN0", + "aW5nLlNpbXBsZVJlc3BvbnNlEmwKE1N0cmVhbWluZ091dHB1dENhbGwSKC5n", + "cnBjLnRlc3RpbmcuU3RyZWFtaW5nT3V0cHV0Q2FsbFJlcXVlc3QaKS5ncnBj", + "LnRlc3RpbmcuU3RyZWFtaW5nT3V0cHV0Q2FsbFJlc3BvbnNlMAESaQoSU3Ry", + "ZWFtaW5nSW5wdXRDYWxsEicuZ3JwYy50ZXN0aW5nLlN0cmVhbWluZ0lucHV0", + "Q2FsbFJlcXVlc3QaKC5ncnBjLnRlc3RpbmcuU3RyZWFtaW5nSW5wdXRDYWxs", + "UmVzcG9uc2UoARJpCg5GdWxsRHVwbGV4Q2FsbBIoLmdycGMudGVzdGluZy5T", + "dHJlYW1pbmdPdXRwdXRDYWxsUmVxdWVzdBopLmdycGMudGVzdGluZy5TdHJl", + "YW1pbmdPdXRwdXRDYWxsUmVzcG9uc2UoATABEmkKDkhhbGZEdXBsZXhDYWxs", + "EiguZ3JwYy50ZXN0aW5nLlN0cmVhbWluZ091dHB1dENhbGxSZXF1ZXN0Giku", + "Z3JwYy50ZXN0aW5nLlN0cmVhbWluZ091dHB1dENhbGxSZXNwb25zZSgBMAFi", + "BnByb3RvMw==")); + descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData, + new pbr::FileDescriptor[] { global::Grpc.Testing.Proto.Empty.Descriptor, global::Grpc.Testing.Messages.Descriptor, }, + new pbr::GeneratedCodeInfo(null, null)); + } + #endregion + + } +} + +#endregion Designer generated code diff --git a/src/csharp/Grpc.IntegrationTesting/TestCredentials.cs b/src/csharp/Grpc.IntegrationTesting/TestCredentials.cs new file mode 100644 index 00000000..7a48d6e9 --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting/TestCredentials.cs @@ -0,0 +1,81 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Grpc.Core; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.IntegrationTesting +{ + /// + /// SSL Credentials for testing. + /// + public static class TestCredentials + { + public const string DefaultHostOverride = "foo.test.google.fr"; + + public const string ClientCertAuthorityPath = "data/ca.pem"; + public const string ClientCertAuthorityEnvName = "SSL_CERT_FILE"; + + public const string ServerCertChainPath = "data/server1.pem"; + public const string ServerPrivateKeyPath = "data/server1.key"; + + public static SslCredentials CreateTestClientCredentials(bool useTestCa) + { + string caPath = ClientCertAuthorityPath; + if (!useTestCa) + { + caPath = Environment.GetEnvironmentVariable(ClientCertAuthorityEnvName); + if (string.IsNullOrEmpty(caPath)) + { + throw new ArgumentException("CA path environment variable is not set."); + } + } + return new SslCredentials(File.ReadAllText(caPath)); + } + + public static SslServerCredentials CreateTestServerCredentials() + { + var keyCertPair = new KeyCertificatePair( + File.ReadAllText(ServerCertChainPath), + File.ReadAllText(ServerPrivateKeyPath)); + return new SslServerCredentials(new[] { keyCertPair }); + } + } +} diff --git a/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs b/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs new file mode 100644 index 00000000..f63e1484 --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs @@ -0,0 +1,211 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: test.proto +#region Designer generated code + +using System; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core; + +namespace Grpc.Testing { + public static class TestService + { + static readonly string __ServiceName = "grpc.testing.TestService"; + + static readonly Marshaller __Marshaller_Empty = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Grpc.Testing.Empty.Parser.ParseFrom); + static readonly Marshaller __Marshaller_SimpleRequest = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Grpc.Testing.SimpleRequest.Parser.ParseFrom); + static readonly Marshaller __Marshaller_SimpleResponse = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Grpc.Testing.SimpleResponse.Parser.ParseFrom); + static readonly Marshaller __Marshaller_StreamingOutputCallRequest = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Grpc.Testing.StreamingOutputCallRequest.Parser.ParseFrom); + static readonly Marshaller __Marshaller_StreamingOutputCallResponse = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Grpc.Testing.StreamingOutputCallResponse.Parser.ParseFrom); + static readonly Marshaller __Marshaller_StreamingInputCallRequest = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Grpc.Testing.StreamingInputCallRequest.Parser.ParseFrom); + static readonly Marshaller __Marshaller_StreamingInputCallResponse = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Grpc.Testing.StreamingInputCallResponse.Parser.ParseFrom); + + static readonly Method __Method_EmptyCall = new Method( + MethodType.Unary, + __ServiceName, + "EmptyCall", + __Marshaller_Empty, + __Marshaller_Empty); + + static readonly Method __Method_UnaryCall = new Method( + MethodType.Unary, + __ServiceName, + "UnaryCall", + __Marshaller_SimpleRequest, + __Marshaller_SimpleResponse); + + static readonly Method __Method_StreamingOutputCall = new Method( + MethodType.ServerStreaming, + __ServiceName, + "StreamingOutputCall", + __Marshaller_StreamingOutputCallRequest, + __Marshaller_StreamingOutputCallResponse); + + static readonly Method __Method_StreamingInputCall = new Method( + MethodType.ClientStreaming, + __ServiceName, + "StreamingInputCall", + __Marshaller_StreamingInputCallRequest, + __Marshaller_StreamingInputCallResponse); + + static readonly Method __Method_FullDuplexCall = new Method( + MethodType.DuplexStreaming, + __ServiceName, + "FullDuplexCall", + __Marshaller_StreamingOutputCallRequest, + __Marshaller_StreamingOutputCallResponse); + + static readonly Method __Method_HalfDuplexCall = new Method( + MethodType.DuplexStreaming, + __ServiceName, + "HalfDuplexCall", + __Marshaller_StreamingOutputCallRequest, + __Marshaller_StreamingOutputCallResponse); + + // service descriptor + public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor + { + get { return global::Grpc.Testing.Test.Descriptor.Services[0]; } + } + + // client interface + public interface ITestServiceClient + { + global::Grpc.Testing.Empty EmptyCall(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + global::Grpc.Testing.Empty EmptyCall(global::Grpc.Testing.Empty request, CallOptions options); + AsyncUnaryCall EmptyCallAsync(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + AsyncUnaryCall EmptyCallAsync(global::Grpc.Testing.Empty request, CallOptions options); + global::Grpc.Testing.SimpleResponse UnaryCall(global::Grpc.Testing.SimpleRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + global::Grpc.Testing.SimpleResponse UnaryCall(global::Grpc.Testing.SimpleRequest request, CallOptions options); + AsyncUnaryCall UnaryCallAsync(global::Grpc.Testing.SimpleRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + AsyncUnaryCall UnaryCallAsync(global::Grpc.Testing.SimpleRequest request, CallOptions options); + AsyncServerStreamingCall StreamingOutputCall(global::Grpc.Testing.StreamingOutputCallRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + AsyncServerStreamingCall StreamingOutputCall(global::Grpc.Testing.StreamingOutputCallRequest request, CallOptions options); + AsyncClientStreamingCall StreamingInputCall(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + AsyncClientStreamingCall StreamingInputCall(CallOptions options); + AsyncDuplexStreamingCall FullDuplexCall(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + AsyncDuplexStreamingCall FullDuplexCall(CallOptions options); + AsyncDuplexStreamingCall HalfDuplexCall(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + AsyncDuplexStreamingCall HalfDuplexCall(CallOptions options); + } + + // server-side interface + public interface ITestService + { + Task EmptyCall(global::Grpc.Testing.Empty request, ServerCallContext context); + Task UnaryCall(global::Grpc.Testing.SimpleRequest request, ServerCallContext context); + Task StreamingOutputCall(global::Grpc.Testing.StreamingOutputCallRequest request, IServerStreamWriter responseStream, ServerCallContext context); + Task StreamingInputCall(IAsyncStreamReader requestStream, ServerCallContext context); + Task FullDuplexCall(IAsyncStreamReader requestStream, IServerStreamWriter responseStream, ServerCallContext context); + Task HalfDuplexCall(IAsyncStreamReader requestStream, IServerStreamWriter responseStream, ServerCallContext context); + } + + // client stub + public class TestServiceClient : ClientBase, ITestServiceClient + { + public TestServiceClient(Channel channel) : base(channel) + { + } + public global::Grpc.Testing.Empty EmptyCall(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var call = CreateCall(__Method_EmptyCall, new CallOptions(headers, deadline, cancellationToken)); + return Calls.BlockingUnaryCall(call, request); + } + public global::Grpc.Testing.Empty EmptyCall(global::Grpc.Testing.Empty request, CallOptions options) + { + var call = CreateCall(__Method_EmptyCall, options); + return Calls.BlockingUnaryCall(call, request); + } + public AsyncUnaryCall EmptyCallAsync(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var call = CreateCall(__Method_EmptyCall, new CallOptions(headers, deadline, cancellationToken)); + return Calls.AsyncUnaryCall(call, request); + } + public AsyncUnaryCall EmptyCallAsync(global::Grpc.Testing.Empty request, CallOptions options) + { + var call = CreateCall(__Method_EmptyCall, options); + return Calls.AsyncUnaryCall(call, request); + } + public global::Grpc.Testing.SimpleResponse UnaryCall(global::Grpc.Testing.SimpleRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var call = CreateCall(__Method_UnaryCall, new CallOptions(headers, deadline, cancellationToken)); + return Calls.BlockingUnaryCall(call, request); + } + public global::Grpc.Testing.SimpleResponse UnaryCall(global::Grpc.Testing.SimpleRequest request, CallOptions options) + { + var call = CreateCall(__Method_UnaryCall, options); + return Calls.BlockingUnaryCall(call, request); + } + public AsyncUnaryCall UnaryCallAsync(global::Grpc.Testing.SimpleRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var call = CreateCall(__Method_UnaryCall, new CallOptions(headers, deadline, cancellationToken)); + return Calls.AsyncUnaryCall(call, request); + } + public AsyncUnaryCall UnaryCallAsync(global::Grpc.Testing.SimpleRequest request, CallOptions options) + { + var call = CreateCall(__Method_UnaryCall, options); + return Calls.AsyncUnaryCall(call, request); + } + public AsyncServerStreamingCall StreamingOutputCall(global::Grpc.Testing.StreamingOutputCallRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var call = CreateCall(__Method_StreamingOutputCall, new CallOptions(headers, deadline, cancellationToken)); + return Calls.AsyncServerStreamingCall(call, request); + } + public AsyncServerStreamingCall StreamingOutputCall(global::Grpc.Testing.StreamingOutputCallRequest request, CallOptions options) + { + var call = CreateCall(__Method_StreamingOutputCall, options); + return Calls.AsyncServerStreamingCall(call, request); + } + public AsyncClientStreamingCall StreamingInputCall(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var call = CreateCall(__Method_StreamingInputCall, new CallOptions(headers, deadline, cancellationToken)); + return Calls.AsyncClientStreamingCall(call); + } + public AsyncClientStreamingCall StreamingInputCall(CallOptions options) + { + var call = CreateCall(__Method_StreamingInputCall, options); + return Calls.AsyncClientStreamingCall(call); + } + public AsyncDuplexStreamingCall FullDuplexCall(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var call = CreateCall(__Method_FullDuplexCall, new CallOptions(headers, deadline, cancellationToken)); + return Calls.AsyncDuplexStreamingCall(call); + } + public AsyncDuplexStreamingCall FullDuplexCall(CallOptions options) + { + var call = CreateCall(__Method_FullDuplexCall, options); + return Calls.AsyncDuplexStreamingCall(call); + } + public AsyncDuplexStreamingCall HalfDuplexCall(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var call = CreateCall(__Method_HalfDuplexCall, new CallOptions(headers, deadline, cancellationToken)); + return Calls.AsyncDuplexStreamingCall(call); + } + public AsyncDuplexStreamingCall HalfDuplexCall(CallOptions options) + { + var call = CreateCall(__Method_HalfDuplexCall, options); + return Calls.AsyncDuplexStreamingCall(call); + } + } + + // creates service definition that can be registered with a server + public static ServerServiceDefinition BindService(ITestService serviceImpl) + { + return ServerServiceDefinition.CreateBuilder(__ServiceName) + .AddMethod(__Method_EmptyCall, serviceImpl.EmptyCall) + .AddMethod(__Method_UnaryCall, serviceImpl.UnaryCall) + .AddMethod(__Method_StreamingOutputCall, serviceImpl.StreamingOutputCall) + .AddMethod(__Method_StreamingInputCall, serviceImpl.StreamingInputCall) + .AddMethod(__Method_FullDuplexCall, serviceImpl.FullDuplexCall) + .AddMethod(__Method_HalfDuplexCall, serviceImpl.HalfDuplexCall).Build(); + } + + // creates a new client + public static TestServiceClient NewClient(Channel channel) + { + return new TestServiceClient(channel); + } + + } +} +#endregion diff --git a/src/csharp/Grpc.IntegrationTesting/TestServiceGrpc.cs b/src/csharp/Grpc.IntegrationTesting/TestServiceGrpc.cs new file mode 100644 index 00000000..9f14dad6 --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting/TestServiceGrpc.cs @@ -0,0 +1,204 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core; + +namespace grpc.testing +{ + /// + /// TestService (this is handwritten version of code that will normally be generated). + /// + public class TestServiceGrpc + { + static readonly string ServiceName = "/grpc.testing.TestService"; + + static readonly Marshaller EmptyMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), Empty.ParseFrom); + static readonly Marshaller SimpleRequestMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), SimpleRequest.ParseFrom); + static readonly Marshaller SimpleResponseMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), SimpleResponse.ParseFrom); + static readonly Marshaller StreamingOutputCallRequestMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), StreamingOutputCallRequest.ParseFrom); + static readonly Marshaller StreamingOutputCallResponseMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), StreamingOutputCallResponse.ParseFrom); + static readonly Marshaller StreamingInputCallRequestMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), StreamingInputCallRequest.ParseFrom); + static readonly Marshaller StreamingInputCallResponseMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), StreamingInputCallResponse.ParseFrom); + + static readonly Method EmptyCallMethod = new Method( + MethodType.Unary, + "EmptyCall", + EmptyMarshaller, + EmptyMarshaller); + + static readonly Method UnaryCallMethod = new Method( + MethodType.Unary, + "UnaryCall", + SimpleRequestMarshaller, + SimpleResponseMarshaller); + + static readonly Method StreamingOutputCallMethod = new Method( + MethodType.ServerStreaming, + "StreamingOutputCall", + StreamingOutputCallRequestMarshaller, + StreamingOutputCallResponseMarshaller); + + static readonly Method StreamingInputCallMethod = new Method( + MethodType.ClientStreaming, + "StreamingInputCall", + StreamingInputCallRequestMarshaller, + StreamingInputCallResponseMarshaller); + + static readonly Method FullDuplexCallMethod = new Method( + MethodType.DuplexStreaming, + "FullDuplexCall", + StreamingOutputCallRequestMarshaller, + StreamingOutputCallResponseMarshaller); + + static readonly Method HalfDuplexCallMethod = new Method( + MethodType.DuplexStreaming, + "HalfDuplexCall", + StreamingOutputCallRequestMarshaller, + StreamingOutputCallResponseMarshaller); + + public interface ITestServiceClient + { + Empty EmptyCall(Empty request, CancellationToken token = default(CancellationToken)); + + Task EmptyCallAsync(Empty request, CancellationToken token = default(CancellationToken)); + + SimpleResponse UnaryCall(SimpleRequest request, CancellationToken token = default(CancellationToken)); + + Task UnaryCallAsync(SimpleRequest request, CancellationToken token = default(CancellationToken)); + + AsyncServerStreamingCall StreamingOutputCall(StreamingOutputCallRequest request, CancellationToken token = default(CancellationToken)); + + AsyncClientStreamingCall StreamingInputCall(CancellationToken token = default(CancellationToken)); + + AsyncDuplexStreamingCall FullDuplexCall(CancellationToken token = default(CancellationToken)); + + AsyncDuplexStreamingCall HalfDuplexCall(CancellationToken token = default(CancellationToken)); + } + + public class TestServiceClientStub : AbstractStub, ITestServiceClient + { + public TestServiceClientStub(Channel channel) : base(channel, StubConfiguration.Default) + { + } + + public TestServiceClientStub(Channel channel, StubConfiguration config) : base(channel, config) + { + } + + public Empty EmptyCall(Empty request, CancellationToken token = default(CancellationToken)) + { + var call = CreateCall(ServiceName, EmptyCallMethod); + return Calls.BlockingUnaryCall(call, request, token); + } + + public Task EmptyCallAsync(Empty request, CancellationToken token = default(CancellationToken)) + { + var call = CreateCall(ServiceName, EmptyCallMethod); + return Calls.AsyncUnaryCall(call, request, token); + } + + public SimpleResponse UnaryCall(SimpleRequest request, CancellationToken token = default(CancellationToken)) + { + var call = CreateCall(ServiceName, UnaryCallMethod); + return Calls.BlockingUnaryCall(call, request, token); + } + + public Task UnaryCallAsync(SimpleRequest request, CancellationToken token = default(CancellationToken)) + { + var call = CreateCall(ServiceName, UnaryCallMethod); + return Calls.AsyncUnaryCall(call, request, token); + } + + public AsyncServerStreamingCall StreamingOutputCall(StreamingOutputCallRequest request, CancellationToken token = default(CancellationToken)) + { + var call = CreateCall(ServiceName, StreamingOutputCallMethod); + return Calls.AsyncServerStreamingCall(call, request, token); + } + + public AsyncClientStreamingCall StreamingInputCall(CancellationToken token = default(CancellationToken)) + { + var call = CreateCall(ServiceName, StreamingInputCallMethod); + return Calls.AsyncClientStreamingCall(call, token); + } + + public AsyncDuplexStreamingCall FullDuplexCall(CancellationToken token = default(CancellationToken)) + { + var call = CreateCall(ServiceName, FullDuplexCallMethod); + return Calls.AsyncDuplexStreamingCall(call, token); + } + + public AsyncDuplexStreamingCall HalfDuplexCall(CancellationToken token = default(CancellationToken)) + { + var call = CreateCall(ServiceName, HalfDuplexCallMethod); + return Calls.AsyncDuplexStreamingCall(call, token); + } + } + + // server-side interface + public interface ITestService + { + Task EmptyCall(ServerCallContext context, Empty request); + + Task UnaryCall(ServerCallContext context, SimpleRequest request); + + Task StreamingOutputCall(ServerCallContext context, StreamingOutputCallRequest request, IServerStreamWriter responseStream); + + Task StreamingInputCall(ServerCallContext context, IAsyncStreamReader requestStream); + + Task FullDuplexCall(ServerCallContext context, IAsyncStreamReader requestStream, IServerStreamWriter responseStream); + + Task HalfDuplexCall(ServerCallContext context, IAsyncStreamReader requestStream, IServerStreamWriter responseStream); + } + + public static ServerServiceDefinition BindService(ITestService serviceImpl) + { + return ServerServiceDefinition.CreateBuilder(ServiceName) + .AddMethod(EmptyCallMethod, serviceImpl.EmptyCall) + .AddMethod(UnaryCallMethod, serviceImpl.UnaryCall) + .AddMethod(StreamingOutputCallMethod, serviceImpl.StreamingOutputCall) + .AddMethod(StreamingInputCallMethod, serviceImpl.StreamingInputCall) + .AddMethod(FullDuplexCallMethod, serviceImpl.FullDuplexCall) + .AddMethod(HalfDuplexCallMethod, serviceImpl.HalfDuplexCall) + .Build(); + } + + public static ITestServiceClient NewStub(Channel channel) + { + return new TestServiceClientStub(channel); + } + } +} diff --git a/src/csharp/Grpc.IntegrationTesting/TestServiceImpl.cs b/src/csharp/Grpc.IntegrationTesting/TestServiceImpl.cs new file mode 100644 index 00000000..c5bfcf08 --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting/TestServiceImpl.cs @@ -0,0 +1,101 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Google.Protobuf; +using Grpc.Core; +using Grpc.Core.Utils; + +namespace Grpc.Testing +{ + /// + /// Implementation of TestService server + /// + public class TestServiceImpl : TestService.ITestService + { + public Task EmptyCall(Empty request, ServerCallContext context) + { + return Task.FromResult(new Empty()); + } + + public Task UnaryCall(SimpleRequest request, ServerCallContext context) + { + var response = new SimpleResponse { Payload = CreateZerosPayload(request.ResponseSize) }; + return Task.FromResult(response); + } + + public async Task StreamingOutputCall(StreamingOutputCallRequest request, IServerStreamWriter responseStream, ServerCallContext context) + { + foreach (var responseParam in request.ResponseParameters) + { + var response = new StreamingOutputCallResponse { Payload = CreateZerosPayload(responseParam.Size) }; + await responseStream.WriteAsync(response); + } + } + + public async Task StreamingInputCall(IAsyncStreamReader requestStream, ServerCallContext context) + { + int sum = 0; + await requestStream.ForEachAsync(async request => + { + sum += request.Payload.Body.Length; + }); + return new StreamingInputCallResponse { AggregatedPayloadSize = sum }; + } + + public async Task FullDuplexCall(IAsyncStreamReader requestStream, IServerStreamWriter responseStream, ServerCallContext context) + { + await requestStream.ForEachAsync(async request => + { + foreach (var responseParam in request.ResponseParameters) + { + var response = new StreamingOutputCallResponse { Payload = CreateZerosPayload(responseParam.Size) }; + await responseStream.WriteAsync(response); + } + }); + } + + public async Task HalfDuplexCall(IAsyncStreamReader requestStream, IServerStreamWriter responseStream, ServerCallContext context) + { + throw new NotImplementedException(); + } + + private static Payload CreateZerosPayload(int size) + { + return new Payload { Body = ByteString.CopyFrom(new byte[size]) }; + } + } +} diff --git a/src/csharp/Grpc.IntegrationTesting/app.config b/src/csharp/Grpc.IntegrationTesting/app.config new file mode 100644 index 00000000..84d7534d --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting/app.config @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/csharp/Grpc.IntegrationTesting/data/README b/src/csharp/Grpc.IntegrationTesting/data/README new file mode 100644 index 00000000..888d95b9 --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting/data/README @@ -0,0 +1 @@ +CONFIRMEDTESTKEY diff --git a/src/csharp/Grpc.IntegrationTesting/data/ca.pem b/src/csharp/Grpc.IntegrationTesting/data/ca.pem new file mode 100644 index 00000000..6c8511a7 --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting/data/ca.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICSjCCAbOgAwIBAgIJAJHGGR4dGioHMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQxDzANBgNVBAMTBnRlc3RjYTAeFw0xNDExMTEyMjMxMjla +Fw0yNDExMDgyMjMxMjlaMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0 +YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMT +BnRlc3RjYTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwEDfBV5MYdlHVHJ7 ++L4nxrZy7mBfAVXpOc5vMYztssUI7mL2/iYujiIXM+weZYNTEpLdjyJdu7R5gGUu +g1jSVK/EPHfc74O7AyZU34PNIP4Sh33N+/A5YexrNgJlPY+E3GdVYi4ldWJjgkAd +Qah2PH5ACLrIIC6tRka9hcaBlIECAwEAAaMgMB4wDAYDVR0TBAUwAwEB/zAOBgNV +HQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQELBQADgYEAHzC7jdYlzAVmddi/gdAeKPau +sPBG/C2HCWqHzpCUHcKuvMzDVkY/MP2o6JIW2DBbY64bO/FceExhjcykgaYtCH/m +oIU63+CFOTtR7otyQAWHqXa7q4SbCDlG7DyRFxqG0txPtGvy12lgldA2+RgcigQG +Dfcog5wrJytaQ6UA0wE= +-----END CERTIFICATE----- diff --git a/src/csharp/Grpc.IntegrationTesting/data/server1.key b/src/csharp/Grpc.IntegrationTesting/data/server1.key new file mode 100644 index 00000000..143a5b87 --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting/data/server1.key @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAOHDFScoLCVJpYDD +M4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1BgzkWF+slf +3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd9N8YwbBY +AckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAECgYAn7qGnM2vbjJNBm0VZCkOkTIWm +V10okw7EPJrdL2mkre9NasghNXbE1y5zDshx5Nt3KsazKOxTT8d0Jwh/3KbaN+YY +tTCbKGW0pXDRBhwUHRcuRzScjli8Rih5UOCiZkhefUTcRb6xIhZJuQy71tjaSy0p +dHZRmYyBYO2YEQ8xoQJBAPrJPhMBkzmEYFtyIEqAxQ/o/A6E+E4w8i+KM7nQCK7q +K4JXzyXVAjLfyBZWHGM2uro/fjqPggGD6QH1qXCkI4MCQQDmdKeb2TrKRh5BY1LR +81aJGKcJ2XbcDu6wMZK4oqWbTX2KiYn9GB0woM6nSr/Y6iy1u145YzYxEV/iMwff +DJULAkB8B2MnyzOg0pNFJqBJuH29bKCcHa8gHJzqXhNO5lAlEbMK95p/P2Wi+4Hd +aiEIAF1BF326QJcvYKmwSmrORp85AkAlSNxRJ50OWrfMZnBgzVjDx3xG6KsFQVk2 +ol6VhqL6dFgKUORFUWBvnKSyhjJxurlPEahV6oo6+A+mPhFY8eUvAkAZQyTdupP3 +XEFQKctGz+9+gKkemDp7LBBMEMBXrGTLPhpEfcjv/7KPdnFHYmhYeBTBnuVmTVWe +F98XJ7tIFfJq +-----END PRIVATE KEY----- diff --git a/src/csharp/Grpc.IntegrationTesting/data/server1.pem b/src/csharp/Grpc.IntegrationTesting/data/server1.pem new file mode 100644 index 00000000..8e582e57 --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting/data/server1.pem @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICmzCCAgSgAwIBAgIBAzANBgkqhkiG9w0BAQUFADBWMQswCQYDVQQGEwJBVTET +MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ +dHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0Y2EwHhcNMTQwNzIyMDYwMDU3WhcNMjQwNzE5 +MDYwMDU3WjBkMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV +BAcTB0NoaWNhZ28xFDASBgNVBAoTC0dvb2dsZSBJbmMuMRowGAYDVQQDFBEqLnRl +c3QuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA4cMVJygs +JUmlgMMzgdi0h1XoCR7+ww1pop04OMMyy7H/i0PJ2W6Y35+b4CM8QrkYeEafUGDO +RYX6yV/cHGGsD/x02ye6ey1UDtkGAD/mpDEx8YCrjAc1Vfvt8Fk6Cn1WVIxV/J30 +3xjBsFgByQ55RBp1OLZfVLo6AleBDSbcxaECAwEAAaNrMGkwCQYDVR0TBAIwADAL +BgNVHQ8EBAMCBeAwTwYDVR0RBEgwRoIQKi50ZXN0Lmdvb2dsZS5mcoIYd2F0ZXJ6 +b29pLnRlc3QuZ29vZ2xlLmJlghIqLnRlc3QueW91dHViZS5jb22HBMCoAQMwDQYJ +KoZIhvcNAQEFBQADgYEAM2Ii0LgTGbJ1j4oqX9bxVcxm+/R5Yf8oi0aZqTJlnLYS +wXcBykxTx181s7WyfJ49WwrYXo78zTDAnf1ma0fPq3e4mpspvyndLh1a+OarHa1e +aT0DIIYk7qeEa1YcVljx2KyLd0r1BBAfrwyGaEPVeJQVYWaOJRU2we/KD4ojf9s= +-----END CERTIFICATE----- diff --git a/src/csharp/Grpc.IntegrationTesting/packages.config b/src/csharp/Grpc.IntegrationTesting/packages.config new file mode 100644 index 00000000..bdb3dadf --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting/packages.config @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/csharp/Grpc.IntegrationTesting/proto/empty.proto b/src/csharp/Grpc.IntegrationTesting/proto/empty.proto new file mode 100644 index 00000000..6d0eb937 --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting/proto/empty.proto @@ -0,0 +1,43 @@ + +// Copyright 2015, 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. + +syntax = "proto3"; + +package grpc.testing; + +// An empty message that you can re-use to avoid defining duplicated empty +// messages in your project. A typical example is to use it as argument or the +// return value of a service API. For instance: +// +// service Foo { +// rpc Bar (grpc.testing.Empty) returns (grpc.testing.Empty) { }; +// }; +// +message Empty {} diff --git a/src/csharp/Grpc.IntegrationTesting/proto/messages.proto b/src/csharp/Grpc.IntegrationTesting/proto/messages.proto new file mode 100644 index 00000000..7df85e3c --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting/proto/messages.proto @@ -0,0 +1,132 @@ + +// Copyright 2015, 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. + +// Message definitions to be used by integration test service definitions. + +syntax = "proto3"; + +package grpc.testing; + +// The type of payload that should be returned. +enum PayloadType { + // Compressable text format. + COMPRESSABLE = 0; + + // Uncompressable binary format. + UNCOMPRESSABLE = 1; + + // Randomly chosen from all other formats defined in this enum. + RANDOM = 2; +} + +// A block of data, to simply increase gRPC message size. +message Payload { + // The type of data in body. + PayloadType type = 1; + // Primary contents of payload. + bytes body = 2; +} + +// Unary request. +message SimpleRequest { + // Desired payload type in the response from the server. + // If response_type is RANDOM, server randomly chooses one from other formats. + PayloadType response_type = 1; + + // Desired payload size in the response from the server. + // If response_type is COMPRESSABLE, this denotes the size before compression. + int32 response_size = 2; + + // Optional input payload sent along with the request. + Payload payload = 3; + + // Whether SimpleResponse should include username. + bool fill_username = 4; + + // Whether SimpleResponse should include OAuth scope. + bool fill_oauth_scope = 5; +} + +// Unary response, as configured by the request. +message SimpleResponse { + // Payload to increase message size. + Payload payload = 1; + // The user the request came from, for verifying authentication was + // successful when the client expected it. + string username = 2; + // OAuth scope. + string oauth_scope = 3; +} + +// Client-streaming request. +message StreamingInputCallRequest { + // Optional input payload sent along with the request. + Payload payload = 1; + + // Not expecting any payload from the response. +} + +// Client-streaming response. +message StreamingInputCallResponse { + // Aggregated size of payloads received from the client. + int32 aggregated_payload_size = 1; +} + +// Configuration for a particular response. +message ResponseParameters { + // Desired payload sizes in responses from the server. + // If response_type is COMPRESSABLE, this denotes the size before compression. + int32 size = 1; + + // Desired interval between consecutive responses in the response stream in + // microseconds. + int32 interval_us = 2; +} + +// Server-streaming request. +message StreamingOutputCallRequest { + // Desired payload type in the response from the server. + // If response_type is RANDOM, the payload from each response in the stream + // might be of different types. This is to simulate a mixed type of payload + // stream. + PayloadType response_type = 1; + + // Configuration for each expected response message. + repeated ResponseParameters response_parameters = 2; + + // Optional input payload sent along with the request. + Payload payload = 3; +} + +// Server-streaming response, as configured by the request and parameters. +message StreamingOutputCallResponse { + // Payload to increase response size. + Payload payload = 1; +} diff --git a/src/csharp/Grpc.IntegrationTesting/proto/test.proto b/src/csharp/Grpc.IntegrationTesting/proto/test.proto new file mode 100644 index 00000000..f9e0d2a0 --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting/proto/test.proto @@ -0,0 +1,72 @@ + +// Copyright 2015, 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. + +// An integration test service that covers all the method signature permutations +// of unary/streaming requests/responses. +syntax = "proto3"; + +import "empty.proto"; +import "messages.proto"; + +package grpc.testing; + +// A simple service to test the various types of RPCs and experiment with +// performance with various types of payload. +service TestService { + // One empty request followed by one empty response. + rpc EmptyCall(grpc.testing.Empty) returns (grpc.testing.Empty); + + // One request followed by one response. + // TODO(Issue 527): Describe required server behavior. + rpc UnaryCall(SimpleRequest) returns (SimpleResponse); + + // One request followed by a sequence of responses (streamed download). + // The server returns the payload with client desired type and sizes. + rpc StreamingOutputCall(StreamingOutputCallRequest) + returns (stream StreamingOutputCallResponse); + + // A sequence of requests followed by one response (streamed upload). + // The server returns the aggregated size of client payload as the result. + rpc StreamingInputCall(stream StreamingInputCallRequest) + returns (StreamingInputCallResponse); + + // A sequence of requests with each request served by the server immediately. + // As one request could lead to multiple responses, this interface + // demonstrates the idea of full duplexing. + rpc FullDuplexCall(stream StreamingOutputCallRequest) + returns (stream StreamingOutputCallResponse); + + // A sequence of requests followed by a sequence of responses. + // The server buffers all the client requests and then serves them in order. A + // stream of responses are returned to the client when the server starts with + // first request. + rpc HalfDuplexCall(stream StreamingOutputCallRequest) + returns (stream StreamingOutputCallResponse); +} diff --git a/src/csharp/Grpc.Tools.nuspec b/src/csharp/Grpc.Tools.nuspec new file mode 100644 index 00000000..48a7b1f3 --- /dev/null +++ b/src/csharp/Grpc.Tools.nuspec @@ -0,0 +1,21 @@ + + + + Grpc.Tools + gRPC C# Tools + Tools for C# implementation of gRPC - an RPC library and framework + Precompiled Windows binary for generating gRPC client/server code + $version$ + Google Inc. + grpc-packages + https://github.com/grpc/grpc/blob/master/LICENSE + https://github.com/grpc/grpc + false + grpc_csharp_plugin.exe - gRPC C# protoc plugin version $version$ + Copyright 2015, Google Inc. + gRPC RPC Protocol HTTP/2 + + + + + diff --git a/src/csharp/Grpc.nuspec b/src/csharp/Grpc.nuspec new file mode 100644 index 00000000..7fbd8619 --- /dev/null +++ b/src/csharp/Grpc.nuspec @@ -0,0 +1,22 @@ + + + + Grpc + gRPC C# + C# implementation of gRPC - an RPC library and framework + C# implementation of gRPC - an RPC library and framework. See project site for more info. + $version$ + Google Inc. + grpc-packages + https://github.com/grpc/grpc/blob/master/LICENSE + https://github.com/grpc/grpc + false + Release $version$ of gRPC C# + Copyright 2015, Google Inc. + gRPC RPC Protocol HTTP/2 + + + + + + diff --git a/src/csharp/Grpc.sln b/src/csharp/Grpc.sln new file mode 100644 index 00000000..f19f29c6 --- /dev/null +++ b/src/csharp/Grpc.sln @@ -0,0 +1,118 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.31101.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grpc.Examples", "Grpc.Examples\Grpc.Examples.csproj", "{7DC1433E-3225-42C7-B7EA-546D56E27A4B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grpc.Core", "Grpc.Core\Grpc.Core.csproj", "{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grpc.Core.Tests", "Grpc.Core.Tests\Grpc.Core.Tests.csproj", "{86EC5CB4-4EA2-40A2-8057-86542A0353BB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grpc.Examples.Tests", "Grpc.Examples.Tests\Grpc.Examples.Tests.csproj", "{143B1C29-C442-4BE0-BF3F-A8F92288AC9F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grpc.Examples.MathClient", "Grpc.Examples.MathClient\Grpc.Examples.MathClient.csproj", "{61ECB8EE-0C96-4F8E-B187-8E4D227417C0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grpc.IntegrationTesting", "Grpc.IntegrationTesting\Grpc.IntegrationTesting.csproj", "{C61154BA-DD4A-4838-8420-0162A28925E0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grpc.IntegrationTesting.Client", "Grpc.IntegrationTesting.Client\Grpc.IntegrationTesting.Client.csproj", "{3D166931-BA2D-416E-95A3-D36E8F6E90B9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grpc.IntegrationTesting.Server", "Grpc.IntegrationTesting.Server\Grpc.IntegrationTesting.Server.csproj", "{A654F3B8-E859-4E6A-B30D-227527DBEF0D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grpc.Examples.MathServer", "Grpc.Examples.MathServer\Grpc.Examples.MathServer.csproj", "{BF62FE08-373A-43D6-9D73-41CAA38B7011}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grpc.Auth", "Grpc.Auth\Grpc.Auth.csproj", "{AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{B5B87121-35FE-49D1-8CB1-8A91AAA398A9}" + ProjectSection(SolutionItems) = preProject + .nuget\packages.config = .nuget\packages.config + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grpc.HealthCheck", "Grpc.HealthCheck\Grpc.HealthCheck.csproj", "{AA5E328A-8835-49D7-98ED-C29F2B3049F0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grpc.HealthCheck.Tests", "Grpc.HealthCheck.Tests\Grpc.HealthCheck.Tests.csproj", "{F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + ReleaseSigned|Any CPU = ReleaseSigned|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Release|Any CPU.Build.0 = Release|Any CPU + {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU + {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU + {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Release|Any CPU.Build.0 = Release|Any CPU + {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU + {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU + {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Release|Any CPU.Build.0 = Release|Any CPU + {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU + {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU + {143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.Release|Any CPU.Build.0 = Release|Any CPU + {143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU + {143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU + {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Release|Any CPU.Build.0 = Release|Any CPU + {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU + {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU + {C61154BA-DD4A-4838-8420-0162A28925E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C61154BA-DD4A-4838-8420-0162A28925E0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C61154BA-DD4A-4838-8420-0162A28925E0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C61154BA-DD4A-4838-8420-0162A28925E0}.Release|Any CPU.Build.0 = Release|Any CPU + {C61154BA-DD4A-4838-8420-0162A28925E0}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU + {C61154BA-DD4A-4838-8420-0162A28925E0}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU + {3D166931-BA2D-416E-95A3-D36E8F6E90B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3D166931-BA2D-416E-95A3-D36E8F6E90B9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3D166931-BA2D-416E-95A3-D36E8F6E90B9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3D166931-BA2D-416E-95A3-D36E8F6E90B9}.Release|Any CPU.Build.0 = Release|Any CPU + {3D166931-BA2D-416E-95A3-D36E8F6E90B9}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU + {3D166931-BA2D-416E-95A3-D36E8F6E90B9}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU + {A654F3B8-E859-4E6A-B30D-227527DBEF0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A654F3B8-E859-4E6A-B30D-227527DBEF0D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A654F3B8-E859-4E6A-B30D-227527DBEF0D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A654F3B8-E859-4E6A-B30D-227527DBEF0D}.Release|Any CPU.Build.0 = Release|Any CPU + {A654F3B8-E859-4E6A-B30D-227527DBEF0D}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU + {A654F3B8-E859-4E6A-B30D-227527DBEF0D}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU + {BF62FE08-373A-43D6-9D73-41CAA38B7011}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BF62FE08-373A-43D6-9D73-41CAA38B7011}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BF62FE08-373A-43D6-9D73-41CAA38B7011}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BF62FE08-373A-43D6-9D73-41CAA38B7011}.Release|Any CPU.Build.0 = Release|Any CPU + {BF62FE08-373A-43D6-9D73-41CAA38B7011}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU + {BF62FE08-373A-43D6-9D73-41CAA38B7011}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU + {AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.Release|Any CPU.Build.0 = Release|Any CPU + {AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU + {AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU + {AA5E328A-8835-49D7-98ED-C29F2B3049F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AA5E328A-8835-49D7-98ED-C29F2B3049F0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AA5E328A-8835-49D7-98ED-C29F2B3049F0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AA5E328A-8835-49D7-98ED-C29F2B3049F0}.Release|Any CPU.Build.0 = Release|Any CPU + {AA5E328A-8835-49D7-98ED-C29F2B3049F0}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU + {AA5E328A-8835-49D7-98ED-C29F2B3049F0}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU + {F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.Release|Any CPU.Build.0 = Release|Any CPU + {F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU + {F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/src/csharp/README.md b/src/csharp/README.md new file mode 100644 index 00000000..3fbc1c5f --- /dev/null +++ b/src/csharp/README.md @@ -0,0 +1,177 @@ +gRPC C# +======= + +A C# implementation of gRPC. + +Status +------ + +Alpha : Ready for early adopters. + +Usage: Windows +-------------- + +- Prerequisites: .NET Framework 4.5+, Visual Studio 2013 with NuGet extension installed (VS2015 should work). + +- Open Visual Studio and start a new project/solution. + +- Add NuGet package `Grpc` as a dependency (Project options -> Manage NuGet Packages). + That will also pull all the transitive dependencies (including the native libraries that + gRPC C# is internally using). + +- Helloworld project example can be found in https://github.com/grpc/grpc/tree/master/examples/csharp. + +Usage: Linux (Mono) +-------------- + +- Prerequisites: Mono 3.2.8+, MonoDevelop 5.9 with NuGet add-in installed. + +- Install Linuxbrew and gRPC C Core using instructions in https://github.com/grpc/homebrew-grpc + +- gRPC C# depends on native shared library libgrpc_csharp_ext.so (Unix flavor of grpc_csharp_ext.dll). + This library will be installed to `~/.linuxbrew/lib` by the previous step. + To make it visible to mono, you need to: + + - (preferred approach) add `libgrpc_csharp_ext.so` to `/etc/ld.so.cache` by running: + + ```sh + $ echo "$HOME/.linuxbrew/lib" | sudo tee /etc/ld.so.conf.d/zzz_brew_lib.conf + $ sudo ldconfig + ``` + + - (adhoc approach) set `LD_LIBRARY_PATH` environment variable to point to directory containing `libgrpc_csharp_ext.so`: + + ```sh + $ export LD_LIBRARY_PATH=$HOME/.linuxbrew/lib:${LD_LIBRARY_PATH} + ``` + - (if you are contributor) installing gRPC from sources using `sudo make install_grpc_csharp_ext` also works. + +- Open MonoDevelop and start a new project/solution. + +- Add NuGet package `Grpc` as a dependency (Project -> Add NuGet packages). + +- Helloworld project example can be found in https://github.com/grpc/grpc/tree/master/examples/csharp. + +Usage: MacOS (Mono) +-------------- + +- WARNING: As of now gRPC C# only works on 64bit version of Mono (because we don't compile + the native extension for C# in 32bit mode yet). That means your development experience + with Xamarin Studio on MacOS will not be great, as you won't be able to run your + code directly from Xamarin Studio (which requires 32bit version of Mono). + +- Prerequisites: Xamarin Studio with NuGet add-in installed. + +- Install Homebrew and gRPC C Core using instructions in https://github.com/grpc/homebrew-grpc + +- Install 64-bit version of mono with command `brew install mono` (assumes you've already installed Homebrew). + +- Open Xamarin Studio and start a new project/solution. + +- Add NuGet package `Grpc` as a dependency (Project -> Add NuGet packages). + +- *You will be able to build your project in Xamarin Studio, but to run or test it, + you will need to run it under 64-bit version of Mono.* + +- Helloworld project example can be found in https://github.com/grpc/grpc/tree/master/examples/csharp. + +Building: Windows +----------------- + +You only need to go through these steps if you are planning to develop gRPC C#. +If you are a user of gRPC C#, go to Usage section above. + +- Prerequisites for development: NET Framework 4.5+, Visual Studio 2013 (with NuGet and NUnit extensions installed). + +- The grpc_csharp_ext native library needs to be built so you can build the Grpc C# solution. You can + either build the native solution in `vsprojects/grpc.sln` from Visual Studio manually, or you can use + a convenience batch script that builds everything for you. + + ``` + > buildall.bat + ``` + +- Open Grpc.sln using Visual Studio 2013. NuGet dependencies will be restored + upon build (you need to have NuGet add-in installed). + + +Building: Linux (Mono) +---------------------- + +You only need to go through these steps if you are planning to develop gRPC C#. +If you are a user of gRPC C#, go to Usage section above. + +- Prerequisites for development: Mono 3.2.8+, MonoDevelop 5.9 with NuGet and NUnit add-ins installed. + + ```sh + $ sudo apt-get install mono-devel + $ sudo apt-get install nunit nunit-console + ``` + +You can use older versions of MonoDevelop, but then you might need to restore +NuGet dependencies manually (by `nuget restore`), because older versions of MonoDevelop +don't support NuGet add-in. + +- Compile and install the gRPC C# extension library (that will be used via + P/Invoke from C#). + ```sh + $ make grpc_csharp_ext + $ sudo make install_grpc_csharp_ext + ``` + +- Use MonoDevelop to open the solution Grpc.sln + +- Build the solution & run all the tests from test view. + +Tests +----- + +gRPC C# is using NUnit as the testing framework. + +Under Visual Studio, make sure NUnit test adapter is installed (under "Extensions and Updates"). +Then you should be able to run all the tests using Test Explorer. + +Under Monodevelop, make sure you installed "NUnit support" in Add-in manager. +Then you should be able to run all the test from the Test View. + +After building the solution, you can also run the tests from command line +using nunit-console tool. +```sh +# from Grpc.Core.Test/bin/Debug directory +$ nunit-console Grpc.Core.Tests.dll +``` + +Contents +-------- + +- ext: + The extension library that wraps C API to be more digestible by C#. +- Grpc.Auth: + gRPC OAuth2 support. +- Grpc.Core: + The main gRPC C# library. +- Grpc.Examples: + API examples for math.proto +- Grpc.Examples.MathClient: + An example client that sends some requests to math server. +- Grpc.Examples.MathServer: + An example client that sends some requests to math server. +- Grpc.IntegrationTesting: + Cross-language gRPC implementation testing (interop testing). + +Troubleshooting +--------------- + +### Problem: Unable to load DLL 'grpc_csharp_ext.dll' + +Internally, gRPC C# uses a native library written in C (gRPC C core) and invokes its functionality via P/Invoke. `grpc_csharp_ext` library is a native extension library that facilitates this by wrapping some C core API into a form that's more digestible for P/Invoke. If you get the above error, it means that the native dependencies could not be located by the C# runtime (or they are incompatible with the current runtime, so they could not be loaded). The solution to this is environment specific. + +- If you are developing on Windows in Visual Studio, the `grpc_csharp_ext.dll` that is shipped by gRPC nuget packages should be automatically copied to your build destination folder once you build. By adjusting project properties in your VS project file, you can influence which exact configuration of `grpc_csharp_ext.dll` will be used (based on VS version, bitness, debug/release configuration). + +- If you are running your application that is using gRPC on Windows machine that doesn't have Visual Studio installed, you might need to install [Visual C++ 2013 redistributable](https://www.microsoft.com/en-us/download/details.aspx?id=40784) that contains some system .dll libraries that `grpc_csharp_ext.dll` depends on (see #905 for more details). + +- On Linux (or Docker), you need to first install gRPC C core and `libgrpc_csharp_ext.so` shared libraries. Currently, the libraries can be installed by `make install_grpc_csharp_ext` or using Linuxbrew (a Debian package is coming soon). Installation on a machine where your application is going to be deployed is no different. + +- On Mac, you need to first install gRPC C core and `libgrpc_csharp_ext.dylib` shared libraries using Homebrew. See above for installation instruction. Installation on a machine where your application is going to be deployed is no different. + +- Possible cause for the problem is that the `grpc_csharp_ext` library is installed, but it has different bitness (32/64bit) than your C# runtime (in case you are using mono) or C# application. diff --git a/src/csharp/Settings.StyleCop b/src/csharp/Settings.StyleCop new file mode 100644 index 00000000..2ecf22f6 --- /dev/null +++ b/src/csharp/Settings.StyleCop @@ -0,0 +1,509 @@ + + + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + + + + + + False + + + + + False + + + + + False + + + + + False + + + + + + + \ No newline at end of file diff --git a/src/csharp/build_packages.bat b/src/csharp/build_packages.bat new file mode 100644 index 00000000..a3505b1e --- /dev/null +++ b/src/csharp/build_packages.bat @@ -0,0 +1,34 @@ +@rem Builds gRPC NuGet packages + +@rem Current package versions +set VERSION=0.7.1 +set CORE_VERSION=0.11.1 +set PROTOBUF_VERSION=3.0.0-alpha4 + +@rem Packages that depend on prerelease packages (like Google.Protobuf) need to have prerelease suffix as well. +set VERSION_WITH_BETA=%VERSION%-beta + +@rem Adjust the location of nuget.exe +set NUGET=C:\nuget\nuget.exe + +setlocal +cd ..\..\vsprojects\nuget_package +@call buildall.bat || goto :error +endlocal + +@call buildall.bat BUILD_SIGNED || goto :error + +@call ..\..\vsprojects\build_plugins.bat || goto :error + +%NUGET% pack ..\..\vsprojects\nuget_package\grpc.native.csharp_ext.nuspec -Version %CORE_VERSION% || goto :error +%NUGET% pack Grpc.Auth\Grpc.Auth.nuspec -Symbols -Version %VERSION% || goto :error +%NUGET% pack Grpc.Core\Grpc.Core.nuspec -Symbols -Version %VERSION% -Properties GrpcNativeCsharpExtVersion=%CORE_VERSION% || goto :error +%NUGET% pack Grpc.HealthCheck\Grpc.HealthCheck.nuspec -Symbols -Version %VERSION_WITH_BETA% -Properties ProtobufVersion=%PROTOBUF_VERSION% || goto :error +%NUGET% pack Grpc.Tools.nuspec -Version %VERSION% || goto :error +%NUGET% pack Grpc.nuspec -Version %VERSION% || goto :error + +goto :EOF + +:error +echo Failed! +exit /b %errorlevel% diff --git a/src/csharp/buildall.bat b/src/csharp/buildall.bat new file mode 100644 index 00000000..d85896c2 --- /dev/null +++ b/src/csharp/buildall.bat @@ -0,0 +1,27 @@ +@rem Convenience script to build gRPC C# from command line + +setlocal + +@rem enter this directory +cd /d %~dp0 + +@rem Set VS variables (uses Visual Studio 2013) +@call "%VS120COMNTOOLS%\..\..\vc\vcvarsall.bat" x86 + +@rem Build the C# native extension +msbuild ..\..\vsprojects\grpc_csharp_ext.sln /p:PlatformToolset=v120 || goto :error + +msbuild Grpc.sln /p:Configuration=Debug || goto :error +msbuild Grpc.sln /p:Configuration=Release || goto :error + +if "%1" == "BUILD_SIGNED" ( +msbuild Grpc.sln /p:Configuration=ReleaseSigned || goto :error +) + +endlocal + +goto :EOF + +:error +echo Failed! +exit /b %errorlevel% diff --git a/src/csharp/doc/README.md b/src/csharp/doc/README.md new file mode 100644 index 00000000..585500b5 --- /dev/null +++ b/src/csharp/doc/README.md @@ -0,0 +1,2 @@ + +SandCastle project files to generate HTML reference documentation. \ No newline at end of file diff --git a/src/csharp/doc/grpc_csharp_public.shfbproj b/src/csharp/doc/grpc_csharp_public.shfbproj new file mode 100644 index 00000000..d9b97498 --- /dev/null +++ b/src/csharp/doc/grpc_csharp_public.shfbproj @@ -0,0 +1,80 @@ + + + + + Debug + AnyCPU + 2.0 + {77e3da09-fc92-486f-a90a-99ca788e8b59} + 2015.6.5.0 + + Documentation + Documentation + Documentation + + .NET Framework 4.5 + ..\..\..\doc\ref\csharp\html + en-US + + + + + OnlyWarningsAndErrors + Website + False + True + False + True + 1.0.0.0 + 2 + False + Standard + Blank + True + VS2013 + False + MemberName + gRPC C# + AboveNamespaces + Documentation + + Provides OAuth2 based authentication for gRPC. <c>Grpc.Auth</c> currently consists of a set of very lightweight wrappers and uses C# <a href="https://www.nuget.org/packages/Google.Apis.Auth/">Google.Apis.Auth</a> library. +Main namespace for gRPC C# functionality. Contains concepts representing both client side and server side gRPC logic. + +<seealso cref="Grpc.Core.Channel"/> +<seealso cref="Grpc.Core.Server"/> +Provides functionality to redirect gRPC logs to application-specified destination. +Various utilities for gRPC C#. + Summary, Parameter, AutoDocumentCtors, Namespace, TypeParameter, AutoDocumentDispose + + + + + + + + + + + + + + + + + + + + + + + + + + + OnBuildSuccess + + \ No newline at end of file diff --git a/src/csharp/ext/grpc_csharp_ext.c b/src/csharp/ext/grpc_csharp_ext.c new file mode 100644 index 00000000..70c0fbcc --- /dev/null +++ b/src/csharp/ext/grpc_csharp_ext.c @@ -0,0 +1,934 @@ +/* + * + * Copyright 2015, 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 "src/core/support/string.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef GPR_WIN32 +#define GPR_EXPORT __declspec(dllexport) +#define GPR_CALLTYPE __stdcall +#endif + +#ifndef GPR_EXPORT +#define GPR_EXPORT +#endif + +#ifndef GPR_CALLTYPE +#define GPR_CALLTYPE +#endif + +grpc_byte_buffer *string_to_byte_buffer(const char *buffer, size_t len) { + gpr_slice slice = gpr_slice_from_copied_buffer(buffer, len); + grpc_byte_buffer *bb = grpc_raw_byte_buffer_create(&slice, 1); + gpr_slice_unref(slice); + return bb; +} + +/* + * Helper to maintain lifetime of batch op inputs and store batch op outputs. + */ +typedef struct gprcsharp_batch_context { + grpc_metadata_array send_initial_metadata; + grpc_byte_buffer *send_message; + struct { + grpc_metadata_array trailing_metadata; + char *status_details; + } send_status_from_server; + grpc_metadata_array recv_initial_metadata; + grpc_byte_buffer *recv_message; + struct { + grpc_metadata_array trailing_metadata; + grpc_status_code status; + char *status_details; + size_t status_details_capacity; + } recv_status_on_client; + int recv_close_on_server_cancelled; + struct { + grpc_call *call; + grpc_call_details call_details; + grpc_metadata_array request_metadata; + } server_rpc_new; +} grpcsharp_batch_context; + +GPR_EXPORT grpcsharp_batch_context *GPR_CALLTYPE grpcsharp_batch_context_create() { + grpcsharp_batch_context *ctx = gpr_malloc(sizeof(grpcsharp_batch_context)); + memset(ctx, 0, sizeof(grpcsharp_batch_context)); + return ctx; +} + +/* + * Destroys array->metadata. + * The array pointer itself is not freed. + */ +void grpcsharp_metadata_array_destroy_metadata_only( + grpc_metadata_array *array) { + gpr_free(array->metadata); +} + +/* + * Destroys keys, values and array->metadata. + * The array pointer itself is not freed. + */ +void grpcsharp_metadata_array_destroy_metadata_including_entries( + grpc_metadata_array *array) { + size_t i; + if (array->metadata) { + for (i = 0; i < array->count; i++) { + gpr_free((void *)array->metadata[i].key); + gpr_free((void *)array->metadata[i].value); + } + } + gpr_free(array->metadata); +} + +/* + * Fully destroys the metadata array. + */ +GPR_EXPORT void GPR_CALLTYPE +grpcsharp_metadata_array_destroy_full(grpc_metadata_array *array) { + if (!array) { + return; + } + grpcsharp_metadata_array_destroy_metadata_including_entries(array); + gpr_free(array); +} + +/* + * Creates an empty metadata array with given capacity. + * Array can later be destroyed by grpc_metadata_array_destroy_full. + */ +GPR_EXPORT grpc_metadata_array *GPR_CALLTYPE +grpcsharp_metadata_array_create(size_t capacity) { + grpc_metadata_array *array = + (grpc_metadata_array *)gpr_malloc(sizeof(grpc_metadata_array)); + grpc_metadata_array_init(array); + array->capacity = capacity; + array->count = 0; + if (capacity > 0) { + array->metadata = + (grpc_metadata *)gpr_malloc(sizeof(grpc_metadata) * capacity); + memset(array->metadata, 0, sizeof(grpc_metadata) * capacity); + } else { + array->metadata = NULL; + } + return array; +} + +GPR_EXPORT void GPR_CALLTYPE +grpcsharp_metadata_array_add(grpc_metadata_array *array, const char *key, + const char *value, size_t value_length) { + size_t i = array->count; + GPR_ASSERT(array->count < array->capacity); + array->metadata[i].key = gpr_strdup(key); + array->metadata[i].value = (char *)gpr_malloc(value_length); + memcpy((void *)array->metadata[i].value, value, value_length); + array->metadata[i].value_length = value_length; + array->count++; +} + +GPR_EXPORT gpr_intptr GPR_CALLTYPE +grpcsharp_metadata_array_count(grpc_metadata_array *array) { + return (gpr_intptr)array->count; +} + +GPR_EXPORT const char *GPR_CALLTYPE +grpcsharp_metadata_array_get_key(grpc_metadata_array *array, size_t index) { + GPR_ASSERT(index < array->count); + return array->metadata[index].key; +} + +GPR_EXPORT const char *GPR_CALLTYPE +grpcsharp_metadata_array_get_value(grpc_metadata_array *array, size_t index) { + GPR_ASSERT(index < array->count); + return array->metadata[index].value; +} + +GPR_EXPORT gpr_intptr GPR_CALLTYPE grpcsharp_metadata_array_get_value_length( + grpc_metadata_array *array, size_t index) { + GPR_ASSERT(index < array->count); + return (gpr_intptr)array->metadata[index].value_length; +} + +/* Move contents of metadata array */ +void grpcsharp_metadata_array_move(grpc_metadata_array *dest, + grpc_metadata_array *src) { + if (!src) { + dest->capacity = 0; + dest->count = 0; + dest->metadata = NULL; + return; + } + + dest->capacity = src->capacity; + dest->count = src->count; + dest->metadata = src->metadata; + + src->capacity = 0; + src->count = 0; + src->metadata = NULL; +} + +GPR_EXPORT void GPR_CALLTYPE grpcsharp_batch_context_destroy(grpcsharp_batch_context *ctx) { + if (!ctx) { + return; + } + grpcsharp_metadata_array_destroy_metadata_including_entries( + &(ctx->send_initial_metadata)); + + grpc_byte_buffer_destroy(ctx->send_message); + + grpcsharp_metadata_array_destroy_metadata_including_entries( + &(ctx->send_status_from_server.trailing_metadata)); + gpr_free(ctx->send_status_from_server.status_details); + + grpcsharp_metadata_array_destroy_metadata_only(&(ctx->recv_initial_metadata)); + + grpc_byte_buffer_destroy(ctx->recv_message); + + grpcsharp_metadata_array_destroy_metadata_only( + &(ctx->recv_status_on_client.trailing_metadata)); + gpr_free((void *)ctx->recv_status_on_client.status_details); + + /* NOTE: ctx->server_rpc_new.call is not destroyed because callback handler is + supposed + to take its ownership. */ + + grpc_call_details_destroy(&(ctx->server_rpc_new.call_details)); + grpcsharp_metadata_array_destroy_metadata_only( + &(ctx->server_rpc_new.request_metadata)); + + gpr_free(ctx); +} + +GPR_EXPORT const grpc_metadata_array *GPR_CALLTYPE +grpcsharp_batch_context_recv_initial_metadata( + const grpcsharp_batch_context *ctx) { + return &(ctx->recv_initial_metadata); +} + +GPR_EXPORT gpr_intptr GPR_CALLTYPE grpcsharp_batch_context_recv_message_length( + const grpcsharp_batch_context *ctx) { + if (!ctx->recv_message) { + return -1; + } + return grpc_byte_buffer_length(ctx->recv_message); +} + +/* + * Copies data from recv_message to a buffer. Fatal error occurs if + * buffer is too small. + */ +GPR_EXPORT void GPR_CALLTYPE grpcsharp_batch_context_recv_message_to_buffer( + const grpcsharp_batch_context *ctx, char *buffer, size_t buffer_len) { + grpc_byte_buffer_reader reader; + gpr_slice slice; + size_t offset = 0; + + grpc_byte_buffer_reader_init(&reader, ctx->recv_message); + + while (grpc_byte_buffer_reader_next(&reader, &slice)) { + size_t len = GPR_SLICE_LENGTH(slice); + GPR_ASSERT(offset + len <= buffer_len); + memcpy(buffer + offset, GPR_SLICE_START_PTR(slice), + GPR_SLICE_LENGTH(slice)); + offset += len; + gpr_slice_unref(slice); + } +} + +GPR_EXPORT grpc_status_code GPR_CALLTYPE +grpcsharp_batch_context_recv_status_on_client_status( + const grpcsharp_batch_context *ctx) { + return ctx->recv_status_on_client.status; +} + +GPR_EXPORT const char *GPR_CALLTYPE +grpcsharp_batch_context_recv_status_on_client_details( + const grpcsharp_batch_context *ctx) { + return ctx->recv_status_on_client.status_details; +} + +GPR_EXPORT const grpc_metadata_array *GPR_CALLTYPE +grpcsharp_batch_context_recv_status_on_client_trailing_metadata( + const grpcsharp_batch_context *ctx) { + return &(ctx->recv_status_on_client.trailing_metadata); +} + +GPR_EXPORT grpc_call *GPR_CALLTYPE grpcsharp_batch_context_server_rpc_new_call( + const grpcsharp_batch_context *ctx) { + return ctx->server_rpc_new.call; +} + +GPR_EXPORT const char *GPR_CALLTYPE +grpcsharp_batch_context_server_rpc_new_method( + const grpcsharp_batch_context *ctx) { + return ctx->server_rpc_new.call_details.method; +} + +GPR_EXPORT const char *GPR_CALLTYPE grpcsharp_batch_context_server_rpc_new_host( + const grpcsharp_batch_context *ctx) { + return ctx->server_rpc_new.call_details.host; +} + +GPR_EXPORT gpr_timespec GPR_CALLTYPE +grpcsharp_batch_context_server_rpc_new_deadline( + const grpcsharp_batch_context *ctx) { + return ctx->server_rpc_new.call_details.deadline; +} + +GPR_EXPORT const grpc_metadata_array *GPR_CALLTYPE +grpcsharp_batch_context_server_rpc_new_request_metadata( + const grpcsharp_batch_context *ctx) { + return &(ctx->server_rpc_new.request_metadata); +} + +GPR_EXPORT gpr_int32 GPR_CALLTYPE +grpcsharp_batch_context_recv_close_on_server_cancelled( + const grpcsharp_batch_context *ctx) { + return (gpr_int32) ctx->recv_close_on_server_cancelled; +} + +/* Init & shutdown */ + +GPR_EXPORT void GPR_CALLTYPE grpcsharp_init(void) { grpc_init(); } + +GPR_EXPORT void GPR_CALLTYPE grpcsharp_shutdown(void) { grpc_shutdown(); } + +/* Completion queue */ + +GPR_EXPORT grpc_completion_queue *GPR_CALLTYPE +grpcsharp_completion_queue_create(void) { + return grpc_completion_queue_create(NULL); +} + +GPR_EXPORT void GPR_CALLTYPE +grpcsharp_completion_queue_shutdown(grpc_completion_queue *cq) { + grpc_completion_queue_shutdown(cq); +} + +GPR_EXPORT void GPR_CALLTYPE +grpcsharp_completion_queue_destroy(grpc_completion_queue *cq) { + grpc_completion_queue_destroy(cq); +} + +GPR_EXPORT grpc_event GPR_CALLTYPE +grpcsharp_completion_queue_next(grpc_completion_queue *cq) { + return grpc_completion_queue_next(cq, gpr_inf_future(GPR_CLOCK_REALTIME), + NULL); +} + +GPR_EXPORT grpc_event GPR_CALLTYPE +grpcsharp_completion_queue_pluck(grpc_completion_queue *cq, void *tag) { + return grpc_completion_queue_pluck(cq, tag, + gpr_inf_future(GPR_CLOCK_REALTIME), NULL); +} + +/* Channel */ + +GPR_EXPORT grpc_channel *GPR_CALLTYPE + +grpcsharp_insecure_channel_create(const char *target, const grpc_channel_args *args) { + return grpc_insecure_channel_create(target, args, NULL); +} + +GPR_EXPORT void GPR_CALLTYPE grpcsharp_channel_destroy(grpc_channel *channel) { + grpc_channel_destroy(channel); +} + +GPR_EXPORT grpc_call *GPR_CALLTYPE +grpcsharp_channel_create_call(grpc_channel *channel, grpc_call *parent_call, + gpr_uint32 propagation_mask, + grpc_completion_queue *cq, + const char *method, const char *host, + gpr_timespec deadline) { + return grpc_channel_create_call(channel, parent_call, propagation_mask, cq, + method, host, deadline, NULL); +} + +GPR_EXPORT grpc_connectivity_state GPR_CALLTYPE +grpcsharp_channel_check_connectivity_state(grpc_channel *channel, gpr_int32 try_to_connect) { + return grpc_channel_check_connectivity_state(channel, try_to_connect); +} + +GPR_EXPORT void GPR_CALLTYPE grpcsharp_channel_watch_connectivity_state( + grpc_channel *channel, grpc_connectivity_state last_observed_state, + gpr_timespec deadline, grpc_completion_queue *cq, grpcsharp_batch_context *ctx) { + grpc_channel_watch_connectivity_state(channel, last_observed_state, + deadline, cq, ctx); +} + +GPR_EXPORT char *GPR_CALLTYPE grpcsharp_channel_get_target(grpc_channel *channel) { + return grpc_channel_get_target(channel); +} + +/* Channel args */ + +GPR_EXPORT grpc_channel_args *GPR_CALLTYPE +grpcsharp_channel_args_create(size_t num_args) { + grpc_channel_args *args = + (grpc_channel_args *)gpr_malloc(sizeof(grpc_channel_args)); + memset(args, 0, sizeof(grpc_channel_args)); + + args->num_args = num_args; + args->args = (grpc_arg *)gpr_malloc(sizeof(grpc_arg) * num_args); + memset(args->args, 0, sizeof(grpc_arg) * num_args); + return args; +} + +GPR_EXPORT void GPR_CALLTYPE +grpcsharp_channel_args_set_string(grpc_channel_args *args, size_t index, + const char *key, const char *value) { + GPR_ASSERT(args); + GPR_ASSERT(index < args->num_args); + args->args[index].type = GRPC_ARG_STRING; + args->args[index].key = gpr_strdup(key); + args->args[index].value.string = gpr_strdup(value); +} + +GPR_EXPORT void GPR_CALLTYPE +grpcsharp_channel_args_set_integer(grpc_channel_args *args, size_t index, + const char *key, int value) { + GPR_ASSERT(args); + GPR_ASSERT(index < args->num_args); + args->args[index].type = GRPC_ARG_INTEGER; + args->args[index].key = gpr_strdup(key); + args->args[index].value.integer = value; +} + +GPR_EXPORT void GPR_CALLTYPE +grpcsharp_channel_args_destroy(grpc_channel_args *args) { + size_t i; + if (args) { + for (i = 0; i < args->num_args; i++) { + gpr_free(args->args[i].key); + if (args->args[i].type == GRPC_ARG_STRING) { + gpr_free(args->args[i].value.string); + } + } + gpr_free(args->args); + gpr_free(args); + } +} + +/* Timespec */ + +GPR_EXPORT gpr_timespec GPR_CALLTYPE gprsharp_now(gpr_clock_type clock_type) { + return gpr_now(clock_type); +} + +GPR_EXPORT gpr_timespec GPR_CALLTYPE gprsharp_inf_future(gpr_clock_type clock_type) { + return gpr_inf_future(clock_type); +} + +GPR_EXPORT gpr_timespec GPR_CALLTYPE gprsharp_inf_past(gpr_clock_type clock_type) { + return gpr_inf_past(clock_type); +} + +GPR_EXPORT gpr_timespec GPR_CALLTYPE gprsharp_convert_clock_type(gpr_timespec t, gpr_clock_type target_clock) { + return gpr_convert_clock_type(t, target_clock); +} + +GPR_EXPORT gpr_int32 GPR_CALLTYPE gprsharp_sizeof_timespec(void) { + return sizeof(gpr_timespec); +} + +/* Call */ + +GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_cancel(grpc_call *call) { + return grpc_call_cancel(call, NULL); +} + +GPR_EXPORT grpc_call_error GPR_CALLTYPE +grpcsharp_call_cancel_with_status(grpc_call *call, grpc_status_code status, + const char *description) { + return grpc_call_cancel_with_status(call, status, description, NULL); +} + +GPR_EXPORT char *GPR_CALLTYPE grpcsharp_call_get_peer(grpc_call *call) { + return grpc_call_get_peer(call); +} + +GPR_EXPORT void GPR_CALLTYPE gprsharp_free(void *p) { + gpr_free(p); +} + +GPR_EXPORT void GPR_CALLTYPE grpcsharp_call_destroy(grpc_call *call) { + grpc_call_destroy(call); +} + +GPR_EXPORT grpc_call_error GPR_CALLTYPE +grpcsharp_call_start_unary(grpc_call *call, grpcsharp_batch_context *ctx, + const char *send_buffer, size_t send_buffer_len, + grpc_metadata_array *initial_metadata, gpr_uint32 write_flags) { + /* TODO: don't use magic number */ + grpc_op ops[6]; + ops[0].op = GRPC_OP_SEND_INITIAL_METADATA; + grpcsharp_metadata_array_move(&(ctx->send_initial_metadata), + initial_metadata); + ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count; + ops[0].data.send_initial_metadata.metadata = + ctx->send_initial_metadata.metadata; + ops[0].flags = 0; + ops[0].reserved = NULL; + + ops[1].op = GRPC_OP_SEND_MESSAGE; + ctx->send_message = string_to_byte_buffer(send_buffer, send_buffer_len); + ops[1].data.send_message = ctx->send_message; + ops[1].flags = write_flags; + ops[1].reserved = NULL; + + ops[2].op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; + ops[2].flags = 0; + ops[2].reserved = NULL; + + ops[3].op = GRPC_OP_RECV_INITIAL_METADATA; + ops[3].data.recv_initial_metadata = &(ctx->recv_initial_metadata); + ops[3].flags = 0; + ops[3].reserved = NULL; + + ops[4].op = GRPC_OP_RECV_MESSAGE; + ops[4].data.recv_message = &(ctx->recv_message); + ops[4].flags = 0; + ops[4].reserved = NULL; + + ops[5].op = GRPC_OP_RECV_STATUS_ON_CLIENT; + ops[5].data.recv_status_on_client.trailing_metadata = + &(ctx->recv_status_on_client.trailing_metadata); + ops[5].data.recv_status_on_client.status = + &(ctx->recv_status_on_client.status); + /* not using preallocation for status_details */ + ops[5].data.recv_status_on_client.status_details = + &(ctx->recv_status_on_client.status_details); + ops[5].data.recv_status_on_client.status_details_capacity = + &(ctx->recv_status_on_client.status_details_capacity); + ops[5].flags = 0; + ops[5].reserved = NULL; + + return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx, + NULL); +} + +GPR_EXPORT grpc_call_error GPR_CALLTYPE +grpcsharp_call_start_client_streaming(grpc_call *call, + grpcsharp_batch_context *ctx, + grpc_metadata_array *initial_metadata) { + /* TODO: don't use magic number */ + grpc_op ops[4]; + ops[0].op = GRPC_OP_SEND_INITIAL_METADATA; + grpcsharp_metadata_array_move(&(ctx->send_initial_metadata), + initial_metadata); + ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count; + ops[0].data.send_initial_metadata.metadata = + ctx->send_initial_metadata.metadata; + ops[0].flags = 0; + ops[0].reserved = NULL; + + ops[1].op = GRPC_OP_RECV_INITIAL_METADATA; + ops[1].data.recv_initial_metadata = &(ctx->recv_initial_metadata); + ops[1].flags = 0; + ops[1].reserved = NULL; + + ops[2].op = GRPC_OP_RECV_MESSAGE; + ops[2].data.recv_message = &(ctx->recv_message); + ops[2].flags = 0; + ops[2].reserved = NULL; + + ops[3].op = GRPC_OP_RECV_STATUS_ON_CLIENT; + ops[3].data.recv_status_on_client.trailing_metadata = + &(ctx->recv_status_on_client.trailing_metadata); + ops[3].data.recv_status_on_client.status = + &(ctx->recv_status_on_client.status); + /* not using preallocation for status_details */ + ops[3].data.recv_status_on_client.status_details = + &(ctx->recv_status_on_client.status_details); + ops[3].data.recv_status_on_client.status_details_capacity = + &(ctx->recv_status_on_client.status_details_capacity); + ops[3].flags = 0; + ops[3].reserved = NULL; + + return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx, + NULL); +} + +GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_start_server_streaming( + grpc_call *call, grpcsharp_batch_context *ctx, const char *send_buffer, + size_t send_buffer_len, grpc_metadata_array *initial_metadata, gpr_uint32 write_flags) { + /* TODO: don't use magic number */ + grpc_op ops[4]; + ops[0].op = GRPC_OP_SEND_INITIAL_METADATA; + grpcsharp_metadata_array_move(&(ctx->send_initial_metadata), + initial_metadata); + ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count; + ops[0].data.send_initial_metadata.metadata = + ctx->send_initial_metadata.metadata; + ops[0].flags = 0; + ops[0].reserved = NULL; + + ops[1].op = GRPC_OP_SEND_MESSAGE; + ctx->send_message = string_to_byte_buffer(send_buffer, send_buffer_len); + ops[1].data.send_message = ctx->send_message; + ops[1].flags = write_flags; + ops[1].reserved = NULL; + + ops[2].op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; + ops[2].flags = 0; + ops[2].reserved = NULL; + + ops[3].op = GRPC_OP_RECV_STATUS_ON_CLIENT; + ops[3].data.recv_status_on_client.trailing_metadata = + &(ctx->recv_status_on_client.trailing_metadata); + ops[3].data.recv_status_on_client.status = + &(ctx->recv_status_on_client.status); + /* not using preallocation for status_details */ + ops[3].data.recv_status_on_client.status_details = + &(ctx->recv_status_on_client.status_details); + ops[3].data.recv_status_on_client.status_details_capacity = + &(ctx->recv_status_on_client.status_details_capacity); + ops[3].flags = 0; + ops[3].reserved = NULL; + + return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx, + NULL); +} + +GPR_EXPORT grpc_call_error GPR_CALLTYPE +grpcsharp_call_start_duplex_streaming(grpc_call *call, + grpcsharp_batch_context *ctx, + grpc_metadata_array *initial_metadata) { + /* TODO: don't use magic number */ + grpc_op ops[2]; + ops[0].op = GRPC_OP_SEND_INITIAL_METADATA; + grpcsharp_metadata_array_move(&(ctx->send_initial_metadata), + initial_metadata); + ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count; + ops[0].data.send_initial_metadata.metadata = + ctx->send_initial_metadata.metadata; + ops[0].flags = 0; + ops[0].reserved = NULL; + + ops[1].op = GRPC_OP_RECV_STATUS_ON_CLIENT; + ops[1].data.recv_status_on_client.trailing_metadata = + &(ctx->recv_status_on_client.trailing_metadata); + ops[1].data.recv_status_on_client.status = + &(ctx->recv_status_on_client.status); + /* not using preallocation for status_details */ + ops[1].data.recv_status_on_client.status_details = + &(ctx->recv_status_on_client.status_details); + ops[1].data.recv_status_on_client.status_details_capacity = + &(ctx->recv_status_on_client.status_details_capacity); + ops[1].flags = 0; + ops[1].reserved = NULL; + + return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx, + NULL); +} + +GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_recv_initial_metadata( + grpc_call *call, grpcsharp_batch_context *ctx) { + /* TODO: don't use magic number */ + grpc_op ops[1]; + ops[0].op = GRPC_OP_RECV_INITIAL_METADATA; + ops[0].data.recv_initial_metadata = &(ctx->recv_initial_metadata); + ops[0].flags = 0; + ops[0].reserved = NULL; + + return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx, + NULL); +} + +GPR_EXPORT grpc_call_error GPR_CALLTYPE +grpcsharp_call_send_message(grpc_call *call, grpcsharp_batch_context *ctx, + const char *send_buffer, size_t send_buffer_len, + gpr_uint32 write_flags, + gpr_int32 send_empty_initial_metadata) { + /* TODO: don't use magic number */ + grpc_op ops[2]; + size_t nops = send_empty_initial_metadata ? 2 : 1; + ops[0].op = GRPC_OP_SEND_MESSAGE; + ctx->send_message = string_to_byte_buffer(send_buffer, send_buffer_len); + ops[0].data.send_message = ctx->send_message; + ops[0].flags = write_flags; + ops[0].reserved = NULL; + ops[1].op = GRPC_OP_SEND_INITIAL_METADATA; + ops[1].data.send_initial_metadata.count = 0; + ops[1].data.send_initial_metadata.metadata = NULL; + ops[1].flags = 0; + ops[1].reserved = NULL; + + return grpc_call_start_batch(call, ops, nops, ctx, NULL); +} + +GPR_EXPORT grpc_call_error GPR_CALLTYPE +grpcsharp_call_send_close_from_client(grpc_call *call, + grpcsharp_batch_context *ctx) { + /* TODO: don't use magic number */ + grpc_op ops[1]; + ops[0].op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; + ops[0].flags = 0; + ops[0].reserved = NULL; + + return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx, + NULL); +} + +GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_send_status_from_server( + grpc_call *call, grpcsharp_batch_context *ctx, grpc_status_code status_code, + const char *status_details, grpc_metadata_array *trailing_metadata, + gpr_int32 send_empty_initial_metadata) { + /* TODO: don't use magic number */ + grpc_op ops[2]; + size_t nops = send_empty_initial_metadata ? 2 : 1; + ops[0].op = GRPC_OP_SEND_STATUS_FROM_SERVER; + ops[0].data.send_status_from_server.status = status_code; + ops[0].data.send_status_from_server.status_details = + gpr_strdup(status_details); + grpcsharp_metadata_array_move( + &(ctx->send_status_from_server.trailing_metadata), trailing_metadata); + ops[0].data.send_status_from_server.trailing_metadata_count = + ctx->send_status_from_server.trailing_metadata.count; + ops[0].data.send_status_from_server.trailing_metadata = + ctx->send_status_from_server.trailing_metadata.metadata; + ops[0].flags = 0; + ops[0].reserved = NULL; + ops[1].op = GRPC_OP_SEND_INITIAL_METADATA; + ops[1].data.send_initial_metadata.count = 0; + ops[1].data.send_initial_metadata.metadata = NULL; + ops[1].flags = 0; + ops[1].reserved = NULL; + + return grpc_call_start_batch(call, ops, nops, ctx, NULL); +} + +GPR_EXPORT grpc_call_error GPR_CALLTYPE +grpcsharp_call_recv_message(grpc_call *call, grpcsharp_batch_context *ctx) { + /* TODO: don't use magic number */ + grpc_op ops[1]; + ops[0].op = GRPC_OP_RECV_MESSAGE; + ops[0].data.recv_message = &(ctx->recv_message); + ops[0].flags = 0; + ops[0].reserved = NULL; + return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx, + NULL); +} + +GPR_EXPORT grpc_call_error GPR_CALLTYPE +grpcsharp_call_start_serverside(grpc_call *call, grpcsharp_batch_context *ctx) { + /* TODO: don't use magic number */ + grpc_op ops[1]; + ops[0].op = GRPC_OP_RECV_CLOSE_ON_SERVER; + ops[0].data.recv_close_on_server.cancelled = + (&ctx->recv_close_on_server_cancelled); + ops[0].flags = 0; + ops[0].reserved = NULL; + + return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx, + NULL); +} + +GPR_EXPORT grpc_call_error GPR_CALLTYPE +grpcsharp_call_send_initial_metadata(grpc_call *call, + grpcsharp_batch_context *ctx, + grpc_metadata_array *initial_metadata) { + /* TODO: don't use magic number */ + grpc_op ops[1]; + ops[0].op = GRPC_OP_SEND_INITIAL_METADATA; + grpcsharp_metadata_array_move(&(ctx->send_initial_metadata), + initial_metadata); + ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count; + ops[0].data.send_initial_metadata.metadata = + ctx->send_initial_metadata.metadata; + ops[0].flags = 0; + ops[0].reserved = NULL; + + return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx, + NULL); +} + +/* Server */ + +GPR_EXPORT grpc_server *GPR_CALLTYPE +grpcsharp_server_create(grpc_completion_queue *cq, + const grpc_channel_args *args) { + grpc_server *server = grpc_server_create(args, NULL); + grpc_server_register_completion_queue(server, cq, NULL); + return server; +} + +GPR_EXPORT gpr_int32 GPR_CALLTYPE +grpcsharp_server_add_insecure_http2_port(grpc_server *server, const char *addr) { + return grpc_server_add_insecure_http2_port(server, addr); +} + +GPR_EXPORT void GPR_CALLTYPE grpcsharp_server_start(grpc_server *server) { + grpc_server_start(server); +} + +GPR_EXPORT void GPR_CALLTYPE +grpcsharp_server_shutdown_and_notify_callback(grpc_server *server, + grpc_completion_queue *cq, + grpcsharp_batch_context *ctx) { + grpc_server_shutdown_and_notify(server, cq, ctx); +} + +GPR_EXPORT void GPR_CALLTYPE grpcsharp_server_cancel_all_calls(grpc_server *server) { + grpc_server_cancel_all_calls(server); +} + +GPR_EXPORT void GPR_CALLTYPE grpcsharp_server_destroy(grpc_server *server) { + grpc_server_destroy(server); +} + +GPR_EXPORT grpc_call_error GPR_CALLTYPE +grpcsharp_server_request_call(grpc_server *server, grpc_completion_queue *cq, + grpcsharp_batch_context *ctx) { + return grpc_server_request_call( + server, &(ctx->server_rpc_new.call), &(ctx->server_rpc_new.call_details), + &(ctx->server_rpc_new.request_metadata), cq, cq, ctx); +} + +/* Security */ + +GPR_EXPORT grpc_credentials *GPR_CALLTYPE +grpcsharp_ssl_credentials_create(const char *pem_root_certs, + const char *key_cert_pair_cert_chain, + const char *key_cert_pair_private_key) { + grpc_ssl_pem_key_cert_pair key_cert_pair; + if (key_cert_pair_cert_chain || key_cert_pair_private_key) { + key_cert_pair.cert_chain = key_cert_pair_cert_chain; + key_cert_pair.private_key = key_cert_pair_private_key; + return grpc_ssl_credentials_create(pem_root_certs, &key_cert_pair, NULL); + } else { + GPR_ASSERT(!key_cert_pair_cert_chain); + GPR_ASSERT(!key_cert_pair_private_key); + return grpc_ssl_credentials_create(pem_root_certs, NULL, NULL); + } +} + +GPR_EXPORT void GPR_CALLTYPE grpcsharp_credentials_release(grpc_credentials *creds) { + grpc_credentials_release(creds); +} + +GPR_EXPORT grpc_channel *GPR_CALLTYPE +grpcsharp_secure_channel_create(grpc_credentials *creds, const char *target, + const grpc_channel_args *args) { + return grpc_secure_channel_create(creds, target, args, NULL); +} + +GPR_EXPORT grpc_server_credentials *GPR_CALLTYPE +grpcsharp_ssl_server_credentials_create( + const char *pem_root_certs, const char **key_cert_pair_cert_chain_array, + const char **key_cert_pair_private_key_array, size_t num_key_cert_pairs, + int force_client_auth) { + size_t i; + grpc_server_credentials *creds; + grpc_ssl_pem_key_cert_pair *key_cert_pairs = + gpr_malloc(sizeof(grpc_ssl_pem_key_cert_pair) * num_key_cert_pairs); + memset(key_cert_pairs, 0, + sizeof(grpc_ssl_pem_key_cert_pair) * num_key_cert_pairs); + + for (i = 0; i < num_key_cert_pairs; i++) { + if (key_cert_pair_cert_chain_array[i] || + key_cert_pair_private_key_array[i]) { + key_cert_pairs[i].cert_chain = key_cert_pair_cert_chain_array[i]; + key_cert_pairs[i].private_key = key_cert_pair_private_key_array[i]; + } + } + creds = grpc_ssl_server_credentials_create(pem_root_certs, key_cert_pairs, + num_key_cert_pairs, + force_client_auth, NULL); + gpr_free(key_cert_pairs); + return creds; +} + +GPR_EXPORT void GPR_CALLTYPE grpcsharp_server_credentials_release( + grpc_server_credentials *creds) { + grpc_server_credentials_release(creds); +} + +GPR_EXPORT gpr_int32 GPR_CALLTYPE +grpcsharp_server_add_secure_http2_port(grpc_server *server, const char *addr, + grpc_server_credentials *creds) { + return grpc_server_add_secure_http2_port(server, addr, creds); +} + +/* Logging */ + +typedef void(GPR_CALLTYPE *grpcsharp_log_func)(const char *file, gpr_int32 line, + gpr_uint64 thd_id, + const char *severity_string, + const char *msg); +static grpcsharp_log_func log_func = NULL; + +/* Redirects gpr_log to log_func callback */ +static void grpcsharp_log_handler(gpr_log_func_args *args) { + log_func(args->file, args->line, gpr_thd_currentid(), + gpr_log_severity_string(args->severity), args->message); +} + +GPR_EXPORT void GPR_CALLTYPE grpcsharp_redirect_log(grpcsharp_log_func func) { + GPR_ASSERT(func); + log_func = func; + gpr_set_log_function(grpcsharp_log_handler); +} + +typedef void(GPR_CALLTYPE *test_callback_funcptr)(gpr_int32 success); + +/* Version info */ +GPR_EXPORT const char *GPR_CALLTYPE grpcsharp_version_string() { + return grpc_version_string(); +} + +/* For testing */ +GPR_EXPORT void GPR_CALLTYPE +grpcsharp_test_callback(test_callback_funcptr callback) { + callback(1); +} + +/* For testing */ +GPR_EXPORT void *GPR_CALLTYPE grpcsharp_test_nop(void *ptr) { return ptr; } + +/* For testing */ +GPR_EXPORT gpr_int32 GPR_CALLTYPE grpcsharp_sizeof_grpc_event(void) { + return sizeof(grpc_event); +} diff --git a/src/csharp/generate_proto_csharp.sh b/src/csharp/generate_proto_csharp.sh new file mode 100755 index 00000000..a17f45b5 --- /dev/null +++ b/src/csharp/generate_proto_csharp.sh @@ -0,0 +1,48 @@ +#!/bin/sh +# Copyright 2015, 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. + +# Regenerates gRPC service stubs from proto files. +set +e +cd $(dirname $0) + +PROTOC=../../bins/opt/protobuf/protoc +PLUGIN=protoc-gen-grpc=../../bins/opt/grpc_csharp_plugin +EXAMPLES_DIR=Grpc.Examples +INTEROP_DIR=Grpc.IntegrationTesting +HEALTHCHECK_DIR=Grpc.HealthCheck + +$PROTOC --plugin=$PLUGIN --csharp_out=$EXAMPLES_DIR --grpc_out=$EXAMPLES_DIR \ + -I $EXAMPLES_DIR/proto $EXAMPLES_DIR/proto/math.proto + +$PROTOC --plugin=$PLUGIN --csharp_out=$INTEROP_DIR --grpc_out=$INTEROP_DIR \ + -I $INTEROP_DIR/proto $INTEROP_DIR/proto/*.proto + +$PROTOC --plugin=$PLUGIN --csharp_out=$HEALTHCHECK_DIR --grpc_out=$HEALTHCHECK_DIR \ + -I $HEALTHCHECK_DIR/proto $HEALTHCHECK_DIR/proto/health.proto diff --git a/src/csharp/keys/Grpc.public.snk b/src/csharp/keys/Grpc.public.snk new file mode 100644 index 0000000000000000000000000000000000000000..bac3046b367212f394cd5ef7e9b802aeccc0d238 GIT binary patch literal 160 zcmV;R0AK$ABme*efB*oL000060ssI2Bme+XQ$aBR1ONa50096mSC^?QZ_Vf%)CrG6 z1VX!x5_X3o-U|$|v0jy1GNBVvbPna8ER@8K+>RH05K>cXipEjA;T+}KIw$;/include LDFLAGS=-L/lib npm install [grpc] +``` + +## TESTING +To run the test suite, simply run `npm test` in the install location. + +## API +This library internally uses [ProtoBuf.js](https://github.com/dcodeIO/ProtoBuf.js), and some structures it exports match those exported by that library + +If you require this module, you will get an object with the following members + +```javascript +function load(filename) +``` + +Takes a filename of a [Protocol Buffer](https://developers.google.com/protocol-buffers/) file, and returns an object representing the structure of the protocol buffer in the following way: + + - Namespaces become maps from the names of their direct members to those member objects + - Service definitions become client constructors for clients for that service. They also have a `service` member that can be used for constructing servers. + - Message definitions become Message constructors like those that ProtoBuf.js would create + - Enum definitions become Enum objects like those that ProtoBuf.js would create + - Anything else becomes the relevant reflection object that ProtoBuf.js would create + + +```javascript +function loadObject(reflectionObject) +``` + +Returns the same structure that `load` returns, but takes a reflection object from `ProtoBuf.js` instead of a file name. + +```javascript +function Server([serverOpions]) +``` + +Constructs a server to which service/implementation pairs can be added. + + +```javascript +status +``` + +An object mapping status names to status code numbers. + + +```javascript +callError +``` + +An object mapping call error names to codes. This is primarily useful for tracking down certain kinds of internal errors. + + +```javascript +Credentials +``` + +An object with factory methods for creating credential objects for clients. + + +```javascript +ServerCredentials +``` + +An object with factory methods for creating credential objects for servers. + +[homebrew]:http://brew.sh +[gRPC install script]:https://raw.githubusercontent.com/grpc/homebrew-grpc/master/scripts/install +[Debian jessie-backports]:http://backports.debian.org/Instructions/ diff --git a/src/node/bin/README.md b/src/node/bin/README.md new file mode 100644 index 00000000..2f856e42 --- /dev/null +++ b/src/node/bin/README.md @@ -0,0 +1,16 @@ +# Command Line Tools + +# Service Packager + +The command line tool `bin/service_packager`, when called with the following command line: + +```bash +service_packager proto_file -o output_path -n name -v version [-i input_path...] +``` + +Populates `output_path` with a node package consisting of a `package.json` populated with `name` and `version`, an `index.js`, a `LICENSE` file copied from gRPC, and a `service.json`, which is compiled from `proto_file` and the given `input_path`s. `require('output_path')` returns an object that is equivalent to + +```js +{ client: require('grpc').load('service.json'), + auth: require('google-auth-library') } +``` diff --git a/src/node/bin/service_packager b/src/node/bin/service_packager new file mode 100755 index 00000000..c7f24609 --- /dev/null +++ b/src/node/bin/service_packager @@ -0,0 +1,2 @@ +#!/usr/bin/env node +require(__dirname+'/../cli/service_packager.js').main(process.argv.slice(2)); \ No newline at end of file diff --git a/src/node/binding.gyp b/src/node/binding.gyp new file mode 100644 index 00000000..a6440309 --- /dev/null +++ b/src/node/binding.gyp @@ -0,0 +1,85 @@ +{ + "targets" : [ + { + 'include_dirs': [ + "/dev/null 2>&1 && echo true || echo false)' + }, + 'conditions': [ + ['pkg_config_grpc == "true"', { + 'link_settings': { + 'libraries': [ + ' 0, Fib + // generates up to limit numbers; otherwise it continues until the call is + // canceled. Unlike Fib above, Fib has no final FibReply. + rpc Fib (FibArgs) returns (stream Num) { + } + + // Sum sums a stream of numbers, returning the final result once the stream + // is closed. + rpc Sum (stream Num) returns (Num) { + } +} diff --git a/src/node/examples/math_server.js b/src/node/examples/math_server.js new file mode 100644 index 00000000..31892c65 --- /dev/null +++ b/src/node/examples/math_server.js @@ -0,0 +1,125 @@ +/* + * + * Copyright 2015, 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. + * + */ + +'use strict'; + +var grpc = require('..'); +var math = grpc.load(__dirname + '/math.proto').math; + +/** + * Server function for division. Provides the /Math/DivMany and /Math/Div + * functions (Div is just DivMany with only one stream element). For each + * DivArgs parameter, responds with a DivReply with the results of the division + * @param {Object} call The object containing request and cancellation info + * @param {function(Error, *)} cb Response callback + */ +function mathDiv(call, cb) { + var req = call.request; + // Unary + is explicit coersion to integer + if (+req.divisor === 0) { + cb(new Error('cannot divide by zero')); + } else { + cb(null, { + quotient: req.dividend / req.divisor, + remainder: req.dividend % req.divisor + }); + } +} + +/** + * Server function for Fibonacci numbers. Provides the /Math/Fib function. Reads + * a single parameter that indicates the number of responses, and then responds + * with a stream of that many Fibonacci numbers. + * @param {stream} stream The stream for sending responses. + */ +function mathFib(stream) { + // Here, call is a standard writable Node object Stream + var previous = 0, current = 1; + for (var i = 0; i < stream.request.limit; i++) { + stream.write({num: current}); + var temp = current; + current += previous; + previous = temp; + } + stream.end(); +} + +/** + * Server function for summation. Provides the /Math/Sum function. Reads a + * stream of number parameters, then responds with their sum. + * @param {stream} call The stream of arguments. + * @param {function(Error, *)} cb Response callback + */ +function mathSum(call, cb) { + // Here, call is a standard readable Node object Stream + var sum = 0; + call.on('data', function(data) { + sum += (+data.num); + }); + call.on('end', function() { + cb(null, {num: sum}); + }); +} + +function mathDivMany(stream) { + stream.on('data', function(div_args) { + if (+div_args.divisor === 0) { + stream.emit('error', new Error('cannot divide by zero')); + } else { + stream.write({ + quotient: div_args.dividend / div_args.divisor, + remainder: div_args.dividend % div_args.divisor + }); + } + }); + stream.on('end', function() { + stream.end(); + }); +} +var server = new grpc.Server(); +server.addProtoService(math.Math.service, { + div: mathDiv, + fib: mathFib, + sum: mathSum, + divMany: mathDivMany +}); + +if (require.main === module) { + server.bind('0.0.0.0:50051', grpc.ServerCredentials.createInsecure()); + server.start(); +} + +/** + * See docs for server + */ +module.exports = server; diff --git a/src/node/examples/perf_test.js b/src/node/examples/perf_test.js new file mode 100644 index 00000000..ba8fbf88 --- /dev/null +++ b/src/node/examples/perf_test.js @@ -0,0 +1,119 @@ +/* + * + * Copyright 2015, 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. + * + */ + +'use strict'; + +var grpc = require('..'); +var testProto = grpc.load(__dirname + '/../interop/test.proto').grpc.testing; +var _ = require('lodash'); +var interop_server = require('../interop/interop_server.js'); + +function runTest(iterations, callback) { + var testServer = interop_server.getServer(0, false); + testServer.server.start(); + var client = new testProto.TestService('localhost:' + testServer.port, + grpc.Credentials.createInsecure()); + + function runIterations(finish) { + var start = process.hrtime(); + var intervals = []; + function next(i) { + if (i >= iterations) { + testServer.server.shutdown(); + var totalDiff = process.hrtime(start); + finish({ + total: totalDiff[0] * 1000000 + totalDiff[1] / 1000, + intervals: intervals + }); + } else{ + var deadline = new Date(); + deadline.setSeconds(deadline.getSeconds() + 3); + var startTime = process.hrtime(); + client.emptyCall({}, function(err, resp) { + var timeDiff = process.hrtime(startTime); + intervals[i] = timeDiff[0] * 1000000 + timeDiff[1] / 1000; + next(i+1); + }, {}, {deadline: deadline}); + } + } + next(0); + } + + function warmUp(num) { + var pending = num; + function startCall() { + client.emptyCall({}, function(err, resp) { + pending--; + if (pending === 0) { + runIterations(callback); + } + }); + } + for (var i = 0; i < num; i++) { + startCall(); + } + } + warmUp(100); +} + +function percentile(arr, pct) { + if (pct > 99) { + pct = 99; + } + if (pct < 0) { + pct = 0; + } + var index = Math.floor(arr.length * pct / 100); + return arr[index]; +} + +if (require.main === module) { + var count; + if (process.argv.length >= 3) { + count = process.argv[2]; + } else { + count = 100; + } + runTest(count, function(results) { + var sorted_intervals = _.sortBy(results.intervals, _.identity); + console.log('count:', count); + console.log('total time:', results.total, 'us'); + console.log('median:', percentile(sorted_intervals, 50), 'us'); + console.log('90th percentile:', percentile(sorted_intervals, 90), 'us'); + console.log('95th percentile:', percentile(sorted_intervals, 95), 'us'); + console.log('99th percentile:', percentile(sorted_intervals, 99), 'us'); + console.log('QPS:', (count / results.total) * 1000000); + }); +} + +module.exports = runTest; diff --git a/src/node/examples/qps_test.js b/src/node/examples/qps_test.js new file mode 100644 index 00000000..ec968b85 --- /dev/null +++ b/src/node/examples/qps_test.js @@ -0,0 +1,137 @@ +/* + * + * Copyright 2015, 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. + * + */ + +/** + * This script runs a QPS test. It sends requests for a specified length of time + * with a specified number pending at any one time. It then outputs the measured + * QPS. Usage: + * node qps_test.js [--concurrent=count] [--time=seconds] + * concurrent defaults to 100 and time defaults to 10 + */ + +'use strict'; + +var async = require('async'); +var parseArgs = require('minimist'); + +var grpc = require('..'); +var testProto = grpc.load(__dirname + '/../interop/test.proto').grpc.testing; +var interop_server = require('../interop/interop_server.js'); + +/** + * Runs the QPS test. Sends requests constantly for the given number of seconds, + * and keeps concurrent_calls requests pending at all times. When the test ends, + * the callback is called with the number of calls that completed within the + * time limit. + * @param {number} concurrent_calls The number of calls to have pending + * simultaneously + * @param {number} seconds The number of seconds to run the test for + * @param {function(Error, number)} callback Callback for test completion + */ +function runTest(concurrent_calls, seconds, callback) { + var testServer = interop_server.getServer(0, false); + testServer.server.start(); + var client = new testProto.TestService('localhost:' + testServer.port, + grpc.Credentials.createInsecure()); + + var warmup_num = 100; + + /** + * Warms up the client to avoid counting startup time in the test result + * @param {function(Error)} callback Called when warmup is complete + */ + function warmUp(callback) { + var pending = warmup_num; + function startCall() { + client.emptyCall({}, function(err, resp) { + if (err) { + callback(err); + return; + } + pending--; + if (pending === 0) { + callback(null); + } + }); + } + for (var i = 0; i < warmup_num; i++) { + startCall(); + } + } + /** + * Run the QPS test. Starts concurrent_calls requests, then starts a new + * request whenever one completes until time runs out. + * @param {function(Error, number)} callback Called when the test is complete. + * The second argument is the number of calls that finished within the + * time limit + */ + function run(callback) { + var running = 0; + var count = 0; + var start = process.hrtime(); + function responseCallback(err, resp) { + if (process.hrtime(start)[0] < seconds) { + count += 1; + client.emptyCall({}, responseCallback); + } else { + running -= 1; + if (running <= 0) { + callback(null, count); + } + } + } + for (var i = 0; i < concurrent_calls; i++) { + running += 1; + client.emptyCall({}, responseCallback); + } + } + async.waterfall([warmUp, run], function(err, count) { + testServer.server.shutdown(); + callback(err, count); + }); +} + +if (require.main === module) { + var argv = parseArgs(process.argv.slice(2), { + default: {'concurrent': 100, + 'time': 10} + }); + runTest(argv.concurrent, argv.time, function(err, count) { + if (err) { + throw err; + } + console.log('Concurrent calls:', argv.concurrent); + console.log('Time:', argv.time, 'seconds'); + console.log('QPS:', (count/argv.time)); + }); +} diff --git a/src/node/examples/stock.proto b/src/node/examples/stock.proto new file mode 100644 index 00000000..5ee2bcbc --- /dev/null +++ b/src/node/examples/stock.proto @@ -0,0 +1,62 @@ +// Copyright 2015, 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. + +syntax = "proto3"; + +package examples; + +// Protocol type definitions +message StockRequest { + string symbol = 1; + int32 num_trades_to_watch = 2; +} + +message StockReply { + float price = 1; + string symbol = 2; +} + + +// Interface exported by the server +service Stock { + // Simple blocking RPC + rpc GetLastTradePrice(StockRequest) returns (StockReply) { + } + // Bidirectional streaming RPC + rpc GetLastTradePriceMultiple(stream StockRequest) returns + (stream StockReply) { + } + // Unidirectional server-to-client streaming RPC + rpc WatchFutureTrades(StockRequest) returns (stream StockReply) { + } + // Unidirectional client-to-server streaming RPC + rpc GetHighestTradePrice(stream StockRequest) returns (StockReply) { + } + +} diff --git a/src/node/examples/stock_client.js b/src/node/examples/stock_client.js new file mode 100644 index 00000000..ab9b050e --- /dev/null +++ b/src/node/examples/stock_client.js @@ -0,0 +1,47 @@ +/* + * + * Copyright 2015, 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. + * + */ + +var grpc = require('..'); +var examples = grpc.load(__dirname + '/stock.proto').examples; + +/** + * This exports a client constructor for the Stock service. The usage looks like + * + * var StockClient = require('stock_client.js'); + * var stockClient = new StockClient(server_address, + * grpc.Credentials.createInsecure()); + * stockClient.getLastTradePrice({symbol: 'GOOG'}, function(error, response) { + * console.log(error || response); + * }); + */ +module.exports = examples.Stock; diff --git a/src/node/examples/stock_server.js b/src/node/examples/stock_server.js new file mode 100644 index 00000000..12e54795 --- /dev/null +++ b/src/node/examples/stock_server.js @@ -0,0 +1,87 @@ +/* + * + * Copyright 2015, 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. + * + */ + +'use strict'; + +var _ = require('lodash'); +var grpc = require('..'); +var examples = grpc.load(__dirname + '/stock.proto').examples; + +function getLastTradePrice(call, callback) { + callback(null, {symbol: call.request.symbol, price: 88}); +} + +function watchFutureTrades(call) { + for (var i = 0; i < call.request.num_trades_to_watch; i++) { + call.write({price: 88.00 + i * 10.00}); + } + call.end(); +} + +function getHighestTradePrice(call, callback) { + var trades = []; + call.on('data', function(data) { + trades.push({symbol: data.symbol, price: _.random(0, 100)}); + }); + call.on('end', function() { + if(_.isEmpty(trades)) { + callback(null, {}); + } else { + callback(null, _.max(trades, function(trade){return trade.price;})); + } + }); +} + +function getLastTradePriceMultiple(call) { + call.on('data', function(data) { + call.write({price: 88}); + }); + call.on('end', function() { + call.end(); + }); +} + +var stockServer = new grpc.Server(); +stockServer.addProtoService(examples.Stock.service, { + getLastTradePrice: getLastTradePrice, + getLastTradePriceMultiple: getLastTradePriceMultiple, + watchFutureTrades: watchFutureTrades, + getHighestTradePrice: getHighestTradePrice +}); + +if (require.main === module) { + stockServer.bind('0.0.0.0:50051', grpc.ServerCredentials.createInsecure()); + stockServer.start(); +} + +module.exports = stockServer; diff --git a/src/node/ext/byte_buffer.cc b/src/node/ext/byte_buffer.cc new file mode 100644 index 00000000..e1786ddb --- /dev/null +++ b/src/node/ext/byte_buffer.cc @@ -0,0 +1,99 @@ +/* + * + * Copyright 2015, 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 + +#include +#include +#include "grpc/grpc.h" +#include "grpc/byte_buffer_reader.h" +#include "grpc/support/slice.h" + +#include "byte_buffer.h" + +namespace grpc { +namespace node { + + +using v8::Context; +using v8::Function; +using v8::Local; +using v8::Object; +using v8::Number; +using v8::Value; + +grpc_byte_buffer *BufferToByteBuffer(Local buffer) { + Nan::HandleScope scope; + int length = ::node::Buffer::Length(buffer); + char *data = ::node::Buffer::Data(buffer); + gpr_slice slice = gpr_slice_malloc(length); + memcpy(GPR_SLICE_START_PTR(slice), data, length); + grpc_byte_buffer *byte_buffer(grpc_raw_byte_buffer_create(&slice, 1)); + gpr_slice_unref(slice); + return byte_buffer; +} + +Local ByteBufferToBuffer(grpc_byte_buffer *buffer) { + Nan::EscapableHandleScope scope; + if (buffer == NULL) { + return scope.Escape(Nan::Null()); + } + size_t length = grpc_byte_buffer_length(buffer); + char *result = reinterpret_cast(calloc(length, sizeof(char))); + size_t offset = 0; + grpc_byte_buffer_reader reader; + grpc_byte_buffer_reader_init(&reader, buffer); + gpr_slice next; + while (grpc_byte_buffer_reader_next(&reader, &next) != 0) { + memcpy(result + offset, GPR_SLICE_START_PTR(next), GPR_SLICE_LENGTH(next)); + offset += GPR_SLICE_LENGTH(next); + } + return scope.Escape(MakeFastBuffer( + Nan::NewBuffer(result, length).ToLocalChecked())); +} + +Local MakeFastBuffer(Local slowBuffer) { + Nan::EscapableHandleScope scope; + Local globalObj = Nan::GetCurrentContext()->Global(); + Local bufferConstructor = Local::Cast( + globalObj->Get(Nan::New("Buffer").ToLocalChecked())); + Local consArgs[3] = { + slowBuffer, + Nan::New(::node::Buffer::Length(slowBuffer)), + Nan::New(0) + }; + Local fastBuffer = bufferConstructor->NewInstance(3, consArgs); + return scope.Escape(fastBuffer); +} +} // namespace node +} // namespace grpc diff --git a/src/node/ext/byte_buffer.h b/src/node/ext/byte_buffer.h new file mode 100644 index 00000000..55bc0ab3 --- /dev/null +++ b/src/node/ext/byte_buffer.h @@ -0,0 +1,60 @@ +/* + * + * Copyright 2015, 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 NET_GRPC_NODE_BYTE_BUFFER_H_ +#define NET_GRPC_NODE_BYTE_BUFFER_H_ + +#include + +#include +#include +#include "grpc/grpc.h" + +namespace grpc { +namespace node { + +/* Convert a Node.js Buffer to grpc_byte_buffer. Requires that + ::node::Buffer::HasInstance(buffer) */ +grpc_byte_buffer *BufferToByteBuffer(v8::Local buffer); + +/* Convert a grpc_byte_buffer to a Node.js Buffer */ +v8::Local ByteBufferToBuffer(grpc_byte_buffer *buffer); + +/* Convert a ::node::Buffer to a fast Buffer, as defined in the Node + Buffer documentation */ +v8::Local MakeFastBuffer(v8::Local slowBuffer); + +} // namespace node +} // namespace grpc + +#endif // NET_GRPC_NODE_BYTE_BUFFER_H_ diff --git a/src/node/ext/call.cc b/src/node/ext/call.cc new file mode 100644 index 00000000..b08a9f96 --- /dev/null +++ b/src/node/ext/call.cc @@ -0,0 +1,728 @@ +/* + * + * Copyright 2015, 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 +#include +#include + +#include + +#include "grpc/support/log.h" +#include "grpc/grpc.h" +#include "grpc/support/alloc.h" +#include "grpc/support/time.h" +#include "byte_buffer.h" +#include "call.h" +#include "channel.h" +#include "completion_queue_async_worker.h" +#include "timeval.h" + +using std::unique_ptr; +using std::shared_ptr; +using std::vector; + +namespace grpc { +namespace node { + +using Nan::Callback; +using Nan::EscapableHandleScope; +using Nan::HandleScope; +using Nan::Maybe; +using Nan::MaybeLocal; +using Nan::ObjectWrap; +using Nan::Persistent; +using Nan::Utf8String; + +using v8::Array; +using v8::Boolean; +using v8::Exception; +using v8::External; +using v8::Function; +using v8::FunctionTemplate; +using v8::Integer; +using v8::Local; +using v8::Number; +using v8::Object; +using v8::ObjectTemplate; +using v8::Uint32; +using v8::String; +using v8::Value; + +Callback *Call::constructor; +Persistent Call::fun_tpl; + +bool EndsWith(const char *str, const char *substr) { + return strcmp(str+strlen(str)-strlen(substr), substr) == 0; +} + +bool CreateMetadataArray(Local metadata, grpc_metadata_array *array, + shared_ptr resources) { + HandleScope scope; + grpc_metadata_array_init(array); + Local keys = Nan::GetOwnPropertyNames(metadata).ToLocalChecked(); + for (unsigned int i = 0; i < keys->Length(); i++) { + Local current_key = Nan::To( + Nan::Get(keys, i).ToLocalChecked()).ToLocalChecked(); + Local value_array = Nan::Get(metadata, current_key).ToLocalChecked(); + if (!value_array->IsArray()) { + return false; + } + array->capacity += Local::Cast(value_array)->Length(); + } + array->metadata = reinterpret_cast( + gpr_malloc(array->capacity * sizeof(grpc_metadata))); + for (unsigned int i = 0; i < keys->Length(); i++) { + Local current_key(keys->Get(i)->ToString()); + Utf8String *utf8_key = new Utf8String(current_key); + resources->strings.push_back(unique_ptr(utf8_key)); + Local values = Local::Cast( + Nan::Get(metadata, current_key).ToLocalChecked()); + for (unsigned int j = 0; j < values->Length(); j++) { + Local value = Nan::Get(values, j).ToLocalChecked(); + grpc_metadata *current = &array->metadata[array->count]; + current->key = **utf8_key; + // Only allow binary headers for "-bin" keys + if (EndsWith(current->key, "-bin")) { + if (::node::Buffer::HasInstance(value)) { + current->value = ::node::Buffer::Data(value); + current->value_length = ::node::Buffer::Length(value); + PersistentValue *handle = new PersistentValue(value); + resources->handles.push_back(unique_ptr(handle)); + } else { + return false; + } + } else { + if (value->IsString()) { + Local string_value = Nan::To(value).ToLocalChecked(); + Utf8String *utf8_value = new Utf8String(string_value); + resources->strings.push_back(unique_ptr(utf8_value)); + current->value = **utf8_value; + current->value_length = string_value->Length(); + } else { + return false; + } + } + array->count += 1; + } + } + return true; +} + +Local ParseMetadata(const grpc_metadata_array *metadata_array) { + EscapableHandleScope scope; + grpc_metadata *metadata_elements = metadata_array->metadata; + size_t length = metadata_array->count; + std::map size_map; + std::map index_map; + + for (unsigned int i = 0; i < length; i++) { + const char *key = metadata_elements[i].key; + if (size_map.count(key)) { + size_map[key] += 1; + } else { + size_map[key] = 1; + } + index_map[key] = 0; + } + Local metadata_object = Nan::New(); + for (unsigned int i = 0; i < length; i++) { + grpc_metadata* elem = &metadata_elements[i]; + Local key_string = Nan::New(elem->key).ToLocalChecked(); + Local array; + MaybeLocal maybe_array = Nan::Get(metadata_object, key_string); + if (maybe_array.IsEmpty() || !maybe_array.ToLocalChecked()->IsArray()) { + array = Nan::New(size_map[elem->key]); + Nan::Set(metadata_object, key_string, array); + } else { + array = Local::Cast(maybe_array.ToLocalChecked()); + } + if (EndsWith(elem->key, "-bin")) { + Nan::Set(array, index_map[elem->key], + Nan::CopyBuffer(elem->value, + elem->value_length).ToLocalChecked()); + } else { + Nan::Set(array, index_map[elem->key], + Nan::New(elem->value).ToLocalChecked()); + } + index_map[elem->key] += 1; + } + return scope.Escape(metadata_object); +} + +Local Op::GetOpType() const { + EscapableHandleScope scope; + return scope.Escape(Nan::New(GetTypeString()).ToLocalChecked()); +} + +Op::~Op() { +} + +class SendMetadataOp : public Op { + public: + Local GetNodeValue() const { + EscapableHandleScope scope; + return scope.Escape(Nan::True()); + } + bool ParseOp(Local value, grpc_op *out, + shared_ptr resources) { + if (!value->IsObject()) { + return false; + } + grpc_metadata_array array; + MaybeLocal maybe_metadata = Nan::To(value); + if (maybe_metadata.IsEmpty()) { + return false; + } + if (!CreateMetadataArray(maybe_metadata.ToLocalChecked(), + &array, resources)) { + return false; + } + out->data.send_initial_metadata.count = array.count; + out->data.send_initial_metadata.metadata = array.metadata; + return true; + } + protected: + std::string GetTypeString() const { + return "send_metadata"; + } +}; + +class SendMessageOp : public Op { + public: + Local GetNodeValue() const { + EscapableHandleScope scope; + return scope.Escape(Nan::True()); + } + bool ParseOp(Local value, grpc_op *out, + shared_ptr resources) { + if (!::node::Buffer::HasInstance(value)) { + return false; + } + Local object_value = Nan::To(value).ToLocalChecked(); + MaybeLocal maybe_flag_value = Nan::Get( + object_value, Nan::New("grpcWriteFlags").ToLocalChecked()); + if (!maybe_flag_value.IsEmpty()) { + Local flag_value = maybe_flag_value.ToLocalChecked(); + if (flag_value->IsUint32()) { + Maybe maybe_flag = Nan::To(flag_value); + out->flags = maybe_flag.FromMaybe(0) & GRPC_WRITE_USED_MASK; + } + } + out->data.send_message = BufferToByteBuffer(value); + PersistentValue *handle = new PersistentValue(value); + resources->handles.push_back(unique_ptr(handle)); + return true; + } + protected: + std::string GetTypeString() const { + return "send_message"; + } +}; + +class SendClientCloseOp : public Op { + public: + Local GetNodeValue() const { + EscapableHandleScope scope; + return scope.Escape(Nan::True()); + } + bool ParseOp(Local value, grpc_op *out, + shared_ptr resources) { + return true; + } + protected: + std::string GetTypeString() const { + return "client_close"; + } +}; + +class SendServerStatusOp : public Op { + public: + Local GetNodeValue() const { + EscapableHandleScope scope; + return scope.Escape(Nan::True()); + } + bool ParseOp(Local value, grpc_op *out, + shared_ptr resources) { + if (!value->IsObject()) { + return false; + } + Local server_status = Nan::To(value).ToLocalChecked(); + MaybeLocal maybe_metadata = Nan::Get( + server_status, Nan::New("metadata").ToLocalChecked()); + if (maybe_metadata.IsEmpty()) { + return false; + } + if (!maybe_metadata.ToLocalChecked()->IsObject()) { + return false; + } + Local metadata = Nan::To( + maybe_metadata.ToLocalChecked()).ToLocalChecked(); + MaybeLocal maybe_code = Nan::Get(server_status, + Nan::New("code").ToLocalChecked()); + if (maybe_code.IsEmpty()) { + return false; + } + if (!maybe_code.ToLocalChecked()->IsUint32()) { + return false; + } + uint32_t code = Nan::To(maybe_code.ToLocalChecked()).FromJust(); + MaybeLocal maybe_details = Nan::Get( + server_status, Nan::New("details").ToLocalChecked()); + if (maybe_details.IsEmpty()) { + return false; + } + if (!maybe_details.ToLocalChecked()->IsString()) { + return false; + } + Local details = Nan::To( + maybe_details.ToLocalChecked()).ToLocalChecked(); + grpc_metadata_array array; + if (!CreateMetadataArray(metadata, &array, resources)) { + return false; + } + out->data.send_status_from_server.trailing_metadata_count = array.count; + out->data.send_status_from_server.trailing_metadata = array.metadata; + out->data.send_status_from_server.status = + static_cast(code); + Utf8String *str = new Utf8String(details); + resources->strings.push_back(unique_ptr(str)); + out->data.send_status_from_server.status_details = **str; + return true; + } + protected: + std::string GetTypeString() const { + return "send_status"; + } +}; + +class GetMetadataOp : public Op { + public: + GetMetadataOp() { + grpc_metadata_array_init(&recv_metadata); + } + + ~GetMetadataOp() { + grpc_metadata_array_destroy(&recv_metadata); + } + + Local GetNodeValue() const { + EscapableHandleScope scope; + return scope.Escape(ParseMetadata(&recv_metadata)); + } + + bool ParseOp(Local value, grpc_op *out, + shared_ptr resources) { + out->data.recv_initial_metadata = &recv_metadata; + return true; + } + + protected: + std::string GetTypeString() const { + return "metadata"; + } + + private: + grpc_metadata_array recv_metadata; +}; + +class ReadMessageOp : public Op { + public: + ReadMessageOp() { + recv_message = NULL; + } + ~ReadMessageOp() { + if (recv_message != NULL) { + grpc_byte_buffer_destroy(recv_message); + } + } + Local GetNodeValue() const { + EscapableHandleScope scope; + return scope.Escape(ByteBufferToBuffer(recv_message)); + } + + bool ParseOp(Local value, grpc_op *out, + shared_ptr resources) { + out->data.recv_message = &recv_message; + return true; + } + + protected: + std::string GetTypeString() const { + return "read"; + } + + private: + grpc_byte_buffer *recv_message; +}; + +class ClientStatusOp : public Op { + public: + ClientStatusOp() { + grpc_metadata_array_init(&metadata_array); + status_details = NULL; + details_capacity = 0; + } + + ~ClientStatusOp() { + grpc_metadata_array_destroy(&metadata_array); + gpr_free(status_details); + } + + bool ParseOp(Local value, grpc_op *out, + shared_ptr resources) { + out->data.recv_status_on_client.trailing_metadata = &metadata_array; + out->data.recv_status_on_client.status = &status; + out->data.recv_status_on_client.status_details = &status_details; + out->data.recv_status_on_client.status_details_capacity = &details_capacity; + return true; + } + + Local GetNodeValue() const { + EscapableHandleScope scope; + Local status_obj = Nan::New(); + Nan::Set(status_obj, Nan::New("code").ToLocalChecked(), + Nan::New(status)); + if (status_details != NULL) { + Nan::Set(status_obj, Nan::New("details").ToLocalChecked(), + Nan::New(status_details).ToLocalChecked()); + } + Nan::Set(status_obj, Nan::New("metadata").ToLocalChecked(), + ParseMetadata(&metadata_array)); + return scope.Escape(status_obj); + } + protected: + std::string GetTypeString() const { + return "status"; + } + private: + grpc_metadata_array metadata_array; + grpc_status_code status; + char *status_details; + size_t details_capacity; +}; + +class ServerCloseResponseOp : public Op { + public: + Local GetNodeValue() const { + EscapableHandleScope scope; + return scope.Escape(Nan::New(cancelled)); + } + + bool ParseOp(Local value, grpc_op *out, + shared_ptr resources) { + out->data.recv_close_on_server.cancelled = &cancelled; + return true; + } + + protected: + std::string GetTypeString() const { + return "cancelled"; + } + + private: + int cancelled; +}; + +tag::tag(Callback *callback, OpVec *ops, + shared_ptr resources) : + callback(callback), ops(ops), resources(resources){ +} + +tag::~tag() { + delete callback; + delete ops; +} + +Local GetTagNodeValue(void *tag) { + EscapableHandleScope scope; + struct tag *tag_struct = reinterpret_cast(tag); + Local tag_obj = Nan::New(); + for (vector >::iterator it = tag_struct->ops->begin(); + it != tag_struct->ops->end(); ++it) { + Op *op_ptr = it->get(); + Nan::Set(tag_obj, op_ptr->GetOpType(), op_ptr->GetNodeValue()); + } + return scope.Escape(tag_obj); +} + +Callback *GetTagCallback(void *tag) { + struct tag *tag_struct = reinterpret_cast(tag); + return tag_struct->callback; +} + +void DestroyTag(void *tag) { + struct tag *tag_struct = reinterpret_cast(tag); + delete tag_struct; +} + +Call::Call(grpc_call *call) : wrapped_call(call) { +} + +Call::~Call() { + grpc_call_destroy(wrapped_call); +} + +void Call::Init(Local exports) { + HandleScope scope; + Local tpl = Nan::New(New); + tpl->SetClassName(Nan::New("Call").ToLocalChecked()); + tpl->InstanceTemplate()->SetInternalFieldCount(1); + Nan::SetPrototypeMethod(tpl, "startBatch", StartBatch); + Nan::SetPrototypeMethod(tpl, "cancel", Cancel); + Nan::SetPrototypeMethod(tpl, "cancelWithStatus", CancelWithStatus); + Nan::SetPrototypeMethod(tpl, "getPeer", GetPeer); + fun_tpl.Reset(tpl); + Local ctr = Nan::GetFunction(tpl).ToLocalChecked(); + Nan::Set(exports, Nan::New("Call").ToLocalChecked(), ctr); + constructor = new Callback(ctr); +} + +bool Call::HasInstance(Local val) { + HandleScope scope; + return Nan::New(fun_tpl)->HasInstance(val); +} + +Local Call::WrapStruct(grpc_call *call) { + EscapableHandleScope scope; + if (call == NULL) { + return scope.Escape(Nan::Null()); + } + const int argc = 1; + Local argv[argc] = {Nan::New( + reinterpret_cast(call))}; + MaybeLocal maybe_instance = Nan::NewInstance( + constructor->GetFunction(), argc, argv); + if (maybe_instance.IsEmpty()) { + return scope.Escape(Nan::Null()); + } else { + return scope.Escape(maybe_instance.ToLocalChecked()); + } +} + +NAN_METHOD(Call::New) { + if (info.IsConstructCall()) { + Call *call; + if (info[0]->IsExternal()) { + Local ext = info[0].As(); + // This option is used for wrapping an existing call + grpc_call *call_value = + reinterpret_cast(ext->Value()); + call = new Call(call_value); + } else { + if (!Channel::HasInstance(info[0])) { + return Nan::ThrowTypeError("Call's first argument must be a Channel"); + } + if (!info[1]->IsString()) { + return Nan::ThrowTypeError("Call's second argument must be a string"); + } + if (!(info[2]->IsNumber() || info[2]->IsDate())) { + return Nan::ThrowTypeError( + "Call's third argument must be a date or a number"); + } + // These arguments are at the end because they are optional + grpc_call *parent_call = NULL; + if (Call::HasInstance(info[4])) { + Call *parent_obj = ObjectWrap::Unwrap( + Nan::To(info[4]).ToLocalChecked()); + parent_call = parent_obj->wrapped_call; + } else if (!(info[4]->IsUndefined() || info[4]->IsNull())) { + return Nan::ThrowTypeError( + "Call's fifth argument must be another call, if provided"); + } + gpr_uint32 propagate_flags = GRPC_PROPAGATE_DEFAULTS; + if (info[5]->IsUint32()) { + propagate_flags = Nan::To(info[5]).FromJust(); + } else if (!(info[5]->IsUndefined() || info[5]->IsNull())) { + return Nan::ThrowTypeError( + "Call's sixth argument must be propagate flags, if provided"); + } + Local channel_object = Nan::To(info[0]).ToLocalChecked(); + Channel *channel = ObjectWrap::Unwrap(channel_object); + if (channel->GetWrappedChannel() == NULL) { + return Nan::ThrowError("Call cannot be created from a closed channel"); + } + Utf8String method(info[1]); + double deadline = Nan::To(info[2]).FromJust(); + grpc_channel *wrapped_channel = channel->GetWrappedChannel(); + grpc_call *wrapped_call; + if (info[3]->IsString()) { + Utf8String host_override(info[3]); + wrapped_call = grpc_channel_create_call( + wrapped_channel, parent_call, propagate_flags, + CompletionQueueAsyncWorker::GetQueue(), *method, + *host_override, MillisecondsToTimespec(deadline), NULL); + } else if (info[3]->IsUndefined() || info[3]->IsNull()) { + wrapped_call = grpc_channel_create_call( + wrapped_channel, parent_call, propagate_flags, + CompletionQueueAsyncWorker::GetQueue(), *method, + NULL, MillisecondsToTimespec(deadline), NULL); + } else { + return Nan::ThrowTypeError("Call's fourth argument must be a string"); + } + call = new Call(wrapped_call); + info.This()->SetHiddenValue(Nan::New("channel_").ToLocalChecked(), + channel_object); + } + call->Wrap(info.This()); + info.GetReturnValue().Set(info.This()); + } else { + const int argc = 4; + Local argv[argc] = {info[0], info[1], info[2], info[3]}; + MaybeLocal maybe_instance = constructor->GetFunction()->NewInstance( + argc, argv); + if (maybe_instance.IsEmpty()) { + // There's probably a pending exception + return; + } else { + info.GetReturnValue().Set(maybe_instance.ToLocalChecked()); + } + } +} + +NAN_METHOD(Call::StartBatch) { + if (!Call::HasInstance(info.This())) { + return Nan::ThrowTypeError("startBatch can only be called on Call objects"); + } + if (!info[0]->IsObject()) { + return Nan::ThrowError("startBatch's first argument must be an object"); + } + if (!info[1]->IsFunction()) { + return Nan::ThrowError("startBatch's second argument must be a callback"); + } + Local callback_func = info[1].As(); + Call *call = ObjectWrap::Unwrap(info.This()); + shared_ptr resources(new Resources); + Local obj = Nan::To(info[0]).ToLocalChecked(); + Local keys = Nan::GetOwnPropertyNames(obj).ToLocalChecked(); + size_t nops = keys->Length(); + vector ops(nops); + unique_ptr op_vector(new OpVec()); + for (unsigned int i = 0; i < nops; i++) { + unique_ptr op; + MaybeLocal maybe_key = Nan::Get(keys, i); + if (maybe_key.IsEmpty() || (!maybe_key.ToLocalChecked()->IsUint32())) { + return Nan::ThrowError( + "startBatch's first argument's keys must be integers"); + } + uint32_t type = Nan::To(maybe_key.ToLocalChecked()).FromJust(); + ops[i].op = static_cast(type); + ops[i].flags = 0; + ops[i].reserved = NULL; + switch (type) { + case GRPC_OP_SEND_INITIAL_METADATA: + op.reset(new SendMetadataOp()); + break; + case GRPC_OP_SEND_MESSAGE: + op.reset(new SendMessageOp()); + break; + case GRPC_OP_SEND_CLOSE_FROM_CLIENT: + op.reset(new SendClientCloseOp()); + break; + case GRPC_OP_SEND_STATUS_FROM_SERVER: + op.reset(new SendServerStatusOp()); + break; + case GRPC_OP_RECV_INITIAL_METADATA: + op.reset(new GetMetadataOp()); + break; + case GRPC_OP_RECV_MESSAGE: + op.reset(new ReadMessageOp()); + break; + case GRPC_OP_RECV_STATUS_ON_CLIENT: + op.reset(new ClientStatusOp()); + break; + case GRPC_OP_RECV_CLOSE_ON_SERVER: + op.reset(new ServerCloseResponseOp()); + break; + default: + return Nan::ThrowError("Argument object had an unrecognized key"); + } + if (!op->ParseOp(obj->Get(type), &ops[i], resources)) { + return Nan::ThrowTypeError("Incorrectly typed arguments to startBatch"); + } + op_vector->push_back(std::move(op)); + } + Callback *callback = new Callback(callback_func); + grpc_call_error error = grpc_call_start_batch( + call->wrapped_call, &ops[0], nops, new struct tag( + callback, op_vector.release(), resources), NULL); + if (error != GRPC_CALL_OK) { + return Nan::ThrowError(nanErrorWithCode("startBatch failed", error)); + } + CompletionQueueAsyncWorker::Next(); +} + +NAN_METHOD(Call::Cancel) { + if (!Call::HasInstance(info.This())) { + return Nan::ThrowTypeError("cancel can only be called on Call objects"); + } + Call *call = ObjectWrap::Unwrap(info.This()); + grpc_call_error error = grpc_call_cancel(call->wrapped_call, NULL); + if (error != GRPC_CALL_OK) { + return Nan::ThrowError(nanErrorWithCode("cancel failed", error)); + } +} + +NAN_METHOD(Call::CancelWithStatus) { + Nan::HandleScope scope; + if (!HasInstance(info.This())) { + return Nan::ThrowTypeError("cancel can only be called on Call objects"); + } + if (!info[0]->IsUint32()) { + return Nan::ThrowTypeError( + "cancelWithStatus's first argument must be a status code"); + } + if (!info[1]->IsString()) { + return Nan::ThrowTypeError( + "cancelWithStatus's second argument must be a string"); + } + Call *call = ObjectWrap::Unwrap(info.This()); + grpc_status_code code = static_cast( + Nan::To(info[0]).FromJust()); + Utf8String details(info[0]); + grpc_call_cancel_with_status(call->wrapped_call, code, *details, NULL); +} + +NAN_METHOD(Call::GetPeer) { + Nan::HandleScope scope; + if (!HasInstance(info.This())) { + return Nan::ThrowTypeError("getPeer can only be called on Call objects"); + } + Call *call = ObjectWrap::Unwrap(info.This()); + char *peer = grpc_call_get_peer(call->wrapped_call); + Local peer_value = Nan::New(peer).ToLocalChecked(); + gpr_free(peer); + info.GetReturnValue().Set(peer_value); +} + +} // namespace node +} // namespace grpc diff --git a/src/node/ext/call.h b/src/node/ext/call.h new file mode 100644 index 00000000..2f8e1f17 --- /dev/null +++ b/src/node/ext/call.h @@ -0,0 +1,135 @@ +/* + * + * Copyright 2015, 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 NET_GRPC_NODE_CALL_H_ +#define NET_GRPC_NODE_CALL_H_ + +#include +#include + +#include +#include +#include "grpc/grpc.h" +#include "grpc/support/log.h" + +#include "channel.h" + + +namespace grpc { +namespace node { + +using std::unique_ptr; +using std::shared_ptr; + +typedef Nan::Persistent> PersistentValue; + +/** + * Helper function for throwing errors with a grpc_call_error value. + * Modified from the answer by Gus Goose to + * http://stackoverflow.com/questions/31794200. + */ +inline v8::Local nanErrorWithCode(const char *msg, + grpc_call_error code) { + Nan::EscapableHandleScope scope; + v8::Local err = Nan::Error(msg).As(); + Nan::Set(err, Nan::New("code").ToLocalChecked(), Nan::New(code)); + return scope.Escape(err); +} + +v8::Local ParseMetadata(const grpc_metadata_array *metadata_array); + +struct Resources { + std::vector > strings; + std::vector > handles; +}; + +class Op { + public: + virtual v8::Local GetNodeValue() const = 0; + virtual bool ParseOp(v8::Local value, grpc_op *out, + shared_ptr resources) = 0; + virtual ~Op(); + v8::Local GetOpType() const; + + protected: + virtual std::string GetTypeString() const = 0; +}; + +typedef std::vector> OpVec; +struct tag { + tag(Nan::Callback *callback, OpVec *ops, + shared_ptr resources); + ~tag(); + Nan::Callback *callback; + OpVec *ops; + shared_ptr resources; +}; + +v8::Local GetTagNodeValue(void *tag); + +Nan::Callback *GetTagCallback(void *tag); + +void DestroyTag(void *tag); + +/* Wrapper class for grpc_call structs. */ +class Call : public Nan::ObjectWrap { + public: + static void Init(v8::Local exports); + static bool HasInstance(v8::Local val); + /* Wrap a grpc_call struct in a javascript object */ + static v8::Local WrapStruct(grpc_call *call); + + private: + explicit Call(grpc_call *call); + ~Call(); + + // Prevent copying + Call(const Call &); + Call &operator=(const Call &); + + static NAN_METHOD(New); + static NAN_METHOD(StartBatch); + static NAN_METHOD(Cancel); + static NAN_METHOD(CancelWithStatus); + static NAN_METHOD(GetPeer); + static Nan::Callback *constructor; + // Used for typechecking instances of this javascript class + static Nan::Persistent fun_tpl; + + grpc_call *wrapped_call; +}; + +} // namespace node +} // namespace grpc + +#endif // NET_GRPC_NODE_CALL_H_ diff --git a/src/node/ext/channel.cc b/src/node/ext/channel.cc new file mode 100644 index 00000000..6eb1e776 --- /dev/null +++ b/src/node/ext/channel.cc @@ -0,0 +1,261 @@ +/* + * + * Copyright 2015, 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 + +#include "grpc/support/log.h" + +#include +#include +#include "grpc/grpc.h" +#include "grpc/grpc_security.h" +#include "call.h" +#include "channel.h" +#include "completion_queue_async_worker.h" +#include "credentials.h" +#include "timeval.h" + +namespace grpc { +namespace node { + +using Nan::Callback; +using Nan::EscapableHandleScope; +using Nan::HandleScope; +using Nan::Maybe; +using Nan::MaybeLocal; +using Nan::ObjectWrap; +using Nan::Persistent; +using Nan::Utf8String; + +using v8::Array; +using v8::Exception; +using v8::Function; +using v8::FunctionTemplate; +using v8::Integer; +using v8::Local; +using v8::Number; +using v8::Object; +using v8::String; +using v8::Value; + +Callback *Channel::constructor; +Persistent Channel::fun_tpl; + +Channel::Channel(grpc_channel *channel) : wrapped_channel(channel) {} + +Channel::~Channel() { + if (wrapped_channel != NULL) { + grpc_channel_destroy(wrapped_channel); + } +} + +void Channel::Init(Local exports) { + Nan::HandleScope scope; + Local tpl = Nan::New(New); + tpl->SetClassName(Nan::New("Channel").ToLocalChecked()); + tpl->InstanceTemplate()->SetInternalFieldCount(1); + Nan::SetPrototypeMethod(tpl, "close", Close); + Nan::SetPrototypeMethod(tpl, "getTarget", GetTarget); + Nan::SetPrototypeMethod(tpl, "getConnectivityState", GetConnectivityState); + Nan::SetPrototypeMethod(tpl, "watchConnectivityState", + WatchConnectivityState); + fun_tpl.Reset(tpl); + Local ctr = Nan::GetFunction(tpl).ToLocalChecked(); + Nan::Set(exports, Nan::New("Channel").ToLocalChecked(), ctr); + constructor = new Callback(ctr); +} + +bool Channel::HasInstance(Local val) { + HandleScope scope; + return Nan::New(fun_tpl)->HasInstance(val); +} + +grpc_channel *Channel::GetWrappedChannel() { return this->wrapped_channel; } + +NAN_METHOD(Channel::New) { + if (info.IsConstructCall()) { + if (!info[0]->IsString()) { + return Nan::ThrowTypeError( + "Channel expects a string, a credential and an object"); + } + grpc_channel *wrapped_channel; + // Owned by the Channel object + Utf8String host(info[0]); + grpc_credentials *creds; + if (!Credentials::HasInstance(info[1])) { + return Nan::ThrowTypeError( + "Channel's second argument must be a credential"); + } + Credentials *creds_object = ObjectWrap::Unwrap( + Nan::To(info[1]).ToLocalChecked()); + creds = creds_object->GetWrappedCredentials(); + grpc_channel_args *channel_args_ptr; + if (info[2]->IsUndefined()) { + channel_args_ptr = NULL; + wrapped_channel = grpc_insecure_channel_create(*host, NULL, NULL); + } else if (info[2]->IsObject()) { + Local args_hash = Nan::To(info[2]).ToLocalChecked(); + Local keys(Nan::GetOwnPropertyNames(args_hash).ToLocalChecked()); + grpc_channel_args channel_args; + channel_args.num_args = keys->Length(); + channel_args.args = reinterpret_cast( + calloc(channel_args.num_args, sizeof(grpc_arg))); + /* These are used to keep all strings until then end of the block, then + destroy them */ + std::vector key_strings(keys->Length()); + std::vector value_strings(keys->Length()); + for (unsigned int i = 0; i < channel_args.num_args; i++) { + MaybeLocal maybe_key = Nan::To( + Nan::Get(keys, i).ToLocalChecked()); + if (maybe_key.IsEmpty()) { + free(channel_args.args); + return Nan::ThrowTypeError("Arg keys must be strings"); + } + Local current_key = maybe_key.ToLocalChecked(); + Local current_value = Nan::Get(args_hash, + current_key).ToLocalChecked(); + key_strings[i] = new Nan::Utf8String(current_key); + channel_args.args[i].key = **key_strings[i]; + if (current_value->IsInt32()) { + channel_args.args[i].type = GRPC_ARG_INTEGER; + channel_args.args[i].value.integer = Nan::To( + current_value).FromJust(); + } else if (current_value->IsString()) { + channel_args.args[i].type = GRPC_ARG_STRING; + value_strings[i] = new Nan::Utf8String(current_value); + channel_args.args[i].value.string = **value_strings[i]; + } else { + free(channel_args.args); + return Nan::ThrowTypeError("Arg values must be strings"); + } + } + channel_args_ptr = &channel_args; + } else { + return Nan::ThrowTypeError("Channel expects a string and an object"); + } + if (creds == NULL) { + wrapped_channel = grpc_insecure_channel_create(*host, channel_args_ptr, + NULL); + } else { + wrapped_channel = + grpc_secure_channel_create(creds, *host, channel_args_ptr, NULL); + } + if (channel_args_ptr != NULL) { + free(channel_args_ptr->args); + } + Channel *channel = new Channel(wrapped_channel); + channel->Wrap(info.This()); + info.GetReturnValue().Set(info.This()); + return; + } else { + const int argc = 3; + Local argv[argc] = {info[0], info[1], info[2]}; + MaybeLocal maybe_instance = constructor->GetFunction()->NewInstance( + argc, argv); + if (maybe_instance.IsEmpty()) { + // There's probably a pending exception + return; + } else { + info.GetReturnValue().Set(maybe_instance.ToLocalChecked()); + } + } +} + +NAN_METHOD(Channel::Close) { + if (!HasInstance(info.This())) { + return Nan::ThrowTypeError("close can only be called on Channel objects"); + } + Channel *channel = ObjectWrap::Unwrap(info.This()); + if (channel->wrapped_channel != NULL) { + grpc_channel_destroy(channel->wrapped_channel); + channel->wrapped_channel = NULL; + } +} + +NAN_METHOD(Channel::GetTarget) { + if (!HasInstance(info.This())) { + return Nan::ThrowTypeError("getTarget can only be called on Channel objects"); + } + Channel *channel = ObjectWrap::Unwrap(info.This()); + info.GetReturnValue().Set(Nan::New( + grpc_channel_get_target(channel->wrapped_channel)).ToLocalChecked()); +} + +NAN_METHOD(Channel::GetConnectivityState) { + if (!HasInstance(info.This())) { + return Nan::ThrowTypeError( + "getConnectivityState can only be called on Channel objects"); + } + Channel *channel = ObjectWrap::Unwrap(info.This()); + int try_to_connect = (int)info[0]->Equals(Nan::True()); + info.GetReturnValue().Set( + grpc_channel_check_connectivity_state(channel->wrapped_channel, + try_to_connect)); +} + +NAN_METHOD(Channel::WatchConnectivityState) { + if (!HasInstance(info.This())) { + return Nan::ThrowTypeError( + "watchConnectivityState can only be called on Channel objects"); + } + if (!info[0]->IsUint32()) { + return Nan::ThrowTypeError( + "watchConnectivityState's first argument must be a channel state"); + } + if (!(info[1]->IsNumber() || info[1]->IsDate())) { + return Nan::ThrowTypeError( + "watchConnectivityState's second argument must be a date or a number"); + } + if (!info[2]->IsFunction()) { + return Nan::ThrowTypeError( + "watchConnectivityState's third argument must be a callback"); + } + grpc_connectivity_state last_state = + static_cast( + Nan::To(info[0]).FromJust()); + double deadline = Nan::To(info[1]).FromJust(); + Local callback_func = info[2].As(); + Nan::Callback *callback = new Callback(callback_func); + Channel *channel = ObjectWrap::Unwrap(info.This()); + unique_ptr ops(new OpVec()); + grpc_channel_watch_connectivity_state( + channel->wrapped_channel, last_state, MillisecondsToTimespec(deadline), + CompletionQueueAsyncWorker::GetQueue(), + new struct tag(callback, + ops.release(), + shared_ptr(nullptr))); + CompletionQueueAsyncWorker::Next(); +} + +} // namespace node +} // namespace grpc diff --git a/src/node/ext/channel.h b/src/node/ext/channel.h new file mode 100644 index 00000000..0062fd03 --- /dev/null +++ b/src/node/ext/channel.h @@ -0,0 +1,78 @@ +/* + * + * Copyright 2015, 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 NET_GRPC_NODE_CHANNEL_H_ +#define NET_GRPC_NODE_CHANNEL_H_ + +#include +#include +#include "grpc/grpc.h" + +namespace grpc { +namespace node { + +/* Wrapper class for grpc_channel structs */ +class Channel : public Nan::ObjectWrap { + public: + static void Init(v8::Local exports); + static bool HasInstance(v8::Local val); + /* This is used to typecheck javascript objects before converting them to + this type */ + static v8::Persistent prototype; + + /* Returns the grpc_channel struct that this object wraps */ + grpc_channel *GetWrappedChannel(); + + private: + explicit Channel(grpc_channel *channel); + ~Channel(); + + // Prevent copying + Channel(const Channel &); + Channel &operator=(const Channel &); + + static NAN_METHOD(New); + static NAN_METHOD(Close); + static NAN_METHOD(GetTarget); + static NAN_METHOD(GetConnectivityState); + static NAN_METHOD(WatchConnectivityState); + static Nan::Callback *constructor; + static Nan::Persistent fun_tpl; + + grpc_channel *wrapped_channel; +}; + +} // namespace node +} // namespace grpc + +#endif // NET_GRPC_NODE_CHANNEL_H_ diff --git a/src/node/ext/completion_queue_async_worker.cc b/src/node/ext/completion_queue_async_worker.cc new file mode 100644 index 00000000..3a79f7c4 --- /dev/null +++ b/src/node/ext/completion_queue_async_worker.cc @@ -0,0 +1,117 @@ +/* + * + * Copyright 2015, 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 +#include + +#include "grpc/grpc.h" +#include "grpc/support/log.h" +#include "grpc/support/time.h" +#include "completion_queue_async_worker.h" +#include "call.h" + +namespace grpc { +namespace node { + +const int max_queue_threads = 2; + +using v8::Function; +using v8::Local; +using v8::Object; +using v8::Value; + +grpc_completion_queue *CompletionQueueAsyncWorker::queue; + +int CompletionQueueAsyncWorker::current_threads; +int CompletionQueueAsyncWorker::waiting_next_calls; + +CompletionQueueAsyncWorker::CompletionQueueAsyncWorker() + : Nan::AsyncWorker(NULL) {} + +CompletionQueueAsyncWorker::~CompletionQueueAsyncWorker() {} + +void CompletionQueueAsyncWorker::Execute() { + result = + grpc_completion_queue_next(queue, gpr_inf_future(GPR_CLOCK_REALTIME), NULL); + if (!result.success) { + SetErrorMessage("The async function encountered an error"); + } +} + +grpc_completion_queue *CompletionQueueAsyncWorker::GetQueue() { return queue; } + +void CompletionQueueAsyncWorker::Next() { + Nan::HandleScope scope; + if (current_threads < max_queue_threads) { + CompletionQueueAsyncWorker *worker = new CompletionQueueAsyncWorker(); + Nan::AsyncQueueWorker(worker); + } else { + waiting_next_calls += 1; + } +} + +void CompletionQueueAsyncWorker::Init(Local exports) { + Nan::HandleScope scope; + current_threads = 0; + waiting_next_calls = 0; + queue = grpc_completion_queue_create(NULL); +} + +void CompletionQueueAsyncWorker::HandleOKCallback() { + Nan::HandleScope scope; + if (waiting_next_calls > 0) { + waiting_next_calls -= 1; + CompletionQueueAsyncWorker *worker = new CompletionQueueAsyncWorker(); + Nan::AsyncQueueWorker(worker); + } else { + current_threads -= 1; + } + Nan::Callback *callback = GetTagCallback(result.tag); + Local argv[] = {Nan::Null(), GetTagNodeValue(result.tag)}; + callback->Call(2, argv); + + DestroyTag(result.tag); +} + +void CompletionQueueAsyncWorker::HandleErrorCallback() { + Nan::HandleScope scope; + Nan::Callback *callback = GetTagCallback(result.tag); + Local argv[] = {Nan::Error(ErrorMessage())}; + + callback->Call(1, argv); + + DestroyTag(result.tag); +} + +} // namespace node +} // namespace grpc diff --git a/src/node/ext/completion_queue_async_worker.h b/src/node/ext/completion_queue_async_worker.h new file mode 100644 index 00000000..6e541167 --- /dev/null +++ b/src/node/ext/completion_queue_async_worker.h @@ -0,0 +1,86 @@ +/* + * + * Copyright 2015, 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 NET_GRPC_NODE_COMPLETION_QUEUE_ASYNC_WORKER_H_ +#define NET_GRPC_NODE_COMPLETION_QUEUE_ASYNC_WORKER_H_ +#include + +#include "grpc/grpc.h" + +namespace grpc { +namespace node { + +/* A worker that asynchronously calls completion_queue_next, and queues onto the + node event loop a call to the function stored in the event's tag. */ +class CompletionQueueAsyncWorker : public Nan::AsyncWorker { + public: + CompletionQueueAsyncWorker(); + + ~CompletionQueueAsyncWorker(); + /* Calls completion_queue_next with the provided deadline, and stores the + event if there was one or sets an error message if there was not */ + void Execute(); + + /* Returns the completion queue attached to this class */ + static grpc_completion_queue *GetQueue(); + + /* Convenience function to create a worker with the given arguments and queue + it to run asynchronously */ + static void Next(); + + /* Initialize the CompletionQueueAsyncWorker class */ + static void Init(v8::Local exports); + + protected: + /* Called when Execute has succeeded (completed without setting an error + message). Calls the saved callback with the event that came from + completion_queue_next */ + void HandleOKCallback(); + + void HandleErrorCallback(); + + private: + grpc_event result; + + static grpc_completion_queue *queue; + + // Number of grpc_completion_queue_next calls in the thread pool + static int current_threads; + // Number of grpc_completion_queue_next calls waiting to enter the thread pool + static int waiting_next_calls; +}; + +} // namespace node +} // namespace grpc + +#endif // NET_GRPC_NODE_COMPLETION_QUEUE_ASYNC_WORKER_H_ diff --git a/src/node/ext/credentials.cc b/src/node/ext/credentials.cc new file mode 100644 index 00000000..4f41c92f --- /dev/null +++ b/src/node/ext/credentials.cc @@ -0,0 +1,246 @@ +/* + * + * Copyright 2015, 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 + +#include "grpc/grpc.h" +#include "grpc/grpc_security.h" +#include "grpc/support/log.h" +#include "credentials.h" + +namespace grpc { +namespace node { + +using Nan::Callback; +using Nan::EscapableHandleScope; +using Nan::HandleScope; +using Nan::Maybe; +using Nan::MaybeLocal; +using Nan::ObjectWrap; +using Nan::Persistent; +using Nan::Utf8String; + +using v8::Exception; +using v8::External; +using v8::Function; +using v8::FunctionTemplate; +using v8::Integer; +using v8::Local; +using v8::Object; +using v8::ObjectTemplate; +using v8::Value; + +Nan::Callback *Credentials::constructor; +Persistent Credentials::fun_tpl; + +Credentials::Credentials(grpc_credentials *credentials) + : wrapped_credentials(credentials) {} + +Credentials::~Credentials() { + grpc_credentials_release(wrapped_credentials); +} + +void Credentials::Init(Local exports) { + HandleScope scope; + Local tpl = Nan::New(New); + tpl->SetClassName(Nan::New("Credentials").ToLocalChecked()); + tpl->InstanceTemplate()->SetInternalFieldCount(1); + fun_tpl.Reset(tpl); + Local ctr = Nan::GetFunction(tpl).ToLocalChecked(); + Nan::Set(ctr, Nan::New("createDefault").ToLocalChecked(), + Nan::GetFunction( + Nan::New(CreateDefault)).ToLocalChecked()); + Nan::Set(ctr, Nan::New("createSsl").ToLocalChecked(), + Nan::GetFunction( + Nan::New(CreateSsl)).ToLocalChecked()); + Nan::Set(ctr, Nan::New("createComposite").ToLocalChecked(), + Nan::GetFunction( + Nan::New(CreateComposite)).ToLocalChecked()); + Nan::Set(ctr, Nan::New("createGce").ToLocalChecked(), + Nan::GetFunction( + Nan::New(CreateGce)).ToLocalChecked()); + Nan::Set(ctr, Nan::New("createIam").ToLocalChecked(), + Nan::GetFunction( + Nan::New(CreateIam)).ToLocalChecked()); + Nan::Set(ctr, Nan::New("createInsecure").ToLocalChecked(), + Nan::GetFunction( + Nan::New(CreateInsecure)).ToLocalChecked()); + Nan::Set(exports, Nan::New("Credentials").ToLocalChecked(), ctr); + constructor = new Nan::Callback(ctr); +} + +bool Credentials::HasInstance(Local val) { + HandleScope scope; + return Nan::New(fun_tpl)->HasInstance(val); +} + +Local Credentials::WrapStruct(grpc_credentials *credentials) { + EscapableHandleScope scope; + const int argc = 1; + Local argv[argc] = { + Nan::New(reinterpret_cast(credentials))}; + MaybeLocal maybe_instance = Nan::NewInstance( + constructor->GetFunction(), argc, argv); + if (maybe_instance.IsEmpty()) { + return scope.Escape(Nan::Null()); + } else { + return scope.Escape(maybe_instance.ToLocalChecked()); + } +} + +grpc_credentials *Credentials::GetWrappedCredentials() { + return wrapped_credentials; +} + +NAN_METHOD(Credentials::New) { + if (info.IsConstructCall()) { + if (!info[0]->IsExternal()) { + return Nan::ThrowTypeError( + "Credentials can only be created with the provided functions"); + } + Local ext = info[0].As(); + grpc_credentials *creds_value = + reinterpret_cast(ext->Value()); + Credentials *credentials = new Credentials(creds_value); + credentials->Wrap(info.This()); + info.GetReturnValue().Set(info.This()); + return; + } else { + const int argc = 1; + Local argv[argc] = {info[0]}; + MaybeLocal maybe_instance = constructor->GetFunction()->NewInstance( + argc, argv); + if (maybe_instance.IsEmpty()) { + // There's probably a pending exception + return; + } else { + info.GetReturnValue().Set(maybe_instance.ToLocalChecked()); + } + } +} + +NAN_METHOD(Credentials::CreateDefault) { + grpc_credentials *creds = grpc_google_default_credentials_create(); + if (creds == NULL) { + info.GetReturnValue().SetNull(); + } else { + info.GetReturnValue().Set(WrapStruct(creds)); + } +} + +NAN_METHOD(Credentials::CreateSsl) { + char *root_certs = NULL; + grpc_ssl_pem_key_cert_pair key_cert_pair = {NULL, NULL}; + if (::node::Buffer::HasInstance(info[0])) { + root_certs = ::node::Buffer::Data(info[0]); + } else if (!(info[0]->IsNull() || info[0]->IsUndefined())) { + return Nan::ThrowTypeError("createSsl's first argument must be a Buffer"); + } + if (::node::Buffer::HasInstance(info[1])) { + key_cert_pair.private_key = ::node::Buffer::Data(info[1]); + } else if (!(info[1]->IsNull() || info[1]->IsUndefined())) { + return Nan::ThrowTypeError( + "createSSl's second argument must be a Buffer if provided"); + } + if (::node::Buffer::HasInstance(info[2])) { + key_cert_pair.cert_chain = ::node::Buffer::Data(info[2]); + } else if (!(info[2]->IsNull() || info[2]->IsUndefined())) { + return Nan::ThrowTypeError( + "createSSl's third argument must be a Buffer if provided"); + } + grpc_credentials *creds = grpc_ssl_credentials_create( + root_certs, key_cert_pair.private_key == NULL ? NULL : &key_cert_pair, + NULL); + if (creds == NULL) { + info.GetReturnValue().SetNull(); + } else { + info.GetReturnValue().Set(WrapStruct(creds)); + } +} + +NAN_METHOD(Credentials::CreateComposite) { + if (!HasInstance(info[0])) { + return Nan::ThrowTypeError( + "createComposite's first argument must be a Credentials object"); + } + if (!HasInstance(info[1])) { + return Nan::ThrowTypeError( + "createComposite's second argument must be a Credentials object"); + } + Credentials *creds1 = ObjectWrap::Unwrap( + Nan::To(info[0]).ToLocalChecked()); + Credentials *creds2 = ObjectWrap::Unwrap( + Nan::To(info[1]).ToLocalChecked()); + grpc_credentials *creds = grpc_composite_credentials_create( + creds1->wrapped_credentials, creds2->wrapped_credentials, NULL); + if (creds == NULL) { + info.GetReturnValue().SetNull(); + } else { + info.GetReturnValue().Set(WrapStruct(creds)); + } +} + +NAN_METHOD(Credentials::CreateGce) { + Nan::HandleScope scope; + grpc_credentials *creds = grpc_google_compute_engine_credentials_create(NULL); + if (creds == NULL) { + info.GetReturnValue().SetNull(); + } else { + info.GetReturnValue().Set(WrapStruct(creds)); + } +} + +NAN_METHOD(Credentials::CreateIam) { + if (!info[0]->IsString()) { + return Nan::ThrowTypeError("createIam's first argument must be a string"); + } + if (!info[1]->IsString()) { + return Nan::ThrowTypeError("createIam's second argument must be a string"); + } + Utf8String auth_token(info[0]); + Utf8String auth_selector(info[1]); + grpc_credentials *creds = + grpc_google_iam_credentials_create(*auth_token, *auth_selector, NULL); + if (creds == NULL) { + info.GetReturnValue().SetNull(); + } else { + info.GetReturnValue().Set(WrapStruct(creds)); + } +} + +NAN_METHOD(Credentials::CreateInsecure) { + info.GetReturnValue().Set(WrapStruct(NULL)); +} + +} // namespace node +} // namespace grpc diff --git a/src/node/ext/credentials.h b/src/node/ext/credentials.h new file mode 100644 index 00000000..1b211175 --- /dev/null +++ b/src/node/ext/credentials.h @@ -0,0 +1,82 @@ +/* + * + * Copyright 2015, 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 NET_GRPC_NODE_CREDENTIALS_H_ +#define NET_GRPC_NODE_CREDENTIALS_H_ + +#include +#include +#include "grpc/grpc.h" +#include "grpc/grpc_security.h" + +namespace grpc { +namespace node { + +/* Wrapper class for grpc_credentials structs */ +class Credentials : public Nan::ObjectWrap { + public: + static void Init(v8::Local exports); + static bool HasInstance(v8::Local val); + /* Wrap a grpc_credentials struct in a javascript object */ + static v8::Local WrapStruct(grpc_credentials *credentials); + + /* Returns the grpc_credentials struct that this object wraps */ + grpc_credentials *GetWrappedCredentials(); + + private: + explicit Credentials(grpc_credentials *credentials); + ~Credentials(); + + // Prevent copying + Credentials(const Credentials &); + Credentials &operator=(const Credentials &); + + static NAN_METHOD(New); + static NAN_METHOD(CreateDefault); + static NAN_METHOD(CreateSsl); + static NAN_METHOD(CreateComposite); + static NAN_METHOD(CreateGce); + static NAN_METHOD(CreateFake); + static NAN_METHOD(CreateIam); + static NAN_METHOD(CreateInsecure); + static Nan::Callback *constructor; + // Used for typechecking instances of this javascript class + static Nan::Persistent fun_tpl; + + grpc_credentials *wrapped_credentials; +}; + +} // namespace node +} // namespace grpc + +#endif // NET_GRPC_NODE_CREDENTIALS_H_ diff --git a/src/node/ext/node_grpc.cc b/src/node/ext/node_grpc.cc new file mode 100644 index 00000000..caca0fc4 --- /dev/null +++ b/src/node/ext/node_grpc.cc @@ -0,0 +1,250 @@ +/* + * + * Copyright 2015, 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 +#include +#include +#include "grpc/grpc.h" + +#include "call.h" +#include "channel.h" +#include "server.h" +#include "completion_queue_async_worker.h" +#include "credentials.h" +#include "server_credentials.h" + +using v8::Local; +using v8::Value; +using v8::Object; +using v8::Uint32; +using v8::String; + +void InitStatusConstants(Local exports) { + Nan::HandleScope scope; + Local status = Nan::New(); + Nan::Set(exports, Nan::New("status").ToLocalChecked(), status); + Local OK(Nan::New(GRPC_STATUS_OK)); + Nan::Set(status, Nan::New("OK").ToLocalChecked(), OK); + Local CANCELLED(Nan::New(GRPC_STATUS_CANCELLED)); + Nan::Set(status, Nan::New("CANCELLED").ToLocalChecked(), CANCELLED); + Local UNKNOWN(Nan::New(GRPC_STATUS_UNKNOWN)); + Nan::Set(status, Nan::New("UNKNOWN").ToLocalChecked(), UNKNOWN); + Local INVALID_ARGUMENT( + Nan::New(GRPC_STATUS_INVALID_ARGUMENT)); + Nan::Set(status, Nan::New("INVALID_ARGUMENT").ToLocalChecked(), + INVALID_ARGUMENT); + Local DEADLINE_EXCEEDED( + Nan::New(GRPC_STATUS_DEADLINE_EXCEEDED)); + Nan::Set(status, Nan::New("DEADLINE_EXCEEDED").ToLocalChecked(), + DEADLINE_EXCEEDED); + Local NOT_FOUND(Nan::New(GRPC_STATUS_NOT_FOUND)); + Nan::Set(status, Nan::New("NOT_FOUND").ToLocalChecked(), NOT_FOUND); + Local ALREADY_EXISTS( + Nan::New(GRPC_STATUS_ALREADY_EXISTS)); + Nan::Set(status, Nan::New("ALREADY_EXISTS").ToLocalChecked(), ALREADY_EXISTS); + Local PERMISSION_DENIED( + Nan::New(GRPC_STATUS_PERMISSION_DENIED)); + Nan::Set(status, Nan::New("PERMISSION_DENIED").ToLocalChecked(), + PERMISSION_DENIED); + Local UNAUTHENTICATED( + Nan::New(GRPC_STATUS_UNAUTHENTICATED)); + Nan::Set(status, Nan::New("UNAUTHENTICATED").ToLocalChecked(), + UNAUTHENTICATED); + Local RESOURCE_EXHAUSTED( + Nan::New(GRPC_STATUS_RESOURCE_EXHAUSTED)); + Nan::Set(status, Nan::New("RESOURCE_EXHAUSTED").ToLocalChecked(), + RESOURCE_EXHAUSTED); + Local FAILED_PRECONDITION( + Nan::New(GRPC_STATUS_FAILED_PRECONDITION)); + Nan::Set(status, Nan::New("FAILED_PRECONDITION").ToLocalChecked(), + FAILED_PRECONDITION); + Local ABORTED(Nan::New(GRPC_STATUS_ABORTED)); + Nan::Set(status, Nan::New("ABORTED").ToLocalChecked(), ABORTED); + Local OUT_OF_RANGE( + Nan::New(GRPC_STATUS_OUT_OF_RANGE)); + Nan::Set(status, Nan::New("OUT_OF_RANGE").ToLocalChecked(), OUT_OF_RANGE); + Local UNIMPLEMENTED( + Nan::New(GRPC_STATUS_UNIMPLEMENTED)); + Nan::Set(status, Nan::New("UNIMPLEMENTED").ToLocalChecked(), UNIMPLEMENTED); + Local INTERNAL(Nan::New(GRPC_STATUS_INTERNAL)); + Nan::Set(status, Nan::New("INTERNAL").ToLocalChecked(), INTERNAL); + Local UNAVAILABLE(Nan::New(GRPC_STATUS_UNAVAILABLE)); + Nan::Set(status, Nan::New("UNAVAILABLE").ToLocalChecked(), UNAVAILABLE); + Local DATA_LOSS(Nan::New(GRPC_STATUS_DATA_LOSS)); + Nan::Set(status, Nan::New("DATA_LOSS").ToLocalChecked(), DATA_LOSS); +} + +void InitCallErrorConstants(Local exports) { + Nan::HandleScope scope; + Local call_error = Nan::New(); + Nan::Set(exports, Nan::New("callError").ToLocalChecked(), call_error); + Local OK(Nan::New(GRPC_CALL_OK)); + Nan::Set(call_error, Nan::New("OK").ToLocalChecked(), OK); + Local ERROR(Nan::New(GRPC_CALL_ERROR)); + Nan::Set(call_error, Nan::New("ERROR").ToLocalChecked(), ERROR); + Local NOT_ON_SERVER( + Nan::New(GRPC_CALL_ERROR_NOT_ON_SERVER)); + Nan::Set(call_error, Nan::New("NOT_ON_SERVER").ToLocalChecked(), + NOT_ON_SERVER); + Local NOT_ON_CLIENT( + Nan::New(GRPC_CALL_ERROR_NOT_ON_CLIENT)); + Nan::Set(call_error, Nan::New("NOT_ON_CLIENT").ToLocalChecked(), + NOT_ON_CLIENT); + Local ALREADY_INVOKED( + Nan::New(GRPC_CALL_ERROR_ALREADY_INVOKED)); + Nan::Set(call_error, Nan::New("ALREADY_INVOKED").ToLocalChecked(), + ALREADY_INVOKED); + Local NOT_INVOKED( + Nan::New(GRPC_CALL_ERROR_NOT_INVOKED)); + Nan::Set(call_error, Nan::New("NOT_INVOKED").ToLocalChecked(), NOT_INVOKED); + Local ALREADY_FINISHED( + Nan::New(GRPC_CALL_ERROR_ALREADY_FINISHED)); + Nan::Set(call_error, Nan::New("ALREADY_FINISHED").ToLocalChecked(), + ALREADY_FINISHED); + Local TOO_MANY_OPERATIONS( + Nan::New(GRPC_CALL_ERROR_TOO_MANY_OPERATIONS)); + Nan::Set(call_error, Nan::New("TOO_MANY_OPERATIONS").ToLocalChecked(), + TOO_MANY_OPERATIONS); + Local INVALID_FLAGS( + Nan::New(GRPC_CALL_ERROR_INVALID_FLAGS)); + Nan::Set(call_error, Nan::New("INVALID_FLAGS").ToLocalChecked(), + INVALID_FLAGS); +} + +void InitOpTypeConstants(Local exports) { + Nan::HandleScope scope; + Local op_type = Nan::New(); + Nan::Set(exports, Nan::New("opType").ToLocalChecked(), op_type); + Local SEND_INITIAL_METADATA( + Nan::New(GRPC_OP_SEND_INITIAL_METADATA)); + Nan::Set(op_type, Nan::New("SEND_INITIAL_METADATA").ToLocalChecked(), + SEND_INITIAL_METADATA); + Local SEND_MESSAGE( + Nan::New(GRPC_OP_SEND_MESSAGE)); + Nan::Set(op_type, Nan::New("SEND_MESSAGE").ToLocalChecked(), SEND_MESSAGE); + Local SEND_CLOSE_FROM_CLIENT( + Nan::New(GRPC_OP_SEND_CLOSE_FROM_CLIENT)); + Nan::Set(op_type, Nan::New("SEND_CLOSE_FROM_CLIENT").ToLocalChecked(), + SEND_CLOSE_FROM_CLIENT); + Local SEND_STATUS_FROM_SERVER( + Nan::New(GRPC_OP_SEND_STATUS_FROM_SERVER)); + Nan::Set(op_type, Nan::New("SEND_STATUS_FROM_SERVER").ToLocalChecked(), + SEND_STATUS_FROM_SERVER); + Local RECV_INITIAL_METADATA( + Nan::New(GRPC_OP_RECV_INITIAL_METADATA)); + Nan::Set(op_type, Nan::New("RECV_INITIAL_METADATA").ToLocalChecked(), + RECV_INITIAL_METADATA); + Local RECV_MESSAGE( + Nan::New(GRPC_OP_RECV_MESSAGE)); + Nan::Set(op_type, Nan::New("RECV_MESSAGE").ToLocalChecked(), RECV_MESSAGE); + Local RECV_STATUS_ON_CLIENT( + Nan::New(GRPC_OP_RECV_STATUS_ON_CLIENT)); + Nan::Set(op_type, Nan::New("RECV_STATUS_ON_CLIENT").ToLocalChecked(), + RECV_STATUS_ON_CLIENT); + Local RECV_CLOSE_ON_SERVER( + Nan::New(GRPC_OP_RECV_CLOSE_ON_SERVER)); + Nan::Set(op_type, Nan::New("RECV_CLOSE_ON_SERVER").ToLocalChecked(), + RECV_CLOSE_ON_SERVER); +} + +void InitPropagateConstants(Local exports) { + Nan::HandleScope scope; + Local propagate = Nan::New(); + Nan::Set(exports, Nan::New("propagate").ToLocalChecked(), propagate); + Local DEADLINE(Nan::New(GRPC_PROPAGATE_DEADLINE)); + Nan::Set(propagate, Nan::New("DEADLINE").ToLocalChecked(), DEADLINE); + Local CENSUS_STATS_CONTEXT( + Nan::New(GRPC_PROPAGATE_CENSUS_STATS_CONTEXT)); + Nan::Set(propagate, Nan::New("CENSUS_STATS_CONTEXT").ToLocalChecked(), + CENSUS_STATS_CONTEXT); + Local CENSUS_TRACING_CONTEXT( + Nan::New(GRPC_PROPAGATE_CENSUS_TRACING_CONTEXT)); + Nan::Set(propagate, Nan::New("CENSUS_TRACING_CONTEXT").ToLocalChecked(), + CENSUS_TRACING_CONTEXT); + Local CANCELLATION( + Nan::New(GRPC_PROPAGATE_CANCELLATION)); + Nan::Set(propagate, Nan::New("CANCELLATION").ToLocalChecked(), CANCELLATION); + Local DEFAULTS(Nan::New(GRPC_PROPAGATE_DEFAULTS)); + Nan::Set(propagate, Nan::New("DEFAULTS").ToLocalChecked(), DEFAULTS); +} + +void InitConnectivityStateConstants(Local exports) { + Nan::HandleScope scope; + Local channel_state = Nan::New(); + Nan::Set(exports, Nan::New("connectivityState").ToLocalChecked(), + channel_state); + Local IDLE(Nan::New(GRPC_CHANNEL_IDLE)); + Nan::Set(channel_state, Nan::New("IDLE").ToLocalChecked(), IDLE); + Local CONNECTING(Nan::New(GRPC_CHANNEL_CONNECTING)); + Nan::Set(channel_state, Nan::New("CONNECTING").ToLocalChecked(), CONNECTING); + Local READY(Nan::New(GRPC_CHANNEL_READY)); + Nan::Set(channel_state, Nan::New("READY").ToLocalChecked(), READY); + Local TRANSIENT_FAILURE( + Nan::New(GRPC_CHANNEL_TRANSIENT_FAILURE)); + Nan::Set(channel_state, Nan::New("TRANSIENT_FAILURE").ToLocalChecked(), + TRANSIENT_FAILURE); + Local FATAL_FAILURE( + Nan::New(GRPC_CHANNEL_FATAL_FAILURE)); + Nan::Set(channel_state, Nan::New("FATAL_FAILURE").ToLocalChecked(), + FATAL_FAILURE); +} + +void InitWriteFlags(Local exports) { + Nan::HandleScope scope; + Local write_flags = Nan::New(); + Nan::Set(exports, Nan::New("writeFlags").ToLocalChecked(), write_flags); + Local BUFFER_HINT(Nan::New(GRPC_WRITE_BUFFER_HINT)); + Nan::Set(write_flags, Nan::New("BUFFER_HINT").ToLocalChecked(), BUFFER_HINT); + Local NO_COMPRESS(Nan::New(GRPC_WRITE_NO_COMPRESS)); + Nan::Set(write_flags, Nan::New("NO_COMPRESS").ToLocalChecked(), NO_COMPRESS); +} + +void init(Local exports) { + Nan::HandleScope scope; + grpc_init(); + InitStatusConstants(exports); + InitCallErrorConstants(exports); + InitOpTypeConstants(exports); + InitPropagateConstants(exports); + InitConnectivityStateConstants(exports); + InitWriteFlags(exports); + + grpc::node::Call::Init(exports); + grpc::node::Channel::Init(exports); + grpc::node::Server::Init(exports); + grpc::node::CompletionQueueAsyncWorker::Init(exports); + grpc::node::Credentials::Init(exports); + grpc::node::ServerCredentials::Init(exports); +} + +NODE_MODULE(grpc, init) diff --git a/src/node/ext/server.cc b/src/node/ext/server.cc new file mode 100644 index 00000000..87363fc4 --- /dev/null +++ b/src/node/ext/server.cc @@ -0,0 +1,317 @@ +/* + * + * Copyright 2015, 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 + +#include "server.h" + +#include +#include + +#include +#include "grpc/grpc.h" +#include "grpc/grpc_security.h" +#include "grpc/support/log.h" +#include "call.h" +#include "completion_queue_async_worker.h" +#include "server_credentials.h" +#include "timeval.h" + +namespace grpc { +namespace node { + +using Nan::Callback; +using Nan::EscapableHandleScope; +using Nan::HandleScope; +using Nan::Maybe; +using Nan::MaybeLocal; +using Nan::ObjectWrap; +using Nan::Persistent; +using Nan::Utf8String; + +using std::unique_ptr; +using v8::Array; +using v8::Boolean; +using v8::Date; +using v8::Exception; +using v8::Function; +using v8::FunctionTemplate; +using v8::Local; +using v8::Number; +using v8::Object; +using v8::String; +using v8::Value; + +Nan::Callback *Server::constructor; +Persistent Server::fun_tpl; + +class NewCallOp : public Op { + public: + NewCallOp() { + call = NULL; + grpc_call_details_init(&details); + grpc_metadata_array_init(&request_metadata); + } + + ~NewCallOp() { + grpc_call_details_destroy(&details); + grpc_metadata_array_destroy(&request_metadata); + } + + Local GetNodeValue() const { + Nan::EscapableHandleScope scope; + if (call == NULL) { + return scope.Escape(Nan::Null()); + } + Local obj = Nan::New(); + Nan::Set(obj, Nan::New("call").ToLocalChecked(), Call::WrapStruct(call)); + Nan::Set(obj, Nan::New("method").ToLocalChecked(), + Nan::New(details.method).ToLocalChecked()); + Nan::Set(obj, Nan::New("host").ToLocalChecked(), + Nan::New(details.host).ToLocalChecked()); + Nan::Set(obj, Nan::New("deadline").ToLocalChecked(), + Nan::New( + TimespecToMilliseconds(details.deadline)).ToLocalChecked()); + Nan::Set(obj, Nan::New("metadata").ToLocalChecked(), + ParseMetadata(&request_metadata)); + return scope.Escape(obj); + } + + bool ParseOp(Local value, grpc_op *out, + shared_ptr resources) { + return true; + } + + grpc_call *call; + grpc_call_details details; + grpc_metadata_array request_metadata; + + protected: + std::string GetTypeString() const { + return "new_call"; + } +}; + +Server::Server(grpc_server *server) : wrapped_server(server) { + shutdown_queue = grpc_completion_queue_create(NULL); + grpc_server_register_completion_queue(server, shutdown_queue, NULL); +} + +Server::~Server() { + this->ShutdownServer(); + grpc_completion_queue_shutdown(this->shutdown_queue); + grpc_server_destroy(this->wrapped_server); + grpc_completion_queue_destroy(this->shutdown_queue); +} + +void Server::Init(Local exports) { + HandleScope scope; + Local tpl = Nan::New(New); + tpl->SetClassName(Nan::New("Server").ToLocalChecked()); + tpl->InstanceTemplate()->SetInternalFieldCount(1); + Nan::SetPrototypeMethod(tpl, "requestCall", RequestCall); + Nan::SetPrototypeMethod(tpl, "addHttp2Port", AddHttp2Port); + Nan::SetPrototypeMethod(tpl, "start", Start); + Nan::SetPrototypeMethod(tpl, "tryShutdown", TryShutdown); + Nan::SetPrototypeMethod(tpl, "forceShutdown", ForceShutdown); + fun_tpl.Reset(tpl); + Local ctr = Nan::GetFunction(tpl).ToLocalChecked(); + Nan::Set(exports, Nan::New("Server").ToLocalChecked(), ctr); + constructor = new Callback(ctr); +} + +bool Server::HasInstance(Local val) { + HandleScope scope; + return Nan::New(fun_tpl)->HasInstance(val); +} + +void Server::ShutdownServer() { + grpc_server_shutdown_and_notify(this->wrapped_server, + this->shutdown_queue, + NULL); + grpc_server_cancel_all_calls(this->wrapped_server); + grpc_completion_queue_pluck(this->shutdown_queue, NULL, + gpr_inf_future(GPR_CLOCK_REALTIME), NULL); +} + +NAN_METHOD(Server::New) { + /* If this is not a constructor call, make a constructor call and return + the result */ + if (!info.IsConstructCall()) { + const int argc = 1; + Local argv[argc] = {info[0]}; + MaybeLocal maybe_instance = constructor->GetFunction()->NewInstance( + argc, argv); + if (maybe_instance.IsEmpty()) { + // There's probably a pending exception + return; + } else { + info.GetReturnValue().Set(maybe_instance.ToLocalChecked()); + return; + } + } + grpc_server *wrapped_server; + grpc_completion_queue *queue = CompletionQueueAsyncWorker::GetQueue(); + if (info[0]->IsUndefined()) { + wrapped_server = grpc_server_create(NULL, NULL); + } else if (info[0]->IsObject()) { + Local args_hash = Nan::To(info[0]).ToLocalChecked(); + Local keys = Nan::GetOwnPropertyNames(args_hash).ToLocalChecked(); + grpc_channel_args channel_args; + channel_args.num_args = keys->Length(); + channel_args.args = reinterpret_cast( + calloc(channel_args.num_args, sizeof(grpc_arg))); + /* These are used to keep all strings until then end of the block, then + destroy them */ + std::vector key_strings(keys->Length()); + std::vector value_strings(keys->Length()); + for (unsigned int i = 0; i < channel_args.num_args; i++) { + MaybeLocal maybe_key = Nan::To( + Nan::Get(keys, i).ToLocalChecked()); + if (maybe_key.IsEmpty()) { + free(channel_args.args); + return Nan::ThrowTypeError("Arg keys must be strings"); + } + Local current_key = maybe_key.ToLocalChecked(); + Local current_value = Nan::Get(args_hash, + current_key).ToLocalChecked(); + key_strings[i] = new Utf8String(current_key); + channel_args.args[i].key = **key_strings[i]; + if (current_value->IsInt32()) { + channel_args.args[i].type = GRPC_ARG_INTEGER; + channel_args.args[i].value.integer = Nan::To( + current_value).FromJust(); + } else if (current_value->IsString()) { + channel_args.args[i].type = GRPC_ARG_STRING; + value_strings[i] = new Utf8String(current_value); + channel_args.args[i].value.string = **value_strings[i]; + } else { + free(channel_args.args); + return Nan::ThrowTypeError("Arg values must be strings"); + } + } + wrapped_server = grpc_server_create(&channel_args, NULL); + free(channel_args.args); + } else { + return Nan::ThrowTypeError("Server expects an object"); + } + grpc_server_register_completion_queue(wrapped_server, queue, NULL); + Server *server = new Server(wrapped_server); + server->Wrap(info.This()); + info.GetReturnValue().Set(info.This()); +} + +NAN_METHOD(Server::RequestCall) { + if (!HasInstance(info.This())) { + return Nan::ThrowTypeError("requestCall can only be called on a Server"); + } + Server *server = ObjectWrap::Unwrap(info.This()); + NewCallOp *op = new NewCallOp(); + unique_ptr ops(new OpVec()); + ops->push_back(unique_ptr(op)); + grpc_call_error error = grpc_server_request_call( + server->wrapped_server, &op->call, &op->details, &op->request_metadata, + CompletionQueueAsyncWorker::GetQueue(), + CompletionQueueAsyncWorker::GetQueue(), + new struct tag(new Callback(info[0].As()), ops.release(), + shared_ptr(nullptr))); + if (error != GRPC_CALL_OK) { + return Nan::ThrowError(nanErrorWithCode("requestCall failed", error)); + } + CompletionQueueAsyncWorker::Next(); +} + +NAN_METHOD(Server::AddHttp2Port) { + if (!HasInstance(info.This())) { + return Nan::ThrowTypeError( + "addHttp2Port can only be called on a Server"); + } + if (!info[0]->IsString()) { + return Nan::ThrowTypeError( + "addHttp2Port's first argument must be a String"); + } + if (!ServerCredentials::HasInstance(info[1])) { + return Nan::ThrowTypeError( + "addHttp2Port's second argument must be ServerCredentials"); + } + Server *server = ObjectWrap::Unwrap(info.This()); + ServerCredentials *creds_object = ObjectWrap::Unwrap( + Nan::To(info[1]).ToLocalChecked()); + grpc_server_credentials *creds = creds_object->GetWrappedServerCredentials(); + int port; + if (creds == NULL) { + port = grpc_server_add_insecure_http2_port(server->wrapped_server, + *Utf8String(info[0])); + } else { + port = grpc_server_add_secure_http2_port(server->wrapped_server, + *Utf8String(info[0]), + creds); + } + info.GetReturnValue().Set(Nan::New(port)); +} + +NAN_METHOD(Server::Start) { + Nan::HandleScope scope; + if (!HasInstance(info.This())) { + return Nan::ThrowTypeError("start can only be called on a Server"); + } + Server *server = ObjectWrap::Unwrap(info.This()); + grpc_server_start(server->wrapped_server); +} + +NAN_METHOD(Server::TryShutdown) { + Nan::HandleScope scope; + if (!HasInstance(info.This())) { + return Nan::ThrowTypeError("tryShutdown can only be called on a Server"); + } + Server *server = ObjectWrap::Unwrap(info.This()); + unique_ptr ops(new OpVec()); + grpc_server_shutdown_and_notify( + server->wrapped_server, + CompletionQueueAsyncWorker::GetQueue(), + new struct tag(new Nan::Callback(info[0].As()), ops.release(), + shared_ptr(nullptr))); + CompletionQueueAsyncWorker::Next(); +} + +NAN_METHOD(Server::ForceShutdown) { + Nan::HandleScope scope; + if (!HasInstance(info.This())) { + return Nan::ThrowTypeError("forceShutdown can only be called on a Server"); + } + Server *server = ObjectWrap::Unwrap(info.This()); + server->ShutdownServer(); +} + +} // namespace node +} // namespace grpc diff --git a/src/node/ext/server.h b/src/node/ext/server.h new file mode 100644 index 00000000..ab5fc210 --- /dev/null +++ b/src/node/ext/server.h @@ -0,0 +1,82 @@ +/* + * + * Copyright 2015, 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 NET_GRPC_NODE_SERVER_H_ +#define NET_GRPC_NODE_SERVER_H_ + +#include +#include +#include "grpc/grpc.h" + +namespace grpc { +namespace node { + +/* Wraps grpc_server as a JavaScript object. Provides a constructor + and wrapper methods for grpc_server_create, grpc_server_request_call, + grpc_server_add_http2_port, and grpc_server_start. */ +class Server : public Nan::ObjectWrap { + public: + /* Initializes the Server class and exposes the constructor and + wrapper methods to JavaScript */ + static void Init(v8::Local exports); + /* Tests whether the given value was constructed by this class's + JavaScript constructor */ + static bool HasInstance(v8::Local val); + + private: + explicit Server(grpc_server *server); + ~Server(); + + // Prevent copying + Server(const Server &); + Server &operator=(const Server &); + + void ShutdownServer(); + + static NAN_METHOD(New); + static NAN_METHOD(RequestCall); + static NAN_METHOD(AddHttp2Port); + static NAN_METHOD(Start); + static NAN_METHOD(TryShutdown); + static NAN_METHOD(ForceShutdown); + static Nan::Callback *constructor; + static Nan::Persistent fun_tpl; + + grpc_server *wrapped_server; + grpc_completion_queue *shutdown_queue; +}; + +} // namespace node +} // namespace grpc + +#endif // NET_GRPC_NODE_SERVER_H_ diff --git a/src/node/ext/server_credentials.cc b/src/node/ext/server_credentials.cc new file mode 100644 index 00000000..5e922bd8 --- /dev/null +++ b/src/node/ext/server_credentials.cc @@ -0,0 +1,217 @@ +/* + * + * Copyright 2015, 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 + +#include "grpc/grpc.h" +#include "grpc/grpc_security.h" +#include "grpc/support/log.h" +#include "server_credentials.h" + +namespace grpc { +namespace node { + +using Nan::Callback; +using Nan::EscapableHandleScope; +using Nan::HandleScope; +using Nan::Maybe; +using Nan::MaybeLocal; +using Nan::ObjectWrap; +using Nan::Persistent; +using Nan::Utf8String; + +using v8::Array; +using v8::Exception; +using v8::External; +using v8::Function; +using v8::FunctionTemplate; +using v8::Integer; +using v8::Local; +using v8::Object; +using v8::ObjectTemplate; +using v8::String; +using v8::Value; + +Nan::Callback *ServerCredentials::constructor; +Persistent ServerCredentials::fun_tpl; + +ServerCredentials::ServerCredentials(grpc_server_credentials *credentials) + : wrapped_credentials(credentials) {} + +ServerCredentials::~ServerCredentials() { + grpc_server_credentials_release(wrapped_credentials); +} + +void ServerCredentials::Init(Local exports) { + Nan::HandleScope scope; + Local tpl = Nan::New(New); + tpl->SetClassName(Nan::New("ServerCredentials").ToLocalChecked()); + tpl->InstanceTemplate()->SetInternalFieldCount(1); + Local ctr = tpl->GetFunction(); + Nan::Set(ctr, Nan::New("createSsl").ToLocalChecked(), + Nan::GetFunction( + Nan::New(CreateSsl)).ToLocalChecked()); + Nan::Set(ctr, Nan::New("createInsecure").ToLocalChecked(), + Nan::GetFunction( + Nan::New(CreateInsecure)).ToLocalChecked()); + fun_tpl.Reset(tpl); + constructor = new Nan::Callback(ctr); + Nan::Set(exports, Nan::New("ServerCredentials").ToLocalChecked(), ctr); +} + +bool ServerCredentials::HasInstance(Local val) { + Nan::HandleScope scope; + return Nan::New(fun_tpl)->HasInstance(val); +} + +Local ServerCredentials::WrapStruct( + grpc_server_credentials *credentials) { + Nan::EscapableHandleScope scope; + const int argc = 1; + Local argv[argc] = { + Nan::New(reinterpret_cast(credentials))}; + MaybeLocal maybe_instance = Nan::NewInstance( + constructor->GetFunction(), argc, argv); + if (maybe_instance.IsEmpty()) { + return scope.Escape(Nan::Null()); + } else { + return scope.Escape(maybe_instance.ToLocalChecked()); + } +} + +grpc_server_credentials *ServerCredentials::GetWrappedServerCredentials() { + return wrapped_credentials; +} + +NAN_METHOD(ServerCredentials::New) { + if (info.IsConstructCall()) { + if (!info[0]->IsExternal()) { + return Nan::ThrowTypeError( + "ServerCredentials can only be created with the provide functions"); + } + Local ext = info[0].As(); + grpc_server_credentials *creds_value = + reinterpret_cast(ext->Value()); + ServerCredentials *credentials = new ServerCredentials(creds_value); + credentials->Wrap(info.This()); + info.GetReturnValue().Set(info.This()); + } else { + const int argc = 1; + Local argv[argc] = {info[0]}; + MaybeLocal maybe_instance = constructor->GetFunction()->NewInstance( + argc, argv); + if (maybe_instance.IsEmpty()) { + // There's probably a pending exception + return; + } else { + info.GetReturnValue().Set(maybe_instance.ToLocalChecked()); + } + } +} + +NAN_METHOD(ServerCredentials::CreateSsl) { + Nan::HandleScope scope; + char *root_certs = NULL; + if (::node::Buffer::HasInstance(info[0])) { + root_certs = ::node::Buffer::Data(info[0]); + } else if (!(info[0]->IsNull() || info[0]->IsUndefined())) { + return Nan::ThrowTypeError( + "createSSl's first argument must be a Buffer if provided"); + } + if (!info[1]->IsArray()) { + return Nan::ThrowTypeError( + "createSsl's second argument must be a list of objects"); + } + int force_client_auth = 0; + if (info[2]->IsBoolean()) { + force_client_auth = (int)Nan::To(info[2]).FromJust(); + } else if (!(info[2]->IsUndefined() || info[2]->IsNull())) { + return Nan::ThrowTypeError( + "createSsl's third argument must be a boolean if provided"); + } + Local pair_list = Local::Cast(info[1]); + uint32_t key_cert_pair_count = pair_list->Length(); + grpc_ssl_pem_key_cert_pair *key_cert_pairs = new grpc_ssl_pem_key_cert_pair[ + key_cert_pair_count]; + + Local key_key = Nan::New("private_key").ToLocalChecked(); + Local cert_key = Nan::New("cert_chain").ToLocalChecked(); + + for(uint32_t i = 0; i < key_cert_pair_count; i++) { + Local pair_val = Nan::Get(pair_list, i).ToLocalChecked(); + if (!pair_val->IsObject()) { + delete key_cert_pairs; + return Nan::ThrowTypeError("Key/cert pairs must be objects"); + } + Local pair_obj = Nan::To(pair_val).ToLocalChecked(); + MaybeLocal maybe_key = Nan::Get(pair_obj, key_key); + if (maybe_key.IsEmpty()) { + delete key_cert_pairs; + return Nan::ThrowTypeError( + "Key/cert pairs must have a private_key and a cert_chain"); + } + MaybeLocal maybe_cert = Nan::Get(pair_obj, cert_key); + if (maybe_cert.IsEmpty()) { + delete key_cert_pairs; + return Nan::ThrowTypeError( + "Key/cert pairs must have a private_key and a cert_chain"); + } + if (!::node::Buffer::HasInstance(maybe_key.ToLocalChecked())) { + delete key_cert_pairs; + return Nan::ThrowTypeError("private_key must be a Buffer"); + } + if (!::node::Buffer::HasInstance(maybe_cert.ToLocalChecked())) { + delete key_cert_pairs; + return Nan::ThrowTypeError("cert_chain must be a Buffer"); + } + key_cert_pairs[i].private_key = ::node::Buffer::Data( + maybe_key.ToLocalChecked()); + key_cert_pairs[i].cert_chain = ::node::Buffer::Data( + maybe_cert.ToLocalChecked()); + } + grpc_server_credentials *creds = grpc_ssl_server_credentials_create( + root_certs, key_cert_pairs, key_cert_pair_count, force_client_auth, NULL); + delete key_cert_pairs; + if (creds == NULL) { + info.GetReturnValue().SetNull(); + } else { + info.GetReturnValue().Set(WrapStruct(creds)); + } +} + +NAN_METHOD(ServerCredentials::CreateInsecure) { + info.GetReturnValue().Set(WrapStruct(NULL)); +} + +} // namespace node +} // namespace grpc diff --git a/src/node/ext/server_credentials.h b/src/node/ext/server_credentials.h new file mode 100644 index 00000000..bf279e48 --- /dev/null +++ b/src/node/ext/server_credentials.h @@ -0,0 +1,77 @@ +/* + * + * Copyright 2015, 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 NET_GRPC_NODE_SERVER_CREDENTIALS_H_ +#define NET_GRPC_NODE_SERVER_CREDENTIALS_H_ + +#include +#include +#include "grpc/grpc.h" +#include "grpc/grpc_security.h" + +namespace grpc { +namespace node { + +/* Wrapper class for grpc_server_credentials structs */ +class ServerCredentials : public Nan::ObjectWrap { + public: + static void Init(v8::Local exports); + static bool HasInstance(v8::Local val); + /* Wrap a grpc_server_credentials struct in a javascript object */ + static v8::Local WrapStruct(grpc_server_credentials *credentials); + + /* Returns the grpc_server_credentials struct that this object wraps */ + grpc_server_credentials *GetWrappedServerCredentials(); + + private: + explicit ServerCredentials(grpc_server_credentials *credentials); + ~ServerCredentials(); + + // Prevent copying + ServerCredentials(const ServerCredentials &); + ServerCredentials &operator=(const ServerCredentials &); + + static NAN_METHOD(New); + static NAN_METHOD(CreateSsl); + static NAN_METHOD(CreateInsecure); + static Nan::Callback *constructor; + // Used for typechecking instances of this javascript class + static Nan::Persistent fun_tpl; + + grpc_server_credentials *wrapped_credentials; +}; + +} // namespace node +} // namespace grpc + +#endif // NET_GRPC_NODE_SERVER_CREDENTIALS_H_ diff --git a/src/node/ext/timeval.cc b/src/node/ext/timeval.cc new file mode 100644 index 00000000..bf68513c --- /dev/null +++ b/src/node/ext/timeval.cc @@ -0,0 +1,67 @@ +/* + * + * Copyright 2015, 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 + +#include "grpc/grpc.h" +#include "grpc/support/time.h" +#include "timeval.h" + +namespace grpc { +namespace node { + +gpr_timespec MillisecondsToTimespec(double millis) { + if (millis == std::numeric_limits::infinity()) { + return gpr_inf_future(GPR_CLOCK_REALTIME); + } else if (millis == -std::numeric_limits::infinity()) { + return gpr_inf_past(GPR_CLOCK_REALTIME); + } else { + return gpr_time_from_micros(static_cast(millis * 1000), + GPR_CLOCK_REALTIME); + } +} + +double TimespecToMilliseconds(gpr_timespec timespec) { + timespec = gpr_convert_clock_type(timespec, GPR_CLOCK_REALTIME); + if (gpr_time_cmp(timespec, gpr_inf_future(GPR_CLOCK_REALTIME)) == 0) { + return std::numeric_limits::infinity(); + } else if (gpr_time_cmp(timespec, gpr_inf_past(GPR_CLOCK_REALTIME)) == 0) { + return -std::numeric_limits::infinity(); + } else { + return (static_cast(timespec.tv_sec) * 1000 + + static_cast(timespec.tv_nsec) / 1000000); + } +} + +} // namespace node +} // namespace grpc diff --git a/src/node/ext/timeval.h b/src/node/ext/timeval.h new file mode 100644 index 00000000..0cada5ac --- /dev/null +++ b/src/node/ext/timeval.h @@ -0,0 +1,48 @@ +/* + * + * Copyright 2015, 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 NET_GRPC_NODE_TIMEVAL_H_ +#define NET_GRPC_NODE_TIMEVAL_H_ + +#include "grpc/support/time.h" + +namespace grpc { +namespace node { + +double TimespecToMilliseconds(gpr_timespec time); +gpr_timespec MillisecondsToTimespec(double millis); + +} // namespace node +} // namespace grpc + +#endif // NET_GRPC_NODE_TIMEVAL_H_ diff --git a/src/node/health_check/health.js b/src/node/health_check/health.js new file mode 100644 index 00000000..84d7e056 --- /dev/null +++ b/src/node/health_check/health.js @@ -0,0 +1,66 @@ +/* + * + * Copyright 2015, 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. + * + */ + +'use strict'; + +var grpc = require('../'); + +var _ = require('lodash'); + +var health_proto = grpc.load(__dirname + '/health.proto'); + +var HealthClient = health_proto.grpc.health.v1alpha.Health; + +function HealthImplementation(statusMap) { + this.statusMap = _.clone(statusMap); +} + +HealthImplementation.prototype.setStatus = function(service, status) { + this.statusMap[service] = status; +}; + +HealthImplementation.prototype.check = function(call, callback){ + var service = call.request.service; + var status = _.get(this.statusMap, service, null); + if (status === null) { + callback({code:grpc.status.NOT_FOUND}); + } else { + callback(null, {status: status}); + } +}; + +module.exports = { + Client: HealthClient, + service: HealthClient.service, + Implementation: HealthImplementation +}; diff --git a/src/node/health_check/health.proto b/src/node/health_check/health.proto new file mode 100644 index 00000000..57f4aaa9 --- /dev/null +++ b/src/node/health_check/health.proto @@ -0,0 +1,49 @@ +// Copyright 2015, 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. + +syntax = "proto3"; + +package grpc.health.v1alpha; + +message HealthCheckRequest { + string service = 1; +} + +message HealthCheckResponse { + enum ServingStatus { + UNKNOWN = 0; + SERVING = 1; + NOT_SERVING = 2; + } + ServingStatus status = 1; +} + +service Health { + rpc Check(HealthCheckRequest) returns (HealthCheckResponse); +} diff --git a/src/node/index.js b/src/node/index.js new file mode 100644 index 00000000..02b73f66 --- /dev/null +++ b/src/node/index.js @@ -0,0 +1,176 @@ +/* + * + * Copyright 2015, 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. + * + */ + +'use strict'; + +var _ = require('lodash'); + +var ProtoBuf = require('protobufjs'); + +var client = require('./src/client.js'); + +var server = require('./src/server.js'); + +var Metadata = require('./src/metadata.js'); + +var grpc = require('bindings')('grpc'); + +/** + * Load a gRPC object from an existing ProtoBuf.Reflect object. + * @param {ProtoBuf.Reflect.Namespace} value The ProtoBuf object to load. + * @return {Object} The resulting gRPC object + */ +exports.loadObject = function loadObject(value) { + var result = {}; + if (value.className === 'Namespace') { + _.each(value.children, function(child) { + result[child.name] = loadObject(child); + }); + return result; + } else if (value.className === 'Service') { + return client.makeProtobufClientConstructor(value); + } else if (value.className === 'Message' || value.className === 'Enum') { + return value.build(); + } else { + return value; + } +}; + +var loadObject = exports.loadObject; + +/** + * Load a gRPC object from a .proto file. + * @param {string} filename The file to load + * @param {string=} format The file format to expect. Must be either 'proto' or + * 'json'. Defaults to 'proto' + * @return {Object} The resulting gRPC object + */ +exports.load = function load(filename, format) { + if (!format) { + format = 'proto'; + } + var builder; + switch(format) { + case 'proto': + builder = ProtoBuf.loadProtoFile(filename); + break; + case 'json': + builder = ProtoBuf.loadJsonFile(filename); + break; + default: + throw new Error('Unrecognized format "' + format + '"'); + } + + return loadObject(builder.ns); +}; + +/** + * Get a function that a client can use to update metadata with authentication + * information from a Google Auth credential object, which comes from the + * google-auth-library. + * @param {Object} credential The credential object to use + * @return {function(Object, callback)} Metadata updater function + */ +exports.getGoogleAuthDelegate = function getGoogleAuthDelegate(credential) { + /** + * Update a metadata object with authentication information. + * @param {string} authURI The uri to authenticate to + * @param {Object} metadata Metadata object + * @param {function(Error, Object)} callback + */ + return function updateMetadata(authURI, metadata, callback) { + credential.getRequestMetadata(authURI, function(err, header) { + if (err) { + callback(err); + return; + } + metadata.add('authorization', header.Authorization); + callback(null, metadata); + }); + }; +}; + +/** + * @see module:src/server.Server + */ +exports.Server = server.Server; + +/** + * @see module:src/metadata + */ +exports.Metadata = Metadata; + +/** + * Status name to code number mapping + */ +exports.status = grpc.status; + +/** + * Propagate flag name to number mapping + */ +exports.propagate = grpc.propagate; + +/** + * Call error name to code number mapping + */ +exports.callError = grpc.callError; + +/** + * Write flag name to code number mapping + */ +exports.writeFlags = grpc.writeFlags; + +/** + * Credentials factories + */ +exports.Credentials = grpc.Credentials; + +/** + * ServerCredentials factories + */ +exports.ServerCredentials = grpc.ServerCredentials; + +/** + * @see module:src/client.makeClientConstructor + */ +exports.makeGenericClientConstructor = client.makeClientConstructor; + +/** + * @see module:src/client.getClientChannel + */ +exports.getClientChannel = client.getClientChannel; + +/** + * @see module:src/client.waitForClientReady + */ +exports.waitForClientReady = client.waitForClientReady; diff --git a/src/node/interop/empty.proto b/src/node/interop/empty.proto new file mode 100644 index 00000000..6d0eb937 --- /dev/null +++ b/src/node/interop/empty.proto @@ -0,0 +1,43 @@ + +// Copyright 2015, 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. + +syntax = "proto3"; + +package grpc.testing; + +// An empty message that you can re-use to avoid defining duplicated empty +// messages in your project. A typical example is to use it as argument or the +// return value of a service API. For instance: +// +// service Foo { +// rpc Bar (grpc.testing.Empty) returns (grpc.testing.Empty) { }; +// }; +// +message Empty {} diff --git a/src/node/interop/interop_client.js b/src/node/interop/interop_client.js new file mode 100644 index 00000000..215d4212 --- /dev/null +++ b/src/node/interop/interop_client.js @@ -0,0 +1,420 @@ +/* + * + * Copyright 2015, 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. + * + */ + +'use strict'; + +var fs = require('fs'); +var path = require('path'); +var _ = require('lodash'); +var grpc = require('..'); +var testProto = grpc.load(__dirname + '/test.proto').grpc.testing; +var GoogleAuth = require('google-auth-library'); + +var assert = require('assert'); + +var AUTH_SCOPE = 'https://www.googleapis.com/auth/xapi.zoo'; +var AUTH_SCOPE_RESPONSE = 'xapi.zoo'; +var AUTH_USER = ('155450119199-vefjjaekcc6cmsd5914v6lqufunmh9ue' + + '@developer.gserviceaccount.com'); +var COMPUTE_ENGINE_USER = ('155450119199-r5aaqa2vqoa9g5mv2m6s3m1l293rlmel' + + '@developer.gserviceaccount.com'); + +/** + * Create a buffer filled with size zeroes + * @param {number} size The length of the buffer + * @return {Buffer} The new buffer + */ +function zeroBuffer(size) { + var zeros = new Buffer(size); + zeros.fill(0); + return zeros; +} + +/** + * Run the empty_unary test + * @param {Client} client The client to test against + * @param {function} done Callback to call when the test is completed. Included + * primarily for use with mocha + */ +function emptyUnary(client, done) { + client.emptyCall({}, function(err, resp) { + assert.ifError(err); + if (done) { + done(); + } + }); +} + +/** + * Run the large_unary test + * @param {Client} client The client to test against + * @param {function} done Callback to call when the test is completed. Included + * primarily for use with mocha + */ +function largeUnary(client, done) { + var arg = { + response_type: 'COMPRESSABLE', + response_size: 314159, + payload: { + body: zeroBuffer(271828) + } + }; + client.unaryCall(arg, function(err, resp) { + assert.ifError(err); + assert.strictEqual(resp.payload.type, 'COMPRESSABLE'); + assert.strictEqual(resp.payload.body.length, 314159); + if (done) { + done(); + } + }); +} + +/** + * Run the client_streaming test + * @param {Client} client The client to test against + * @param {function} done Callback to call when the test is completed. Included + * primarily for use with mocha + */ +function clientStreaming(client, done) { + var call = client.streamingInputCall(function(err, resp) { + assert.ifError(err); + assert.strictEqual(resp.aggregated_payload_size, 74922); + if (done) { + done(); + } + }); + var payload_sizes = [27182, 8, 1828, 45904]; + for (var i = 0; i < payload_sizes.length; i++) { + call.write({payload: {body: zeroBuffer(payload_sizes[i])}}); + } + call.end(); +} + +/** + * Run the server_streaming test + * @param {Client} client The client to test against + * @param {function} done Callback to call when the test is completed. Included + * primarily for use with mocha + */ +function serverStreaming(client, done) { + var arg = { + response_type: 'COMPRESSABLE', + response_parameters: [ + {size: 31415}, + {size: 9}, + {size: 2653}, + {size: 58979} + ] + }; + var call = client.streamingOutputCall(arg); + var resp_index = 0; + call.on('data', function(value) { + assert(resp_index < 4); + assert.strictEqual(value.payload.type, 'COMPRESSABLE'); + assert.strictEqual(value.payload.body.length, + arg.response_parameters[resp_index].size); + resp_index += 1; + }); + call.on('end', function() { + assert.strictEqual(resp_index, 4); + if (done) { + done(); + } + }); + call.on('status', function(status) { + assert.strictEqual(status.code, grpc.status.OK); + }); +} + +/** + * Run the ping_pong test + * @param {Client} client The client to test against + * @param {function} done Callback to call when the test is completed. Included + * primarily for use with mocha + */ +function pingPong(client, done) { + var payload_sizes = [27182, 8, 1828, 45904]; + var response_sizes = [31415, 9, 2653, 58979]; + var call = client.fullDuplexCall(); + call.on('status', function(status) { + assert.strictEqual(status.code, grpc.status.OK); + if (done) { + done(); + } + }); + var index = 0; + call.write({ + response_type: 'COMPRESSABLE', + response_parameters: [ + {size: response_sizes[index]} + ], + payload: {body: zeroBuffer(payload_sizes[index])} + }); + call.on('data', function(response) { + assert.strictEqual(response.payload.type, 'COMPRESSABLE'); + assert.equal(response.payload.body.length, response_sizes[index]); + index += 1; + if (index === 4) { + call.end(); + } else { + call.write({ + response_type: 'COMPRESSABLE', + response_parameters: [ + {size: response_sizes[index]} + ], + payload: {body: zeroBuffer(payload_sizes[index])} + }); + } + }); +} + +/** + * Run the empty_stream test. + * @param {Client} client The client to test against + * @param {function} done Callback to call when the test is completed. Included + * primarily for use with mocha + */ +function emptyStream(client, done) { + var call = client.fullDuplexCall(); + call.on('status', function(status) { + assert.strictEqual(status.code, grpc.status.OK); + if (done) { + done(); + } + }); + call.on('data', function(value) { + assert.fail(value, null, 'No data should have been received', '!=='); + }); + call.end(); +} + +/** + * Run the cancel_after_begin test. + * @param {Client} client The client to test against + * @param {function} done Callback to call when the test is completed. Included + * primarily for use with mocha + */ +function cancelAfterBegin(client, done) { + var call = client.streamingInputCall(function(err, resp) { + assert.strictEqual(err.code, grpc.status.CANCELLED); + done(); + }); + call.cancel(); +} + +/** + * Run the cancel_after_first_response test. + * @param {Client} client The client to test against + * @param {function} done Callback to call when the test is completed. Included + * primarily for use with mocha + */ +function cancelAfterFirstResponse(client, done) { + var call = client.fullDuplexCall(); + call.write({ + response_type: 'COMPRESSABLE', + response_parameters: [ + {size: 31415} + ], + payload: {body: zeroBuffer(27182)} + }); + call.on('data', function(data) { + call.cancel(); + }); + call.on('error', function(error) { + assert.strictEqual(error.code, grpc.status.CANCELLED); + done(); + }); +} + +function timeoutOnSleepingServer(client, done) { + var deadline = new Date(); + deadline.setMilliseconds(deadline.getMilliseconds() + 1); + var call = client.fullDuplexCall(null, {deadline: deadline}); + call.write({ + payload: {body: zeroBuffer(27182)} + }); + call.on('error', function(error) { + + assert(error.code === grpc.status.DEADLINE_EXCEEDED || + error.code === grpc.status.INTERNAL); + done(); + }); +} + +/** + * Run one of the authentication tests. + * @param {string} expected_user The expected username in the response + * @param {Client} client The client to test against + * @param {?string} scope The scope to apply to the credentials + * @param {function} done Callback to call when the test is completed. Included + * primarily for use with mocha + */ +function authTest(expected_user, scope, client, done) { + (new GoogleAuth()).getApplicationDefault(function(err, credential) { + assert.ifError(err); + if (credential.createScopedRequired() && scope) { + credential = credential.createScoped(scope); + } + client.$updateMetadata = grpc.getGoogleAuthDelegate(credential); + var arg = { + response_type: 'COMPRESSABLE', + response_size: 314159, + payload: { + body: zeroBuffer(271828) + }, + fill_username: true, + fill_oauth_scope: true + }; + client.unaryCall(arg, function(err, resp) { + assert.ifError(err); + assert.strictEqual(resp.payload.type, 'COMPRESSABLE'); + assert.strictEqual(resp.payload.body.length, 314159); + assert.strictEqual(resp.username, expected_user); + if (scope) { + assert.strictEqual(resp.oauth_scope, AUTH_SCOPE_RESPONSE); + } + if (done) { + done(); + } + }); + }); +} + +function oauth2Test(expected_user, scope, per_rpc, client, done) { + (new GoogleAuth()).getApplicationDefault(function(err, credential) { + assert.ifError(err); + var arg = { + fill_username: true, + fill_oauth_scope: true + }; + credential = credential.createScoped(scope); + credential.getAccessToken(function(err, token) { + assert.ifError(err); + var updateMetadata = function(authURI, metadata, callback) { + metadata.add('authorization', 'Bearer ' + token); + callback(null, metadata); + }; + var makeTestCall = function(error, client_metadata) { + assert.ifError(error); + client.unaryCall(arg, function(err, resp) { + assert.ifError(err); + assert.strictEqual(resp.username, expected_user); + assert.strictEqual(resp.oauth_scope, AUTH_SCOPE_RESPONSE); + if (done) { + done(); + } + }, client_metadata); + }; + if (per_rpc) { + updateMetadata('', new grpc.Metadata(), makeTestCall); + } else { + client.$updateMetadata = updateMetadata; + makeTestCall(null, new grpc.Metadata()); + } + }); + }); +} + +/** + * Map from test case names to test functions + */ +var test_cases = { + empty_unary: emptyUnary, + large_unary: largeUnary, + client_streaming: clientStreaming, + server_streaming: serverStreaming, + ping_pong: pingPong, + empty_stream: emptyStream, + cancel_after_begin: cancelAfterBegin, + cancel_after_first_response: cancelAfterFirstResponse, + timeout_on_sleeping_server: timeoutOnSleepingServer, + compute_engine_creds: _.partial(authTest, COMPUTE_ENGINE_USER, null), + service_account_creds: _.partial(authTest, AUTH_USER, AUTH_SCOPE), + jwt_token_creds: _.partial(authTest, AUTH_USER, null), + oauth2_auth_token: _.partial(oauth2Test, AUTH_USER, AUTH_SCOPE, false), + per_rpc_creds: _.partial(oauth2Test, AUTH_USER, AUTH_SCOPE, true) +}; + +/** + * Execute a single test case. + * @param {string} address The address of the server to connect to, in the + * format 'hostname:port' + * @param {string} host_overrirde The hostname of the server to use as an SSL + * override + * @param {string} test_case The name of the test case to run + * @param {bool} tls Indicates that a secure channel should be used + * @param {function} done Callback to call when the test is completed. Included + * primarily for use with mocha + */ +function runTest(address, host_override, test_case, tls, test_ca, done) { + // TODO(mlumish): enable TLS functionality + var options = {}; + var creds; + if (tls) { + var ca_path; + if (test_ca) { + ca_path = path.join(__dirname, '../test/data/ca.pem'); + } else { + ca_path = process.env.SSL_CERT_FILE; + } + var ca_data = fs.readFileSync(ca_path); + creds = grpc.Credentials.createSsl(ca_data); + if (host_override) { + options['grpc.ssl_target_name_override'] = host_override; + options['grpc.default_authority'] = host_override; + } + } else { + creds = grpc.Credentials.createInsecure(); + } + var client = new testProto.TestService(address, creds, options); + + test_cases[test_case](client, done); +} + +if (require.main === module) { + var parseArgs = require('minimist'); + var argv = parseArgs(process.argv, { + string: ['server_host', 'server_host_override', 'server_port', 'test_case', + 'use_tls', 'use_test_ca'] + }); + runTest(argv.server_host + ':' + argv.server_port, argv.server_host_override, + argv.test_case, argv.use_tls === 'true', argv.use_test_ca === 'true', + function () { + console.log('OK:', argv.test_case); + }); +} + +/** + * See docs for runTest + */ +exports.runTest = runTest; diff --git a/src/node/interop/interop_server.js b/src/node/interop/interop_server.js new file mode 100644 index 00000000..99155e99 --- /dev/null +++ b/src/node/interop/interop_server.js @@ -0,0 +1,203 @@ +/* + * + * Copyright 2015, 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. + * + */ + +'use strict'; + +var fs = require('fs'); +var path = require('path'); +var _ = require('lodash'); +var grpc = require('..'); +var testProto = grpc.load(__dirname + '/test.proto').grpc.testing; + +/** + * Create a buffer filled with size zeroes + * @param {number} size The length of the buffer + * @return {Buffer} The new buffer + */ +function zeroBuffer(size) { + var zeros = new Buffer(size); + zeros.fill(0); + return zeros; +} + +/** + * Respond to an empty parameter with an empty response. + * NOTE: this currently does not work due to issue #137 + * @param {Call} call Call to handle + * @param {function(Error, Object)} callback Callback to call with result + * or error + */ +function handleEmpty(call, callback) { + callback(null, {}); +} + +/** + * Handle a unary request by sending the requested payload + * @param {Call} call Call to handle + * @param {function(Error, Object)} callback Callback to call with result or + * error + */ +function handleUnary(call, callback) { + var req = call.request; + var zeros = zeroBuffer(req.response_size); + var payload_type = req.response_type; + if (payload_type === 'RANDOM') { + payload_type = ['COMPRESSABLE', + 'UNCOMPRESSABLE'][Math.random() < 0.5 ? 0 : 1]; + } + callback(null, {payload: {type: payload_type, body: zeros}}); +} + +/** + * Respond to a streaming call with the total size of all payloads + * @param {Call} call Call to handle + * @param {function(Error, Object)} callback Callback to call with result or + * error + */ +function handleStreamingInput(call, callback) { + var aggregate_size = 0; + call.on('data', function(value) { + aggregate_size += value.payload.body.length; + }); + call.on('end', function() { + callback(null, {aggregated_payload_size: aggregate_size}); + }); +} + +/** + * Respond to a payload request with a stream of the requested payloads + * @param {Call} call Call to handle + */ +function handleStreamingOutput(call) { + var req = call.request; + var payload_type = req.response_type; + if (payload_type === 'RANDOM') { + payload_type = ['COMPRESSABLE', + 'UNCOMPRESSABLE'][Math.random() < 0.5 ? 0 : 1]; + } + _.each(req.response_parameters, function(resp_param) { + call.write({ + payload: { + body: zeroBuffer(resp_param.size), + type: payload_type + } + }); + }); + call.end(); +} + +/** + * Respond to a stream of payload requests with a stream of payload responses as + * they arrive. + * @param {Call} call Call to handle + */ +function handleFullDuplex(call) { + call.on('data', function(value) { + var payload_type = value.response_type; + if (payload_type === 'RANDOM') { + payload_type = ['COMPRESSABLE', + 'UNCOMPRESSABLE'][Math.random() < 0.5 ? 0 : 1]; + } + _.each(value.response_parameters, function(resp_param) { + call.write({ + payload: { + body: zeroBuffer(resp_param.size), + type: payload_type + } + }); + }); + }); + call.on('end', function() { + call.end(); + }); +} + +/** + * Respond to a stream of payload requests with a stream of payload responses + * after all requests have arrived + * @param {Call} call Call to handle + */ +function handleHalfDuplex(call) { + throw new Error('HalfDuplexCall not yet implemented'); +} + +/** + * Get a server object bound to the given port + * @param {string} port Port to which to bind + * @param {boolean} tls Indicates that the bound port should use TLS + * @return {{server: Server, port: number}} Server object bound to the support, + * and port number that the server is bound to + */ +function getServer(port, tls) { + // TODO(mlumish): enable TLS functionality + var options = {}; + var server_creds; + if (tls) { + var key_path = path.join(__dirname, '../test/data/server1.key'); + var pem_path = path.join(__dirname, '../test/data/server1.pem'); + + var key_data = fs.readFileSync(key_path); + var pem_data = fs.readFileSync(pem_path); + server_creds = grpc.ServerCredentials.createSsl(null, + [{private_key: key_data, + cert_chain: pem_data}]); + } else { + server_creds = grpc.ServerCredentials.createInsecure(); + } + var server = new grpc.Server(options); + server.addProtoService(testProto.TestService.service, { + emptyCall: handleEmpty, + unaryCall: handleUnary, + streamingOutputCall: handleStreamingOutput, + streamingInputCall: handleStreamingInput, + fullDuplexCall: handleFullDuplex, + halfDuplexCall: handleHalfDuplex + }); + var port_num = server.bind('0.0.0.0:' + port, server_creds); + return {server: server, port: port_num}; +} + +if (require.main === module) { + var parseArgs = require('minimist'); + var argv = parseArgs(process.argv, { + string: ['port', 'use_tls'] + }); + var server_obj = getServer(argv.port, argv.use_tls === 'true'); + console.log('Server attaching to port ' + argv.port); + server_obj.server.start(); +} + +/** + * See docs for getServer + */ +exports.getServer = getServer; diff --git a/src/node/interop/messages.proto b/src/node/interop/messages.proto new file mode 100644 index 00000000..7df85e3c --- /dev/null +++ b/src/node/interop/messages.proto @@ -0,0 +1,132 @@ + +// Copyright 2015, 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. + +// Message definitions to be used by integration test service definitions. + +syntax = "proto3"; + +package grpc.testing; + +// The type of payload that should be returned. +enum PayloadType { + // Compressable text format. + COMPRESSABLE = 0; + + // Uncompressable binary format. + UNCOMPRESSABLE = 1; + + // Randomly chosen from all other formats defined in this enum. + RANDOM = 2; +} + +// A block of data, to simply increase gRPC message size. +message Payload { + // The type of data in body. + PayloadType type = 1; + // Primary contents of payload. + bytes body = 2; +} + +// Unary request. +message SimpleRequest { + // Desired payload type in the response from the server. + // If response_type is RANDOM, server randomly chooses one from other formats. + PayloadType response_type = 1; + + // Desired payload size in the response from the server. + // If response_type is COMPRESSABLE, this denotes the size before compression. + int32 response_size = 2; + + // Optional input payload sent along with the request. + Payload payload = 3; + + // Whether SimpleResponse should include username. + bool fill_username = 4; + + // Whether SimpleResponse should include OAuth scope. + bool fill_oauth_scope = 5; +} + +// Unary response, as configured by the request. +message SimpleResponse { + // Payload to increase message size. + Payload payload = 1; + // The user the request came from, for verifying authentication was + // successful when the client expected it. + string username = 2; + // OAuth scope. + string oauth_scope = 3; +} + +// Client-streaming request. +message StreamingInputCallRequest { + // Optional input payload sent along with the request. + Payload payload = 1; + + // Not expecting any payload from the response. +} + +// Client-streaming response. +message StreamingInputCallResponse { + // Aggregated size of payloads received from the client. + int32 aggregated_payload_size = 1; +} + +// Configuration for a particular response. +message ResponseParameters { + // Desired payload sizes in responses from the server. + // If response_type is COMPRESSABLE, this denotes the size before compression. + int32 size = 1; + + // Desired interval between consecutive responses in the response stream in + // microseconds. + int32 interval_us = 2; +} + +// Server-streaming request. +message StreamingOutputCallRequest { + // Desired payload type in the response from the server. + // If response_type is RANDOM, the payload from each response in the stream + // might be of different types. This is to simulate a mixed type of payload + // stream. + PayloadType response_type = 1; + + // Configuration for each expected response message. + repeated ResponseParameters response_parameters = 2; + + // Optional input payload sent along with the request. + Payload payload = 3; +} + +// Server-streaming response, as configured by the request and parameters. +message StreamingOutputCallResponse { + // Payload to increase response size. + Payload payload = 1; +} diff --git a/src/node/interop/test.proto b/src/node/interop/test.proto new file mode 100644 index 00000000..d2c3f9be --- /dev/null +++ b/src/node/interop/test.proto @@ -0,0 +1,73 @@ + +// Copyright 2015, 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. + +// An integration test service that covers all the method signature permutations +// of unary/streaming requests/responses. + +syntax = "proto3"; + +import "empty.proto"; +import "messages.proto"; + +package grpc.testing; + +// A simple service to test the various types of RPCs and experiment with +// performance with various types of payload. +service TestService { + // One empty request followed by one empty response. + rpc EmptyCall(grpc.testing.Empty) returns (grpc.testing.Empty); + + // One request followed by one response. + // TODO(Issue 527): Describe required server behavior. + rpc UnaryCall(SimpleRequest) returns (SimpleResponse); + + // One request followed by a sequence of responses (streamed download). + // The server returns the payload with client desired type and sizes. + rpc StreamingOutputCall(StreamingOutputCallRequest) + returns (stream StreamingOutputCallResponse); + + // A sequence of requests followed by one response (streamed upload). + // The server returns the aggregated size of client payload as the result. + rpc StreamingInputCall(stream StreamingInputCallRequest) + returns (StreamingInputCallResponse); + + // A sequence of requests with each request served by the server immediately. + // As one request could lead to multiple responses, this interface + // demonstrates the idea of full duplexing. + rpc FullDuplexCall(stream StreamingOutputCallRequest) + returns (stream StreamingOutputCallResponse); + + // A sequence of requests followed by a sequence of responses. + // The server buffers all the client requests and then serves them in order. A + // stream of responses are returned to the client when the server starts with + // first request. + rpc HalfDuplexCall(stream StreamingOutputCallRequest) + returns (stream StreamingOutputCallResponse); +} diff --git a/src/node/jsdoc_conf.json b/src/node/jsdoc_conf.json new file mode 100644 index 00000000..876a8e19 --- /dev/null +++ b/src/node/jsdoc_conf.json @@ -0,0 +1,22 @@ +{ + "tags": { + "allowUnknownTags": true + }, + "source": { + "include": [ "index.js", "src" ], + "includePattern": ".+\\.js(doc)?$", + "excludePattern": "(^|\\/|\\\\)_" + }, + "opts": { + "package": "package.json", + "readme": "README.md" + }, + "plugins": [], + "templates": { + "cleverLinks": false, + "monospaceLinks": false, + "default": { + "outputSourceFiles": true + } + } +} diff --git a/src/node/package.json b/src/node/package.json new file mode 100644 index 00000000..22f94757 --- /dev/null +++ b/src/node/package.json @@ -0,0 +1,61 @@ +{ + "name": "grpc", + "version": "0.11.1", + "author": "Google Inc.", + "description": "gRPC Library for Node", + "homepage": "http://www.grpc.io/", + "repository": { + "type": "git", + "url": "https://github.com/grpc/grpc.git" + }, + "bugs": "https://github.com/grpc/grpc/issues", + "contributors": [ + { + "name": "Michael Lumish", + "email": "mlumish@google.com" + } + ], + "directories": { + "lib": "src", + "example": "examples" + }, + "scripts": { + "lint": "node ./node_modules/jshint/bin/jshint src test examples interop index.js", + "test": "node ./node_modules/mocha/bin/mocha && npm run-script lint", + "gen_docs": "./node_modules/.bin/jsdoc -c jsdoc_conf.json" + }, + "dependencies": { + "bindings": "^1.2.0", + "lodash": "^3.9.3", + "nan": "^2.0.0", + "protobufjs": "^4.0.0" + }, + "devDependencies": { + "async": "^0.9.0", + "google-auth-library": "^0.9.2", + "jsdoc": "^3.3.2", + "jshint": "^2.5.0", + "minimist": "^1.1.0", + "mocha": "~1.21.0", + "mustache": "^2.0.0", + "strftime": "^0.8.2" + }, + "engines": { + "node": ">=0.10.13" + }, + "files": [ + "LICENSE", + "README.md", + "index.js", + "binding.gyp", + "bin", + "cli", + "examples", + "ext", + "interop", + "src", + "test" + ], + "main": "index.js", + "license": "BSD-3-Clause" +} diff --git a/src/node/src/client.js b/src/node/src/client.js new file mode 100644 index 00000000..7f510231 --- /dev/null +++ b/src/node/src/client.js @@ -0,0 +1,733 @@ +/* + * + * Copyright 2015, 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. + * + */ + +/** + * Client module + * @module + */ + +'use strict'; + +var _ = require('lodash'); + +var grpc = require('bindings')('grpc.node'); + +var common = require('./common'); + +var Metadata = require('./metadata'); + +var EventEmitter = require('events').EventEmitter; + +var stream = require('stream'); + +var Readable = stream.Readable; +var Writable = stream.Writable; +var Duplex = stream.Duplex; +var util = require('util'); +var version = require('../package.json').version; + +util.inherits(ClientWritableStream, Writable); + +/** + * A stream that the client can write to. Used for calls that are streaming from + * the client side. + * @constructor + * @param {grpc.Call} call The call object to send data with + * @param {function(*):Buffer=} serialize Serialization function for writes. + */ +function ClientWritableStream(call, serialize) { + Writable.call(this, {objectMode: true}); + this.call = call; + this.serialize = common.wrapIgnoreNull(serialize); + this.on('finish', function() { + var batch = {}; + batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; + call.startBatch(batch, function() {}); + }); +} + +/** + * Attempt to write the given chunk. Calls the callback when done. This is an + * implementation of a method needed for implementing stream.Writable. + * @access private + * @param {Buffer} chunk The chunk to write + * @param {string} encoding Used to pass write flags + * @param {function(Error=)} callback Called when the write is complete + */ +function _write(chunk, encoding, callback) { + /* jshint validthis: true */ + var batch = {}; + var message = this.serialize(chunk); + if (_.isFinite(encoding)) { + /* Attach the encoding if it is a finite number. This is the closest we + * can get to checking that it is valid flags */ + message.grpcWriteFlags = encoding; + } + batch[grpc.opType.SEND_MESSAGE] = message; + this.call.startBatch(batch, function(err, event) { + if (err) { + // Something has gone wrong. Stop writing by failing to call callback + return; + } + callback(); + }); +} + +ClientWritableStream.prototype._write = _write; + +util.inherits(ClientReadableStream, Readable); + +/** + * A stream that the client can read from. Used for calls that are streaming + * from the server side. + * @constructor + * @param {grpc.Call} call The call object to read data with + * @param {function(Buffer):*=} deserialize Deserialization function for reads + */ +function ClientReadableStream(call, deserialize) { + Readable.call(this, {objectMode: true}); + this.call = call; + this.finished = false; + this.reading = false; + this.deserialize = common.wrapIgnoreNull(deserialize); +} + +/** + * Read the next object from the stream. + * @access private + * @param {*} size Ignored because we use objectMode=true + */ +function _read(size) { + /* jshint validthis: true */ + var self = this; + /** + * Callback to be called when a READ event is received. Pushes the data onto + * the read queue and starts reading again if applicable + * @param {grpc.Event} event READ event object + */ + function readCallback(err, event) { + if (err) { + // Something has gone wrong. Stop reading and wait for status + self.finished = true; + return; + } + var data = event.read; + var deserialized; + try { + deserialized = self.deserialize(data); + } catch (e) { + self.call.cancelWithStatus(grpc.status.INTERNAL, + 'Failed to parse server response'); + } + if (self.push(deserialized) && data !== null) { + var read_batch = {}; + read_batch[grpc.opType.RECV_MESSAGE] = true; + self.call.startBatch(read_batch, readCallback); + } else { + self.reading = false; + } + } + if (self.finished) { + self.push(null); + } else { + if (!self.reading) { + self.reading = true; + var read_batch = {}; + read_batch[grpc.opType.RECV_MESSAGE] = true; + self.call.startBatch(read_batch, readCallback); + } + } +} + +ClientReadableStream.prototype._read = _read; + +util.inherits(ClientDuplexStream, Duplex); + +/** + * A stream that the client can read from or write to. Used for calls with + * duplex streaming. + * @constructor + * @param {grpc.Call} call Call object to proxy + * @param {function(*):Buffer=} serialize Serialization function for requests + * @param {function(Buffer):*=} deserialize Deserialization function for + * responses + */ +function ClientDuplexStream(call, serialize, deserialize) { + Duplex.call(this, {objectMode: true}); + this.serialize = common.wrapIgnoreNull(serialize); + this.deserialize = common.wrapIgnoreNull(deserialize); + this.call = call; + this.on('finish', function() { + var batch = {}; + batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; + call.startBatch(batch, function() {}); + }); +} + +ClientDuplexStream.prototype._read = _read; +ClientDuplexStream.prototype._write = _write; + +/** + * Cancel the ongoing call + */ +function cancel() { + /* jshint validthis: true */ + this.call.cancel(); +} + +ClientReadableStream.prototype.cancel = cancel; +ClientWritableStream.prototype.cancel = cancel; +ClientDuplexStream.prototype.cancel = cancel; + +/** + * Get the endpoint this call/stream is connected to. + * @return {string} The URI of the endpoint + */ +function getPeer() { + /* jshint validthis: true */ + return this.call.getPeer(); +} + +ClientReadableStream.prototype.getPeer = getPeer; +ClientWritableStream.prototype.getPeer = getPeer; +ClientDuplexStream.prototype.getPeer = getPeer; + +/** + * Get a call object built with the provided options. Keys for options are + * 'deadline', which takes a date or number, and 'host', which takes a string + * and overrides the hostname to connect to. + * @param {Object} options Options map. + */ +function getCall(channel, method, options) { + var deadline; + var host; + var parent; + var propagate_flags; + if (options) { + deadline = options.deadline; + host = options.host; + parent = _.get(options, 'parent.call'); + propagate_flags = options.propagate_flags; + } + if (deadline === undefined) { + deadline = Infinity; + } + return new grpc.Call(channel, method, deadline, host, + parent, propagate_flags); +} + +/** + * Get a function that can make unary requests to the specified method. + * @param {string} method The name of the method to request + * @param {function(*):Buffer} serialize The serialization function for inputs + * @param {function(Buffer)} deserialize The deserialization function for + * outputs + * @return {Function} makeUnaryRequest + */ +function makeUnaryRequestFunction(method, serialize, deserialize) { + /** + * Make a unary request with this method on the given channel with the given + * argument, callback, etc. + * @this {Client} Client object. Must have a channel member. + * @param {*} argument The argument to the call. Should be serializable with + * serialize + * @param {function(?Error, value=)} callback The callback to for when the + * response is received + * @param {Metadata=} metadata Metadata to add to the call + * @param {Object=} options Options map + * @return {EventEmitter} An event emitter for stream related events + */ + function makeUnaryRequest(argument, callback, metadata, options) { + /* jshint validthis: true */ + var emitter = new EventEmitter(); + var call = getCall(this.$channel, method, options); + if (metadata === null || metadata === undefined) { + metadata = new Metadata(); + } else { + metadata = metadata.clone(); + } + emitter.cancel = function cancel() { + call.cancel(); + }; + emitter.getPeer = function getPeer() { + return call.getPeer(); + }; + this.$updateMetadata(this.$auth_uri, metadata, function(error, metadata) { + if (error) { + call.cancel(); + callback(error); + return; + } + var client_batch = {}; + var message = serialize(argument); + if (options) { + message.grpcWriteFlags = options.flags; + } + client_batch[grpc.opType.SEND_INITIAL_METADATA] = + metadata._getCoreRepresentation(); + client_batch[grpc.opType.SEND_MESSAGE] = message; + client_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; + client_batch[grpc.opType.RECV_INITIAL_METADATA] = true; + client_batch[grpc.opType.RECV_MESSAGE] = true; + client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; + call.startBatch(client_batch, function(err, response) { + response.status.metadata = Metadata._fromCoreRepresentation( + response.status.metadata); + var status = response.status; + var error; + var deserialized; + if (status.code === grpc.status.OK) { + if (err) { + // Got a batch error, but OK status. Something went wrong + callback(err); + return; + } else { + try { + deserialized = deserialize(response.read); + } catch (e) { + /* Change status to indicate bad server response. This will result + * in passing an error to the callback */ + status = { + code: grpc.status.INTERNAL, + details: 'Failed to parse server response' + }; + } + } + } + if (status.code !== grpc.status.OK) { + error = new Error(response.status.details); + error.code = status.code; + error.metadata = status.metadata; + callback(error); + } else { + callback(null, deserialized); + } + emitter.emit('status', status); + emitter.emit('metadata', Metadata._fromCoreRepresentation( + response.metadata)); + }); + }); + return emitter; + } + return makeUnaryRequest; +} + +/** + * Get a function that can make client stream requests to the specified method. + * @param {string} method The name of the method to request + * @param {function(*):Buffer} serialize The serialization function for inputs + * @param {function(Buffer)} deserialize The deserialization function for + * outputs + * @return {Function} makeClientStreamRequest + */ +function makeClientStreamRequestFunction(method, serialize, deserialize) { + /** + * Make a client stream request with this method on the given channel with the + * given callback, etc. + * @this {Client} Client object. Must have a channel member. + * @param {function(?Error, value=)} callback The callback to for when the + * response is received + * @param {Metadata=} metadata Array of metadata key/value pairs to add to the + * call + * @param {Object=} options Options map + * @return {EventEmitter} An event emitter for stream related events + */ + function makeClientStreamRequest(callback, metadata, options) { + /* jshint validthis: true */ + var call = getCall(this.$channel, method, options); + if (metadata === null || metadata === undefined) { + metadata = new Metadata(); + } else { + metadata = metadata.clone(); + } + var stream = new ClientWritableStream(call, serialize); + this.$updateMetadata(this.$auth_uri, metadata, function(error, metadata) { + if (error) { + call.cancel(); + callback(error); + return; + } + var metadata_batch = {}; + metadata_batch[grpc.opType.SEND_INITIAL_METADATA] = + metadata._getCoreRepresentation(); + metadata_batch[grpc.opType.RECV_INITIAL_METADATA] = true; + call.startBatch(metadata_batch, function(err, response) { + if (err) { + // The call has stopped for some reason. A non-OK status will arrive + // in the other batch. + return; + } + stream.emit('metadata', Metadata._fromCoreRepresentation( + response.metadata)); + }); + var client_batch = {}; + client_batch[grpc.opType.RECV_MESSAGE] = true; + client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; + call.startBatch(client_batch, function(err, response) { + response.status.metadata = Metadata._fromCoreRepresentation( + response.status.metadata); + var status = response.status; + var error; + var deserialized; + if (status.code === grpc.status.OK) { + if (err) { + // Got a batch error, but OK status. Something went wrong + callback(err); + return; + } else { + try { + deserialized = deserialize(response.read); + } catch (e) { + /* Change status to indicate bad server response. This will result + * in passing an error to the callback */ + status = { + code: grpc.status.INTERNAL, + details: 'Failed to parse server response' + }; + } + } + } + if (status.code !== grpc.status.OK) { + error = new Error(response.status.details); + error.code = status.code; + error.metadata = status.metadata; + callback(error); + } else { + callback(null, deserialized); + } + stream.emit('status', status); + }); + }); + return stream; + } + return makeClientStreamRequest; +} + +/** + * Get a function that can make server stream requests to the specified method. + * @param {string} method The name of the method to request + * @param {function(*):Buffer} serialize The serialization function for inputs + * @param {function(Buffer)} deserialize The deserialization function for + * outputs + * @return {Function} makeServerStreamRequest + */ +function makeServerStreamRequestFunction(method, serialize, deserialize) { + /** + * Make a server stream request with this method on the given channel with the + * given argument, etc. + * @this {SurfaceClient} Client object. Must have a channel member. + * @param {*} argument The argument to the call. Should be serializable with + * serialize + * @param {Metadata=} metadata Array of metadata key/value pairs to add to the + * call + * @param {Object} options Options map + * @return {EventEmitter} An event emitter for stream related events + */ + function makeServerStreamRequest(argument, metadata, options) { + /* jshint validthis: true */ + var call = getCall(this.$channel, method, options); + if (metadata === null || metadata === undefined) { + metadata = new Metadata(); + } else { + metadata = metadata.clone(); + } + var stream = new ClientReadableStream(call, deserialize); + this.$updateMetadata(this.$auth_uri, metadata, function(error, metadata) { + if (error) { + call.cancel(); + stream.emit('error', error); + return; + } + var start_batch = {}; + var message = serialize(argument); + if (options) { + message.grpcWriteFlags = options.flags; + } + start_batch[grpc.opType.SEND_INITIAL_METADATA] = + metadata._getCoreRepresentation(); + start_batch[grpc.opType.RECV_INITIAL_METADATA] = true; + start_batch[grpc.opType.SEND_MESSAGE] = message; + start_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; + call.startBatch(start_batch, function(err, response) { + if (err) { + // The call has stopped for some reason. A non-OK status will arrive + // in the other batch. + return; + } + stream.emit('metadata', Metadata._fromCoreRepresentation( + response.metadata)); + }); + var status_batch = {}; + status_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; + call.startBatch(status_batch, function(err, response) { + response.status.metadata = Metadata._fromCoreRepresentation( + response.status.metadata); + stream.emit('status', response.status); + if (response.status.code !== grpc.status.OK) { + var error = new Error(response.status.details); + error.code = response.status.code; + error.metadata = response.status.metadata; + stream.emit('error', error); + return; + } else { + if (err) { + // Got a batch error, but OK status. Something went wrong + stream.emit('error', err); + return; + } + } + }); + }); + return stream; + } + return makeServerStreamRequest; +} + +/** + * Get a function that can make bidirectional stream requests to the specified + * method. + * @param {string} method The name of the method to request + * @param {function(*):Buffer} serialize The serialization function for inputs + * @param {function(Buffer)} deserialize The deserialization function for + * outputs + * @return {Function} makeBidiStreamRequest + */ +function makeBidiStreamRequestFunction(method, serialize, deserialize) { + /** + * Make a bidirectional stream request with this method on the given channel. + * @this {SurfaceClient} Client object. Must have a channel member. + * @param {Metadata=} metadata Array of metadata key/value pairs to add to the + * call + * @param {Options} options Options map + * @return {EventEmitter} An event emitter for stream related events + */ + function makeBidiStreamRequest(metadata, options) { + /* jshint validthis: true */ + var call = getCall(this.$channel, method, options); + if (metadata === null || metadata === undefined) { + metadata = new Metadata(); + } else { + metadata = metadata.clone(); + } + var stream = new ClientDuplexStream(call, serialize, deserialize); + this.$updateMetadata(this.$auth_uri, metadata, function(error, metadata) { + if (error) { + call.cancel(); + stream.emit('error', error); + return; + } + var start_batch = {}; + start_batch[grpc.opType.SEND_INITIAL_METADATA] = + metadata._getCoreRepresentation(); + start_batch[grpc.opType.RECV_INITIAL_METADATA] = true; + call.startBatch(start_batch, function(err, response) { + if (err) { + // The call has stopped for some reason. A non-OK status will arrive + // in the other batch. + return; + } + stream.emit('metadata', Metadata._fromCoreRepresentation( + response.metadata)); + }); + var status_batch = {}; + status_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; + call.startBatch(status_batch, function(err, response) { + response.status.metadata = Metadata._fromCoreRepresentation( + response.status.metadata); + stream.emit('status', response.status); + if (response.status.code !== grpc.status.OK) { + var error = new Error(response.status.details); + error.code = response.status.code; + error.metadata = response.status.metadata; + stream.emit('error', error); + return; + } else { + if (err) { + // Got a batch error, but OK status. Something went wrong + stream.emit('error', err); + return; + } + } + }); + }); + return stream; + } + return makeBidiStreamRequest; +} + + +/** + * Map with short names for each of the requester maker functions. Used in + * makeClientConstructor + */ +var requester_makers = { + unary: makeUnaryRequestFunction, + server_stream: makeServerStreamRequestFunction, + client_stream: makeClientStreamRequestFunction, + bidi: makeBidiStreamRequestFunction +}; + +/** + * Creates a constructor for a client with the given methods. The methods object + * maps method name to an object with the following keys: + * path: The path on the server for accessing the method. For example, for + * protocol buffers, we use "/service_name/method_name" + * requestStream: bool indicating whether the client sends a stream + * resonseStream: bool indicating whether the server sends a stream + * requestSerialize: function to serialize request objects + * responseDeserialize: function to deserialize response objects + * @param {Object} methods An object mapping method names to method attributes + * @param {string} serviceName The fully qualified name of the service + * @return {function(string, Object)} New client constructor + */ +exports.makeClientConstructor = function(methods, serviceName) { + /** + * Create a client with the given methods + * @constructor + * @param {string} address The address of the server to connect to + * @param {grpc.Credentials} credentials Credentials to use to connect + * to the server + * @param {Object} options Options to pass to the underlying channel + * @param {function(string, Object, function)=} updateMetadata function to + * update the metadata for each request + */ + function Client(address, credentials, options, updateMetadata) { + if (!updateMetadata) { + updateMetadata = function(uri, metadata, callback) { + callback(null, metadata); + }; + } + if (!options) { + options = {}; + } + options['grpc.primary_user_agent'] = 'grpc-node/' + version; + /* Private fields use $ as a prefix instead of _ because it is an invalid + * prefix of a method name */ + this.$channel = new grpc.Channel(address, credentials, options); + // Remove the optional DNS scheme, trailing port, and trailing backslash + address = address.replace(/^(dns:\/{3})?([^:\/]+)(:\d+)?\/?$/, '$2'); + this.$server_address = address; + this.$auth_uri = 'https://' + this.$server_address + '/' + serviceName; + this.$updateMetadata = updateMetadata; + } + + _.each(methods, function(attrs, name) { + var method_type; + if (_.startsWith(name, '$')) { + throw new Error('Method names cannot start with $'); + } + if (attrs.requestStream) { + if (attrs.responseStream) { + method_type = 'bidi'; + } else { + method_type = 'client_stream'; + } + } else { + if (attrs.responseStream) { + method_type = 'server_stream'; + } else { + method_type = 'unary'; + } + } + var serialize = attrs.requestSerialize; + var deserialize = attrs.responseDeserialize; + Client.prototype[name] = requester_makers[method_type]( + attrs.path, serialize, deserialize); + Client.prototype[name].serialize = serialize; + Client.prototype[name].deserialize = deserialize; + }); + + return Client; +}; + +/** + * Return the underlying channel object for the specified client + * @param {Client} client + * @return {Channel} The channel + */ +exports.getClientChannel = function(client) { + return client.$channel; +}; + +/** + * Wait for the client to be ready. The callback will be called when the + * client has successfully connected to the server, and it will be called + * with an error if the attempt to connect to the server has unrecoverablly + * failed or if the deadline expires. This function will make the channel + * start connecting if it has not already done so. + * @param {Client} client The client to wait on + * @param {(Date|Number)} deadline When to stop waiting for a connection. Pass + * Infinity to wait forever. + * @param {function(Error)} callback The callback to call when done attempting + * to connect. + */ +exports.waitForClientReady = function(client, deadline, callback) { + var checkState = function(err) { + if (err) { + callback(new Error('Failed to connect before the deadline')); + } + var new_state = client.$channel.getConnectivityState(true); + if (new_state === grpc.connectivityState.READY) { + callback(); + } else if (new_state === grpc.connectivityState.FATAL_FAILURE) { + callback(new Error('Failed to connect to server')); + } else { + client.$channel.watchConnectivityState(new_state, deadline, checkState); + } + }; + checkState(); +}; + +/** + * Creates a constructor for clients for the given service + * @param {ProtoBuf.Reflect.Service} service The service to generate a client + * for + * @return {function(string, Object)} New client constructor + */ +exports.makeProtobufClientConstructor = function(service) { + var method_attrs = common.getProtobufServiceAttrs(service, service.name); + var Client = exports.makeClientConstructor( + method_attrs, common.fullyQualifiedName(service)); + Client.service = service; + return Client; +}; + +/** + * Map of status code names to status codes + */ +exports.status = grpc.status; + +/** + * See docs for client.callError + */ +exports.callError = grpc.callError; diff --git a/src/node/src/common.js b/src/node/src/common.js new file mode 100644 index 00000000..5551ebee --- /dev/null +++ b/src/node/src/common.js @@ -0,0 +1,140 @@ +/* + * + * Copyright 2015, 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. + * + */ + +/** + * @module + */ + +'use strict'; + +var _ = require('lodash'); + +/** + * Get a function that deserializes a specific type of protobuf. + * @param {function()} cls The constructor of the message type to deserialize + * @return {function(Buffer):cls} The deserialization function + */ +exports.deserializeCls = function deserializeCls(cls) { + /** + * Deserialize a buffer to a message object + * @param {Buffer} arg_buf The buffer to deserialize + * @return {cls} The resulting object + */ + return function deserialize(arg_buf) { + // Convert to a native object with binary fields as Buffers (first argument) + // and longs as strings (second argument) + return cls.decode(arg_buf).toRaw(false, true); + }; +}; + +var deserializeCls = exports.deserializeCls; + +/** + * Get a function that serializes objects to a buffer by protobuf class. + * @param {function()} Cls The constructor of the message type to serialize + * @return {function(Cls):Buffer} The serialization function + */ +exports.serializeCls = function serializeCls(Cls) { + /** + * Serialize an object to a Buffer + * @param {Object} arg The object to serialize + * @return {Buffer} The serialized object + */ + return function serialize(arg) { + return new Buffer(new Cls(arg).encode().toBuffer()); + }; +}; + +var serializeCls = exports.serializeCls; + +/** + * Get the fully qualified (dotted) name of a ProtoBuf.Reflect value. + * @param {ProtoBuf.Reflect.Namespace} value The value to get the name of + * @return {string} The fully qualified name of the value + */ +exports.fullyQualifiedName = function fullyQualifiedName(value) { + if (value === null || value === undefined) { + return ''; + } + var name = value.name; + if (value.className === 'Service.RPCMethod') { + name = _.capitalize(name); + } + if (value.hasOwnProperty('parent')) { + var parent_name = fullyQualifiedName(value.parent); + if (parent_name !== '') { + name = parent_name + '.' + name; + } + } + return name; +}; + +var fullyQualifiedName = exports.fullyQualifiedName; + +/** + * Wrap a function to pass null-like values through without calling it. If no + * function is given, just uses the identity; + * @param {?function} func The function to wrap + * @return {function} The wrapped function + */ +exports.wrapIgnoreNull = function wrapIgnoreNull(func) { + if (!func) { + return _.identity; + } + return function(arg) { + if (arg === null || arg === undefined) { + return null; + } + return func(arg); + }; +}; + +/** + * Return a map from method names to method attributes for the service. + * @param {ProtoBuf.Reflect.Service} service The service to get attributes for + * @return {Object} The attributes map + */ +exports.getProtobufServiceAttrs = function getProtobufServiceAttrs(service) { + var prefix = '/' + fullyQualifiedName(service) + '/'; + return _.object(_.map(service.children, function(method) { + return [_.camelCase(method.name), { + path: prefix + _.capitalize(method.name), + requestStream: method.requestStream, + responseStream: method.responseStream, + requestSerialize: serializeCls(method.resolvedRequestType.build()), + requestDeserialize: deserializeCls(method.resolvedRequestType.build()), + responseSerialize: serializeCls(method.resolvedResponseType.build()), + responseDeserialize: deserializeCls(method.resolvedResponseType.build()) + }]; + })); +}; diff --git a/src/node/src/metadata.js b/src/node/src/metadata.js new file mode 100644 index 00000000..c1da70b1 --- /dev/null +++ b/src/node/src/metadata.js @@ -0,0 +1,181 @@ +/* + * + * Copyright 2015, 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. + * + */ + +/** + * Metadata module + * @module + */ + +'use strict'; + +var _ = require('lodash'); + +/** + * Class for storing metadata. Keys are normalized to lowercase ASCII. + * @constructor + */ +function Metadata() { + this._internal_repr = {}; +} + +function normalizeKey(key) { + if (!(/^[A-Za-z\d_-]+$/.test(key))) { + throw new Error('Metadata keys must be nonempty strings containing only ' + + 'alphanumeric characters and hyphens'); + } + return key.toLowerCase(); +} + +function validate(key, value) { + if (_.endsWith(key, '-bin')) { + if (!(value instanceof Buffer)) { + throw new Error('keys that end with \'-bin\' must have Buffer values'); + } + } else { + if (!_.isString(value)) { + throw new Error( + 'keys that don\'t end with \'-bin\' must have String values'); + } + if (!(/^[\x20-\x7E]*$/.test(value))) { + throw new Error('Metadata string values can only contain printable ' + + 'ASCII characters and space'); + } + } +} + +/** + * Sets the given value for the given key, replacing any other values associated + * with that key. Normalizes the key. + * @param {String} key The key to set + * @param {String|Buffer} value The value to set. Must be a buffer if and only + * if the normalized key ends with '-bin' + */ +Metadata.prototype.set = function(key, value) { + key = normalizeKey(key); + validate(key, value); + this._internal_repr[key] = [value]; +}; + +/** + * Adds the given value for the given key. Normalizes the key. + * @param {String} key The key to add to. + * @param {String|Buffer} value The value to add. Must be a buffer if and only + * if the normalized key ends with '-bin' + */ +Metadata.prototype.add = function(key, value) { + key = normalizeKey(key); + validate(key, value); + if (!this._internal_repr[key]) { + this._internal_repr[key] = []; + } + this._internal_repr[key].push(value); +}; + +/** + * Remove the given key and any associated values. Normalizes the key. + * @param {String} key The key to remove + */ +Metadata.prototype.remove = function(key) { + key = normalizeKey(key); + if (Object.prototype.hasOwnProperty.call(this._internal_repr, key)) { + delete this._internal_repr[key]; + } +}; + +/** + * Gets a list of all values associated with the key. Normalizes the key. + * @param {String} key The key to get + * @return {Array.} The values associated with that key + */ +Metadata.prototype.get = function(key) { + key = normalizeKey(key); + if (Object.prototype.hasOwnProperty.call(this._internal_repr, key)) { + return this._internal_repr[key]; + } else { + return []; + } +}; + +/** + * Get a map of each key to a single associated value. This reflects the most + * common way that people will want to see metadata. + * @return {Object.} A key/value mapping of the metadata + */ +Metadata.prototype.getMap = function() { + var result = {}; + _.forOwn(this._internal_repr, function(values, key) { + if(values.length > 0) { + result[key] = values[0]; + } + }); + return result; +}; + +/** + * Clone the metadata object. + * @return {Metadata} The new cloned object + */ +Metadata.prototype.clone = function() { + var copy = new Metadata(); + _.forOwn(this._internal_repr, function(value, key) { + copy._internal_repr[key] = _.clone(value); + }); + return copy; +}; + +/** + * Gets the metadata in the format used by interal code. Intended for internal + * use only. API stability is not guaranteed. + * @private + * @return {Object.>} The metadata + */ +Metadata.prototype._getCoreRepresentation = function() { + return this._internal_repr; +}; + +/** + * Creates a Metadata object from a metadata map in the internal format. + * Intended for internal use only. API stability is not guaranteed. + * @private + * @param {Object.>} The metadata + * @return {Metadata} The new Metadata object + */ +Metadata._fromCoreRepresentation = function(metadata) { + var newMetadata = new Metadata(); + if (metadata) { + newMetadata._internal_repr = _.cloneDeep(metadata); + } + return newMetadata; +}; + +module.exports = Metadata; diff --git a/src/node/src/server.js b/src/node/src/server.js new file mode 100644 index 00000000..70b4a9d8 --- /dev/null +++ b/src/node/src/server.js @@ -0,0 +1,766 @@ +/* + * + * Copyright 2015, 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. + * + */ + +/** + * Server module + * @module + */ + +'use strict'; + +var _ = require('lodash'); + +var grpc = require('bindings')('grpc.node'); + +var common = require('./common'); + +var Metadata = require('./metadata'); + +var stream = require('stream'); + +var Readable = stream.Readable; +var Writable = stream.Writable; +var Duplex = stream.Duplex; +var util = require('util'); + +var EventEmitter = require('events').EventEmitter; + +/** + * Handle an error on a call by sending it as a status + * @access private + * @param {grpc.Call} call The call to send the error on + * @param {Object} error The error object + */ +function handleError(call, error) { + var statusMetadata = new Metadata(); + var status = { + code: grpc.status.UNKNOWN, + details: 'Unknown Error' + }; + if (error.hasOwnProperty('message')) { + status.details = error.message; + } + if (error.hasOwnProperty('code')) { + status.code = error.code; + if (error.hasOwnProperty('details')) { + status.details = error.details; + } + } + if (error.hasOwnProperty('metadata')) { + statusMetadata = error.metadata; + } + status.metadata = statusMetadata._getCoreRepresentation(); + var error_batch = {}; + if (!call.metadataSent) { + error_batch[grpc.opType.SEND_INITIAL_METADATA] = + (new Metadata())._getCoreRepresentation(); + } + error_batch[grpc.opType.SEND_STATUS_FROM_SERVER] = status; + call.startBatch(error_batch, function(){}); +} + +/** + * Wait for the client to close, then emit a cancelled event if the client + * cancelled. + * @access private + * @param {grpc.Call} call The call object to wait on + * @param {EventEmitter} emitter The event emitter to emit the cancelled event + * on + */ +function waitForCancel(call, emitter) { + var cancel_batch = {}; + cancel_batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true; + call.startBatch(cancel_batch, function(err, result) { + if (err) { + emitter.emit('error', err); + } + if (result.cancelled) { + emitter.cancelled = true; + emitter.emit('cancelled'); + } + }); +} + +/** + * Send a response to a unary or client streaming call. + * @access private + * @param {grpc.Call} call The call to respond on + * @param {*} value The value to respond with + * @param {function(*):Buffer=} serialize Serialization function for the + * response + * @param {Metadata=} metadata Optional trailing metadata to send with status + * @param {number=} flags Flags for modifying how the message is sent. + * Defaults to 0. + */ +function sendUnaryResponse(call, value, serialize, metadata, flags) { + var end_batch = {}; + var statusMetadata = new Metadata(); + var status = { + code: grpc.status.OK, + details: 'OK' + }; + if (metadata) { + statusMetadata = metadata; + } + status.metadata = statusMetadata._getCoreRepresentation(); + if (!call.metadataSent) { + end_batch[grpc.opType.SEND_INITIAL_METADATA] = + (new Metadata())._getCoreRepresentation(); + call.metadataSent = true; + } + var message = serialize(value); + message.grpcWriteFlags = flags; + end_batch[grpc.opType.SEND_MESSAGE] = message; + end_batch[grpc.opType.SEND_STATUS_FROM_SERVER] = status; + call.startBatch(end_batch, function (){}); +} + +/** + * Initialize a writable stream. This is used for both the writable and duplex + * stream constructors. + * @access private + * @param {Writable} stream The stream to set up + * @param {function(*):Buffer=} Serialization function for responses + */ +function setUpWritable(stream, serialize) { + stream.finished = false; + stream.status = { + code : grpc.status.OK, + details : 'OK', + metadata : new Metadata() + }; + stream.serialize = common.wrapIgnoreNull(serialize); + function sendStatus() { + var batch = {}; + if (!stream.call.metadataSent) { + stream.call.metadataSent = true; + batch[grpc.opType.SEND_INITIAL_METADATA] = + (new Metadata())._getCoreRepresentation(); + } + + if (stream.status.metadata) { + stream.status.metadata = stream.status.metadata._getCoreRepresentation(); + } + batch[grpc.opType.SEND_STATUS_FROM_SERVER] = stream.status; + stream.call.startBatch(batch, function(){}); + } + stream.on('finish', sendStatus); + /** + * Set the pending status to a given error status. If the error does not have + * code or details properties, the code will be set to grpc.status.UNKNOWN + * and the details will be set to 'Unknown Error'. + * @param {Error} err The error object + */ + function setStatus(err) { + var code = grpc.status.UNKNOWN; + var details = 'Unknown Error'; + var metadata = new Metadata(); + if (err.hasOwnProperty('message')) { + details = err.message; + } + if (err.hasOwnProperty('code')) { + code = err.code; + if (err.hasOwnProperty('details')) { + details = err.details; + } + } + if (err.hasOwnProperty('metadata')) { + metadata = err.metadata; + } + stream.status = {code: code, details: details, metadata: metadata}; + } + /** + * Terminate the call. This includes indicating that reads are done, draining + * all pending writes, and sending the given error as a status + * @param {Error} err The error object + * @this GrpcServerStream + */ + function terminateCall(err) { + // Drain readable data + setStatus(err); + stream.end(); + } + stream.on('error', terminateCall); + /** + * Override of Writable#end method that allows for sending metadata with a + * success status. + * @param {Metadata=} metadata Metadata to send with the status + */ + stream.end = function(metadata) { + if (metadata) { + stream.status.metadata = metadata; + } + Writable.prototype.end.call(this); + }; +} + +/** + * Initialize a readable stream. This is used for both the readable and duplex + * stream constructors. + * @access private + * @param {Readable} stream The stream to initialize + * @param {function(Buffer):*=} deserialize Deserialization function for + * incoming data. + */ +function setUpReadable(stream, deserialize) { + stream.deserialize = common.wrapIgnoreNull(deserialize); + stream.finished = false; + stream.reading = false; + + stream.terminate = function() { + stream.finished = true; + stream.on('data', function() {}); + }; + + stream.on('cancelled', function() { + stream.terminate(); + }); +} + +util.inherits(ServerWritableStream, Writable); + +/** + * A stream that the server can write to. Used for calls that are streaming from + * the server side. + * @constructor + * @param {grpc.Call} call The call object to send data with + * @param {function(*):Buffer=} serialize Serialization function for writes + */ +function ServerWritableStream(call, serialize) { + Writable.call(this, {objectMode: true}); + this.call = call; + + this.finished = false; + setUpWritable(this, serialize); +} + +/** + * Start writing a chunk of data. This is an implementation of a method required + * for implementing stream.Writable. + * @access private + * @param {Buffer} chunk The chunk of data to write + * @param {string} encoding Used to pass write flags + * @param {function(Error=)} callback Callback to indicate that the write is + * complete + */ +function _write(chunk, encoding, callback) { + /* jshint validthis: true */ + var batch = {}; + var self = this; + if (!this.call.metadataSent) { + batch[grpc.opType.SEND_INITIAL_METADATA] = + (new Metadata())._getCoreRepresentation(); + this.call.metadataSent = true; + } + var message = this.serialize(chunk); + if (_.isFinite(encoding)) { + /* Attach the encoding if it is a finite number. This is the closest we + * can get to checking that it is valid flags */ + message.grpcWriteFlags = encoding; + } + batch[grpc.opType.SEND_MESSAGE] = message; + this.call.startBatch(batch, function(err, value) { + if (err) { + self.emit('error', err); + return; + } + callback(); + }); +} + +ServerWritableStream.prototype._write = _write; + +/** + * Send the initial metadata for a writable stream. + * @param {Metadata} responseMetadata Metadata to send + */ +function sendMetadata(responseMetadata) { + /* jshint validthis: true */ + var self = this; + if (!this.call.metadataSent) { + this.call.metadataSent = true; + var batch = []; + batch[grpc.opType.SEND_INITIAL_METADATA] = + responseMetadata._getCoreRepresentation(); + this.call.startBatch(batch, function(err) { + if (err) { + self.emit('error', err); + return; + } + }); + } +} + +/** + * @inheritdoc + * @alias module:src/server~ServerWritableStream#sendMetadata + */ +ServerWritableStream.prototype.sendMetadata = sendMetadata; + +util.inherits(ServerReadableStream, Readable); + +/** + * A stream that the server can read from. Used for calls that are streaming + * from the client side. + * @constructor + * @param {grpc.Call} call The call object to read data with + * @param {function(Buffer):*=} deserialize Deserialization function for reads + */ +function ServerReadableStream(call, deserialize) { + Readable.call(this, {objectMode: true}); + this.call = call; + setUpReadable(this, deserialize); +} + +/** + * Start reading from the gRPC data source. This is an implementation of a + * method required for implementing stream.Readable + * @access private + * @param {number} size Ignored + */ +function _read(size) { + /* jshint validthis: true */ + var self = this; + /** + * Callback to be called when a READ event is received. Pushes the data onto + * the read queue and starts reading again if applicable + * @param {grpc.Event} event READ event object + */ + function readCallback(err, event) { + if (err) { + self.terminate(); + return; + } + if (self.finished) { + self.push(null); + return; + } + var data = event.read; + var deserialized; + try { + deserialized = self.deserialize(data); + } catch (e) { + e.code = grpc.status.INVALID_ARGUMENT; + self.emit('error', e); + return; + } + if (self.push(deserialized) && data !== null) { + var read_batch = {}; + read_batch[grpc.opType.RECV_MESSAGE] = true; + self.call.startBatch(read_batch, readCallback); + } else { + self.reading = false; + } + } + if (self.finished) { + self.push(null); + } else { + if (!self.reading) { + self.reading = true; + var batch = {}; + batch[grpc.opType.RECV_MESSAGE] = true; + self.call.startBatch(batch, readCallback); + } + } +} + +ServerReadableStream.prototype._read = _read; + +util.inherits(ServerDuplexStream, Duplex); + +/** + * A stream that the server can read from or write to. Used for calls with + * duplex streaming. + * @constructor + * @param {grpc.Call} call Call object to proxy + * @param {function(*):Buffer=} serialize Serialization function for requests + * @param {function(Buffer):*=} deserialize Deserialization function for + * responses + */ +function ServerDuplexStream(call, serialize, deserialize) { + Duplex.call(this, {objectMode: true}); + this.call = call; + setUpWritable(this, serialize); + setUpReadable(this, deserialize); +} + +ServerDuplexStream.prototype._read = _read; +ServerDuplexStream.prototype._write = _write; +ServerDuplexStream.prototype.sendMetadata = sendMetadata; + +/** + * Get the endpoint this call/stream is connected to. + * @return {string} The URI of the endpoint + */ +function getPeer() { + /* jshint validthis: true */ + return this.call.getPeer(); +} + +ServerReadableStream.prototype.getPeer = getPeer; +ServerWritableStream.prototype.getPeer = getPeer; +ServerDuplexStream.prototype.getPeer = getPeer; + +/** + * Fully handle a unary call + * @access private + * @param {grpc.Call} call The call to handle + * @param {Object} handler Request handler object for the method that was called + * @param {Metadata} metadata Metadata from the client + */ +function handleUnary(call, handler, metadata) { + var emitter = new EventEmitter(); + emitter.sendMetadata = function(responseMetadata) { + if (!call.metadataSent) { + call.metadataSent = true; + var batch = {}; + batch[grpc.opType.SEND_INITIAL_METADATA] = + responseMetadata._getCoreRepresentation(); + call.startBatch(batch, function() {}); + } + }; + emitter.getPeer = function() { + return call.getPeer(); + }; + emitter.on('error', function(error) { + handleError(call, error); + }); + emitter.metadata = metadata; + waitForCancel(call, emitter); + emitter.call = call; + var batch = {}; + batch[grpc.opType.RECV_MESSAGE] = true; + call.startBatch(batch, function(err, result) { + if (err) { + handleError(call, err); + return; + } + try { + emitter.request = handler.deserialize(result.read); + } catch (e) { + e.code = grpc.status.INVALID_ARGUMENT; + handleError(call, e); + return; + } + if (emitter.cancelled) { + return; + } + handler.func(emitter, function sendUnaryData(err, value, trailer, flags) { + if (err) { + if (trailer) { + err.metadata = trailer; + } + handleError(call, err); + } else { + sendUnaryResponse(call, value, handler.serialize, trailer, flags); + } + }); + }); +} + +/** + * Fully handle a server streaming call + * @access private + * @param {grpc.Call} call The call to handle + * @param {Object} handler Request handler object for the method that was called + * @param {Metadata} metadata Metadata from the client + */ +function handleServerStreaming(call, handler, metadata) { + var stream = new ServerWritableStream(call, handler.serialize); + waitForCancel(call, stream); + stream.metadata = metadata; + var batch = {}; + batch[grpc.opType.RECV_MESSAGE] = true; + call.startBatch(batch, function(err, result) { + if (err) { + stream.emit('error', err); + return; + } + try { + stream.request = handler.deserialize(result.read); + } catch (e) { + e.code = grpc.status.INVALID_ARGUMENT; + stream.emit('error', e); + return; + } + handler.func(stream); + }); +} + +/** + * Fully handle a client streaming call + * @access private + * @param {grpc.Call} call The call to handle + * @param {Object} handler Request handler object for the method that was called + * @param {Metadata} metadata Metadata from the client + */ +function handleClientStreaming(call, handler, metadata) { + var stream = new ServerReadableStream(call, handler.deserialize); + stream.sendMetadata = function(responseMetadata) { + if (!call.metadataSent) { + call.metadataSent = true; + var batch = {}; + batch[grpc.opType.SEND_INITIAL_METADATA] = + responseMetadata._getCoreRepresentation(); + call.startBatch(batch, function() {}); + } + }; + stream.on('error', function(error) { + handleError(call, error); + }); + waitForCancel(call, stream); + stream.metadata = metadata; + handler.func(stream, function(err, value, trailer, flags) { + stream.terminate(); + if (err) { + if (trailer) { + err.metadata = trailer; + } + handleError(call, err); + } else { + sendUnaryResponse(call, value, handler.serialize, trailer, flags); + } + }); +} + +/** + * Fully handle a bidirectional streaming call + * @access private + * @param {grpc.Call} call The call to handle + * @param {Object} handler Request handler object for the method that was called + * @param {Metadata} metadata Metadata from the client + */ +function handleBidiStreaming(call, handler, metadata) { + var stream = new ServerDuplexStream(call, handler.serialize, + handler.deserialize); + waitForCancel(call, stream); + stream.metadata = metadata; + handler.func(stream); +} + +var streamHandlers = { + unary: handleUnary, + server_stream: handleServerStreaming, + client_stream: handleClientStreaming, + bidi: handleBidiStreaming +}; + +/** + * Constructs a server object that stores request handlers and delegates + * incoming requests to those handlers + * @constructor + * @param {Object=} options Options that should be passed to the internal server + * implementation + */ +function Server(options) { + this.handlers = {}; + var handlers = this.handlers; + var server = new grpc.Server(options); + this._server = server; + this.started = false; + /** + * Start the server and begin handling requests + * @this Server + */ + this.start = function() { + if (this.started) { + throw new Error('Server is already running'); + } + this.started = true; + console.log('Server starting'); + _.each(handlers, function(handler, handler_name) { + console.log('Serving', handler_name); + }); + server.start(); + /** + * Handles the SERVER_RPC_NEW event. If there is a handler associated with + * the requested method, use that handler to respond to the request. Then + * wait for the next request + * @param {grpc.Event} event The event to handle with tag SERVER_RPC_NEW + */ + function handleNewCall(err, event) { + if (err) { + return; + } + var details = event.new_call; + var call = details.call; + var method = details.method; + var metadata = Metadata._fromCoreRepresentation(details.metadata); + if (method === null) { + return; + } + server.requestCall(handleNewCall); + var handler; + if (handlers.hasOwnProperty(method)) { + handler = handlers[method]; + } else { + var batch = {}; + batch[grpc.opType.SEND_INITIAL_METADATA] = + (new Metadata())._getCoreRepresentation(); + batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { + code: grpc.status.UNIMPLEMENTED, + details: 'This method is not available on this server.', + metadata: {} + }; + batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true; + call.startBatch(batch, function() {}); + return; + } + streamHandlers[handler.type](call, handler, metadata); + } + server.requestCall(handleNewCall); + }; + + /** + * Gracefully shuts down the server. The server will stop receiving new calls, + * and any pending calls will complete. The callback will be called when all + * pending calls have completed and the server is fully shut down. This method + * is idempotent with itself and forceShutdown. + * @param {function()} callback The shutdown complete callback + */ + this.tryShutdown = function(callback) { + server.tryShutdown(callback); + }; + + /** + * Forcibly shuts down the server. The server will stop receiving new calls + * and cancel all pending calls. When it returns, the server has shut down. + * This method is idempotent with itself and tryShutdown, and it will trigger + * any outstanding tryShutdown callbacks. + */ + this.forceShutdown = function() { + server.forceShutdown(); + }; +} + +/** + * Registers a handler to handle the named method. Fails if there already is + * a handler for the given method. Returns true on success + * @param {string} name The name of the method that the provided function should + * handle/respond to. + * @param {function} handler Function that takes a stream of request values and + * returns a stream of response values + * @param {function(*):Buffer} serialize Serialization function for responses + * @param {function(Buffer):*} deserialize Deserialization function for requests + * @param {string} type The streaming type of method that this handles + * @return {boolean} True if the handler was set. False if a handler was already + * set for that name. + */ +Server.prototype.register = function(name, handler, serialize, deserialize, + type) { + if (this.handlers.hasOwnProperty(name)) { + return false; + } + this.handlers[name] = { + func: handler, + serialize: serialize, + deserialize: deserialize, + type: type + }; + return true; +}; + +/** + * Add a service to the server, with a corresponding implementation. If you are + * generating this from a proto file, you should instead use + * addProtoService. + * @param {Object} service The service descriptor, as + * {@link module:src/common.getProtobufServiceAttrs} returns + * @param {Object} implementation Map of method names to + * method implementation for the provided service. + */ +Server.prototype.addService = function(service, implementation) { + if (this.started) { + throw new Error('Can\'t add a service to a started server.'); + } + var self = this; + _.each(service, function(attrs, name) { + var method_type; + if (attrs.requestStream) { + if (attrs.responseStream) { + method_type = 'bidi'; + } else { + method_type = 'client_stream'; + } + } else { + if (attrs.responseStream) { + method_type = 'server_stream'; + } else { + method_type = 'unary'; + } + } + if (implementation[name] === undefined) { + throw new Error('Method handler for ' + attrs.path + + ' not provided.'); + } + var serialize = attrs.responseSerialize; + var deserialize = attrs.requestDeserialize; + var register_success = self.register(attrs.path, + _.bind(implementation[name], + implementation), + serialize, deserialize, method_type); + if (!register_success) { + throw new Error('Method handler for ' + attrs.path + + ' already provided.'); + } + }); +}; + +/** + * Add a proto service to the server, with a corresponding implementation + * @param {Protobuf.Reflect.Service} service The proto service descriptor + * @param {Object} implementation Map of method names to + * method implementation for the provided service. + */ +Server.prototype.addProtoService = function(service, implementation) { + this.addService(common.getProtobufServiceAttrs(service), implementation); +}; + +/** + * Binds the server to the given port, with SSL enabled if creds is given + * @param {string} port The port that the server should bind on, in the format + * "address:port" + * @param {boolean=} creds Server credential object to be used for SSL. Pass + * nothing for an insecure port + */ +Server.prototype.bind = function(port, creds) { + if (this.started) { + throw new Error('Can\'t bind an already running server to an address'); + } + return this._server.addHttp2Port(port, creds); +}; + +/** + * @see module:src/server~Server + */ +exports.Server = Server; diff --git a/src/node/test/call_test.js b/src/node/test/call_test.js new file mode 100644 index 00000000..e7f071bc --- /dev/null +++ b/src/node/test/call_test.js @@ -0,0 +1,201 @@ +/* + * + * Copyright 2015, 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. + * + */ + +'use strict'; + +var assert = require('assert'); +var grpc = require('bindings')('grpc.node'); + +/** + * Helper function to return an absolute deadline given a relative timeout in + * seconds. + * @param {number} timeout_secs The number of seconds to wait before timing out + * @return {Date} A date timeout_secs in the future + */ +function getDeadline(timeout_secs) { + var deadline = new Date(); + deadline.setSeconds(deadline.getSeconds() + timeout_secs); + return deadline; +} + +var insecureCreds = grpc.Credentials.createInsecure(); + +describe('call', function() { + var channel; + var server; + before(function() { + server = new grpc.Server(); + var port = server.addHttp2Port('localhost:0', + grpc.ServerCredentials.createInsecure()); + server.start(); + channel = new grpc.Channel('localhost:' + port, insecureCreds); + }); + after(function() { + server.forceShutdown(); + }); + describe('constructor', function() { + it('should reject anything less than 3 arguments', function() { + assert.throws(function() { + new grpc.Call(); + }, TypeError); + assert.throws(function() { + new grpc.Call(channel); + }, TypeError); + assert.throws(function() { + new grpc.Call(channel, 'method'); + }, TypeError); + }); + it('should succeed with a Channel, a string, and a date or number', + function() { + assert.doesNotThrow(function() { + new grpc.Call(channel, 'method', new Date()); + }); + assert.doesNotThrow(function() { + new grpc.Call(channel, 'method', 0); + }); + }); + it('should accept an optional fourth string parameter', function() { + assert.doesNotThrow(function() { + new grpc.Call(channel, 'method', new Date(), 'host_override'); + }); + }); + it('should fail with a closed channel', function() { + var local_channel = new grpc.Channel('hostname', insecureCreds); + local_channel.close(); + assert.throws(function() { + new grpc.Call(channel, 'method'); + }); + }); + it('should fail with other types', function() { + assert.throws(function() { + new grpc.Call({}, 'method', 0); + }, TypeError); + assert.throws(function() { + new grpc.Call(channel, null, 0); + }, TypeError); + assert.throws(function() { + new grpc.Call(channel, 'method', 'now'); + }, TypeError); + }); + }); + describe('startBatch', function() { + it('should fail without an object and a function', function() { + var call = new grpc.Call(channel, 'method', getDeadline(1)); + assert.throws(function() { + call.startBatch(); + }); + assert.throws(function() { + call.startBatch({}); + }); + assert.throws(function() { + call.startBatch(null, function(){}); + }); + }); + it('should succeed with an empty object', function(done) { + var call = new grpc.Call(channel, 'method', getDeadline(1)); + assert.doesNotThrow(function() { + call.startBatch({}, function(err) { + assert.ifError(err); + done(); + }); + }); + }); + }); + describe('startBatch with metadata', function() { + it('should succeed with a map of strings to string arrays', function(done) { + var call = new grpc.Call(channel, 'method', getDeadline(1)); + assert.doesNotThrow(function() { + var batch = {}; + batch[grpc.opType.SEND_INITIAL_METADATA] = {'key1': ['value1'], + 'key2': ['value2']}; + call.startBatch(batch, function(err, resp) { + assert.ifError(err); + assert.deepEqual(resp, {'send_metadata': true}); + done(); + }); + }); + }); + it('should succeed with a map of strings to buffer arrays', function(done) { + var call = new grpc.Call(channel, 'method', getDeadline(1)); + assert.doesNotThrow(function() { + var batch = {}; + batch[grpc.opType.SEND_INITIAL_METADATA] = { + 'key1-bin': [new Buffer('value1')], + 'key2-bin': [new Buffer('value2')] + }; + call.startBatch(batch, function(err, resp) { + assert.ifError(err); + assert.deepEqual(resp, {'send_metadata': true}); + done(); + }); + }); + }); + it('should fail with other parameter types', function() { + var call = new grpc.Call(channel, 'method', getDeadline(1)); + assert.throws(function() { + var batch = {}; + batch[grpc.opType.SEND_INITIAL_METADATA] = undefined; + call.startBatch(batch, function(){}); + }); + assert.throws(function() { + var batch = {}; + batch[grpc.opType.SEND_INITIAL_METADATA] = null; + call.startBatch(batch, function(){}); + }, TypeError); + assert.throws(function() { + var batch = {}; + batch[grpc.opType.SEND_INITIAL_METADATA] = 'value'; + call.startBatch(batch, function(){}); + }, TypeError); + assert.throws(function() { + var batch = {}; + batch[grpc.opType.SEND_INITIAL_METADATA] = 5; + call.startBatch(batch, function(){}); + }, TypeError); + }); + }); + describe('cancel', function() { + it('should succeed', function() { + var call = new grpc.Call(channel, 'method', getDeadline(1)); + assert.doesNotThrow(function() { + call.cancel(); + }); + }); + }); + describe('getPeer', function() { + it('should return a string', function() { + var call = new grpc.Call(channel, 'method', getDeadline(1)); + assert.strictEqual(typeof call.getPeer(), 'string'); + }); + }); +}); diff --git a/src/node/test/channel_test.js b/src/node/test/channel_test.js new file mode 100644 index 00000000..d81df2a3 --- /dev/null +++ b/src/node/test/channel_test.js @@ -0,0 +1,190 @@ +/* + * + * Copyright 2015, 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. + * + */ + +'use strict'; + +var assert = require('assert'); +var grpc = require('bindings')('grpc.node'); + +/** + * This is used for testing functions with multiple asynchronous calls that + * can happen in different orders. This should be passed the number of async + * function invocations that can occur last, and each of those should call this + * function's return value + * @param {function()} done The function that should be called when a test is + * complete. + * @param {number} count The number of calls to the resulting function if the + * test passes. + * @return {function()} The function that should be called at the end of each + * sequence of asynchronous functions. + */ +function multiDone(done, count) { + return function() { + count -= 1; + if (count <= 0) { + done(); + } + }; +} +var insecureCreds = grpc.Credentials.createInsecure(); + +describe('channel', function() { + describe('constructor', function() { + it('should require a string for the first argument', function() { + assert.doesNotThrow(function() { + new grpc.Channel('hostname', insecureCreds); + }); + assert.throws(function() { + new grpc.Channel(); + }, TypeError); + assert.throws(function() { + new grpc.Channel(5); + }); + }); + it('should require a credential for the second argument', function() { + assert.doesNotThrow(function() { + new grpc.Channel('hostname', insecureCreds); + }); + assert.throws(function() { + new grpc.Channel('hostname', 5); + }); + assert.throws(function() { + new grpc.Channel('hostname'); + }); + }); + it('should accept an object for the third argument', function() { + assert.doesNotThrow(function() { + new grpc.Channel('hostname', insecureCreds, {}); + }); + assert.throws(function() { + new grpc.Channel('hostname', insecureCreds, 'abc'); + }); + }); + it('should only accept objects with string or int values', function() { + assert.doesNotThrow(function() { + new grpc.Channel('hostname', insecureCreds,{'key' : 'value'}); + }); + assert.doesNotThrow(function() { + new grpc.Channel('hostname', insecureCreds, {'key' : 5}); + }); + assert.throws(function() { + new grpc.Channel('hostname', insecureCreds, {'key' : null}); + }); + assert.throws(function() { + new grpc.Channel('hostname', insecureCreds, {'key' : new Date()}); + }); + }); + }); + describe('close', function() { + var channel; + beforeEach(function() { + channel = new grpc.Channel('hostname', insecureCreds, {}); + }); + it('should succeed silently', function() { + assert.doesNotThrow(function() { + channel.close(); + }); + }); + it('should be idempotent', function() { + assert.doesNotThrow(function() { + channel.close(); + channel.close(); + }); + }); + }); + describe('getTarget', function() { + var channel; + beforeEach(function() { + channel = new grpc.Channel('hostname', insecureCreds, {}); + }); + it('should return a string', function() { + assert.strictEqual(typeof channel.getTarget(), 'string'); + }); + }); + describe('getConnectivityState', function() { + var channel; + beforeEach(function() { + channel = new grpc.Channel('hostname', insecureCreds, {}); + }); + it('should return IDLE for a new channel', function() { + assert.strictEqual(channel.getConnectivityState(), + grpc.connectivityState.IDLE); + }); + }); + describe('watchConnectivityState', function() { + var channel; + beforeEach(function() { + channel = new grpc.Channel('localhost', insecureCreds, {}); + }); + afterEach(function() { + channel.close(); + }); + it('should time out if called alone', function(done) { + var old_state = channel.getConnectivityState(); + var deadline = new Date(); + deadline.setSeconds(deadline.getSeconds() + 1); + channel.watchConnectivityState(old_state, deadline, function(err, value) { + assert(err); + done(); + }); + }); + it('should complete if a connection attempt is forced', function(done) { + var old_state = channel.getConnectivityState(); + var deadline = new Date(); + deadline.setSeconds(deadline.getSeconds() + 1); + channel.watchConnectivityState(old_state, deadline, function(err, value) { + assert.ifError(err); + assert.notEqual(value.new_state, old_state); + done(); + }); + channel.getConnectivityState(true); + }); + it('should complete twice if called twice', function(done) { + done = multiDone(done, 2); + var old_state = channel.getConnectivityState(); + var deadline = new Date(); + deadline.setSeconds(deadline.getSeconds() + 1); + channel.watchConnectivityState(old_state, deadline, function(err, value) { + assert.ifError(err); + assert.notEqual(value.new_state, old_state); + done(); + }); + channel.watchConnectivityState(old_state, deadline, function(err, value) { + assert.ifError(err); + assert.notEqual(value.new_state, old_state); + done(); + }); + channel.getConnectivityState(true); + }); + }); +}); diff --git a/src/node/test/common_test.js b/src/node/test/common_test.js new file mode 100644 index 00000000..08ba429e --- /dev/null +++ b/src/node/test/common_test.js @@ -0,0 +1,90 @@ +/* + * + * Copyright 2015, 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. + * + */ + +'use strict'; + +var assert = require('assert'); + +var common = require('../src/common.js'); + +var ProtoBuf = require('protobufjs'); + +var messages_proto = ProtoBuf.loadProtoFile( + __dirname + '/test_messages.proto').build(); + +describe('Proto message serialize and deserialize', function() { + var longSerialize = common.serializeCls(messages_proto.LongValues); + var longDeserialize = common.deserializeCls(messages_proto.LongValues); + var pos_value = '314159265358979'; + var neg_value = '-27182818284590'; + it('should preserve positive int64 values', function() { + var serialized = longSerialize({int_64: pos_value}); + assert.strictEqual(longDeserialize(serialized).int_64.toString(), + pos_value); + }); + it('should preserve negative int64 values', function() { + var serialized = longSerialize({int_64: neg_value}); + assert.strictEqual(longDeserialize(serialized).int_64.toString(), + neg_value); + }); + it('should preserve uint64 values', function() { + var serialized = longSerialize({uint_64: pos_value}); + assert.strictEqual(longDeserialize(serialized).uint_64.toString(), + pos_value); + }); + it('should preserve positive sint64 values', function() { + var serialized = longSerialize({sint_64: pos_value}); + assert.strictEqual(longDeserialize(serialized).sint_64.toString(), + pos_value); + }); + it('should preserve negative sint64 values', function() { + var serialized = longSerialize({sint_64: neg_value}); + assert.strictEqual(longDeserialize(serialized).sint_64.toString(), + neg_value); + }); + it('should preserve fixed64 values', function() { + var serialized = longSerialize({fixed_64: pos_value}); + assert.strictEqual(longDeserialize(serialized).fixed_64.toString(), + pos_value); + }); + it('should preserve positive sfixed64 values', function() { + var serialized = longSerialize({sfixed_64: pos_value}); + assert.strictEqual(longDeserialize(serialized).sfixed_64.toString(), + pos_value); + }); + it('should preserve negative sfixed64 values', function() { + var serialized = longSerialize({sfixed_64: neg_value}); + assert.strictEqual(longDeserialize(serialized).sfixed_64.toString(), + neg_value); + }); +}); diff --git a/src/node/test/constant_test.js b/src/node/test/constant_test.js new file mode 100644 index 00000000..fa06ad4e --- /dev/null +++ b/src/node/test/constant_test.js @@ -0,0 +1,131 @@ +/* + * + * Copyright 2015, 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. + * + */ + +'use strict'; + +var assert = require('assert'); +var grpc = require('bindings')('grpc.node'); + +/** + * List of all status names + * @const + * @type {Array.} + */ +var statusNames = [ + 'OK', + 'CANCELLED', + 'UNKNOWN', + 'INVALID_ARGUMENT', + 'DEADLINE_EXCEEDED', + 'NOT_FOUND', + 'ALREADY_EXISTS', + 'PERMISSION_DENIED', + 'UNAUTHENTICATED', + 'RESOURCE_EXHAUSTED', + 'FAILED_PRECONDITION', + 'ABORTED', + 'OUT_OF_RANGE', + 'UNIMPLEMENTED', + 'INTERNAL', + 'UNAVAILABLE', + 'DATA_LOSS' +]; + +/** + * List of all call error names + * @const + * @type {Array.} + */ +var callErrorNames = [ + 'OK', + 'ERROR', + 'NOT_ON_SERVER', + 'NOT_ON_CLIENT', + 'ALREADY_INVOKED', + 'NOT_INVOKED', + 'ALREADY_FINISHED', + 'TOO_MANY_OPERATIONS', + 'INVALID_FLAGS' +]; + +/** + * List of all propagate flag names + * @const + * @type {Array.} + */ +var propagateFlagNames = [ + 'DEADLINE', + 'CENSUS_STATS_CONTEXT', + 'CENSUS_TRACING_CONTEXT', + 'CANCELLATION', + 'DEFAULTS' +]; +/* + * List of all connectivity state names + * @const + * @type {Array.} + */ +var connectivityStateNames = [ + 'IDLE', + 'CONNECTING', + 'READY', + 'TRANSIENT_FAILURE', + 'FATAL_FAILURE' +]; + +describe('constants', function() { + it('should have all of the status constants', function() { + for (var i = 0; i < statusNames.length; i++) { + assert(grpc.status.hasOwnProperty(statusNames[i]), + 'status missing: ' + statusNames[i]); + } + }); + it('should have all of the call errors', function() { + for (var i = 0; i < callErrorNames.length; i++) { + assert(grpc.callError.hasOwnProperty(callErrorNames[i]), + 'call error missing: ' + callErrorNames[i]); + } + }); + it('should have all of the propagate flags', function() { + for (var i = 0; i < propagateFlagNames.length; i++) { + assert(grpc.propagate.hasOwnProperty(propagateFlagNames[i]), + 'call error missing: ' + propagateFlagNames[i]); + } + }); + it('should have all of the connectivity states', function() { + for (var i = 0; i < connectivityStateNames.length; i++) { + assert(grpc.connectivityState.hasOwnProperty(connectivityStateNames[i]), + 'connectivity status missing: ' + connectivityStateNames[i]); + } + }); +}); diff --git a/src/node/test/data/README b/src/node/test/data/README new file mode 100644 index 00000000..888d95b9 --- /dev/null +++ b/src/node/test/data/README @@ -0,0 +1 @@ +CONFIRMEDTESTKEY diff --git a/src/node/test/data/ca.pem b/src/node/test/data/ca.pem new file mode 100644 index 00000000..6c8511a7 --- /dev/null +++ b/src/node/test/data/ca.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICSjCCAbOgAwIBAgIJAJHGGR4dGioHMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQxDzANBgNVBAMTBnRlc3RjYTAeFw0xNDExMTEyMjMxMjla +Fw0yNDExMDgyMjMxMjlaMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0 +YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMT +BnRlc3RjYTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwEDfBV5MYdlHVHJ7 ++L4nxrZy7mBfAVXpOc5vMYztssUI7mL2/iYujiIXM+weZYNTEpLdjyJdu7R5gGUu +g1jSVK/EPHfc74O7AyZU34PNIP4Sh33N+/A5YexrNgJlPY+E3GdVYi4ldWJjgkAd +Qah2PH5ACLrIIC6tRka9hcaBlIECAwEAAaMgMB4wDAYDVR0TBAUwAwEB/zAOBgNV +HQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQELBQADgYEAHzC7jdYlzAVmddi/gdAeKPau +sPBG/C2HCWqHzpCUHcKuvMzDVkY/MP2o6JIW2DBbY64bO/FceExhjcykgaYtCH/m +oIU63+CFOTtR7otyQAWHqXa7q4SbCDlG7DyRFxqG0txPtGvy12lgldA2+RgcigQG +Dfcog5wrJytaQ6UA0wE= +-----END CERTIFICATE----- diff --git a/src/node/test/data/server1.key b/src/node/test/data/server1.key new file mode 100644 index 00000000..143a5b87 --- /dev/null +++ b/src/node/test/data/server1.key @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAOHDFScoLCVJpYDD +M4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1BgzkWF+slf +3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd9N8YwbBY +AckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAECgYAn7qGnM2vbjJNBm0VZCkOkTIWm +V10okw7EPJrdL2mkre9NasghNXbE1y5zDshx5Nt3KsazKOxTT8d0Jwh/3KbaN+YY +tTCbKGW0pXDRBhwUHRcuRzScjli8Rih5UOCiZkhefUTcRb6xIhZJuQy71tjaSy0p +dHZRmYyBYO2YEQ8xoQJBAPrJPhMBkzmEYFtyIEqAxQ/o/A6E+E4w8i+KM7nQCK7q +K4JXzyXVAjLfyBZWHGM2uro/fjqPggGD6QH1qXCkI4MCQQDmdKeb2TrKRh5BY1LR +81aJGKcJ2XbcDu6wMZK4oqWbTX2KiYn9GB0woM6nSr/Y6iy1u145YzYxEV/iMwff +DJULAkB8B2MnyzOg0pNFJqBJuH29bKCcHa8gHJzqXhNO5lAlEbMK95p/P2Wi+4Hd +aiEIAF1BF326QJcvYKmwSmrORp85AkAlSNxRJ50OWrfMZnBgzVjDx3xG6KsFQVk2 +ol6VhqL6dFgKUORFUWBvnKSyhjJxurlPEahV6oo6+A+mPhFY8eUvAkAZQyTdupP3 +XEFQKctGz+9+gKkemDp7LBBMEMBXrGTLPhpEfcjv/7KPdnFHYmhYeBTBnuVmTVWe +F98XJ7tIFfJq +-----END PRIVATE KEY----- diff --git a/src/node/test/data/server1.pem b/src/node/test/data/server1.pem new file mode 100644 index 00000000..8e582e57 --- /dev/null +++ b/src/node/test/data/server1.pem @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICmzCCAgSgAwIBAgIBAzANBgkqhkiG9w0BAQUFADBWMQswCQYDVQQGEwJBVTET +MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ +dHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0Y2EwHhcNMTQwNzIyMDYwMDU3WhcNMjQwNzE5 +MDYwMDU3WjBkMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV +BAcTB0NoaWNhZ28xFDASBgNVBAoTC0dvb2dsZSBJbmMuMRowGAYDVQQDFBEqLnRl +c3QuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA4cMVJygs +JUmlgMMzgdi0h1XoCR7+ww1pop04OMMyy7H/i0PJ2W6Y35+b4CM8QrkYeEafUGDO +RYX6yV/cHGGsD/x02ye6ey1UDtkGAD/mpDEx8YCrjAc1Vfvt8Fk6Cn1WVIxV/J30 +3xjBsFgByQ55RBp1OLZfVLo6AleBDSbcxaECAwEAAaNrMGkwCQYDVR0TBAIwADAL +BgNVHQ8EBAMCBeAwTwYDVR0RBEgwRoIQKi50ZXN0Lmdvb2dsZS5mcoIYd2F0ZXJ6 +b29pLnRlc3QuZ29vZ2xlLmJlghIqLnRlc3QueW91dHViZS5jb22HBMCoAQMwDQYJ +KoZIhvcNAQEFBQADgYEAM2Ii0LgTGbJ1j4oqX9bxVcxm+/R5Yf8oi0aZqTJlnLYS +wXcBykxTx181s7WyfJ49WwrYXo78zTDAnf1ma0fPq3e4mpspvyndLh1a+OarHa1e +aT0DIIYk7qeEa1YcVljx2KyLd0r1BBAfrwyGaEPVeJQVYWaOJRU2we/KD4ojf9s= +-----END CERTIFICATE----- diff --git a/src/node/test/echo_service.proto b/src/node/test/echo_service.proto new file mode 100644 index 00000000..b2c7e3dc --- /dev/null +++ b/src/node/test/echo_service.proto @@ -0,0 +1,39 @@ +// Copyright 2015, 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. + +syntax = "proto3"; + +message EchoMessage { + string value = 1; + int32 value2 = 2; +} + +service EchoService { + rpc Echo (EchoMessage) returns (EchoMessage); +} \ No newline at end of file diff --git a/src/node/test/end_to_end_test.js b/src/node/test/end_to_end_test.js new file mode 100644 index 00000000..4b8da3bf --- /dev/null +++ b/src/node/test/end_to_end_test.js @@ -0,0 +1,306 @@ +/* + * + * Copyright 2015, 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. + * + */ + +'use strict'; + +var assert = require('assert'); +var grpc = require('bindings')('grpc.node'); + +/** + * This is used for testing functions with multiple asynchronous calls that + * can happen in different orders. This should be passed the number of async + * function invocations that can occur last, and each of those should call this + * function's return value + * @param {function()} done The function that should be called when a test is + * complete. + * @param {number} count The number of calls to the resulting function if the + * test passes. + * @return {function()} The function that should be called at the end of each + * sequence of asynchronous functions. + */ +function multiDone(done, count) { + return function() { + count -= 1; + if (count <= 0) { + done(); + } + }; +} + +var insecureCreds = grpc.Credentials.createInsecure(); + +describe('end-to-end', function() { + var server; + var channel; + before(function() { + server = new grpc.Server(); + var port_num = server.addHttp2Port('0.0.0.0:0', + grpc.ServerCredentials.createInsecure()); + server.start(); + channel = new grpc.Channel('localhost:' + port_num, insecureCreds); + }); + after(function() { + server.forceShutdown(); + }); + it('should start and end a request without error', function(complete) { + var done = multiDone(complete, 2); + var status_text = 'xyz'; + var call = new grpc.Call(channel, + 'dummy_method', + Infinity); + var client_batch = {}; + client_batch[grpc.opType.SEND_INITIAL_METADATA] = {}; + client_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; + client_batch[grpc.opType.RECV_INITIAL_METADATA] = true; + client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; + call.startBatch(client_batch, function(err, response) { + assert.ifError(err); + assert.deepEqual(response, { + send_metadata: true, + client_close: true, + metadata: {}, + status: { + code: grpc.status.OK, + details: status_text, + metadata: {} + } + }); + done(); + }); + + server.requestCall(function(err, call_details) { + var new_call = call_details.new_call; + assert.notEqual(new_call, null); + var server_call = new_call.call; + assert.notEqual(server_call, null); + var server_batch = {}; + server_batch[grpc.opType.SEND_INITIAL_METADATA] = {}; + server_batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { + metadata: {}, + code: grpc.status.OK, + details: status_text + }; + server_batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true; + server_call.startBatch(server_batch, function(err, response) { + assert.ifError(err); + assert.deepEqual(response, { + send_metadata: true, + send_status: true, + cancelled: false + }); + done(); + }); + }); + }); + it('should successfully send and receive metadata', function(complete) { + var done = multiDone(complete, 2); + var status_text = 'xyz'; + var call = new grpc.Call(channel, + 'dummy_method', + Infinity); + var client_batch = {}; + client_batch[grpc.opType.SEND_INITIAL_METADATA] = { + client_key: ['client_value'] + }; + client_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; + client_batch[grpc.opType.RECV_INITIAL_METADATA] = true; + client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; + call.startBatch(client_batch, function(err, response) { + assert.ifError(err); + assert.deepEqual(response,{ + send_metadata: true, + client_close: true, + metadata: {server_key: ['server_value']}, + status: {code: grpc.status.OK, + details: status_text, + metadata: {}} + }); + done(); + }); + + server.requestCall(function(err, call_details) { + var new_call = call_details.new_call; + assert.notEqual(new_call, null); + assert.strictEqual(new_call.metadata.client_key[0], + 'client_value'); + var server_call = new_call.call; + assert.notEqual(server_call, null); + var server_batch = {}; + server_batch[grpc.opType.SEND_INITIAL_METADATA] = { + server_key: ['server_value'] + }; + server_batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { + metadata: {}, + code: grpc.status.OK, + details: status_text + }; + server_batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true; + server_call.startBatch(server_batch, function(err, response) { + assert.ifError(err); + assert.deepEqual(response, { + send_metadata: true, + send_status: true, + cancelled: false + }); + done(); + }); + }); + }); + it('should send and receive data without error', function(complete) { + var req_text = 'client_request'; + var reply_text = 'server_response'; + var done = multiDone(complete, 2); + var status_text = 'success'; + var call = new grpc.Call(channel, + 'dummy_method', + Infinity); + var client_batch = {}; + client_batch[grpc.opType.SEND_INITIAL_METADATA] = {}; + client_batch[grpc.opType.SEND_MESSAGE] = new Buffer(req_text); + client_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; + client_batch[grpc.opType.RECV_INITIAL_METADATA] = true; + client_batch[grpc.opType.RECV_MESSAGE] = true; + client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; + call.startBatch(client_batch, function(err, response) { + assert.ifError(err); + assert(response.send_metadata); + assert(response.client_close); + assert.deepEqual(response.metadata, {}); + assert(response.send_message); + assert.strictEqual(response.read.toString(), reply_text); + assert.deepEqual(response.status, {code: grpc.status.OK, + details: status_text, + metadata: {}}); + done(); + }); + + server.requestCall(function(err, call_details) { + var new_call = call_details.new_call; + assert.notEqual(new_call, null); + var server_call = new_call.call; + assert.notEqual(server_call, null); + var server_batch = {}; + server_batch[grpc.opType.SEND_INITIAL_METADATA] = {}; + server_batch[grpc.opType.RECV_MESSAGE] = true; + server_call.startBatch(server_batch, function(err, response) { + assert.ifError(err); + assert(response.send_metadata); + assert.strictEqual(response.read.toString(), req_text); + var response_batch = {}; + response_batch[grpc.opType.SEND_MESSAGE] = new Buffer(reply_text); + response_batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { + metadata: {}, + code: grpc.status.OK, + details: status_text + }; + response_batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true; + server_call.startBatch(response_batch, function(err, response) { + assert(response.send_status); + assert(!response.cancelled); + done(); + }); + }); + }); + }); + it('should send multiple messages', function(complete) { + var done = multiDone(complete, 2); + var requests = ['req1', 'req2']; + var status_text = 'xyz'; + var call = new grpc.Call(channel, + 'dummy_method', + Infinity); + var client_batch = {}; + client_batch[grpc.opType.SEND_INITIAL_METADATA] = {}; + client_batch[grpc.opType.SEND_MESSAGE] = new Buffer(requests[0]); + client_batch[grpc.opType.RECV_INITIAL_METADATA] = true; + call.startBatch(client_batch, function(err, response) { + assert.ifError(err); + assert.deepEqual(response, { + send_metadata: true, + send_message: true, + metadata: {} + }); + var req2_batch = {}; + req2_batch[grpc.opType.SEND_MESSAGE] = new Buffer(requests[1]); + req2_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; + req2_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; + call.startBatch(req2_batch, function(err, resp) { + assert.ifError(err); + assert.deepEqual(resp, { + send_message: true, + client_close: true, + status: { + code: grpc.status.OK, + details: status_text, + metadata: {} + } + }); + done(); + }); + }); + + server.requestCall(function(err, call_details) { + var new_call = call_details.new_call; + assert.notEqual(new_call, null); + var server_call = new_call.call; + assert.notEqual(server_call, null); + var server_batch = {}; + server_batch[grpc.opType.SEND_INITIAL_METADATA] = {}; + server_batch[grpc.opType.RECV_MESSAGE] = true; + server_call.startBatch(server_batch, function(err, response) { + assert.ifError(err); + assert(response.send_metadata); + assert.strictEqual(response.read.toString(), requests[0]); + var snd_batch = {}; + snd_batch[grpc.opType.RECV_MESSAGE] = true; + server_call.startBatch(snd_batch, function(err, response) { + assert.ifError(err); + assert.strictEqual(response.read.toString(), requests[1]); + var end_batch = {}; + end_batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true; + end_batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { + metadata: {}, + code: grpc.status.OK, + details: status_text + }; + server_call.startBatch(end_batch, function(err, response) { + assert.ifError(err); + assert(response.send_status); + assert(!response.cancelled); + done(); + }); + }); + }); + }); + }); +}); diff --git a/src/node/test/health_test.js b/src/node/test/health_test.js new file mode 100644 index 00000000..9267bff7 --- /dev/null +++ b/src/node/test/health_test.js @@ -0,0 +1,92 @@ +/* + * + * Copyright 2015, 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. + * + */ + +'use strict'; + +var assert = require('assert'); + +var health = require('../health_check/health.js'); + +var grpc = require('../'); + +describe('Health Checking', function() { + var statusMap = { + '': 'SERVING', + 'grpc.test.TestServiceNotServing': 'NOT_SERVING', + 'grpc.test.TestServiceServing': 'SERVING' + }; + var healthServer = new grpc.Server(); + healthServer.addProtoService(health.service, + new health.Implementation(statusMap)); + var healthClient; + before(function() { + var port_num = healthServer.bind('0.0.0.0:0', + grpc.ServerCredentials.createInsecure()); + healthServer.start(); + healthClient = new health.Client('localhost:' + port_num, + grpc.Credentials.createInsecure()); + }); + after(function() { + healthServer.forceShutdown(); + }); + it('should say an enabled service is SERVING', function(done) { + healthClient.check({service: ''}, function(err, response) { + assert.ifError(err); + assert.strictEqual(response.status, 'SERVING'); + done(); + }); + }); + it('should say that a disabled service is NOT_SERVING', function(done) { + healthClient.check({service: 'grpc.test.TestServiceNotServing'}, + function(err, response) { + assert.ifError(err); + assert.strictEqual(response.status, 'NOT_SERVING'); + done(); + }); + }); + it('should say that an enabled service is SERVING', function(done) { + healthClient.check({service: 'grpc.test.TestServiceServing'}, + function(err, response) { + assert.ifError(err); + assert.strictEqual(response.status, 'SERVING'); + done(); + }); + }); + it('should get NOT_FOUND if the service is not registered', function(done) { + healthClient.check({service: 'not_registered'}, function(err, response) { + assert(err); + assert.strictEqual(err.code, grpc.status.NOT_FOUND); + done(); + }); + }); +}); diff --git a/src/node/test/interop_sanity_test.js b/src/node/test/interop_sanity_test.js new file mode 100644 index 00000000..2ca07c1d --- /dev/null +++ b/src/node/test/interop_sanity_test.js @@ -0,0 +1,93 @@ +/* + * + * Copyright 2015, 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. + * + */ + +'use strict'; + +var interop_server = require('../interop/interop_server.js'); +var interop_client = require('../interop/interop_client.js'); + +var server; + +var port; + +var name_override = 'foo.test.google.fr'; + +describe('Interop tests', function() { + before(function(done) { + var server_obj = interop_server.getServer(0, true); + server = server_obj.server; + server.start(); + port = 'localhost:' + server_obj.port; + done(); + }); + after(function() { + server.forceShutdown(); + }); + // This depends on not using a binary stream + it('should pass empty_unary', function(done) { + interop_client.runTest(port, name_override, 'empty_unary', true, true, + done); + }); + // This fails due to an unknown bug + it('should pass large_unary', function(done) { + interop_client.runTest(port, name_override, 'large_unary', true, true, + done); + }); + it('should pass client_streaming', function(done) { + interop_client.runTest(port, name_override, 'client_streaming', true, true, + done); + }); + it('should pass server_streaming', function(done) { + interop_client.runTest(port, name_override, 'server_streaming', true, true, + done); + }); + it('should pass ping_pong', function(done) { + interop_client.runTest(port, name_override, 'ping_pong', true, true, done); + }); + it('should pass empty_stream', function(done) { + interop_client.runTest(port, name_override, 'empty_stream', true, true, + done); + }); + it('should pass cancel_after_begin', function(done) { + interop_client.runTest(port, name_override, 'cancel_after_begin', true, + true, done); + }); + it('should pass cancel_after_first_response', function(done) { + interop_client.runTest(port, name_override, 'cancel_after_first_response', + true, true, done); + }); + it('should pass timeout_on_sleeping_server', function(done) { + interop_client.runTest(port, name_override, 'timeout_on_sleeping_server', + true, true, done); + }); +}); diff --git a/src/node/test/math_client_test.js b/src/node/test/math_client_test.js new file mode 100644 index 00000000..80b0c5ff --- /dev/null +++ b/src/node/test/math_client_test.js @@ -0,0 +1,139 @@ +/* + * + * Copyright 2015, 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. + * + */ + +'use strict'; + +var assert = require('assert'); + +var grpc = require('..'); +var math = grpc.load(__dirname + '/../examples/math.proto').math; + +/** + * Client to use to make requests to a running server. + */ +var math_client; + +/** + * Server to test against + */ +var server = require('../examples/math_server.js'); + + +describe('Math client', function() { + before(function(done) { + var port_num = server.bind('0.0.0.0:0', + grpc.ServerCredentials.createInsecure()); + server.start(); + math_client = new math.Math('localhost:' + port_num, + grpc.Credentials.createInsecure()); + done(); + }); + after(function() { + server.forceShutdown(); + }); + it('should handle a single request', function(done) { + var arg = {dividend: 7, divisor: 4}; + math_client.div(arg, function handleDivResult(err, value) { + assert.ifError(err); + assert.equal(value.quotient, 1); + assert.equal(value.remainder, 3); + done(); + }); + }); + it('should handle an error from a unary request', function(done) { + var arg = {dividend: 7, divisor: 0}; + math_client.div(arg, function handleDivResult(err, value) { + assert(err); + done(); + }); + }); + it('should handle a server streaming request', function(done) { + var call = math_client.fib({limit: 7}); + var expected_results = [1, 1, 2, 3, 5, 8, 13]; + var next_expected = 0; + call.on('data', function checkResponse(value) { + assert.equal(value.num, expected_results[next_expected]); + next_expected += 1; + }); + call.on('status', function checkStatus(status) { + assert.strictEqual(status.code, grpc.status.OK); + done(); + }); + }); + it('should handle a client streaming request', function(done) { + var call = math_client.sum(function handleSumResult(err, value) { + assert.ifError(err); + assert.equal(value.num, 21); + }); + for (var i = 0; i < 7; i++) { + call.write({'num': i}); + } + call.end(); + call.on('status', function checkStatus(status) { + assert.strictEqual(status.code, grpc.status.OK); + done(); + }); + }); + it('should handle a bidirectional streaming request', function(done) { + function checkResponse(index, value) { + assert.equal(value.quotient, index); + assert.equal(value.remainder, 1); + } + var call = math_client.divMany(); + var response_index = 0; + call.on('data', function(value) { + checkResponse(response_index, value); + response_index += 1; + }); + for (var i = 0; i < 7; i++) { + call.write({dividend: 2 * i + 1, divisor: 2}); + } + call.end(); + call.on('status', function checkStatus(status) { + assert.strictEqual(status.code, grpc.status.OK); + done(); + }); + }); + it('should handle an error from a bidi request', function(done) { + var call = math_client.divMany(); + call.on('data', function(value) { + assert.fail(value, undefined, 'Unexpected data response on failing call', + '!='); + }); + call.write({dividend: 7, divisor: 0}); + call.end(); + call.on('error', function checkStatus(status) { + done(); + }); + }); +}); diff --git a/src/node/test/metadata_test.js b/src/node/test/metadata_test.js new file mode 100644 index 00000000..86383f1b --- /dev/null +++ b/src/node/test/metadata_test.js @@ -0,0 +1,193 @@ +/* + * + * Copyright 2015, 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. + * + */ + +'use strict'; + +var Metadata = require('../src/metadata.js'); + +var assert = require('assert'); + +describe('Metadata', function() { + var metadata; + beforeEach(function() { + metadata = new Metadata(); + }); + describe('#set', function() { + it('Only accepts string values for non "-bin" keys', function() { + assert.throws(function() { + metadata.set('key', new Buffer('value')); + }); + assert.doesNotThrow(function() { + metadata.set('key', 'value'); + }); + }); + it('Only accepts Buffer values for "-bin" keys', function() { + assert.throws(function() { + metadata.set('key-bin', 'value'); + }); + assert.doesNotThrow(function() { + metadata.set('key-bin', new Buffer('value')); + }); + }); + it('Rejects invalid keys', function() { + assert.throws(function() { + metadata.set('key$', 'value'); + }); + assert.throws(function() { + metadata.set('', 'value'); + }); + }); + it('Rejects values with non-ASCII characters', function() { + assert.throws(function() { + metadata.set('key', 'résumé'); + }); + }); + it('Saves values that can be retrieved', function() { + metadata.set('key', 'value'); + assert.deepEqual(metadata.get('key'), ['value']); + }); + it('Overwrites previous values', function() { + metadata.set('key', 'value1'); + metadata.set('key', 'value2'); + assert.deepEqual(metadata.get('key'), ['value2']); + }); + it('Normalizes keys', function() { + metadata.set('Key', 'value1'); + assert.deepEqual(metadata.get('key'), ['value1']); + metadata.set('KEY', 'value2'); + assert.deepEqual(metadata.get('key'), ['value2']); + }); + }); + describe('#add', function() { + it('Only accepts string values for non "-bin" keys', function() { + assert.throws(function() { + metadata.add('key', new Buffer('value')); + }); + assert.doesNotThrow(function() { + metadata.add('key', 'value'); + }); + }); + it('Only accepts Buffer values for "-bin" keys', function() { + assert.throws(function() { + metadata.add('key-bin', 'value'); + }); + assert.doesNotThrow(function() { + metadata.add('key-bin', new Buffer('value')); + }); + }); + it('Rejects invalid keys', function() { + assert.throws(function() { + metadata.add('key$', 'value'); + }); + assert.throws(function() { + metadata.add('', 'value'); + }); + }); + it('Saves values that can be retrieved', function() { + metadata.add('key', 'value'); + assert.deepEqual(metadata.get('key'), ['value']); + }); + it('Combines with previous values', function() { + metadata.add('key', 'value1'); + metadata.add('key', 'value2'); + assert.deepEqual(metadata.get('key'), ['value1', 'value2']); + }); + it('Normalizes keys', function() { + metadata.add('Key', 'value1'); + assert.deepEqual(metadata.get('key'), ['value1']); + metadata.add('KEY', 'value2'); + assert.deepEqual(metadata.get('key'), ['value1', 'value2']); + }); + }); + describe('#remove', function() { + it('clears values from a key', function() { + metadata.add('key', 'value'); + metadata.remove('key'); + assert.deepEqual(metadata.get('key'), []); + }); + it('Normalizes keys', function() { + metadata.add('key', 'value'); + metadata.remove('KEY'); + assert.deepEqual(metadata.get('key'), []); + }); + }); + describe('#get', function() { + beforeEach(function() { + metadata.add('key', 'value1'); + metadata.add('key', 'value2'); + metadata.add('key-bin', new Buffer('value')); + }); + it('gets all values associated with a key', function() { + assert.deepEqual(metadata.get('key'), ['value1', 'value2']); + }); + it('Normalizes keys', function() { + assert.deepEqual(metadata.get('KEY'), ['value1', 'value2']); + }); + it('returns an empty list for non-existent keys', function() { + assert.deepEqual(metadata.get('non-existent-key'), []); + }); + it('returns Buffers for "-bin" keys', function() { + assert(metadata.get('key-bin')[0] instanceof Buffer); + }); + }); + describe('#getMap', function() { + it('gets a map of keys to values', function() { + metadata.add('key1', 'value1'); + metadata.add('Key2', 'value2'); + metadata.add('KEY3', 'value3'); + assert.deepEqual(metadata.getMap(), + {key1: 'value1', + key2: 'value2', + key3: 'value3'}); + }); + }); + describe('#clone', function() { + it('retains values from the original', function() { + metadata.add('key', 'value'); + var copy = metadata.clone(); + assert.deepEqual(copy.get('key'), ['value']); + }); + it('Does not see newly added values', function() { + metadata.add('key', 'value1'); + var copy = metadata.clone(); + metadata.add('key', 'value2'); + assert.deepEqual(copy.get('key'), ['value1']); + }); + it('Does not add new values to the original', function() { + metadata.add('key', 'value1'); + var copy = metadata.clone(); + copy.add('key', 'value2'); + assert.deepEqual(metadata.get('key'), ['value1']); + }); + }); +}); diff --git a/src/node/test/server_test.js b/src/node/test/server_test.js new file mode 100644 index 00000000..1e69d52e --- /dev/null +++ b/src/node/test/server_test.js @@ -0,0 +1,132 @@ +/* + * + * Copyright 2015, 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. + * + */ + +'use strict'; + +var assert = require('assert'); +var fs = require('fs'); +var path = require('path'); +var grpc = require('bindings')('grpc.node'); + +describe('server', function() { + describe('constructor', function() { + it('should work with no arguments', function() { + assert.doesNotThrow(function() { + new grpc.Server(); + }); + }); + it('should work with an empty list argument', function() { + assert.doesNotThrow(function() { + new grpc.Server([]); + }); + }); + }); + describe('addHttp2Port', function() { + var server; + before(function() { + server = new grpc.Server(); + }); + it('should bind to an unused port', function() { + var port; + assert.doesNotThrow(function() { + port = server.addHttp2Port('0.0.0.0:0', + grpc.ServerCredentials.createInsecure()); + }); + assert(port > 0); + }); + it('should bind to an unused port with ssl credentials', function() { + var port; + var key_path = path.join(__dirname, '../test/data/server1.key'); + var pem_path = path.join(__dirname, '../test/data/server1.pem'); + var key_data = fs.readFileSync(key_path); + var pem_data = fs.readFileSync(pem_path); + var creds = grpc.ServerCredentials.createSsl(null, + [{private_key: key_data, + cert_chain: pem_data}]); + assert.doesNotThrow(function() { + port = server.addHttp2Port('0.0.0.0:0', creds); + }); + assert(port > 0); + }); + }); + describe('addSecureHttp2Port', function() { + var server; + before(function() { + server = new grpc.Server(); + }); + }); + describe('start', function() { + var server; + before(function() { + server = new grpc.Server(); + server.addHttp2Port('0.0.0.0:0', grpc.ServerCredentials.createInsecure()); + }); + after(function() { + server.forceShutdown(); + }); + it('should start without error', function() { + assert.doesNotThrow(function() { + server.start(); + }); + }); + }); + describe('shutdown', function() { + var server; + beforeEach(function() { + server = new grpc.Server(); + server.addHttp2Port('0.0.0.0:0', grpc.ServerCredentials.createInsecure()); + server.start(); + }); + afterEach(function() { + server.forceShutdown(); + }); + it('tryShutdown should shutdown successfully', function(done) { + server.tryShutdown(done); + }); + it('forceShutdown should shutdown successfully', function() { + server.forceShutdown(); + }); + it('tryShutdown should be idempotent', function(done) { + server.tryShutdown(done); + server.tryShutdown(function() {}); + }); + it('forceShutdown should be idempotent', function() { + server.forceShutdown(); + server.forceShutdown(); + }); + it('forceShutdown should trigger tryShutdown', function(done) { + server.tryShutdown(done); + server.forceShutdown(); + }); + }); +}); diff --git a/src/node/test/surface_test.js b/src/node/test/surface_test.js new file mode 100644 index 00000000..d917c7a1 --- /dev/null +++ b/src/node/test/surface_test.js @@ -0,0 +1,905 @@ +/* + * + * Copyright 2015, 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. + * + */ + +'use strict'; + +var assert = require('assert'); + +var surface_client = require('../src/client.js'); + +var ProtoBuf = require('protobufjs'); + +var grpc = require('..'); + +var math_proto = ProtoBuf.loadProtoFile(__dirname + '/../examples/math.proto'); + +var mathService = math_proto.lookup('math.Math'); + +var _ = require('lodash'); + +/** + * This is used for testing functions with multiple asynchronous calls that + * can happen in different orders. This should be passed the number of async + * function invocations that can occur last, and each of those should call this + * function's return value + * @param {function()} done The function that should be called when a test is + * complete. + * @param {number} count The number of calls to the resulting function if the + * test passes. + * @return {function()} The function that should be called at the end of each + * sequence of asynchronous functions. + */ +function multiDone(done, count) { + return function() { + count -= 1; + if (count <= 0) { + done(); + } + }; +} + +var server_insecure_creds = grpc.ServerCredentials.createInsecure(); + +describe('File loader', function() { + it('Should load a proto file by default', function() { + assert.doesNotThrow(function() { + grpc.load(__dirname + '/test_service.proto'); + }); + }); + it('Should load a proto file with the proto format', function() { + assert.doesNotThrow(function() { + grpc.load(__dirname + '/test_service.proto', 'proto'); + }); + }); + it('Should load a json file with the json format', function() { + assert.doesNotThrow(function() { + grpc.load(__dirname + '/test_service.json', 'json'); + }); + }); + it('Should fail to load a file with an unknown format', function() { + assert.throws(function() { + grpc.load(__dirname + '/test_service.proto', 'fake_format'); + }); + }); +}); +describe('Server.prototype.addProtoService', function() { + var server; + var dummyImpls = { + 'div': function() {}, + 'divMany': function() {}, + 'fib': function() {}, + 'sum': function() {} + }; + beforeEach(function() { + server = new grpc.Server(); + }); + afterEach(function() { + server.forceShutdown(); + }); + it('Should succeed with a single service', function() { + assert.doesNotThrow(function() { + server.addProtoService(mathService, dummyImpls); + }); + }); + it('Should fail with conflicting method names', function() { + server.addProtoService(mathService, dummyImpls); + assert.throws(function() { + server.addProtoService(mathService, dummyImpls); + }); + }); + it('Should fail with missing handlers', function() { + assert.throws(function() { + server.addProtoService(mathService, { + 'div': function() {}, + 'divMany': function() {}, + 'fib': function() {} + }); + }, /math.Math.Sum/); + }); + it('Should fail if the server has been started', function() { + server.start(); + assert.throws(function() { + server.addProtoService(mathService, dummyImpls); + }); + }); +}); +describe('Client constructor building', function() { + var illegal_service_attrs = { + $method : { + path: '/illegal/$method', + requestStream: false, + responseStream: false, + requestSerialize: _.identity, + requestDeserialize: _.identity, + responseSerialize: _.identity, + responseDeserialize: _.identity + } + }; + it('Should reject method names starting with $', function() { + assert.throws(function() { + grpc.makeGenericClientConstructor(illegal_service_attrs); + }, /\$/); + }); +}); +describe('waitForClientReady', function() { + var server; + var port; + var Client; + var client; + before(function() { + server = new grpc.Server(); + port = server.bind('localhost:0', grpc.ServerCredentials.createInsecure()); + server.start(); + Client = surface_client.makeProtobufClientConstructor(mathService); + }); + beforeEach(function() { + client = new Client('localhost:' + port, grpc.Credentials.createInsecure()); + }); + after(function() { + server.forceShutdown(); + }); + it('should complete when called alone', function(done) { + grpc.waitForClientReady(client, Infinity, function(error) { + assert.ifError(error); + done(); + }); + }); + it('should complete when a call is initiated', function(done) { + grpc.waitForClientReady(client, Infinity, function(error) { + assert.ifError(error); + done(); + }); + var call = client.div({}, function(err, response) {}); + call.cancel(); + }); + it('should complete if called more than once', function(done) { + done = multiDone(done, 2); + grpc.waitForClientReady(client, Infinity, function(error) { + assert.ifError(error); + done(); + }); + grpc.waitForClientReady(client, Infinity, function(error) { + assert.ifError(error); + done(); + }); + }); + it('should complete if called when already ready', function(done) { + grpc.waitForClientReady(client, Infinity, function(error) { + assert.ifError(error); + grpc.waitForClientReady(client, Infinity, function(error) { + assert.ifError(error); + done(); + }); + }); + }); +}); +describe('Echo service', function() { + var server; + var client; + before(function() { + var test_proto = ProtoBuf.loadProtoFile(__dirname + '/echo_service.proto'); + var echo_service = test_proto.lookup('EchoService'); + server = new grpc.Server(); + server.addProtoService(echo_service, { + echo: function(call, callback) { + callback(null, call.request); + } + }); + var port = server.bind('localhost:0', server_insecure_creds); + var Client = surface_client.makeProtobufClientConstructor(echo_service); + client = new Client('localhost:' + port, grpc.Credentials.createInsecure()); + server.start(); + }); + after(function() { + server.forceShutdown(); + }); + it('should echo the recieved message directly', function(done) { + client.echo({value: 'test value', value2: 3}, function(error, response) { + assert.ifError(error); + assert.deepEqual(response, {value: 'test value', value2: 3}); + done(); + }); + }); +}); +describe('Generic client and server', function() { + function toString(val) { + return val.toString(); + } + function toBuffer(str) { + return new Buffer(str); + } + var string_service_attrs = { + 'capitalize' : { + path: '/string/capitalize', + requestStream: false, + responseStream: false, + requestSerialize: toBuffer, + requestDeserialize: toString, + responseSerialize: toBuffer, + responseDeserialize: toString + } + }; + describe('String client and server', function() { + var client; + var server; + before(function() { + server = new grpc.Server(); + server.addService(string_service_attrs, { + capitalize: function(call, callback) { + callback(null, _.capitalize(call.request)); + } + }); + var port = server.bind('localhost:0', server_insecure_creds); + server.start(); + var Client = grpc.makeGenericClientConstructor(string_service_attrs); + client = new Client('localhost:' + port, + grpc.Credentials.createInsecure()); + }); + after(function() { + server.forceShutdown(); + }); + it('Should respond with a capitalized string', function(done) { + client.capitalize('abc', function(err, response) { + assert.ifError(err); + assert.strictEqual(response, 'Abc'); + done(); + }); + }); + }); +}); +describe('Echo metadata', function() { + var client; + var server; + var metadata; + before(function() { + var test_proto = ProtoBuf.loadProtoFile(__dirname + '/test_service.proto'); + var test_service = test_proto.lookup('TestService'); + server = new grpc.Server(); + server.addProtoService(test_service, { + unary: function(call, cb) { + call.sendMetadata(call.metadata); + cb(null, {}); + }, + clientStream: function(stream, cb){ + stream.on('data', function(data) {}); + stream.on('end', function() { + stream.sendMetadata(stream.metadata); + cb(null, {}); + }); + }, + serverStream: function(stream) { + stream.sendMetadata(stream.metadata); + stream.end(); + }, + bidiStream: function(stream) { + stream.on('data', function(data) {}); + stream.on('end', function() { + stream.sendMetadata(stream.metadata); + stream.end(); + }); + } + }); + var port = server.bind('localhost:0', server_insecure_creds); + var Client = surface_client.makeProtobufClientConstructor(test_service); + client = new Client('localhost:' + port, grpc.Credentials.createInsecure()); + server.start(); + metadata = new grpc.Metadata(); + metadata.set('key', 'value'); + }); + after(function() { + server.forceShutdown(); + }); + it('with unary call', function(done) { + var call = client.unary({}, function(err, data) { + assert.ifError(err); + }, metadata); + call.on('metadata', function(metadata) { + assert.deepEqual(metadata.get('key'), ['value']); + done(); + }); + }); + it('with client stream call', function(done) { + var call = client.clientStream(function(err, data) { + assert.ifError(err); + }, metadata); + call.on('metadata', function(metadata) { + assert.deepEqual(metadata.get('key'), ['value']); + done(); + }); + call.end(); + }); + it('with server stream call', function(done) { + var call = client.serverStream({}, metadata); + call.on('data', function() {}); + call.on('metadata', function(metadata) { + assert.deepEqual(metadata.get('key'), ['value']); + done(); + }); + }); + it('with bidi stream call', function(done) { + var call = client.bidiStream(metadata); + call.on('data', function() {}); + call.on('metadata', function(metadata) { + assert.deepEqual(metadata.get('key'), ['value']); + done(); + }); + call.end(); + }); + it('shows the correct user-agent string', function(done) { + var version = require('../package.json').version; + var call = client.unary({}, function(err, data) { assert.ifError(err); }, + metadata); + call.on('metadata', function(metadata) { + assert(_.startsWith(metadata.get('user-agent')[0], + 'grpc-node/' + version)); + done(); + }); + }); +}); +describe('Other conditions', function() { + var test_service; + var Client; + var client; + var server; + var port; + before(function() { + var test_proto = ProtoBuf.loadProtoFile(__dirname + '/test_service.proto'); + test_service = test_proto.lookup('TestService'); + server = new grpc.Server(); + var trailer_metadata = new grpc.Metadata(); + trailer_metadata.add('trailer-present', 'yes'); + server.addProtoService(test_service, { + unary: function(call, cb) { + var req = call.request; + if (req.error) { + cb(new Error('Requested error'), null, trailer_metadata); + } else { + cb(null, {count: 1}, trailer_metadata); + } + }, + clientStream: function(stream, cb){ + var count = 0; + var errored; + stream.on('data', function(data) { + if (data.error) { + errored = true; + cb(new Error('Requested error'), null, trailer_metadata); + } else { + count += 1; + } + }); + stream.on('end', function() { + if (!errored) { + cb(null, {count: count}, trailer_metadata); + } + }); + }, + serverStream: function(stream) { + var req = stream.request; + if (req.error) { + var err = new Error('Requested error'); + err.metadata = trailer_metadata; + stream.emit('error', err); + } else { + for (var i = 0; i < 5; i++) { + stream.write({count: i}); + } + stream.end(trailer_metadata); + } + }, + bidiStream: function(stream) { + var count = 0; + stream.on('data', function(data) { + if (data.error) { + var err = new Error('Requested error'); + err.metadata = trailer_metadata.clone(); + err.metadata.add('count', '' + count); + stream.emit('error', err); + } else { + stream.write({count: count}); + count += 1; + } + }); + stream.on('end', function() { + stream.end(trailer_metadata); + }); + } + }); + port = server.bind('localhost:0', server_insecure_creds); + Client = surface_client.makeProtobufClientConstructor(test_service); + client = new Client('localhost:' + port, grpc.Credentials.createInsecure()); + server.start(); + }); + after(function() { + server.forceShutdown(); + }); + it('channel.getTarget should be available', function() { + assert.strictEqual(typeof grpc.getClientChannel(client).getTarget(), + 'string'); + }); + describe('Server recieving bad input', function() { + var misbehavingClient; + var badArg = new Buffer([0xFF]); + before(function() { + var test_service_attrs = { + unary: { + path: '/TestService/Unary', + requestStream: false, + responseStream: false, + requestSerialize: _.identity, + responseDeserialize: _.identity + }, + clientStream: { + path: '/TestService/ClientStream', + requestStream: true, + responseStream: false, + requestSerialize: _.identity, + responseDeserialize: _.identity + }, + serverStream: { + path: '/TestService/ServerStream', + requestStream: false, + responseStream: true, + requestSerialize: _.identity, + responseDeserialize: _.identity + }, + bidiStream: { + path: '/TestService/BidiStream', + requestStream: true, + responseStream: true, + requestSerialize: _.identity, + responseDeserialize: _.identity + } + }; + var Client = surface_client.makeClientConstructor(test_service_attrs, + 'TestService'); + misbehavingClient = new Client('localhost:' + port, + grpc.Credentials.createInsecure()); + }); + it('should respond correctly to a unary call', function(done) { + misbehavingClient.unary(badArg, function(err, data) { + assert(err); + assert.strictEqual(err.code, grpc.status.INVALID_ARGUMENT); + done(); + }); + }); + it('should respond correctly to a client stream', function(done) { + var call = misbehavingClient.clientStream(function(err, data) { + assert(err); + assert.strictEqual(err.code, grpc.status.INVALID_ARGUMENT); + done(); + }); + call.write(badArg); + // TODO(mlumish): Remove call.end() + call.end(); + }); + it('should respond correctly to a server stream', function(done) { + var call = misbehavingClient.serverStream(badArg); + call.on('data', function(data) { + assert.fail(data, null, 'Unexpected data', '==='); + }); + call.on('error', function(err) { + assert.strictEqual(err.code, grpc.status.INVALID_ARGUMENT); + done(); + }); + }); + it('should respond correctly to a bidi stream', function(done) { + var call = misbehavingClient.bidiStream(); + call.on('data', function(data) { + assert.fail(data, null, 'Unexpected data', '==='); + }); + call.on('error', function(err) { + assert.strictEqual(err.code, grpc.status.INVALID_ARGUMENT); + done(); + }); + call.write(badArg); + // TODO(mlumish): Remove call.end() + call.end(); + }); + }); + describe('Trailing metadata', function() { + it('should be present when a unary call succeeds', function(done) { + var call = client.unary({error: false}, function(err, data) { + assert.ifError(err); + }); + call.on('status', function(status) { + assert.deepEqual(status.metadata.get('trailer-present'), ['yes']); + done(); + }); + }); + it('should be present when a unary call fails', function(done) { + var call = client.unary({error: true}, function(err, data) { + assert(err); + }); + call.on('status', function(status) { + assert.deepEqual(status.metadata.get('trailer-present'), ['yes']); + done(); + }); + }); + it('should be present when a client stream call succeeds', function(done) { + var call = client.clientStream(function(err, data) { + assert.ifError(err); + }); + call.write({error: false}); + call.write({error: false}); + call.end(); + call.on('status', function(status) { + assert.deepEqual(status.metadata.get('trailer-present'), ['yes']); + done(); + }); + }); + it('should be present when a client stream call fails', function(done) { + var call = client.clientStream(function(err, data) { + assert(err); + }); + call.write({error: false}); + call.write({error: true}); + call.end(); + call.on('status', function(status) { + assert.deepEqual(status.metadata.get('trailer-present'), ['yes']); + done(); + }); + }); + it('should be present when a server stream call succeeds', function(done) { + var call = client.serverStream({error: false}); + call.on('data', function(){}); + call.on('status', function(status) { + assert.strictEqual(status.code, grpc.status.OK); + assert.deepEqual(status.metadata.get('trailer-present'), ['yes']); + done(); + }); + }); + it('should be present when a server stream call fails', function(done) { + var call = client.serverStream({error: true}); + call.on('data', function(){}); + call.on('error', function(error) { + assert.deepEqual(error.metadata.get('trailer-present'), ['yes']); + done(); + }); + }); + it('should be present when a bidi stream succeeds', function(done) { + var call = client.bidiStream(); + call.write({error: false}); + call.write({error: false}); + call.end(); + call.on('data', function(){}); + call.on('status', function(status) { + assert.strictEqual(status.code, grpc.status.OK); + assert.deepEqual(status.metadata.get('trailer-present'), ['yes']); + done(); + }); + }); + it('should be present when a bidi stream fails', function(done) { + var call = client.bidiStream(); + call.write({error: false}); + call.write({error: true}); + call.end(); + call.on('data', function(){}); + call.on('error', function(error) { + assert.deepEqual(error.metadata.get('trailer-present'), ['yes']); + done(); + }); + }); + }); + describe('Error object should contain the status', function() { + it('for a unary call', function(done) { + client.unary({error: true}, function(err, data) { + assert(err); + assert.strictEqual(err.code, grpc.status.UNKNOWN); + assert.strictEqual(err.message, 'Requested error'); + done(); + }); + }); + it('for a client stream call', function(done) { + var call = client.clientStream(function(err, data) { + assert(err); + assert.strictEqual(err.code, grpc.status.UNKNOWN); + assert.strictEqual(err.message, 'Requested error'); + done(); + }); + call.write({error: false}); + call.write({error: true}); + call.end(); + }); + it('for a server stream call', function(done) { + var call = client.serverStream({error: true}); + call.on('data', function(){}); + call.on('error', function(error) { + assert.strictEqual(error.code, grpc.status.UNKNOWN); + assert.strictEqual(error.message, 'Requested error'); + done(); + }); + }); + it('for a bidi stream call', function(done) { + var call = client.bidiStream(); + call.write({error: false}); + call.write({error: true}); + call.end(); + call.on('data', function(){}); + call.on('error', function(error) { + assert.strictEqual(error.code, grpc.status.UNKNOWN); + assert.strictEqual(error.message, 'Requested error'); + done(); + }); + }); + }); + describe('call.getPeer should return the peer', function() { + it('for a unary call', function(done) { + var call = client.unary({error: false}, function(err, data) { + assert.ifError(err); + done(); + }); + assert.strictEqual(typeof call.getPeer(), 'string'); + }); + it('for a client stream call', function(done) { + var call = client.clientStream(function(err, data) { + assert.ifError(err); + done(); + }); + assert.strictEqual(typeof call.getPeer(), 'string'); + call.write({error: false}); + call.end(); + }); + it('for a server stream call', function(done) { + var call = client.serverStream({error: false}); + assert.strictEqual(typeof call.getPeer(), 'string'); + call.on('data', function(){}); + call.on('status', function(status) { + assert.strictEqual(status.code, grpc.status.OK); + done(); + }); + }); + it('for a bidi stream call', function(done) { + var call = client.bidiStream(); + assert.strictEqual(typeof call.getPeer(), 'string'); + call.write({error: false}); + call.end(); + call.on('data', function(){}); + call.on('status', function(status) { + done(); + }); + }); + }); + describe('Call propagation', function() { + var proxy; + var proxy_impl; + beforeEach(function() { + proxy = new grpc.Server(); + proxy_impl = { + unary: function(call) {}, + clientStream: function(stream) {}, + serverStream: function(stream) {}, + bidiStream: function(stream) {} + }; + }); + afterEach(function() { + console.log('Shutting down server'); + proxy.forceShutdown(); + }); + describe('Cancellation', function() { + it('With a unary call', function(done) { + done = multiDone(done, 2); + proxy_impl.unary = function(parent, callback) { + client.unary(parent.request, function(err, value) { + try { + assert(err); + assert.strictEqual(err.code, grpc.status.CANCELLED); + } finally { + callback(err, value); + done(); + } + }, null, {parent: parent}); + call.cancel(); + }; + proxy.addProtoService(test_service, proxy_impl); + var proxy_port = proxy.bind('localhost:0', server_insecure_creds); + proxy.start(); + var proxy_client = new Client('localhost:' + proxy_port, + grpc.Credentials.createInsecure()); + var call = proxy_client.unary({}, function(err, value) { + done(); + }); + }); + it('With a client stream call', function(done) { + done = multiDone(done, 2); + proxy_impl.clientStream = function(parent, callback) { + client.clientStream(function(err, value) { + try { + assert(err); + assert.strictEqual(err.code, grpc.status.CANCELLED); + } finally { + callback(err, value); + done(); + } + }, null, {parent: parent}); + call.cancel(); + }; + proxy.addProtoService(test_service, proxy_impl); + var proxy_port = proxy.bind('localhost:0', server_insecure_creds); + proxy.start(); + var proxy_client = new Client('localhost:' + proxy_port, + grpc.Credentials.createInsecure()); + var call = proxy_client.clientStream(function(err, value) { + done(); + }); + }); + it('With a server stream call', function(done) { + done = multiDone(done, 2); + proxy_impl.serverStream = function(parent) { + var child = client.serverStream(parent.request, null, + {parent: parent}); + child.on('error', function(err) { + assert(err); + assert.strictEqual(err.code, grpc.status.CANCELLED); + done(); + }); + call.cancel(); + }; + proxy.addProtoService(test_service, proxy_impl); + var proxy_port = proxy.bind('localhost:0', server_insecure_creds); + proxy.start(); + var proxy_client = new Client('localhost:' + proxy_port, + grpc.Credentials.createInsecure()); + var call = proxy_client.serverStream({}); + call.on('error', function(err) { + done(); + }); + }); + it('With a bidi stream call', function(done) { + done = multiDone(done, 2); + proxy_impl.bidiStream = function(parent) { + var child = client.bidiStream(null, {parent: parent}); + child.on('error', function(err) { + assert(err); + assert.strictEqual(err.code, grpc.status.CANCELLED); + done(); + }); + call.cancel(); + }; + proxy.addProtoService(test_service, proxy_impl); + var proxy_port = proxy.bind('localhost:0', server_insecure_creds); + proxy.start(); + var proxy_client = new Client('localhost:' + proxy_port, + grpc.Credentials.createInsecure()); + var call = proxy_client.bidiStream(); + call.on('error', function(err) { + done(); + }); + }); + }); + describe('Deadline', function() { + /* jshint bitwise:false */ + var deadline_flags = (grpc.propagate.DEFAULTS & + ~grpc.propagate.CANCELLATION); + it('With a client stream call', function(done) { + done = multiDone(done, 2); + proxy_impl.clientStream = function(parent, callback) { + client.clientStream(function(err, value) { + try { + assert(err); + assert(err.code === grpc.status.DEADLINE_EXCEEDED || + err.code === grpc.status.INTERNAL); + } finally { + callback(err, value); + done(); + } + }, null, {parent: parent, propagate_flags: deadline_flags}); + }; + proxy.addProtoService(test_service, proxy_impl); + var proxy_port = proxy.bind('localhost:0', server_insecure_creds); + proxy.start(); + var proxy_client = new Client('localhost:' + proxy_port, + grpc.Credentials.createInsecure()); + var deadline = new Date(); + deadline.setSeconds(deadline.getSeconds() + 1); + proxy_client.clientStream(function(err, value) { + done(); + }, null, {deadline: deadline}); + }); + it('With a bidi stream call', function(done) { + done = multiDone(done, 2); + proxy_impl.bidiStream = function(parent) { + var child = client.bidiStream( + null, {parent: parent, propagate_flags: deadline_flags}); + child.on('error', function(err) { + assert(err); + assert(err.code === grpc.status.DEADLINE_EXCEEDED || + err.code === grpc.status.INTERNAL); + done(); + }); + }; + proxy.addProtoService(test_service, proxy_impl); + var proxy_port = proxy.bind('localhost:0', server_insecure_creds); + proxy.start(); + var proxy_client = new Client('localhost:' + proxy_port, + grpc.Credentials.createInsecure()); + var deadline = new Date(); + deadline.setSeconds(deadline.getSeconds() + 1); + var call = proxy_client.bidiStream(null, {deadline: deadline}); + call.on('error', function(err) { + done(); + }); + }); + }); + }); +}); +describe('Cancelling surface client', function() { + var client; + var server; + before(function() { + server = new grpc.Server(); + server.addProtoService(mathService, { + 'div': function(stream) {}, + 'divMany': function(stream) {}, + 'fib': function(stream) {}, + 'sum': function(stream) {} + }); + var port = server.bind('localhost:0', server_insecure_creds); + var Client = surface_client.makeProtobufClientConstructor(mathService); + client = new Client('localhost:' + port, grpc.Credentials.createInsecure()); + server.start(); + }); + after(function() { + server.forceShutdown(); + }); + it('Should correctly cancel a unary call', function(done) { + var call = client.div({'divisor': 0, 'dividend': 0}, function(err, resp) { + assert.strictEqual(err.code, surface_client.status.CANCELLED); + done(); + }); + call.cancel(); + }); + it('Should correctly cancel a client stream call', function(done) { + var call = client.sum(function(err, resp) { + assert.strictEqual(err.code, surface_client.status.CANCELLED); + done(); + }); + call.cancel(); + }); + it('Should correctly cancel a server stream call', function(done) { + var call = client.fib({'limit': 5}); + call.on('error', function(error) { + assert.strictEqual(error.code, surface_client.status.CANCELLED); + done(); + }); + call.cancel(); + }); + it('Should correctly cancel a bidi stream call', function(done) { + var call = client.divMany(); + call.on('error', function(error) { + assert.strictEqual(error.code, surface_client.status.CANCELLED); + done(); + }); + call.cancel(); + }); +}); diff --git a/src/node/test/test_messages.proto b/src/node/test/test_messages.proto new file mode 100644 index 00000000..685e9482 --- /dev/null +++ b/src/node/test/test_messages.proto @@ -0,0 +1,38 @@ +// Copyright 2015, 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. + +syntax = "proto3"; + +message LongValues { + int64 int_64 = 1; + uint64 uint_64 = 2; + sint64 sint_64 = 3; + fixed64 fixed_64 = 4; + sfixed64 sfixed_64 = 5; +} \ No newline at end of file diff --git a/src/node/test/test_service.json b/src/node/test/test_service.json new file mode 100644 index 00000000..6f952c6a --- /dev/null +++ b/src/node/test/test_service.json @@ -0,0 +1,55 @@ +{ + "package": null, + "messages": [ + { + "name": "Request", + "fields": [ + { + "rule": "optional", + "type": "bool", + "name": "error", + "id": 1 + } + ] + }, + { + "name": "Response", + "fields": [ + { + "rule": "optional", + "type": "int32", + "name": "count", + "id": 1 + } + ] + } + ], + "services": [ + { + "name": "TestService", + "options": {}, + "rpc": { + "Unary": { + "request": "Request", + "response": "Response", + "options": {} + }, + "ClientStream": { + "request": "Request", + "response": "Response", + "options": {} + }, + "ServerStream": { + "request": "Request", + "response": "Response", + "options": {} + }, + "BidiStream": { + "request": "Request", + "response": "Response", + "options": {} + } + } + } + ] +} \ No newline at end of file diff --git a/src/node/test/test_service.proto b/src/node/test/test_service.proto new file mode 100644 index 00000000..56416982 --- /dev/null +++ b/src/node/test/test_service.proto @@ -0,0 +1,52 @@ +// Copyright 2015, 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. + +syntax = "proto3"; + +message Request { + bool error = 1; +} + +message Response { + int32 count = 1; +} + +service TestService { + rpc Unary (Request) returns (Response) { + } + + rpc ClientStream (stream Request) returns (Response) { + } + + rpc ServerStream (Request) returns (stream Response) { + } + + rpc BidiStream (stream Request) returns (stream Response) { + } +} \ No newline at end of file diff --git a/src/objective-c/.gitignore b/src/objective-c/.gitignore new file mode 100644 index 00000000..15110668 --- /dev/null +++ b/src/objective-c/.gitignore @@ -0,0 +1,19 @@ +# Xcode +# +build/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata +*.xccheckout +*.moved-aside +DerivedData +*.hmap +*.ipa +*.xcuserstate +*.DS_Store \ No newline at end of file diff --git a/src/objective-c/GRPCClient/GRPCCall+OAuth2.h b/src/objective-c/GRPCClient/GRPCCall+OAuth2.h new file mode 100644 index 00000000..2e379a71 --- /dev/null +++ b/src/objective-c/GRPCClient/GRPCCall+OAuth2.h @@ -0,0 +1,49 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "GRPCCall.h" + +// Helpers for setting and reading headers compatible with OAuth2. +@interface GRPCCall (OAuth2) + +// Setting this property is equivalent to setting "Bearer " as the value of the +// request header with key "authorization" (the authorization header). Setting it to nil removes the +// authorization header from the request. +// The value obtained by getting the property is the OAuth2 bearer token if the authorization header +// of the request has the form "Bearer ", or nil otherwise. +@property(atomic, copy) NSString *oauth2AccessToken; + +// Returns the value (if any) of the "www-authenticate" response header (the challenge header). +@property(atomic, readonly) NSString *oauth2ChallengeHeader; + +@end diff --git a/src/objective-c/GRPCClient/GRPCCall+OAuth2.m b/src/objective-c/GRPCClient/GRPCCall+OAuth2.m new file mode 100644 index 00000000..83b0de18 --- /dev/null +++ b/src/objective-c/GRPCClient/GRPCCall+OAuth2.m @@ -0,0 +1,63 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "GRPCCall+OAuth2.h" + +static NSString * const kAuthorizationHeader = @"authorization"; +static NSString * const kBearerPrefix = @"Bearer "; +static NSString * const kChallengeHeader = @"www-authenticate"; + +@implementation GRPCCall (OAuth2) + +- (NSString *)oauth2AccessToken { + NSString *headerValue = self.requestHeaders[kAuthorizationHeader]; + if ([headerValue hasPrefix:kBearerPrefix]) { + return [headerValue substringFromIndex:kBearerPrefix.length]; + } else { + return nil; + } +} + +- (void)setOauth2AccessToken:(NSString *)token { + if (token) { + self.requestHeaders[kAuthorizationHeader] = [kBearerPrefix stringByAppendingString:token]; + } else { + [self.requestHeaders removeObjectForKey:kAuthorizationHeader]; + } +} + +- (NSString *)oauth2ChallengeHeader { + return self.responseHeaders[kChallengeHeader]; +} + +@end diff --git a/src/objective-c/GRPCClient/GRPCCall+Tests.h b/src/objective-c/GRPCClient/GRPCCall+Tests.h new file mode 100644 index 00000000..cca16146 --- /dev/null +++ b/src/objective-c/GRPCClient/GRPCCall+Tests.h @@ -0,0 +1,54 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "GRPCCall.h" + +// Methods to let tune down the security of gRPC connections for specific hosts. These shouldn't be +// used in releases, but are sometimes needed for testing. +@interface GRPCCall (Tests) + +// Establish all SSL connections to the provided host using the passed SSL target name and the root +// certificates found in the file at |certsPath|. +// +// Must be called before any gRPC call to that host is made. It's illegal to pass the same host to +// more than one invocation of the methods of this category. ++ (void)useTestCertsPath:(NSString *)certsPath + testName:(NSString *)testName + forHost:(NSString *)host; + +// Establish all connections to the provided host using cleartext instead of SSL. +// +// Must be called before any gRPC call to that host is made. It's illegal to pass the same host to +// more than one invocation of the methods of this category. ++ (void)useInsecureConnectionsForHost:(NSString *)host; +@end diff --git a/src/objective-c/GRPCClient/GRPCCall+Tests.m b/src/objective-c/GRPCClient/GRPCCall+Tests.m new file mode 100644 index 00000000..bade0b29 --- /dev/null +++ b/src/objective-c/GRPCClient/GRPCCall+Tests.m @@ -0,0 +1,53 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "GRPCCall+Tests.h" + +#import "private/GRPCHost.h" + +@implementation GRPCCall (Tests) + ++ (void)useTestCertsPath:(NSString *)certsPath + testName:(NSString *)testName + forHost:(NSString *)host { + GRPCHost *hostConfig = [GRPCHost hostWithAddress:host]; + hostConfig.pathToCertificates = certsPath; + hostConfig.hostNameOverride = testName; +} + ++ (void)useInsecureConnectionsForHost:(NSString *)host { + GRPCHost *hostConfig = [GRPCHost hostWithAddress:host]; + hostConfig.secure = NO; +} + +@end diff --git a/src/objective-c/GRPCClient/GRPCCall.h b/src/objective-c/GRPCClient/GRPCCall.h new file mode 100644 index 00000000..35f7e16a --- /dev/null +++ b/src/objective-c/GRPCClient/GRPCCall.h @@ -0,0 +1,207 @@ +/* + * + * Copyright 2015, 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. + * + */ + +// The gRPC protocol is an RPC protocol on top of HTTP2. +// +// While the most common type of RPC receives only one request message and returns only one response +// message, the protocol also supports RPCs that return multiple individual messages in a streaming +// fashion, RPCs that accept a stream of request messages, or RPCs with both streaming requests and +// responses. +// +// Conceptually, each gRPC call consists of a bidirectional stream of binary messages, with RPCs of +// the "non-streaming type" sending only one message in the corresponding direction (the protocol +// doesn't make any distinction). +// +// Each RPC uses a different HTTP2 stream, and thus multiple simultaneous RPCs can be multiplexed +// transparently on the same TCP connection. + +#import +#import + +#pragma mark gRPC errors + +// Domain of NSError objects produced by gRPC. +extern NSString *const kGRPCErrorDomain; + +// gRPC error codes. +// Note that a few of these are never produced by the gRPC libraries, but are of general utility for +// server applications to produce. +typedef NS_ENUM(NSUInteger, GRPCErrorCode) { + // The operation was cancelled (typically by the caller). + GRPCErrorCodeCancelled = 1, + + // Unknown error. Errors raised by APIs that do not return enough error information may be + // converted to this error. + GRPCErrorCodeUnknown = 2, + + // The client specified an invalid argument. Note that this differs from FAILED_PRECONDITION. + // INVALID_ARGUMENT indicates arguments that are problematic regardless of the state of the + // server (e.g., a malformed file name). + GRPCErrorCodeInvalidArgument = 3, + + // Deadline expired before operation could complete. For operations that change the state of the + // server, this error may be returned even if the operation has completed successfully. For + // example, a successful response from the server could have been delayed long enough for the + // deadline to expire. + GRPCErrorCodeDeadlineExceeded = 4, + + // Some requested entity (e.g., file or directory) was not found. + GRPCErrorCodeNotFound = 5, + + // Some entity that we attempted to create (e.g., file or directory) already exists. + GRPCErrorCodeAlreadyExists = 6, + + // The caller does not have permission to execute the specified operation. PERMISSION_DENIED isn't + // used for rejections caused by exhausting some resource (RESOURCE_EXHAUSTED is used instead for + // those errors). PERMISSION_DENIED doesn't indicate a failure to identify the caller + // (UNAUTHENTICATED is used instead for those errors). + GRPCErrorCodePermissionDenied = 7, + + // The request does not have valid authentication credentials for the operation (e.g. the caller's + // identity can't be verified). + GRPCErrorCodeUnauthenticated = 16, + + // Some resource has been exhausted, perhaps a per-user quota. + GRPCErrorCodeResourceExhausted = 8, + + // The RPC was rejected because the server is not in a state required for the procedure's + // execution. For example, a directory to be deleted may be non-empty, etc. + // The client should not retry until the server state has been explicitly fixed (e.g. by + // performing another RPC). The details depend on the service being called, and should be found in + // the NSError's userInfo. + GRPCErrorCodeFailedPrecondition = 9, + + // The RPC was aborted, typically due to a concurrency issue like sequencer check failures, + // transaction aborts, etc. The client should retry at a higher-level (e.g., restarting a read- + // modify-write sequence). + GRPCErrorCodeAborted = 10, + + // The RPC was attempted past the valid range. E.g., enumerating past the end of a list. + // Unlike INVALID_ARGUMENT, this error indicates a problem that may be fixed if the system state + // changes. For example, an RPC to get elements of a list will generate INVALID_ARGUMENT if asked + // to return the element at a negative index, but it will generate OUT_OF_RANGE if asked to return + // the element at an index past the current size of the list. + GRPCErrorCodeOutOfRange = 11, + + // The procedure is not implemented or not supported/enabled in this server. + GRPCErrorCodeUnimplemented = 12, + + // Internal error. Means some invariant expected by the server application or the gRPC library has + // been broken. + GRPCErrorCodeInternal = 13, + + // The server is currently unavailable. This is most likely a transient condition and may be + // corrected by retrying with a backoff. + GRPCErrorCodeUnavailable = 14, + + // Unrecoverable data loss or corruption. + GRPCErrorCodeDataLoss = 15, +}; + +// Keys used in |NSError|'s |userInfo| dictionary to store the response headers and trailers sent by +// the server. +extern id const kGRPCHeadersKey; +extern id const kGRPCTrailersKey; + +#pragma mark GRPCCall + +// The container of the request headers of an RPC conforms to this protocol, which is a subset of +// NSMutableDictionary's interface. It will become a NSMutableDictionary later on. +// The keys of this container are the header names, which per the HTTP standard are case- +// insensitive. They are stored in lowercase (which is how HTTP/2 mandates them on the wire), and +// can only consist of ASCII characters. +// A header value is a NSString object (with only ASCII characters), unless the header name has the +// suffix "-bin", in which case the value has to be a NSData object. +@protocol GRPCRequestHeaders + +@property(nonatomic, readonly) NSUInteger count; + +- (id)objectForKeyedSubscript:(NSString *)key; +- (void)setObject:(id)obj forKeyedSubscript:(NSString *)key; + +- (void)removeAllObjects; +- (void)removeObjectForKey:(NSString *)key; + +@end + +// Represents a single gRPC remote call. +@interface GRPCCall : GRXWriter + +// These HTTP headers will be passed to the server as part of this call. Each HTTP header is a +// name-value pair with string names and either string or binary values. +// +// The passed dictionary has to use NSString keys, corresponding to the header names. The value +// associated to each can be a NSString object or a NSData object. E.g.: +// +// call.requestHeaders = @{@"authorization": @"Bearer ..."}; +// +// call.requestHeaders[@"my-header-bin"] = someData; +// +// After the call is started, trying to modify this property is an error. +// +// The property is initialized to an empty NSMutableDictionary. +@property(atomic, readonly) id requestHeaders; + +// This dictionary is populated with the HTTP headers received from the server. This happens before +// any response message is received from the server. It has the same structure as the request +// headers dictionary: Keys are NSString header names; names ending with the suffix "-bin" have a +// NSData value; the others have a NSString value. +// +// The value of this property is nil until all response headers are received, and will change before +// any of -writeValue: or -writesFinishedWithError: are sent to the writeable. +@property(atomic, readonly) NSDictionary *responseHeaders; + +// Same as responseHeaders, but populated with the HTTP trailers received from the server before the +// call finishes. +// +// The value of this property is nil until all response trailers are received, and will change +// before -writesFinishedWithError: is sent to the writeable. +@property(atomic, readonly) NSDictionary *responseTrailers; + +// The request writer has to write NSData objects into the provided Writeable. The server will +// receive each of those separately and in order as distinct messages. +// A gRPC call might not complete until the request writer finishes. On the other hand, the request +// finishing doesn't necessarily make the call to finish, as the server might continue sending +// messages to the response side of the call indefinitely (depending on the semantics of the +// specific remote method called). +// To finish a call right away, invoke cancel. +- (instancetype)initWithHost:(NSString *)host + path:(NSString *)path + requestsWriter:(GRXWriter *)requestsWriter NS_DESIGNATED_INITIALIZER; + +// Finishes the request side of this call, notifies the server that the RPC should be cancelled, and +// finishes the response side of the call with an error of code CANCELED. +- (void)cancel; + +// TODO(jcanizales): Let specify a deadline. As a category of GRXWriter? +@end diff --git a/src/objective-c/GRPCClient/GRPCCall.m b/src/objective-c/GRPCClient/GRPCCall.m new file mode 100644 index 00000000..b6986bf5 --- /dev/null +++ b/src/objective-c/GRPCClient/GRPCCall.m @@ -0,0 +1,388 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "GRPCCall.h" + +#include +#include +#import + +#import "private/GRPCRequestHeaders.h" +#import "private/GRPCWrappedCall.h" +#import "private/NSData+GRPC.h" +#import "private/NSDictionary+GRPC.h" +#import "private/NSError+GRPC.h" + +NSString * const kGRPCHeadersKey = @"io.grpc.HeadersKey"; +NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey"; + +@interface GRPCCall () +// Make them read-write. +@property(atomic, strong) NSDictionary *responseHeaders; +@property(atomic, strong) NSDictionary *responseTrailers; +@end + +// The following methods of a C gRPC call object aren't reentrant, and thus +// calls to them must be serialized: +// - start_batch +// - destroy +// +// start_batch with a SEND_MESSAGE argument can only be called after the +// OP_COMPLETE event for any previous write is received. This is achieved by +// pausing the requests writer immediately every time it writes a value, and +// resuming it again when OP_COMPLETE is received. +// +// Similarly, start_batch with a RECV_MESSAGE argument can only be called after +// the OP_COMPLETE event for any previous read is received.This is easier to +// enforce, as we're writing the received messages into the writeable: +// start_batch is enqueued once upon receiving the OP_COMPLETE event for the +// RECV_METADATA batch, and then once after receiving each OP_COMPLETE event for +// each RECV_MESSAGE batch. +@implementation GRPCCall { + dispatch_queue_t _callQueue; + + GRPCWrappedCall *_wrappedCall; + dispatch_once_t _callAlreadyInvoked; + + // The C gRPC library has less guarantees on the ordering of events than we + // do. Particularly, in the face of errors, there's no ordering guarantee at + // all. This wrapper over our actual writeable ensures thread-safety and + // correct ordering. + GRXConcurrentWriteable *_responseWriteable; + + // The network thread wants the requestWriter to resume (when the server is ready for more input), + // or to stop (on errors), concurrently with user threads that want to start it, pause it or stop + // it. Because a writer isn't thread-safe, we'll synchronize those operations on it. + // We don't use a dispatch queue for that purpose, because the writer can call writeValue: or + // writesFinishedWithError: on this GRPCCall as part of those operations. We want to be able to + // pause the writer immediately on writeValue:, so we need our locking to be recursive. + GRXWriter *_requestWriter; + + // To create a retain cycle when a call is started, up until it finishes. See + // |startWithWriteable:| and |finishWithError:|. This saves users from having to retain a + // reference to the call object if all they're interested in is the handler being executed when + // the response arrives. + GRPCCall *_retainSelf; + + GRPCRequestHeaders *_requestHeaders; +} + +@synthesize state = _state; + +- (instancetype)init { + return [self initWithHost:nil path:nil requestsWriter:nil]; +} + +// Designated initializer +- (instancetype)initWithHost:(NSString *)host + path:(NSString *)path + requestsWriter:(GRXWriter *)requestWriter { + if (!host || !path) { + [NSException raise:NSInvalidArgumentException format:@"Neither host nor path can be nil."]; + } + if (requestWriter.state != GRXWriterStateNotStarted) { + [NSException raise:NSInvalidArgumentException + format:@"The requests writer can't be already started."]; + } + if ((self = [super init])) { + _wrappedCall = [[GRPCWrappedCall alloc] initWithHost:host path:path]; + if (!_wrappedCall) { + return nil; + } + + // Serial queue to invoke the non-reentrant methods of the grpc_call object. + _callQueue = dispatch_queue_create("org.grpc.call", NULL); + + _requestWriter = requestWriter; + + _requestHeaders = [[GRPCRequestHeaders alloc] initWithCall:self]; + } + return self; +} + +#pragma mark Finish + +- (void)finishWithError:(NSError *)errorOrNil { + // If the call isn't retained anywhere else, it can be deallocated now. + _retainSelf = nil; + + // If there were still request messages coming, stop them. + @synchronized(_requestWriter) { + _requestWriter.state = GRXWriterStateFinished; + } + + if (errorOrNil) { + [_responseWriteable cancelWithError:errorOrNil]; + } else { + [_responseWriteable enqueueSuccessfulCompletion]; + } +} + +- (void)cancelCall { + // Can be called from any thread, any number of times. + [_wrappedCall cancel]; +} + +- (void)cancel { + [self finishWithError:[NSError errorWithDomain:kGRPCErrorDomain + code:GRPCErrorCodeCancelled + userInfo:nil]]; + [self cancelCall]; +} + +- (void)dealloc { + __block GRPCWrappedCall *wrappedCall = _wrappedCall; + dispatch_async(_callQueue, ^{ + wrappedCall = nil; + }); +} + +#pragma mark Read messages + +// Only called from the call queue. +// The handler will be called from the network queue. +- (void)startReadWithHandler:(void(^)(grpc_byte_buffer *))handler { + // TODO(jcanizales): Add error handlers for async failures + [_wrappedCall startBatchWithOperations:@[[[GRPCOpRecvMessage alloc] initWithHandler:handler]]]; +} + +// Called initially from the network queue once response headers are received, +// then "recursively" from the responseWriteable queue after each response from the +// server has been written. +// If the call is currently paused, this is a noop. Restarting the call will invoke this +// method. +// TODO(jcanizales): Rename to readResponseIfNotPaused. +- (void)startNextRead { + if (self.state == GRXWriterStatePaused) { + return; + } + __weak GRPCCall *weakSelf = self; + __weak GRXConcurrentWriteable *weakWriteable = _responseWriteable; + + dispatch_async(_callQueue, ^{ + [weakSelf startReadWithHandler:^(grpc_byte_buffer *message) { + if (message == NULL) { + // No more messages from the server + return; + } + NSData *data = [NSData grpc_dataWithByteBuffer:message]; + grpc_byte_buffer_destroy(message); + if (!data) { + // The app doesn't have enough memory to hold the server response. We + // don't want to throw, because the app shouldn't crash for a behavior + // that's on the hands of any server to have. Instead we finish and ask + // the server to cancel. + // + // TODO(jcanizales): No canonical code is appropriate for this situation + // (because it's just a client problem). Use another domain and an + // appropriately-documented code. + [weakSelf finishWithError:[NSError errorWithDomain:kGRPCErrorDomain + code:GRPCErrorCodeInternal + userInfo:nil]]; + [weakSelf cancelCall]; + return; + } + [weakWriteable enqueueValue:data completionHandler:^{ + [weakSelf startNextRead]; + }]; + }]; + }); +} + +#pragma mark Send headers + +- (void)sendHeaders:(id)headers { + // TODO(jcanizales): Add error handlers for async failures + [_wrappedCall startBatchWithOperations:@[[[GRPCOpSendMetadata alloc] initWithMetadata:headers + handler:nil]]]; +} + +#pragma mark GRXWriteable implementation + +// Only called from the call queue. The error handler will be called from the +// network queue if the write didn't succeed. +- (void)writeMessage:(NSData *)message withErrorHandler:(void (^)())errorHandler { + + __weak GRPCCall *weakSelf = self; + void(^resumingHandler)(void) = ^{ + // Resume the request writer. + GRPCCall *strongSelf = weakSelf; + if (strongSelf) { + @synchronized(strongSelf->_requestWriter) { + strongSelf->_requestWriter.state = GRXWriterStateStarted; + } + } + }; + [_wrappedCall startBatchWithOperations:@[[[GRPCOpSendMessage alloc] initWithMessage:message + handler:resumingHandler]] + errorHandler:errorHandler]; +} + +- (void)writeValue:(id)value { + // TODO(jcanizales): Throw/assert if value isn't NSData. + + // Pause the input and only resume it when the C layer notifies us that writes + // can proceed. + @synchronized(_requestWriter) { + _requestWriter.state = GRXWriterStatePaused; + } + + __weak GRPCCall *weakSelf = self; + dispatch_async(_callQueue, ^{ + [weakSelf writeMessage:value withErrorHandler:^{ + [weakSelf finishWithError:[NSError errorWithDomain:kGRPCErrorDomain + code:GRPCErrorCodeInternal + userInfo:nil]]; + }]; + }); +} + +// Only called from the call queue. The error handler will be called from the +// network queue if the requests stream couldn't be closed successfully. +- (void)finishRequestWithErrorHandler:(void (^)())errorHandler { + [_wrappedCall startBatchWithOperations:@[[[GRPCOpSendClose alloc] init]] + errorHandler:errorHandler]; +} + +- (void)writesFinishedWithError:(NSError *)errorOrNil { + if (errorOrNil) { + [self cancel]; + } else { + __weak GRPCCall *weakSelf = self; + dispatch_async(_callQueue, ^{ + [weakSelf finishRequestWithErrorHandler:^{ + [weakSelf finishWithError:[NSError errorWithDomain:kGRPCErrorDomain + code:GRPCErrorCodeInternal + userInfo:nil]]; + }]; + }); + } +} + +#pragma mark Invoke + +// Both handlers will eventually be called, from the network queue. Writes can start immediately +// after this. +// The first one (headersHandler), when the response headers are received. +// The second one (completionHandler), whenever the RPC finishes for any reason. +- (void)invokeCallWithHeadersHandler:(void(^)(NSDictionary *))headersHandler + completionHandler:(void(^)(NSError *, NSDictionary *))completionHandler { + // TODO(jcanizales): Add error handlers for async failures + [_wrappedCall startBatchWithOperations:@[[[GRPCOpRecvMetadata alloc] + initWithHandler:headersHandler]]]; + [_wrappedCall startBatchWithOperations:@[[[GRPCOpRecvStatus alloc] + initWithHandler:completionHandler]]]; +} + +- (void)invokeCall { + __weak GRPCCall *weakSelf = self; + [self invokeCallWithHeadersHandler:^(NSDictionary *headers) { + // Response headers received. + GRPCCall *strongSelf = weakSelf; + if (strongSelf) { + strongSelf.responseHeaders = headers; + [strongSelf startNextRead]; + } + } completionHandler:^(NSError *error, NSDictionary *trailers) { + GRPCCall *strongSelf = weakSelf; + if (strongSelf) { + strongSelf.responseTrailers = trailers; + + if (error) { + NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; + if (error.userInfo) { + [userInfo addEntriesFromDictionary:error.userInfo]; + } + userInfo[kGRPCTrailersKey] = strongSelf.responseTrailers; + // TODO(jcanizales): The C gRPC library doesn't guarantee that the headers block will be + // called before this one, so an error might end up with trailers but no headers. We + // shouldn't call finishWithError until ater both blocks are called. It is also when this is + // done that we can provide a merged view of response headers and trailers in a thread-safe + // way. + if (strongSelf.responseHeaders) { + userInfo[kGRPCHeadersKey] = strongSelf.responseHeaders; + } + error = [NSError errorWithDomain:error.domain code:error.code userInfo:userInfo]; + } + [strongSelf finishWithError:error]; + } + }]; + // Now that the RPC has been initiated, request writes can start. + @synchronized(_requestWriter) { + [_requestWriter startWithWriteable:self]; + } +} + +#pragma mark GRXWriter implementation + +- (void)startWithWriteable:(id)writeable { + // Create a retain cycle so that this instance lives until the RPC finishes (or is cancelled). + // This makes RPCs in which the call isn't externally retained possible (as long as it is started + // before being autoreleased). + // Care is taken not to retain self strongly in any of the blocks used in this implementation, so + // that the life of the instance is determined by this retain cycle. + _retainSelf = self; + + _responseWriteable = [[GRXConcurrentWriteable alloc] initWithWriteable:writeable]; + [self sendHeaders:_requestHeaders]; + [self invokeCall]; +} + +- (void)setState:(GRXWriterState)newState { + // Manual transitions are only allowed from the started or paused states. + if (_state == GRXWriterStateNotStarted || _state == GRXWriterStateFinished) { + return; + } + + switch (newState) { + case GRXWriterStateFinished: + _state = newState; + // Per GRXWriter's contract, setting the state to Finished manually + // means one doesn't wish the writeable to be messaged anymore. + [_responseWriteable cancelSilently]; + _responseWriteable = nil; + return; + case GRXWriterStatePaused: + _state = newState; + return; + case GRXWriterStateStarted: + if (_state == GRXWriterStatePaused) { + _state = newState; + [self startNextRead]; + } + return; + case GRXWriterStateNotStarted: + return; + } +} +@end diff --git a/src/objective-c/GRPCClient/README.md b/src/objective-c/GRPCClient/README.md new file mode 100644 index 00000000..9b87f031 --- /dev/null +++ b/src/objective-c/GRPCClient/README.md @@ -0,0 +1,4 @@ +This is a generic gRPC client for Objective-C on iOS. + +If you're trying to get started with the library or with gRPC, you should first +read GRPCCall.h. diff --git a/src/objective-c/GRPCClient/private/GRPCChannel.h b/src/objective-c/GRPCClient/private/GRPCChannel.h new file mode 100644 index 00000000..2a7b7015 --- /dev/null +++ b/src/objective-c/GRPCClient/private/GRPCChannel.h @@ -0,0 +1,46 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import + +struct grpc_channel; + +// Each separate instance of this class represents at least one TCP connection to the provided host. +// Create them using one of the subclasses |GRPCSecureChannel| and |GRPCUnsecuredChannel|. +@interface GRPCChannel : NSObject +@property(nonatomic, readonly) struct grpc_channel *unmanagedChannel; + +// This initializer takes ownership of the passed channel, and will destroy it when this object is +// deallocated. It's illegal to pass the same grpc_channel to two different GRPCChannel objects. +- (instancetype)initWithChannel:(struct grpc_channel *)unmanagedChannel NS_DESIGNATED_INITIALIZER; +@end diff --git a/src/objective-c/GRPCClient/private/GRPCChannel.m b/src/objective-c/GRPCClient/private/GRPCChannel.m new file mode 100644 index 00000000..4366e633 --- /dev/null +++ b/src/objective-c/GRPCClient/private/GRPCChannel.m @@ -0,0 +1,60 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "GRPCChannel.h" + +#include + +@implementation GRPCChannel + +- (instancetype)init { + return [self initWithChannel:NULL]; +} + +// Designated initializer +- (instancetype)initWithChannel:(grpc_channel *)unmanagedChannel { + if (!unmanagedChannel) { + return nil; + } + if ((self = [super init])) { + _unmanagedChannel = unmanagedChannel; + } + return self; +} + +- (void)dealloc { + // TODO(jcanizales): Be sure to add a test with a server that closes the connection prematurely, + // as in the past that made this call to crash. + grpc_channel_destroy(_unmanagedChannel); +} +@end diff --git a/src/objective-c/GRPCClient/private/GRPCCompletionQueue.h b/src/objective-c/GRPCClient/private/GRPCCompletionQueue.h new file mode 100644 index 00000000..ab8d714d --- /dev/null +++ b/src/objective-c/GRPCClient/private/GRPCCompletionQueue.h @@ -0,0 +1,52 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import +#include + +typedef void(^GRPCQueueCompletionHandler)(bool success); + +// This class lets one more easily use |grpc_completion_queue|. To use it, pass the value of the +// |unmanagedQueue| property of an instance of this class to |grpc_channel_create_call|. Then for +// every |grpc_call_*| method that accepts a tag, you can pass a block of type +// |GRPCQueueCompletionHandler| (remembering to cast it using |__bridge_retained|). The block is +// guaranteed to eventually be called, by a concurrent queue, and then released. Each such block is +// passed a |bool| that tells if the operation was successful. +// +// Release the GRPCCompletionQueue object only after you are not going to pass any more blocks to +// the |grpc_call| that's using it. +@interface GRPCCompletionQueue : NSObject +@property(nonatomic, readonly) grpc_completion_queue *unmanagedQueue; + ++ (instancetype)completionQueue; +@end diff --git a/src/objective-c/GRPCClient/private/GRPCCompletionQueue.m b/src/objective-c/GRPCClient/private/GRPCCompletionQueue.m new file mode 100644 index 00000000..ea2b01ee --- /dev/null +++ b/src/objective-c/GRPCClient/private/GRPCCompletionQueue.m @@ -0,0 +1,94 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "GRPCCompletionQueue.h" + +#import + +@implementation GRPCCompletionQueue + ++ (instancetype)completionQueue { + return [[self alloc] init]; +} + +- (instancetype)init { + if ((self = [super init])) { + _unmanagedQueue = grpc_completion_queue_create(NULL); + + // This is for the following block to capture the pointer by value (instead + // of retaining self and doing self->_unmanagedQueue). This is essential + // because the block doesn't end until after grpc_completion_queue_shutdown + // is called, and we only want that to happen after nobody's using the queue + // anymore (i.e. on self dealloc). So the block would never end if it + // retained self. + grpc_completion_queue *unmanagedQueue = _unmanagedQueue; + + // Start a loop on a concurrent queue to read events from the completion + // queue and dispatch each. + static dispatch_once_t initialization; + static dispatch_queue_t gDefaultConcurrentQueue; + dispatch_once(&initialization, ^{ + gDefaultConcurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + }); + dispatch_async(gDefaultConcurrentQueue, ^{ + while (YES) { + // The following call blocks until an event is available. + grpc_event event = grpc_completion_queue_next(unmanagedQueue, + gpr_inf_future(GPR_CLOCK_REALTIME), + NULL); + GRPCQueueCompletionHandler handler; + switch (event.type) { + case GRPC_OP_COMPLETE: + handler = (__bridge_transfer GRPCQueueCompletionHandler)event.tag; + handler(event.success); + break; + case GRPC_QUEUE_SHUTDOWN: + grpc_completion_queue_destroy(unmanagedQueue); + return; + default: + [NSException raise:@"Unrecognized completion type" format:@""]; + } + }; + }); + } + return self; +} + +- (void)dealloc { + // This makes the completion queue produce a GRPC_QUEUE_SHUTDOWN event *after* + // all other pending events are flushed. What this means is all the blocks + // passed to the gRPC C library as void* are eventually called, even if some + // are called after self is dealloc'd. + grpc_completion_queue_shutdown(_unmanagedQueue); +} +@end diff --git a/src/objective-c/GRPCClient/private/GRPCHost.h b/src/objective-c/GRPCClient/private/GRPCHost.h new file mode 100644 index 00000000..f0bbd530 --- /dev/null +++ b/src/objective-c/GRPCClient/private/GRPCHost.h @@ -0,0 +1,58 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import + +@class GRPCCompletionQueue; +struct grpc_call; + +@interface GRPCHost : NSObject + +@property(nonatomic, readonly) NSString *address; + +// The following properties should only be modified for testing: + +@property(nonatomic, getter=isSecure) BOOL secure; + +@property(nonatomic, copy) NSString *pathToCertificates; +@property(nonatomic, copy) NSString *hostNameOverride; + +// Host objects initialized with the same address are the same. ++ (instancetype)hostWithAddress:(NSString *)address; +- (instancetype)initWithAddress:(NSString *)address NS_DESIGNATED_INITIALIZER; + +// Create a grpc_call object to the provided path on this host. +- (struct grpc_call *)unmanagedCallWithPath:(NSString *)path + completionQueue:(GRPCCompletionQueue *)queue; + +@end diff --git a/src/objective-c/GRPCClient/private/GRPCHost.m b/src/objective-c/GRPCClient/private/GRPCHost.m new file mode 100644 index 00000000..a8cd3a0e --- /dev/null +++ b/src/objective-c/GRPCClient/private/GRPCHost.m @@ -0,0 +1,129 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "GRPCHost.h" + +#include + +#import "GRPCChannel.h" +#import "GRPCCompletionQueue.h" +#import "GRPCSecureChannel.h" +#import "GRPCUnsecuredChannel.h" + +@interface GRPCHost () +// TODO(mlumish): Investigate whether caching channels with strong links is a good idea. +@property(nonatomic, strong) GRPCChannel *channel; +@end + +@implementation GRPCHost + ++ (instancetype)hostWithAddress:(NSString *)address { + return [[self alloc] initWithAddress:address]; +} + +- (instancetype)init { + return [self initWithAddress:nil]; +} + +// Default initializer. +- (instancetype)initWithAddress:(NSString *)address { + if (!address) { + return nil; + } + + // To provide a default port, we try to interpret the address. If it's just a host name without + // scheme and without port, we'll use port 443. If it has a scheme, we pass it untouched to the C + // gRPC library. + // TODO(jcanizales): Add unit tests for the types of addresses we want to let pass untouched. + NSURL *hostURL = [NSURL URLWithString:[@"https://" stringByAppendingString:address]]; + if (hostURL.host && !hostURL.port) { + address = [hostURL.host stringByAppendingString:@":443"]; + } + + // Look up the GRPCHost in the cache. + static NSMutableDictionary *hostCache; + static dispatch_once_t cacheInitialization; + dispatch_once(&cacheInitialization, ^{ + hostCache = [NSMutableDictionary dictionary]; + }); + @synchronized(hostCache) { + GRPCHost *cachedHost = hostCache[address]; + if (cachedHost) { + return cachedHost; + } + + if ((self = [super init])) { + _address = address; + _secure = YES; + hostCache[address] = self; + } + } + return self; +} + +- (grpc_call *)unmanagedCallWithPath:(NSString *)path completionQueue:(GRPCCompletionQueue *)queue { + if (!queue || !path || !self.channel) { + return NULL; + } + return grpc_channel_create_call(self.channel.unmanagedChannel, + NULL, GRPC_PROPAGATE_DEFAULTS, + queue.unmanagedQueue, + path.UTF8String, + self.hostName.UTF8String, + gpr_inf_future(GPR_CLOCK_REALTIME), NULL); +} + +- (GRPCChannel *)channel { + // Create it lazily, because we don't want to open a connection just because someone is + // configuring a host. + if (!_channel) { + if (_secure) { + _channel = [[GRPCSecureChannel alloc] initWithHost:_address + pathToCertificates:_pathToCertificates + hostNameOverride:_hostNameOverride]; + } else { + _channel = [[GRPCUnsecuredChannel alloc] initWithHost:_address]; + } + } + return _channel; +} + +- (NSString *)hostName { + // TODO(jcanizales): Default to nil instead of _address when Issue #2635 is clarified. + return _hostNameOverride ?: _address; +} + +// TODO(jcanizales): Don't let set |secure| to |NO| if |pathToCertificates| or |hostNameOverride| +// have been set. Don't let set either of the latter if |secure| has been set to |NO|. + +@end diff --git a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.h b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.h new file mode 100644 index 00000000..cf5a1be9 --- /dev/null +++ b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.h @@ -0,0 +1,52 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import +#include + +#import "../GRPCCall.h" + +@interface GRPCRequestHeaders : NSObject + +@property(nonatomic, readonly) NSUInteger count; +@property(nonatomic, readonly) grpc_metadata *grpc_metadataArray; + +- (instancetype)initWithCall:(GRPCCall *)call; + +- (id)objectForKeyedSubscript:(NSString *)key; +- (void)setObject:(id)obj forKeyedSubscript:(NSString *)key; + +- (void)removeAllObjects; +- (void)removeObjectForKey:(NSString *)key; + +@end diff --git a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m new file mode 100644 index 00000000..d23f21c0 --- /dev/null +++ b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m @@ -0,0 +1,118 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "GRPCRequestHeaders.h" + +#import + +#import "NSDictionary+GRPC.h" + +// Used by the setter. +static void CheckIsNonNilASCII(NSString *name, NSString* value) { + if (!value) { + [NSException raise:NSInvalidArgumentException format:@"%@ cannot be nil", name]; + } + if (![value canBeConvertedToEncoding:NSASCIIStringEncoding]) { + [NSException raise:NSInvalidArgumentException + format:@"%@ %@ contains non-ASCII characters", name, value]; + } +} + +// Precondition: key isn't nil. +static void CheckKeyValuePairIsValid(NSString *key, id value) { + if ([key hasSuffix:@"-bin"]) { + if (![value isKindOfClass:NSData.class]) { + [NSException raise:NSInvalidArgumentException + format:@"Expected NSData value for header %@ ending in \"-bin\", " + @"instead got %@", key, value]; + } + } else { + if (![value isKindOfClass:NSString.class]) { + [NSException raise:NSInvalidArgumentException + format:@"Expected NSString value for header %@ not ending in \"-bin\", " + @"instead got %@", key, value]; + } + CheckIsNonNilASCII(@"Text header value", (NSString *)value); + } +} + +@implementation GRPCRequestHeaders { + __weak GRPCCall *_call; + NSMutableDictionary *_delegate; +} + +- (instancetype)initWithCall:(GRPCCall *)call { + if ((self = [super init])) { + _call = call; + _delegate = [NSMutableDictionary dictionary]; + } + return self; +} + +- (void)checkCallIsNotStarted { + if (_call.state != GRXWriterStateNotStarted) { + [NSException raise:@"Invalid modification" + format:@"Cannot modify request headers after call is started"]; + } +} + +- (id)objectForKeyedSubscript:(NSString *)key { + return _delegate[key.lowercaseString]; +} + +- (void)setObject:(id)obj forKeyedSubscript:(NSString *)key { + [self checkCallIsNotStarted]; + CheckIsNonNilASCII(@"Header name", key); + key = key.lowercaseString; + CheckKeyValuePairIsValid(key, obj); + _delegate[key] = obj; +} + +- (void)removeObjectForKey:(NSString *)key { + [self checkCallIsNotStarted]; + [_delegate removeObjectForKey:key.lowercaseString]; +} + +- (void)removeAllObjects { + [self checkCallIsNotStarted]; + [_delegate removeAllObjects]; +} + +- (NSUInteger)count { + return _delegate.count; +} + +- (grpc_metadata *)grpc_metadataArray { + return _delegate.grpc_metadataArray; +} +@end diff --git a/src/objective-c/GRPCClient/private/GRPCSecureChannel.h b/src/objective-c/GRPCClient/private/GRPCSecureChannel.h new file mode 100644 index 00000000..74257eb0 --- /dev/null +++ b/src/objective-c/GRPCClient/private/GRPCSecureChannel.h @@ -0,0 +1,53 @@ +/* + * + * Copyright 2015, 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 + +#import "GRPCChannel.h" + +struct grpc_credentials; + +@interface GRPCSecureChannel : GRPCChannel +- (instancetype)initWithHost:(NSString *)host; + +// Only in tests shouldn't pathToCertificates or hostNameOverride be nil. Passing nil for +// pathToCertificates results in using the default root certificates distributed with the library. +- (instancetype)initWithHost:(NSString *)host + pathToCertificates:(NSString *)path + hostNameOverride:(NSString *)hostNameOverride; + +// The passed arguments aren't required to be valid beyond the invocation of this initializer. +- (instancetype)initWithHost:(NSString *)host + credentials:(struct grpc_credentials *)credentials + args:(grpc_channel_args *)args NS_DESIGNATED_INITIALIZER; +@end diff --git a/src/objective-c/GRPCClient/private/GRPCSecureChannel.m b/src/objective-c/GRPCClient/private/GRPCSecureChannel.m new file mode 100644 index 00000000..ce166553 --- /dev/null +++ b/src/objective-c/GRPCClient/private/GRPCSecureChannel.m @@ -0,0 +1,116 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "GRPCSecureChannel.h" + +#include + +// Returns NULL if the file at path couldn't be read. In that case, if errorPtr isn't NULL, +// *errorPtr will be an object describing what went wrong. +static grpc_credentials *CertificatesAtPath(NSString *path, NSError **errorPtr) { + // Files in PEM format can have non-ASCII characters in their comments (e.g. for the name of the + // issuer). Load them as UTF8 and produce an ASCII equivalent. + NSString *contentInUTF8 = [NSString stringWithContentsOfFile:path + encoding:NSUTF8StringEncoding + error:errorPtr]; + NSData *contentInASCII = [contentInUTF8 dataUsingEncoding:NSASCIIStringEncoding + allowLossyConversion:YES]; + if (!contentInASCII.bytes) { + // Passing NULL to grpc_ssl_credentials_create produces behavior we don't want, so return. + return NULL; + } + return grpc_ssl_credentials_create(contentInASCII.bytes, NULL, NULL); +} + +@implementation GRPCSecureChannel + +- (instancetype)initWithHost:(NSString *)host { + return [self initWithHost:host pathToCertificates:nil hostNameOverride:nil]; +} + +- (instancetype)initWithHost:(NSString *)host + pathToCertificates:(NSString *)path + hostNameOverride:(NSString *)hostNameOverride { + // Load default SSL certificates once. + static grpc_credentials *kDefaultCertificates; + static dispatch_once_t loading; + dispatch_once(&loading, ^{ + NSString *defaultPath = @"gRPCCertificates.bundle/roots"; // .pem + // Do not use NSBundle.mainBundle, as it's nil for tests of library projects. + NSBundle *bundle = [NSBundle bundleForClass:self.class]; + NSString *path = [bundle pathForResource:defaultPath ofType:@"pem"]; + NSError *error; + kDefaultCertificates = CertificatesAtPath(path, &error); + NSAssert(kDefaultCertificates, @"Could not read %@/%@.pem. This file, with the root " + "certificates, is needed to establish secure (TLS) connections. Because the file is " + "distributed with the gRPC library, this error is usually a sign that the library " + "wasn't configured correctly for your project. Error: %@", + bundle.bundlePath, defaultPath, error); + }); + + //TODO(jcanizales): Add NSError** parameter to the initializer. + grpc_credentials *certificates = path ? CertificatesAtPath(path, NULL) : kDefaultCertificates; + if (!certificates) { + return nil; + } + + // Ritual to pass the SSL host name override to the C library. + grpc_channel_args channelArgs; + grpc_arg nameOverrideArg; + channelArgs.num_args = 1; + channelArgs.args = &nameOverrideArg; + nameOverrideArg.type = GRPC_ARG_STRING; + nameOverrideArg.key = GRPC_SSL_TARGET_NAME_OVERRIDE_ARG; + // Cast const away. Hope C gRPC doesn't modify it! + nameOverrideArg.value.string = (char *) hostNameOverride.UTF8String; + grpc_channel_args *args = hostNameOverride ? &channelArgs : NULL; + + return [self initWithHost:host credentials:certificates args:args]; +} + +- (instancetype)initWithHost:(NSString *)host + credentials:(grpc_credentials *)credentials + args:(grpc_channel_args *)args { + return (self = [super + initWithChannel:grpc_secure_channel_create( + credentials, host.UTF8String, args, NULL)]); +} + +// TODO(jcanizales): GRPCSecureChannel and GRPCUnsecuredChannel are just convenience initializers +// for GRPCChannel. Move them into GRPCChannel, which will make the following unnecessary. +- (instancetype)initWithChannel:(grpc_channel *)unmanagedChannel { + [NSException raise:NSInternalInconsistencyException format:@"use another initializer"]; + return [self initWithHost:nil]; // silence warnings +} + +@end diff --git a/src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.h b/src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.h new file mode 100644 index 00000000..8528be44 --- /dev/null +++ b/src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.h @@ -0,0 +1,38 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "GRPCChannel.h" + +@interface GRPCUnsecuredChannel : GRPCChannel +- (instancetype)initWithHost:(NSString *)host NS_DESIGNATED_INITIALIZER; +@end diff --git a/src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.m b/src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.m new file mode 100644 index 00000000..15b6ffc7 --- /dev/null +++ b/src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.m @@ -0,0 +1,50 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "GRPCUnsecuredChannel.h" + +#include + +@implementation GRPCUnsecuredChannel + +- (instancetype)initWithHost:(NSString *)host { + return (self = [super initWithChannel:grpc_insecure_channel_create(host.UTF8String, NULL, NULL)]); +} + +// TODO(jcanizales): GRPCSecureChannel and GRPCUnsecuredChannel are just convenience initializers +// for GRPCChannel. Move them into GRPCChannel, which will make the following unnecessary. +- (instancetype)initWithChannel:(grpc_channel *)unmanagedChannel { + [NSException raise:NSInternalInconsistencyException format:@"use the other initializer"]; + return [self initWithHost:nil]; // silence warnings +} +@end diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedCall.h b/src/objective-c/GRPCClient/private/GRPCWrappedCall.h new file mode 100644 index 00000000..4ca27661 --- /dev/null +++ b/src/objective-c/GRPCClient/private/GRPCWrappedCall.h @@ -0,0 +1,97 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import +#include + +#import "GRPCChannel.h" +#import "GRPCRequestHeaders.h" + +@interface GRPCOperation : NSObject +@property(nonatomic, readonly) grpc_op op; +// Guaranteed to be called when the operation has finished. +- (void)finish; +@end + +@interface GRPCOpSendMetadata : GRPCOperation + +- (instancetype)initWithMetadata:(GRPCRequestHeaders *)metadata + handler:(void(^)())handler NS_DESIGNATED_INITIALIZER; + +@end + +@interface GRPCOpSendMessage : GRPCOperation + +- (instancetype)initWithMessage:(NSData *)message + handler:(void(^)())handler NS_DESIGNATED_INITIALIZER; + +@end + +@interface GRPCOpSendClose : GRPCOperation + +- (instancetype)initWithHandler:(void(^)())handler NS_DESIGNATED_INITIALIZER; + +@end + +@interface GRPCOpRecvMetadata : GRPCOperation + +- (instancetype)initWithHandler:(void(^)(NSDictionary *))handler NS_DESIGNATED_INITIALIZER; + +@end + +@interface GRPCOpRecvMessage : GRPCOperation + +- (instancetype)initWithHandler:(void(^)(grpc_byte_buffer *))handler NS_DESIGNATED_INITIALIZER; + +@end + +@interface GRPCOpRecvStatus : GRPCOperation + +- (instancetype)initWithHandler:(void(^)(NSError *, NSDictionary *))handler + NS_DESIGNATED_INITIALIZER; + +@end + +#pragma mark GRPCWrappedCall + +@interface GRPCWrappedCall : NSObject + +- (instancetype)initWithHost:(NSString *)host + path:(NSString *)path NS_DESIGNATED_INITIALIZER; + +- (void)startBatchWithOperations:(NSArray *)ops errorHandler:(void(^)())errorHandler; + +- (void)startBatchWithOperations:(NSArray *)ops; + +- (void)cancel; +@end diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m new file mode 100644 index 00000000..cea7c479 --- /dev/null +++ b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m @@ -0,0 +1,303 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "GRPCWrappedCall.h" + +#import +#include +#include +#include + +#import "GRPCCompletionQueue.h" +#import "GRPCHost.h" +#import "NSDictionary+GRPC.h" +#import "NSData+GRPC.h" +#import "NSError+GRPC.h" + +@implementation GRPCOperation { +@protected + // Most operation subclasses don't set any flags in the grpc_op, and rely on the flag member being + // initialized to zero. + grpc_op _op; + void(^_handler)(); +} + +- (void)finish { + if (_handler) { + _handler(); + } +} +@end + +@implementation GRPCOpSendMetadata + +- (instancetype)init { + return [self initWithMetadata:nil handler:nil]; +} + +- (instancetype)initWithMetadata:(GRPCRequestHeaders *)metadata handler:(void (^)())handler { + if (self = [super init]) { + _op.op = GRPC_OP_SEND_INITIAL_METADATA; + _op.data.send_initial_metadata.count = metadata.count; + _op.data.send_initial_metadata.metadata = metadata.grpc_metadataArray; + _handler = handler; + } + return self; +} + +- (void)dealloc { + gpr_free(_op.data.send_initial_metadata.metadata); +} + +@end + +@implementation GRPCOpSendMessage + +- (instancetype)init { + return [self initWithMessage:nil handler:nil]; +} + +- (instancetype)initWithMessage:(NSData *)message handler:(void (^)())handler { + if (!message) { + [NSException raise:NSInvalidArgumentException format:@"message cannot be nil"]; + } + if (self = [super init]) { + _op.op = GRPC_OP_SEND_MESSAGE; + _op.data.send_message = message.grpc_byteBuffer; + _handler = handler; + } + return self; +} + +- (void)dealloc { + gpr_free(_op.data.send_message); +} + +@end + +@implementation GRPCOpSendClose + +- (instancetype)init { + return [self initWithHandler:nil]; +} + +- (instancetype)initWithHandler:(void (^)())handler { + if (self = [super init]) { + _op.op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; + _handler = handler; + } + return self; +} + +@end + +@implementation GRPCOpRecvMetadata { + grpc_metadata_array _headers; +} + +- (instancetype) init { + return [self initWithHandler:nil]; +} + +- (instancetype) initWithHandler:(void (^)(NSDictionary *))handler { + if (self = [super init]) { + _op.op = GRPC_OP_RECV_INITIAL_METADATA; + grpc_metadata_array_init(&_headers); + _op.data.recv_initial_metadata = &_headers; + if (handler) { + // Prevent reference cycle with _handler + __weak typeof(self) weakSelf = self; + _handler = ^{ + __strong typeof(self) strongSelf = weakSelf; + NSDictionary *metadata = [NSDictionary + grpc_dictionaryFromMetadataArray:strongSelf->_headers]; + handler(metadata); + }; + } + } + return self; +} + +- (void)dealloc { + grpc_metadata_array_destroy(&_headers); +} + +@end + +@implementation GRPCOpRecvMessage{ + grpc_byte_buffer *_receivedMessage; +} + +- (instancetype)init { + return [self initWithHandler:nil]; +} + +- (instancetype)initWithHandler:(void (^)(grpc_byte_buffer *))handler { + if (self = [super init]) { + _op.op = GRPC_OP_RECV_MESSAGE; + _op.data.recv_message = &_receivedMessage; + if (handler) { + // Prevent reference cycle with _handler + __weak typeof(self) weakSelf = self; + _handler = ^{ + __strong typeof(self) strongSelf = weakSelf; + handler(strongSelf->_receivedMessage); + }; + } + } + return self; +} + +@end + +@implementation GRPCOpRecvStatus{ + grpc_status_code _statusCode; + char *_details; + size_t _detailsCapacity; + grpc_metadata_array _trailers; +} + +- (instancetype) init { + return [self initWithHandler:nil]; +} + +- (instancetype) initWithHandler:(void (^)(NSError *, NSDictionary *))handler { + if (self = [super init]) { + _op.op = GRPC_OP_RECV_STATUS_ON_CLIENT; + _op.data.recv_status_on_client.status = &_statusCode; + _op.data.recv_status_on_client.status_details = &_details; + _op.data.recv_status_on_client.status_details_capacity = &_detailsCapacity; + grpc_metadata_array_init(&_trailers); + _op.data.recv_status_on_client.trailing_metadata = &_trailers; + if (handler) { + // Prevent reference cycle with _handler + __weak typeof(self) weakSelf = self; + _handler = ^{ + __strong typeof(self) strongSelf = weakSelf; + NSError *error = [NSError grpc_errorFromStatusCode:strongSelf->_statusCode + details:strongSelf->_details]; + NSDictionary *trailers = [NSDictionary + grpc_dictionaryFromMetadataArray:strongSelf->_trailers]; + handler(error, trailers); + }; + } + } + return self; +} + +- (void)dealloc { + grpc_metadata_array_destroy(&_trailers); + gpr_free(_details); +} + +@end + +#pragma mark GRPCWrappedCall + +@implementation GRPCWrappedCall { + GRPCCompletionQueue *_queue; + grpc_call *_call; +} + +- (instancetype)init { + return [self initWithHost:nil path:nil]; +} + +- (instancetype)initWithHost:(NSString *)host + path:(NSString *)path { + if (!path || !host) { + [NSException raise:NSInvalidArgumentException + format:@"path and host cannot be nil."]; + } + + if (self = [super init]) { + static dispatch_once_t initialization; + dispatch_once(&initialization, ^{ + grpc_init(); + }); + + // Each completion queue consumes one thread. There's a trade to be made between creating and + // consuming too many threads and having contention of multiple calls in a single completion + // queue. Currently we favor latency and use one per call. + _queue = [GRPCCompletionQueue completionQueue]; + + _call = [[GRPCHost hostWithAddress:host] unmanagedCallWithPath:path completionQueue:_queue]; + if (_call == NULL) { + return nil; + } + } + return self; +} + +- (void)startBatchWithOperations:(NSArray *)operations { + [self startBatchWithOperations:operations errorHandler:nil]; +} + +- (void)startBatchWithOperations:(NSArray *)operations errorHandler:(void (^)())errorHandler { + size_t nops = operations.count; + grpc_op *ops_array = gpr_malloc(nops * sizeof(grpc_op)); + size_t i = 0; + for (GRPCOperation *operation in operations) { + ops_array[i++] = operation.op; + } + grpc_call_error error = grpc_call_start_batch(_call, ops_array, nops, + (__bridge_retained void *)(^(bool success){ + if (!success) { + if (errorHandler) { + errorHandler(); + } else { + return; + } + } + for (GRPCOperation *operation in operations) { + [operation finish]; + } + }), NULL); + gpr_free(ops_array); + + if (error != GRPC_CALL_OK) { + [NSException raise:NSInternalInconsistencyException + format:@"A precondition for calling grpc_call_start_batch wasn't met. Error %i", + error]; + } +} + +- (void)cancel { + grpc_call_cancel(_call, NULL); +} + +- (void)dealloc { + grpc_call_destroy(_call); +} + +@end diff --git a/src/objective-c/GRPCClient/private/NSData+GRPC.h b/src/objective-c/GRPCClient/private/NSData+GRPC.h new file mode 100644 index 00000000..65ac151c --- /dev/null +++ b/src/objective-c/GRPCClient/private/NSData+GRPC.h @@ -0,0 +1,41 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import + +struct grpc_byte_buffer; + +@interface NSData (GRPC) ++ (instancetype)grpc_dataWithByteBuffer:(struct grpc_byte_buffer *)buffer; +- (struct grpc_byte_buffer *)grpc_byteBuffer; +@end diff --git a/src/objective-c/GRPCClient/private/NSData+GRPC.m b/src/objective-c/GRPCClient/private/NSData+GRPC.m new file mode 100644 index 00000000..e6a6c3c6 --- /dev/null +++ b/src/objective-c/GRPCClient/private/NSData+GRPC.m @@ -0,0 +1,92 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "NSData+GRPC.h" + +#include +#include +#include + +// TODO(jcanizales): Move these two incantations to the C library. + +static void CopyByteBufferToCharArray(grpc_byte_buffer *buffer, char *array) { + size_t offset = 0; + grpc_byte_buffer_reader reader; + grpc_byte_buffer_reader_init(&reader, buffer); + gpr_slice next; + while (grpc_byte_buffer_reader_next(&reader, &next) != 0){ + memcpy(array + offset, GPR_SLICE_START_PTR(next), + (size_t)GPR_SLICE_LENGTH(next)); + offset += GPR_SLICE_LENGTH(next); + gpr_slice_unref(next); + } +} + +static grpc_byte_buffer *CopyCharArrayToNewByteBuffer(const char *array, + size_t length) { + gpr_slice slice = gpr_slice_from_copied_buffer(array, length); + grpc_byte_buffer *buffer = grpc_raw_byte_buffer_create(&slice, 1); + gpr_slice_unref(slice); + return buffer; +} + +@implementation NSData (GRPC) ++ (instancetype)grpc_dataWithByteBuffer:(grpc_byte_buffer *)buffer { + if (buffer == NULL) { + return nil; + } + NSUInteger length = grpc_byte_buffer_length(buffer); + char *array = malloc(length * sizeof(*array)); + if (!array) { + // TODO(jcanizales): grpc_byte_buffer is reference-counted, so we can + // prevent this memory problem by implementing a subclass of NSData + // that wraps the grpc_byte_buffer. Then enumerateByteRangesUsingBlock: + // can be implemented using a grpc_byte_buffer_reader. + return nil; + } + CopyByteBufferToCharArray(buffer, array); + return [self dataWithBytesNoCopy:array length:length freeWhenDone:YES]; +} + +- (grpc_byte_buffer *)grpc_byteBuffer { + // Some implementations of NSData, as well as grpc_byte_buffer, support O(1) + // appending of byte arrays by not using internally a single contiguous memory + // block for representation. + // The following implementation is thus not optimal, sometimes requiring two + // copies (one by self.bytes and another by gpr_slice_from_copied_buffer). + // If it turns out to be an issue, we can use enumerateByteRangesUsingblock: + // to create an array of gpr_slice objects to pass to grpc_raw_byte_buffer_create. + // That would make it do exactly one copy, always. + return CopyCharArrayToNewByteBuffer((const char *)self.bytes, (size_t)self.length); +} +@end diff --git a/src/objective-c/GRPCClient/private/NSDictionary+GRPC.h b/src/objective-c/GRPCClient/private/NSDictionary+GRPC.h new file mode 100644 index 00000000..7335681a --- /dev/null +++ b/src/objective-c/GRPCClient/private/NSDictionary+GRPC.h @@ -0,0 +1,41 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import +#include + +@interface NSDictionary (GRPC) ++ (instancetype)grpc_dictionaryFromMetadataArray:(grpc_metadata_array)array; ++ (instancetype)grpc_dictionaryFromMetadata:(grpc_metadata *)entries count:(size_t)count; +- (grpc_metadata *)grpc_metadataArray; +@end diff --git a/src/objective-c/GRPCClient/private/NSDictionary+GRPC.m b/src/objective-c/GRPCClient/private/NSDictionary+GRPC.m new file mode 100644 index 00000000..7477da76 --- /dev/null +++ b/src/objective-c/GRPCClient/private/NSDictionary+GRPC.m @@ -0,0 +1,126 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "NSDictionary+GRPC.h" + +#include + +#pragma mark Category for binary metadata elements + +@interface NSData (GRPCMetadata) ++ (instancetype)grpc_dataFromMetadataValue:(grpc_metadata *)metadata; + +// Fill a metadata object with the binary value in this NSData. +- (void)grpc_initMetadata:(grpc_metadata *)metadata; +@end + +@implementation NSData (GRPCMetadata) ++ (instancetype)grpc_dataFromMetadataValue:(grpc_metadata *)metadata { + // TODO(jcanizales): Should we use a non-copy constructor? + return [self dataWithBytes:metadata->value length:metadata->value_length]; +} + +- (void)grpc_initMetadata:(grpc_metadata *)metadata { + metadata->value = self.bytes; + metadata->value_length = self.length; +} +@end + +#pragma mark Category for textual metadata elements + +@interface NSString (GRPCMetadata) ++ (instancetype)grpc_stringFromMetadataValue:(grpc_metadata *)metadata; + +// Fill a metadata object with the textual value in this NSString. +- (void)grpc_initMetadata:(grpc_metadata *)metadata; +@end + +@implementation NSString (GRPCMetadata) ++ (instancetype)grpc_stringFromMetadataValue:(grpc_metadata *)metadata { + return [[self alloc] initWithBytes:metadata->value + length:metadata->value_length + encoding:NSASCIIStringEncoding]; +} + +// Precondition: This object contains only ASCII characters. +- (void)grpc_initMetadata:(grpc_metadata *)metadata { + metadata->value = self.UTF8String; + metadata->value_length = self.length; +} +@end + +#pragma mark Category for metadata arrays + +@implementation NSDictionary (GRPC) ++ (instancetype)grpc_dictionaryFromMetadataArray:(grpc_metadata_array)array { + return [self grpc_dictionaryFromMetadata:array.metadata count:array.count]; +} + ++ (instancetype)grpc_dictionaryFromMetadata:(grpc_metadata *)entries count:(size_t)count { + NSMutableDictionary *metadata = [NSMutableDictionary dictionaryWithCapacity:count]; + for (grpc_metadata *entry = entries; entry < entries + count; entry++) { + NSString *name = [NSString stringWithCString:entry->key encoding:NSASCIIStringEncoding]; + if (!name || metadata[name]) { + // Log if name is nil? + continue; + } + id value; + if ([name hasSuffix:@"-bin"]) { + value = [NSData grpc_dataFromMetadataValue:entry]; + } else { + value = [NSString grpc_stringFromMetadataValue:entry]; + } + metadata[name] = value; + } + return metadata; +} + +// Preconditions: All keys are ASCII strings. Keys ending in -bin have NSData values; the others +// have NSString values. +- (grpc_metadata *)grpc_metadataArray { + grpc_metadata *metadata = gpr_malloc([self count] * sizeof(grpc_metadata)); + grpc_metadata *current = metadata; + for (NSString* key in self) { + id value = self[key]; + current->key = key.UTF8String; + if ([value respondsToSelector:@selector(grpc_initMetadata:)]) { + [value grpc_initMetadata:current]; + } else { + [NSException raise:NSInvalidArgumentException + format:@"Metadata values must be NSString or NSData."]; + } + ++current; + } + return metadata; +} +@end diff --git a/src/objective-c/GRPCClient/private/NSError+GRPC.h b/src/objective-c/GRPCClient/private/NSError+GRPC.h new file mode 100644 index 00000000..f4729dc8 --- /dev/null +++ b/src/objective-c/GRPCClient/private/NSError+GRPC.h @@ -0,0 +1,41 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import +#include + +@interface NSError (GRPC) +// Returns nil if the status code is OK. Otherwise, a NSError whose code is one of |GRPCErrorCode| +// and whose domain is |kGRPCErrorDomain|. ++ (instancetype)grpc_errorFromStatusCode:(grpc_status_code)statusCode details:(char *)details; +@end diff --git a/src/objective-c/GRPCClient/private/NSError+GRPC.m b/src/objective-c/GRPCClient/private/NSError+GRPC.m new file mode 100644 index 00000000..638f41cb --- /dev/null +++ b/src/objective-c/GRPCClient/private/NSError+GRPC.m @@ -0,0 +1,50 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "NSError+GRPC.h" + +#include + +NSString * const kGRPCErrorDomain = @"io.grpc"; + +@implementation NSError (GRPC) ++ (instancetype)grpc_errorFromStatusCode:(grpc_status_code)statusCode details:(char *)details { + if (statusCode == GRPC_STATUS_OK) { + return nil; + } + NSString *message = [NSString stringWithCString:details encoding:NSASCIIStringEncoding]; + return [NSError errorWithDomain:kGRPCErrorDomain + code:statusCode + userInfo:@{NSLocalizedDescriptionKey: message}]; +} +@end diff --git a/src/objective-c/ProtoRPC/ProtoMethod.h b/src/objective-c/ProtoRPC/ProtoMethod.h new file mode 100644 index 00000000..8f554a04 --- /dev/null +++ b/src/objective-c/ProtoRPC/ProtoMethod.h @@ -0,0 +1,48 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import + +// A fully-qualified proto service method name. Full qualification is needed because a gRPC endpoint +// can implement multiple services. +@interface ProtoMethod : NSObject +@property(nonatomic, readonly) NSString *package; +@property(nonatomic, readonly) NSString *service; +@property(nonatomic, readonly) NSString *method; + +@property(nonatomic, readonly) NSString *HTTPPath; + +- (instancetype)initWithPackage:(NSString *)package + service:(NSString *)service + method:(NSString *)method; +@end diff --git a/src/objective-c/ProtoRPC/ProtoMethod.m b/src/objective-c/ProtoRPC/ProtoMethod.m new file mode 100644 index 00000000..1113b4fb --- /dev/null +++ b/src/objective-c/ProtoRPC/ProtoMethod.m @@ -0,0 +1,55 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "ProtoMethod.h" + +@implementation ProtoMethod +- (instancetype)initWithPackage:(NSString *)package + service:(NSString *)service + method:(NSString *)method { + if ((self = [super init])) { + _package = [package copy]; + _service = [service copy]; + _method = [method copy]; + } + return self; +} + +- (NSString *)HTTPPath { + if (_package) { + return [NSString stringWithFormat:@"/%@.%@/%@", _package, _service, _method]; + } else { + return [NSString stringWithFormat:@"/%@/%@", _service, _method]; + } +} +@end diff --git a/src/objective-c/ProtoRPC/ProtoRPC.h b/src/objective-c/ProtoRPC/ProtoRPC.h new file mode 100644 index 00000000..bd926b73 --- /dev/null +++ b/src/objective-c/ProtoRPC/ProtoRPC.h @@ -0,0 +1,48 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import +#import + +#import "ProtoMethod.h" + +@interface ProtoRPC : GRPCCall + +- (instancetype)initWithHost:(NSString *)host + method:(ProtoMethod *)method + requestsWriter:(GRXWriter *)requestsWriter + responseClass:(Class)responseClass + responsesWriteable:(id)responsesWriteable NS_DESIGNATED_INITIALIZER; + +- (void)start; +@end diff --git a/src/objective-c/ProtoRPC/ProtoRPC.m b/src/objective-c/ProtoRPC/ProtoRPC.m new file mode 100644 index 00000000..9bf66f34 --- /dev/null +++ b/src/objective-c/ProtoRPC/ProtoRPC.m @@ -0,0 +1,119 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "ProtoRPC.h" + +#import +#import +#import + +static NSError *ErrorForBadProto(id proto, Class expectedClass, NSError *parsingError) { + NSDictionary *info = @{ + NSLocalizedDescriptionKey: @"Unable to parse response from the server", + NSLocalizedRecoverySuggestionErrorKey: @"If this RPC is idempotent, retry " + @"with exponential backoff. Otherwise, query the server status before " + @"retrying.", + NSUnderlyingErrorKey: parsingError, + @"Expected class": expectedClass, + @"Received value": proto, + }; + // TODO(jcanizales): Use kGRPCErrorDomain and GRPCErrorCodeInternal when they're public. + return [NSError errorWithDomain:@"io.grpc" + code:13 + userInfo:info]; +} + +@implementation ProtoRPC { + id _responseWriteable; +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wobjc-designated-initializers" +- (instancetype)initWithHost:(NSString *)host + path:(NSString *)path + requestsWriter:(GRXWriter *)requestsWriter { + [NSException raise:NSInvalidArgumentException + format:@"Please use ProtoRPC's designated initializer instead."]; + return nil; +} +#pragma clang diagnostic pop + +// Designated initializer +- (instancetype)initWithHost:(NSString *)host + method:(ProtoMethod *)method + requestsWriter:(GRXWriter *)requestsWriter + responseClass:(Class)responseClass + responsesWriteable:(id)responsesWriteable { + // Because we can't tell the type system to constrain the class, we need to check at runtime: + if (![responseClass respondsToSelector:@selector(parseFromData:error:)]) { + [NSException raise:NSInvalidArgumentException + format:@"A protobuf class to parse the responses must be provided."]; + } + // A writer that serializes the proto messages to send. + GRXWriter *bytesWriter = [requestsWriter map:^id(GPBMessage *proto) { + if (![proto isKindOfClass:GPBMessage.class]) { + [NSException raise:NSInvalidArgumentException + format:@"Request must be a proto message: %@", proto]; + } + return [proto data]; + }]; + if ((self = [super initWithHost:host path:method.HTTPPath requestsWriter:bytesWriter])) { + __weak ProtoRPC *weakSelf = self; + + // A writeable that parses the proto messages received. + _responseWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) { + // TODO(jcanizales): This is done in the main thread, and needs to happen in another thread. + NSError *error = nil; + id parsed = [responseClass parseFromData:value error:&error]; + if (parsed) { + [responsesWriteable writeValue:parsed]; + } else { + [weakSelf finishWithError:ErrorForBadProto(value, responseClass, error)]; + } + } completionHandler:^(NSError *errorOrNil) { + [responsesWriteable writesFinishedWithError:errorOrNil]; + }]; + } + return self; +} + +- (void)start { + [self startWithWriteable:_responseWriteable]; +} + +- (void)startWithWriteable:(id)writeable { + [super startWithWriteable:writeable]; + // Break retain cycles. + _responseWriteable = nil; +} +@end diff --git a/src/objective-c/ProtoRPC/ProtoService.h b/src/objective-c/ProtoRPC/ProtoService.h new file mode 100644 index 00000000..2e8cb336 --- /dev/null +++ b/src/objective-c/ProtoRPC/ProtoService.h @@ -0,0 +1,49 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import + +@class ProtoRPC; +@protocol GRXWriteable; +@class GRXWriter; + +@interface ProtoService : NSObject +- (instancetype)initWithHost:(NSString *)host + packageName:(NSString *)packageName + serviceName:(NSString *)serviceName NS_DESIGNATED_INITIALIZER; + +- (ProtoRPC *)RPCToMethod:(NSString *)method + requestsWriter:(GRXWriter *)requestsWriter + responseClass:(Class)responseClass + responsesWriteable:(id)responsesWriteable; +@end diff --git a/src/objective-c/ProtoRPC/ProtoService.m b/src/objective-c/ProtoRPC/ProtoService.m new file mode 100644 index 00000000..fccc6aad --- /dev/null +++ b/src/objective-c/ProtoRPC/ProtoService.m @@ -0,0 +1,81 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "ProtoService.h" + +#import +#import + +#import "ProtoMethod.h" +#import "ProtoRPC.h" + +@implementation ProtoService { + NSString *_host; + NSString *_packageName; + NSString *_serviceName; +} + +- (instancetype)init { + return [self initWithHost:nil packageName:nil serviceName:nil]; +} + +// Designated initializer +- (instancetype)initWithHost:(NSString *)host + packageName:(NSString *)packageName + serviceName:(NSString *)serviceName { + if (!host || !serviceName) { + [NSException raise:NSInvalidArgumentException + format:@"Neither host nor serviceName can be nil."]; + } + if ((self = [super init])) { + _host = [host copy]; + _packageName = [packageName copy]; + _serviceName = [serviceName copy]; + } + return self; +} + +- (ProtoRPC *)RPCToMethod:(NSString *)method + requestsWriter:(GRXWriter *)requestsWriter + responseClass:(Class)responseClass + responsesWriteable:(id)responsesWriteable { + ProtoMethod *methodName = [[ProtoMethod alloc] initWithPackage:_packageName + service:_serviceName + method:method]; + return [[ProtoRPC alloc] initWithHost:_host + method:methodName + requestsWriter:requestsWriter + responseClass:responseClass + responsesWriteable:responsesWriteable]; +} +@end diff --git a/src/objective-c/README.md b/src/objective-c/README.md new file mode 100644 index 00000000..6c27657d --- /dev/null +++ b/src/objective-c/README.md @@ -0,0 +1,174 @@ +# gRPC for Objective-C + +- [Install protoc with the gRPC plugin](#install) +- [Write your API declaration in proto format](#write-protos) +- [Integrate a proto library in your project](#cocoapods) +- [Use the generated library in your code](#use) +- [Use gRPC without Protobuf](#no-proto) +- [Alternative installation methods](#alternatives) + - [Install protoc and the gRPC plugin without using Homebrew](#no-homebrew) + - [Integrate the generated gRPC library without using Cocoapods](#no-cocoapods) + +While gRPC doesn't require the use of an IDL to describe the API of services, using one simplifies +usage and adds some interoperability guarantees. Here we use [Protocol Buffers][], and provide a +plugin for the Protobuf Compiler (_protoc_) to generate client libraries to communicate with gRPC +services. + + +## Install protoc with the gRPC plugin + +On Mac OS X, install [homebrew][]. On Linux, install [linuxbrew][]. + +Run the following command to install _protoc_ and the gRPC _protoc_ plugin: +```sh +$ curl -fsSL https://goo.gl/getgrpc | bash - +``` +This will download and run the [gRPC install script][]. After the command completes, you're ready to +proceed. + + +## Write your API declaration in proto format + +For this you can consult the [Protocol Buffers][]' official documentation, or learn from a quick +example [here](https://github.com/grpc/grpc/tree/master/examples#defining-a-service). + + +## Integrate a proto library in your project + +Install [Cocoapods](https://cocoapods.org/#install). + +You need to create a Podspec file for your proto library. You may simply copy the following example +to the directory where your `.proto` files are located, updating the name, version and license as +necessary: + +```ruby +Pod::Spec.new do |s| + s.name = '' + s.version = '0.0.1' + s.license = '...' + + s.ios.deployment_target = '6.0' + s.osx.deployment_target = '10.8' + + # Run protoc with the Objective-C and gRPC plugins to generate protocol messages and gRPC clients. + # You can run this command manually if you later change your protos and need to regenerate. + s.prepare_command = "protoc --objc_out=. --objcgrpc_out=. *.proto" + + # The --objc_out plugin generates a pair of .pbobjc.h/.pbobjc.m files for each .proto file. + s.subspec "Messages" do |ms| + ms.source_files = "*.pbobjc.{h,m}" + ms.header_mappings_dir = "." + ms.requires_arc = false + ms.dependency "Protobuf", "~> 3.0.0-alpha-3" + end + + # The --objcgrpc_out plugin generates a pair of .pbrpc.h/.pbrpc.m files for each .proto file with + # a service defined. + s.subspec "Services" do |ss| + ss.source_files = "*.pbrpc.{h,m}" + ss.header_mappings_dir = "." + ss.requires_arc = true + ss.dependency "gRPC", "~> 0.5" + ss.dependency "#{s.name}/Messages" + end +end +``` + +The file should be named `.podspec`. + +Note: If your proto files are in a directory hierarchy, you might want to adjust the _globs_ used in +the sample Podspec above. For example, you could use: + +```ruby + s.prepare_command = "protoc --objc_out=. --objcgrpc_out=. *.proto **/*.proto" + ... + ms.source_files = "*.pbobjc.{h,m}", "**/*.pbobjc.{h,m}" + ... + ss.source_files = "*.pbrpc.{h,m}", "**/*.pbrpc.{h,m}" +``` + +Once your library has a Podspec, Cocoapods can install it into any XCode project. For that, go into +your project's directory and create a Podfile by running: + +```sh +pod init +``` + +Next add a line to your Podfile to refer to your library's Podspec. Use `:path` as described +[here](https://guides.cocoapods.org/using/the-podfile.html#using-the-files-from-a-folder-local-to-the-machine): + +```ruby +pod '', :path => 'path/to/the/directory/of/your/podspec' +``` + +You can look at this [example Podfile][]. + +Finally, in your project's directory, run: + +```sh +pod install +``` + + +## Use the generated library in your code + +Please check this [sample app][] for examples of how to use a generated gRPC library. + + +## Use gRPC without Protobuf + +The [sample app][] has an example of how to use the generic gRPC Objective-C client without +generated files. + + +## Alternative installation methods + + +### Install protoc and the gRPC plugin without using Homebrew + +First install v3 of the Protocol Buffers compiler (_protoc_), by cloning +[its Git repository](https://github.com/google/protobuf) and following these +[installation instructions](https://github.com/google/protobuf#c-installation---unix) +(the ones titled C++; don't miss the note for Mac users). + +Then clone this repository and execute the following commands from the root directory where it was +cloned. + +Compile the gRPC plugins for _protoc_: +```sh +make plugins +``` + +Create a symbolic link to the compiled plugin binary somewhere in your `$PATH`: +```sh +ln -s `pwd`/bins/opt/grpc_objective_c_plugin /usr/local/bin/protoc-gen-objcgrpc +``` +(Notice that the name of the created link must begin with "protoc-gen-" for _protoc_ to recognize it +as a plugin). + +If you don't want to create the symbolic link, you can alternatively copy the binary (with the +appropriate name). Or you might prefer instead to specify the plugin's path as a flag when invoking +_protoc_, in which case no system modification nor renaming is necessary. + + +### Integrate the generated gRPC library without using Cocoapods + +You need to compile the generated `.pbpbjc.*` files (the enums and messages) without ARC support, +and the generated `.pbrpc.*` files (the services) with ARC support. The generated code depends on +v0.5+ of the Objective-C gRPC runtime library and v3.0.0-alpha-3+ of the Objective-C Protobuf +runtime library. + +These libraries need to be integrated into your project as described in their respective Podspec +files: + +* [Podspec](https://github.com/grpc/grpc/blob/master/gRPC.podspec) for the Objective-C gRPC runtime +library. This can be tedious to configure manually. +* [Podspec](https://github.com/google/protobuf/blob/master/Protobuf.podspec) for the +Objective-C Protobuf runtime library. + +[Protocol Buffers]:https://developers.google.com/protocol-buffers/ +[homebrew]:http://brew.sh +[linuxbrew]:https://github.com/Homebrew/linuxbrew +[gRPC install script]:https://raw.githubusercontent.com/grpc/homebrew-grpc/master/scripts/install +[example Podfile]:https://github.com/grpc/grpc/blob/master/src/objective-c/examples/Sample/Podfile +[sample app]: https://github.com/grpc/grpc/tree/master/src/objective-c/examples/Sample diff --git a/src/objective-c/RxLibrary/GRXBufferedPipe.h b/src/objective-c/RxLibrary/GRXBufferedPipe.h new file mode 100644 index 00000000..ca94ce27 --- /dev/null +++ b/src/objective-c/RxLibrary/GRXBufferedPipe.h @@ -0,0 +1,60 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import + +#import "GRXWriteable.h" +#import "GRXWriter.h" + +// A buffered pipe is a Writer that also acts as a Writeable. +// Once it is started, whatever values are written into it (via -writeValue:) will be propagated +// immediately, unless flow control prevents it. +// If it is throttled and keeps receiving values, as well as if it receives values before being +// started, it will buffer them and propagate them in order as soon as its state becomes Started. +// If it receives an error (via -writesFinishedWithError:), it will drop any buffered values and +// propagate the error immediately. +// +// Beware that a pipe of this type can't prevent receiving more values when it is paused (for +// example if used to write data to a congested network connection). Because in such situations the +// pipe will keep buffering all data written to it, your application could run out of memory and +// crash. If you want to react to flow control signals to prevent that, instead of using this class +// you can implement an object that conforms to GRXWriter. +// +// Thread-safety: +// The methods of an object of this class should not be called concurrently from different threads. +@interface GRXBufferedPipe : GRXWriter + +// Convenience constructor. ++ (instancetype)pipe; + +@end diff --git a/src/objective-c/RxLibrary/GRXBufferedPipe.m b/src/objective-c/RxLibrary/GRXBufferedPipe.m new file mode 100644 index 00000000..4820c84a --- /dev/null +++ b/src/objective-c/RxLibrary/GRXBufferedPipe.m @@ -0,0 +1,146 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "GRXBufferedPipe.h" + +@implementation GRXBufferedPipe { + id _writeable; + NSMutableArray *_queue; + BOOL _inputIsFinished; + NSError *_errorOrNil; +} + +@synthesize state = _state; + ++ (instancetype)pipe { + return [[self alloc] init]; +} + +- (instancetype)init { + if (self = [super init]) { + _queue = [NSMutableArray array]; + _state = GRXWriterStateNotStarted; + } + return self; +} + +- (id)popValue { + id value = _queue[0]; + [_queue removeObjectAtIndex:0]; + return value; +} + +- (void)writeBufferUntilPausedOrStopped { + while (_state == GRXWriterStateStarted && _queue.count > 0) { + [_writeable writeValue:[self popValue]]; + } + if (_inputIsFinished && _queue.count == 0) { + // Our writer finished normally while we were paused or not-started-yet. + [self finishWithError:_errorOrNil]; + } +} + +#pragma mark GRXWriteable implementation + +// Returns whether events can be simply propagated to the other end of the pipe. +- (BOOL)shouldFastForward { + return _state == GRXWriterStateStarted && _queue.count == 0; +} + +- (void)writeValue:(id)value { + if (self.shouldFastForward) { + // Skip the queue. + [_writeable writeValue:value]; + } else { + // Even if we're paused and with enqueued values, we can't excert back-pressure to our writer. + // So just buffer the new value. + // We need a copy, so that it doesn't mutate before it's written at the other end of the pipe. + if ([value respondsToSelector:@selector(copy)]) { + value = [value copy]; + } + [_queue addObject:value]; + } +} + +- (void)writesFinishedWithError:(NSError *)errorOrNil { + _inputIsFinished = YES; + _errorOrNil = errorOrNil; + if (errorOrNil || self.shouldFastForward) { + // No need to write pending values. + [self finishWithError:_errorOrNil]; + } +} + +#pragma mark GRXWriter implementation + +- (void)setState:(GRXWriterState)newState { + // Manual transitions are only allowed from the started or paused states. + if (_state == GRXWriterStateNotStarted || _state == GRXWriterStateFinished) { + return; + } + + switch (newState) { + case GRXWriterStateFinished: + _state = newState; + _queue = nil; + // Per GRXWriter's contract, setting the state to Finished manually means one doesn't wish the + // writeable to be messaged anymore. + _writeable = nil; + return; + case GRXWriterStatePaused: + _state = newState; + return; + case GRXWriterStateStarted: + if (_state == GRXWriterStatePaused) { + _state = newState; + [self writeBufferUntilPausedOrStopped]; + } + return; + case GRXWriterStateNotStarted: + return; + } +} + +- (void)startWithWriteable:(id)writeable { + _state = GRXWriterStateStarted; + _writeable = writeable; + [self writeBufferUntilPausedOrStopped]; +} + +- (void)finishWithError:(NSError *)errorOrNil { + id writeable = _writeable; + self.state = GRXWriterStateFinished; + [writeable writesFinishedWithError:errorOrNil]; +} + +@end diff --git a/src/objective-c/RxLibrary/GRXConcurrentWriteable.h b/src/objective-c/RxLibrary/GRXConcurrentWriteable.h new file mode 100644 index 00000000..10800019 --- /dev/null +++ b/src/objective-c/RxLibrary/GRXConcurrentWriteable.h @@ -0,0 +1,71 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import + +#import "GRXWriter.h" +#import "GRXWriteable.h" + +// This is a thread-safe wrapper over a GRXWriteable instance. It lets one enqueue calls to a +// GRXWriteable instance for the main thread, guaranteeing that writesFinishedWithError: is the last +// message sent to it (no matter what messages are sent to the wrapper, in what order, nor from +// which thread). It also guarantees that, if cancelWithError: is called from the main thread (e.g. +// by the app cancelling the writes), no further messages are sent to the writeable except +// writesFinishedWithError:. +// +// TODO(jcanizales): Let the user specify another queue for the writeable callbacks. +@interface GRXConcurrentWriteable : NSObject + +// The GRXWriteable passed is the wrapped writeable. +// The GRXWriteable instance is retained until writesFinishedWithError: is sent to it, and released +// after that. +- (instancetype)initWithWriteable:(id)writeable NS_DESIGNATED_INITIALIZER; + +// Enqueues writeValue: to be sent to the writeable in the main thread. +// The passed handler is invoked from the main thread after writeValue: returns. +- (void)enqueueValue:(id)value completionHandler:(void (^)())handler; + +// Enqueues writesFinishedWithError:nil to be sent to the writeable in the main thread. After that +// message is sent to the writeable, all other methods of this object are effectively noops. +- (void)enqueueSuccessfulCompletion; + +// If the writeable has not yet received a writesFinishedWithError: message, this will enqueue one +// to be sent to it in the main thread, and cancel all other pending messages to the writeable +// enqueued by this object (both past and future). +// The error argument cannot be nil. +- (void)cancelWithError:(NSError *)error; + +// Cancels all pending messages to the writeable enqueued by this object (both past and future). +// Because the writeable won't receive writesFinishedWithError:, this also releases the writeable. +- (void)cancelSilently; +@end diff --git a/src/objective-c/RxLibrary/GRXConcurrentWriteable.m b/src/objective-c/RxLibrary/GRXConcurrentWriteable.m new file mode 100644 index 00000000..08bd079a --- /dev/null +++ b/src/objective-c/RxLibrary/GRXConcurrentWriteable.m @@ -0,0 +1,110 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "GRXConcurrentWriteable.h" + +#import + +@interface GRXConcurrentWriteable () +// This is atomic so that cancellation can nillify it from any thread. +@property(atomic, strong) id writeable; +@end + +@implementation GRXConcurrentWriteable { + dispatch_queue_t _writeableQueue; + // This ensures that writesFinishedWithError: is only sent once to the writeable. + dispatch_once_t _alreadyFinished; +} + +- (instancetype)init { + return [self initWithWriteable:nil]; +} + +// Designated initializer +- (instancetype)initWithWriteable:(id)writeable { + if (self = [super init]) { + _writeableQueue = dispatch_get_main_queue(); + _writeable = writeable; + } + return self; +} + +- (void)enqueueValue:(id)value completionHandler:(void (^)())handler { + dispatch_async(_writeableQueue, ^{ + // We're racing a possible cancellation performed by another thread. To turn all already- + // enqueued messages into noops, cancellation nillifies the writeable property. If we get it + // before it's nil, we won the race. + id writeable = self.writeable; + if (writeable) { + [writeable writeValue:value]; + handler(); + } + }); +} + +- (void)enqueueSuccessfulCompletion { + dispatch_async(_writeableQueue, ^{ + dispatch_once(&_alreadyFinished, ^{ + // Cancellation is now impossible. None of the other three blocks can run concurrently with + // this one. + [self.writeable writesFinishedWithError:nil]; + // Skip any possible message to the wrapped writeable enqueued after this one. + self.writeable = nil; + }); + }); +} + +- (void)cancelWithError:(NSError *)error { + NSAssert(error, @"For a successful completion, use enqueueSuccessfulCompletion."); + dispatch_once(&_alreadyFinished, ^{ + // Skip any of the still-enqueued messages to the wrapped writeable. We use the atomic setter to + // nillify writeable because we might be running concurrently with the blocks in + // _writeableQueue, and assignment with ARC isn't atomic. + id writeable = self.writeable; + self.writeable = nil; + + dispatch_async(_writeableQueue, ^{ + [writeable writesFinishedWithError:error]; + }); + }); +} + +- (void)cancelSilently { + dispatch_once(&_alreadyFinished, ^{ + // Skip any of the still-enqueued messages to the wrapped writeable. We use the atomic setter to + // nillify writeable because we might be running concurrently with the blocks in + // _writeableQueue, and assignment with ARC isn't atomic. + self.writeable = nil; + }); +} +@end diff --git a/src/objective-c/RxLibrary/GRXForwardingWriter.h b/src/objective-c/RxLibrary/GRXForwardingWriter.h new file mode 100644 index 00000000..f3108322 --- /dev/null +++ b/src/objective-c/RxLibrary/GRXForwardingWriter.h @@ -0,0 +1,49 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "GRXWriter.h" + +// A "proxy" class that simply forwards values, completion, and errors from its input writer to its +// writeable. +// It is useful as a superclass for pipes that act as a transformation of their +// input writer, and for classes that represent objects with input and +// output sequences of values, like an RPC. +// +// Thread-safety: +// All messages sent to this object need to be serialized. When it is started, the writer it wraps +// is started in the same thread. Manual state changes are propagated to the wrapped writer in the +// same thread too. Importantly, all messages the wrapped writer sends to its writeable need to be +// serialized with any message sent to this object. +@interface GRXForwardingWriter : GRXWriter +- (instancetype)initWithWriter:(GRXWriter *)writer NS_DESIGNATED_INITIALIZER; +@end diff --git a/src/objective-c/RxLibrary/GRXForwardingWriter.m b/src/objective-c/RxLibrary/GRXForwardingWriter.m new file mode 100644 index 00000000..a72be9ac --- /dev/null +++ b/src/objective-c/RxLibrary/GRXForwardingWriter.m @@ -0,0 +1,116 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "GRXForwardingWriter.h" + +@interface GRXForwardingWriter () +@end + +@implementation GRXForwardingWriter { + GRXWriter *_writer; + id _writeable; +} + +- (instancetype)init { + return [self initWithWriter:nil]; +} + +// Designated initializer +- (instancetype)initWithWriter:(GRXWriter *)writer { + if (!writer) { + return nil; + } + if (writer.state != GRXWriterStateNotStarted) { + [NSException raise:NSInvalidArgumentException + format:@"The writer argument must not have already started."]; + } + if ((self = [super init])) { + _writer = writer; + } + return self; +} + +// This is used to send a completion or an error to the writeable. It nillifies +// our reference to it in order to guarantee no more messages are sent to it, +// and to release it. +- (void)finishOutputWithError:(NSError *)errorOrNil { + id writeable = _writeable; + _writeable = nil; + [writeable writesFinishedWithError:errorOrNil]; +} + +// This is used to stop the input writer. It nillifies our reference to it +// to release it. +- (void)finishInput { + GRXWriter *writer = _writer; + _writer = nil; + writer.state = GRXWriterStateFinished; +} + +#pragma mark GRXWriteable implementation + +- (void)writeValue:(id)value { + [_writeable writeValue:value]; +} + +- (void)writesFinishedWithError:(NSError *)errorOrNil { + _writer = nil; + [self finishOutputWithError:errorOrNil]; +} + +#pragma mark GRXWriter implementation + +- (GRXWriterState)state { + return _writer ? _writer.state : GRXWriterStateFinished; +} + +- (void)setState:(GRXWriterState)state { + if (state == GRXWriterStateFinished) { + _writeable = nil; + [self finishInput]; + } else { + _writer.state = state; + } +} + +- (void)startWithWriteable:(id)writeable { + _writeable = writeable; + [_writer startWithWriteable:self]; +} + +- (void)finishWithError:(NSError *)errorOrNil { + [self finishOutputWithError:errorOrNil]; + [self finishInput]; +} + +@end diff --git a/src/objective-c/RxLibrary/GRXImmediateWriter.h b/src/objective-c/RxLibrary/GRXImmediateWriter.h new file mode 100644 index 00000000..3fcc2594 --- /dev/null +++ b/src/objective-c/RxLibrary/GRXImmediateWriter.h @@ -0,0 +1,80 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import + +#import "GRXWriter.h" + +// Utility to construct GRXWriter instances from values that are immediately available when +// required. +// +// Thread-safety: +// +// An object of this class shouldn't be messaged concurrently by more than one thread. It will start +// messaging the writeable before |startWithWriteable:| returns, in the same thread. That is the +// only place where the writer can be paused or stopped prematurely. +// +// If a paused writer of this class is resumed, it will start messaging the writeable, in the same +// thread, before |setState:| returns. Because the object can't be legally accessed concurrently, +// that's the only place where it can be paused again (or stopped). +@interface GRXImmediateWriter : GRXWriter + +// Returns a writer that pulls values from the passed NSEnumerator instance and pushes them to +// its writeable. The NSEnumerator is released when it finishes. ++ (GRXWriter *)writerWithEnumerator:(NSEnumerator *)enumerator; + +// Returns a writer that pushes to its writeable the successive values returned by the passed +// block. When the block first returns nil, it is released. ++ (GRXWriter *)writerWithValueSupplier:(id (^)())block; + +// Returns a writer that iterates over the values of the passed container and pushes them to +// its writeable. The container is released when the iteration is over. +// +// Note that the usual speed gain of NSFastEnumeration over NSEnumerator results from not having to +// call one method per element. Because GRXWriteable instances accept values one by one, that speed +// gain doesn't happen here. ++ (GRXWriter *)writerWithContainer:(id)container; + +// Returns a writer that sends the passed value to its writeable and then finishes (releasing the +// value). ++ (GRXWriter *)writerWithValue:(id)value; + +// Returns a writer that, as part of its start method, sends the passed error to the writeable +// (then releasing the error). ++ (GRXWriter *)writerWithError:(NSError *)error; + +// Returns a writer that, as part of its start method, finishes immediately without sending any +// values to its writeable. ++ (GRXWriter *)emptyWriter; + +@end diff --git a/src/objective-c/RxLibrary/GRXImmediateWriter.m b/src/objective-c/RxLibrary/GRXImmediateWriter.m new file mode 100644 index 00000000..3edae788 --- /dev/null +++ b/src/objective-c/RxLibrary/GRXImmediateWriter.m @@ -0,0 +1,152 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "GRXImmediateWriter.h" + +#import "NSEnumerator+GRXUtil.h" + +@implementation GRXImmediateWriter { + NSEnumerator *_enumerator; + NSError *_errorOrNil; + id _writeable; +} + +@synthesize state = _state; + +- (instancetype) init { + return [self initWithEnumerator:nil error:nil]; // results in an empty writer. +} + +// Designated initializer +- (instancetype)initWithEnumerator:(NSEnumerator *)enumerator error:(NSError *)errorOrNil { + if (((self = [super init]))) { + _enumerator = enumerator; + _errorOrNil = errorOrNil; + _state = GRXWriterStateNotStarted; + } + return self; +} + +#pragma mark Convenience constructors + ++ (instancetype)writerWithEnumerator:(NSEnumerator *)enumerator error:(NSError *)errorOrNil { + return [[self alloc] initWithEnumerator:enumerator error:errorOrNil]; +} + ++ (GRXWriter *)writerWithEnumerator:(NSEnumerator *)enumerator { + return [self writerWithEnumerator:enumerator error:nil]; +} + ++ (GRXWriter *)writerWithValueSupplier:(id (^)())block { + return [self writerWithEnumerator:[NSEnumerator grx_enumeratorWithValueSupplier:block]]; +} + ++ (GRXWriter *)writerWithContainer:(id)container { + return [self writerWithEnumerator:[NSEnumerator grx_enumeratorWithContainer:container]];; +} + ++ (GRXWriter *)writerWithValue:(id)value { + return [self writerWithEnumerator:[NSEnumerator grx_enumeratorWithSingleValue:value]]; +} + ++ (GRXWriter *)writerWithError:(NSError *)error { + return [self writerWithEnumerator:nil error:error]; +} + ++ (GRXWriter *)emptyWriter { + return [self writerWithEnumerator:nil error:nil]; +} + +#pragma mark Conformance with GRXWriter + +// Most of the complexity in this implementation is the result of supporting pause and resumption of +// the GRXWriter. It's an important feature for instances of GRXWriter that are backed by a +// container (which may be huge), or by a NSEnumerator (which may even be infinite). + +- (void)writeUntilPausedOrStopped { + id value; + while (value = [_enumerator nextObject]) { + [_writeable writeValue:value]; + // If the writeable has a reference to us, it might change our state to paused or finished. + if (_state == GRXWriterStatePaused || _state == GRXWriterStateFinished) { + return; + } + } + [self finishWithError:_errorOrNil]; +} + +- (void)startWithWriteable:(id)writeable { + _state = GRXWriterStateStarted; + _writeable = writeable; + [self writeUntilPausedOrStopped]; +} + +- (void)finishWithError:(NSError *)errorOrNil { + _state = GRXWriterStateFinished; + _enumerator = nil; + _errorOrNil = nil; + id writeable = _writeable; + _writeable = nil; + [writeable writesFinishedWithError:errorOrNil]; +} + +- (void)setState:(GRXWriterState)newState { + // Manual transitions are only allowed from the started or paused states. + if (_state == GRXWriterStateNotStarted || _state == GRXWriterStateFinished) { + return; + } + + switch (newState) { + case GRXWriterStateFinished: + _state = newState; + _enumerator = nil; + _errorOrNil = nil; + // Per GRXWriter's contract, setting the state to Finished manually + // means one doesn't wish the writeable to be messaged anymore. + _writeable = nil; + return; + case GRXWriterStatePaused: + _state = newState; + return; + case GRXWriterStateStarted: + if (_state == GRXWriterStatePaused) { + _state = newState; + [self writeUntilPausedOrStopped]; + } + return; + case GRXWriterStateNotStarted: + return; + } +} + +@end diff --git a/src/objective-c/RxLibrary/GRXWriteable.h b/src/objective-c/RxLibrary/GRXWriteable.h new file mode 100644 index 00000000..45613d6d --- /dev/null +++ b/src/objective-c/RxLibrary/GRXWriteable.h @@ -0,0 +1,64 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import + +// A GRXWriteable is an object to which a sequence of values can be sent. The +// sequence finishes with an optional error. +@protocol GRXWriteable + +// Push the next value of the sequence to the receiving object. +- (void)writeValue:(id)value; + +// Signal that the sequence is completed, or that an error ocurred. After this +// message is sent to the instance, neither it nor writeValue: may be +// called again. +- (void)writesFinishedWithError:(NSError *)errorOrNil; +@end + +typedef void (^GRXValueHandler)(id value); +typedef void (^GRXCompletionHandler)(NSError *errorOrNil); +typedef void (^GRXSingleHandler)(id value, NSError *errorOrNil); +typedef void (^GRXEventHandler)(BOOL done, id value, NSError *error); + +// Utility to create objects that conform to the GRXWriteable protocol, from +// blocks that handle each of the two methods of the protocol. +@interface GRXWriteable : NSObject + ++ (instancetype)writeableWithSingleHandler:(GRXSingleHandler)handler; ++ (instancetype)writeableWithEventHandler:(GRXEventHandler)handler; + +- (instancetype)initWithValueHandler:(GRXValueHandler)valueHandler + completionHandler:(GRXCompletionHandler)completionHandler + NS_DESIGNATED_INITIALIZER; +@end diff --git a/src/objective-c/RxLibrary/GRXWriteable.m b/src/objective-c/RxLibrary/GRXWriteable.m new file mode 100644 index 00000000..2729d62b --- /dev/null +++ b/src/objective-c/RxLibrary/GRXWriteable.m @@ -0,0 +1,90 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "GRXWriteable.h" + +@implementation GRXWriteable { + GRXValueHandler _valueHandler; + GRXCompletionHandler _completionHandler; +} + ++ (instancetype)writeableWithSingleHandler:(GRXSingleHandler)handler { + if (!handler) { + return [[self alloc] init]; + } + return [[self alloc] initWithValueHandler:^(id value) { + handler(value, nil); + } completionHandler:^(NSError *errorOrNil) { + if (errorOrNil) { + handler(nil, errorOrNil); + } + }]; +} + ++ (instancetype)writeableWithEventHandler:(GRXEventHandler)handler { + if (!handler) { + return [[self alloc] init]; + } + return [[self alloc] initWithValueHandler:^(id value) { + handler(NO, value, nil); + } completionHandler:^(NSError *errorOrNil) { + handler(YES, nil, errorOrNil); + }]; +} + +- (instancetype)init { + return [self initWithValueHandler:nil completionHandler:nil]; +} + +// Designated initializer +- (instancetype)initWithValueHandler:(GRXValueHandler)valueHandler + completionHandler:(GRXCompletionHandler)completionHandler { + if ((self = [super init])) { + _valueHandler = valueHandler; + _completionHandler = completionHandler; + } + return self; +} + +- (void)writeValue:(id)value { + if (_valueHandler) { + _valueHandler(value); + } +} + +- (void)writesFinishedWithError:(NSError *)errorOrNil { + if (_completionHandler) { + _completionHandler(errorOrNil); + } +} +@end diff --git a/src/objective-c/RxLibrary/GRXWriter+Immediate.h b/src/objective-c/RxLibrary/GRXWriter+Immediate.h new file mode 100644 index 00000000..b75c0a5a --- /dev/null +++ b/src/objective-c/RxLibrary/GRXWriter+Immediate.h @@ -0,0 +1,66 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "GRXWriter.h" + +@interface GRXWriter (Immediate) + +// Returns a writer that pulls values from the passed NSEnumerator instance and pushes them to +// its writeable. The NSEnumerator is released when it finishes. ++ (instancetype)writerWithEnumerator:(NSEnumerator *)enumerator; + +// Returns a writer that pushes to its writeable the successive values returned by the passed +// block. When the block first returns nil, it is released. ++ (instancetype)writerWithValueSupplier:(id (^)())block; + +// Returns a writer that iterates over the values of the passed container and pushes them to +// its writeable. The container is released when the iteration is over. +// +// Note that the usual speed gain of NSFastEnumeration over NSEnumerator results from not having to +// call one method per element. Because GRXWriteable instances accept values one by one, that speed +// gain doesn't happen here. ++ (instancetype)writerWithContainer:(id)container; + +// Returns a writer that sends the passed value to its writeable and then finishes (releasing the +// value). ++ (instancetype)writerWithValue:(id)value; + +// Returns a writer that, as part of its start method, sends the passed error to the writeable +// (then releasing the error). ++ (instancetype)writerWithError:(NSError *)error; + +// Returns a writer that, as part of its start method, finishes immediately without sending any +// values to its writeable. ++ (instancetype)emptyWriter; + +@end diff --git a/src/objective-c/RxLibrary/GRXWriter+Immediate.m b/src/objective-c/RxLibrary/GRXWriter+Immediate.m new file mode 100644 index 00000000..1d55eb35 --- /dev/null +++ b/src/objective-c/RxLibrary/GRXWriter+Immediate.m @@ -0,0 +1,64 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "GRXWriter+Immediate.h" + +#import "GRXImmediateWriter.h" + +@implementation GRXWriter (Immediate) + ++ (instancetype)writerWithEnumerator:(NSEnumerator *)enumerator { + return [GRXImmediateWriter writerWithEnumerator:enumerator]; +} + ++ (instancetype)writerWithValueSupplier:(id (^)())block { + return [GRXImmediateWriter writerWithValueSupplier:block]; +} + ++ (instancetype)writerWithContainer:(id)container { + return [GRXImmediateWriter writerWithContainer:container]; +} + ++ (instancetype)writerWithValue:(id)value { + return [GRXImmediateWriter writerWithValue:value]; +} + ++ (instancetype)writerWithError:(NSError *)error { + return [GRXImmediateWriter writerWithError:error]; +} + ++ (instancetype)emptyWriter { + return [GRXImmediateWriter emptyWriter]; +} + +@end diff --git a/src/objective-c/RxLibrary/GRXWriter+Transformations.h b/src/objective-c/RxLibrary/GRXWriter+Transformations.h new file mode 100644 index 00000000..60c4da37 --- /dev/null +++ b/src/objective-c/RxLibrary/GRXWriter+Transformations.h @@ -0,0 +1,42 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "GRXWriter.h" + +@interface GRXWriter (Transformations) + +// Returns a writer that wraps the receiver, and has all the values the receiver would write +// transformed by the provided mapping function. +- (GRXWriter *)map:(id (^)(id value))map; + +@end diff --git a/src/objective-c/RxLibrary/GRXWriter+Transformations.m b/src/objective-c/RxLibrary/GRXWriter+Transformations.m new file mode 100644 index 00000000..d37ed308 --- /dev/null +++ b/src/objective-c/RxLibrary/GRXWriter+Transformations.m @@ -0,0 +1,47 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "GRXWriter+Transformations.h" + +#import "transformations/GRXMappingWriter.h" + +@implementation GRXWriter (Transformations) + +- (GRXWriter *)map:(id (^)(id))map { + if (!map) { + return self; + } + return [[GRXMappingWriter alloc] initWithWriter:self map:map]; +} + +@end diff --git a/src/objective-c/RxLibrary/GRXWriter.h b/src/objective-c/RxLibrary/GRXWriter.h new file mode 100644 index 00000000..b1c994aa --- /dev/null +++ b/src/objective-c/RxLibrary/GRXWriter.h @@ -0,0 +1,107 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import + +#import "GRXWriteable.h" + +// States of a writer. +typedef NS_ENUM(NSInteger, GRXWriterState) { + + // The writer has not yet been given a writeable to which it can push its values. To have a writer + // transition to the Started state, send it a startWithWriteable: message. + // + // A writer's state cannot be manually set to this value. + GRXWriterStateNotStarted, + + // The writer might push values to the writeable at any moment. + GRXWriterStateStarted, + + // The writer is temporarily paused, and won't send any more values to the writeable unless its + // state is set back to Started. The writer might still transition to the Finished state at any + // moment, and is allowed to send writesFinishedWithError: to its writeable. + GRXWriterStatePaused, + + // The writer has released its writeable and won't interact with it anymore. + // + // One seldomly wants to set a writer's state to this value, as its writeable isn't notified with + // a writesFinishedWithError: message. Instead, sending finishWithError: to the writer will make + // it notify the writeable and then transition to this state. + GRXWriterStateFinished +}; + +// An GRXWriter object can produce, on demand, a sequence of values. The sequence may be produced +// asynchronously, and it may consist of any number of elements, including none or an infinite +// number. +// +// GRXWriter is the active dual of NSEnumerator. The difference between them is thus whether the +// object plays an active or passive role during usage: A user of NSEnumerator pulls values off it, +// and passes the values to a writeable. A user of GRXWriter, though, just gives it a writeable, and +// the GRXWriter instance pushes values to the writeable. This makes this protocol suitable to +// represent a sequence of future values, as well as collections with internal iteration. +// +// An instance of GRXWriter can start producing values after a writeable is passed to it. It can +// also be commanded to finish the sequence immediately (with an optional error). Finally, it can be +// asked to pause, and resumed later. All GRXWriter objects support pausing and early termination. +// +// Thread-safety: +// +// State transitions take immediate effect if the object is used from a single thread. Subclasses +// might offer stronger guarantees. +// +// Unless otherwise indicated by a conforming subclass, no messages should be sent concurrently to a +// GRXWriter. I.e., conforming classes aren't required to be thread-safe. +@interface GRXWriter : NSObject + +// This property can be used to query the current state of the writer, which determines how it might +// currently use its writeable. Some state transitions can be triggered by setting this property to +// the corresponding value, and that's useful for advanced use cases like pausing an writer. For +// more details, see the documentation of the enum further down. +@property(nonatomic) GRXWriterState state; + +// Transition to the Started state, and start sending messages to the writeable (a reference to it +// is retained). Messages to the writeable may be sent before the method returns, or they may be +// sent later in the future. See GRXWriteable.h for the different messages a writeable can receive. +// +// If this writer draws its values from an external source (e.g. from the filesystem or from a +// server), calling this method will commonly trigger side effects (like network connections). +// +// This method might only be called on writers in the NotStarted state. +- (void)startWithWriteable:(id)writeable; + +// Send writesFinishedWithError:errorOrNil to the writeable. Then release the reference to it and +// transition to the Finished state. +// +// This method might only be called on writers in the Started or Paused state. +- (void)finishWithError:(NSError *)errorOrNil; +@end diff --git a/src/objective-c/RxLibrary/GRXWriter.m b/src/objective-c/RxLibrary/GRXWriter.m new file mode 100644 index 00000000..019fcbd7 --- /dev/null +++ b/src/objective-c/RxLibrary/GRXWriter.m @@ -0,0 +1,38 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "GRXWriter.h" + +@implementation GRXWriter + +@end diff --git a/src/objective-c/RxLibrary/NSEnumerator+GRXUtil.h b/src/objective-c/RxLibrary/NSEnumerator+GRXUtil.h new file mode 100644 index 00000000..400e834c --- /dev/null +++ b/src/objective-c/RxLibrary/NSEnumerator+GRXUtil.h @@ -0,0 +1,51 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import + +@interface NSEnumerator (GRXUtil) + +// Returns a NSEnumerator instance that iterates through the elements of the passed container that +// supports fast enumeration. Note that this negates the speed benefits of fast enumeration over +// NSEnumerator. It's only intended for the rare cases when one needs the latter and only has the +// former, e.g. for iteration that needs to be paused and resumed later. ++ (NSEnumerator *)grx_enumeratorWithContainer:(id)container; + +// Returns a NSEnumerator instance that provides a single object before finishing. The value is then +// released. ++ (NSEnumerator *)grx_enumeratorWithSingleValue:(id)value; + +// Returns a NSEnumerator instance that delegates the invocations of nextObject to the passed block. +// When the block first returns nil, it is released. ++ (NSEnumerator *)grx_enumeratorWithValueSupplier:(id (^)())block; +@end diff --git a/src/objective-c/RxLibrary/NSEnumerator+GRXUtil.m b/src/objective-c/RxLibrary/NSEnumerator+GRXUtil.m new file mode 100644 index 00000000..5fc81e32 --- /dev/null +++ b/src/objective-c/RxLibrary/NSEnumerator+GRXUtil.m @@ -0,0 +1,54 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "NSEnumerator+GRXUtil.h" + +#import "private/GRXNSBlockEnumerator.h" +#import "private/GRXNSFastEnumerator.h" +#import "private/GRXNSScalarEnumerator.h" + +@implementation NSEnumerator (GRXUtil) + ++ (NSEnumerator *)grx_enumeratorWithContainer:(id)container { + // TODO(jcanizales): Consider checking if container responds to objectEnumerator and return that? + return [[GRXNSFastEnumerator alloc] initWithContainer:container]; +} + ++ (NSEnumerator *)grx_enumeratorWithSingleValue:(id)value { + return [[GRXNSScalarEnumerator alloc] initWithValue:value]; +} + ++ (NSEnumerator *)grx_enumeratorWithValueSupplier:(id (^)())block { + return [[GRXNSBlockEnumerator alloc] initWithValueSupplier:block]; +} +@end diff --git a/src/objective-c/RxLibrary/README.md b/src/objective-c/RxLibrary/README.md new file mode 100644 index 00000000..88e90723 --- /dev/null +++ b/src/objective-c/RxLibrary/README.md @@ -0,0 +1,8 @@ +This is a generic Reactive Extensions library for Objective-C, created to ease +the implementation of the gRPC Objective-C runtime. + +It has no dependencies on gRPC nor other libraries, and should eventually be +moved under its own GitHub project. + +If you're trying to get started on the library, you might want to first read +GRXWriter.h and then GRXWriteable.h. diff --git a/src/objective-c/RxLibrary/private/GRXNSBlockEnumerator.h b/src/objective-c/RxLibrary/private/GRXNSBlockEnumerator.h new file mode 100644 index 00000000..34cfc4d8 --- /dev/null +++ b/src/objective-c/RxLibrary/private/GRXNSBlockEnumerator.h @@ -0,0 +1,42 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import + +// Concrete subclass of NSEnumerator that delegates the invocations of nextObject to a block passed +// on initialization. +@interface GRXNSBlockEnumerator : NSEnumerator +// The first time the passed block returns nil, the enumeration will end and the block will be +// released. +- (instancetype)initWithValueSupplier:(id (^)())block; +@end diff --git a/src/objective-c/RxLibrary/private/GRXNSBlockEnumerator.m b/src/objective-c/RxLibrary/private/GRXNSBlockEnumerator.m new file mode 100644 index 00000000..163e310b --- /dev/null +++ b/src/objective-c/RxLibrary/private/GRXNSBlockEnumerator.m @@ -0,0 +1,61 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "GRXNSBlockEnumerator.h" + +@implementation GRXNSBlockEnumerator { + id (^_block)(); +} + +- (instancetype)init { + return [self initWithValueSupplier:nil]; +} + +- (instancetype)initWithValueSupplier:(id (^)())block { + if ((self = [super init])) { + _block = block; + } + return self; +} + +- (id)nextObject { + if (!_block) { + return nil; + } + id value = _block(); + if (!value) { + _block = nil; + } + return value; +} +@end diff --git a/src/objective-c/RxLibrary/private/GRXNSFastEnumerator.h b/src/objective-c/RxLibrary/private/GRXNSFastEnumerator.h new file mode 100644 index 00000000..15650292 --- /dev/null +++ b/src/objective-c/RxLibrary/private/GRXNSFastEnumerator.h @@ -0,0 +1,43 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import + +// This is a bridge to interact through NSEnumerator's interface with objects that only conform to +// NSFastEnumeration. (There's nothing specifically fast about it - you certainly don't win any +// speed by using this instead of a NSEnumerator provided by your container). +@interface GRXNSFastEnumerator : NSEnumerator +// After the iteration of the container (via the NSFastEnumeration protocol) is over, the container +// is released. If the container is modified during enumeration, an exception is thrown. +- (instancetype)initWithContainer:(id)container; +@end diff --git a/src/objective-c/RxLibrary/private/GRXNSFastEnumerator.m b/src/objective-c/RxLibrary/private/GRXNSFastEnumerator.m new file mode 100644 index 00000000..0387c995 --- /dev/null +++ b/src/objective-c/RxLibrary/private/GRXNSFastEnumerator.m @@ -0,0 +1,87 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "GRXNSFastEnumerator.h" + +@implementation GRXNSFastEnumerator { + id _container; + NSFastEnumerationState _state; + // Number of elements of the container currently in the _state.itemsPtr array. + NSUInteger _count; + // The index of the next object to return from the _state.itemsPtr array. + NSUInteger _index; + // A "buffer of one element," for the containers that enumerate their elements one by one. Those + // will set _state.itemsPtr to point to this. + // The NSFastEnumeration protocol requires it to be __unsafe_unretained, but that's alright + // because the only use we'd make of its value is to return it immediately as the result of + // nextObject. + __unsafe_unretained id _bufferValue; + // Neither NSEnumerator nor NSFastEnumeration instances are required to work correctly when the + // underlying container is mutated during iteration. The expectation is that an exception is + // thrown when that happens. So we check for mutations. + unsigned long _mutationFlag; + BOOL _mutationFlagIsSet; +} + +- (instancetype)init { + return [self initWithContainer:nil]; +} + +// Designated initializer. +- (instancetype)initWithContainer:(id)container { + if ((self = [super init])) { + _container = container; + } + return self; +} + +- (id)nextObject { + if (_index == _count) { + _index = 0; + _count = [_container countByEnumeratingWithState:&_state objects:&_bufferValue count:1]; + if (_count == 0) { + // Enumeration is over. + _container = nil; + return nil; + } + if (_mutationFlagIsSet) { + NSAssert(_mutationFlag == *(_state.mutationsPtr), + @"container was mutated while being enumerated"); + } else { + _mutationFlag = *(_state.mutationsPtr); + _mutationFlagIsSet = YES; + } + } + return _state.itemsPtr[_index++]; +} +@end diff --git a/src/objective-c/RxLibrary/private/GRXNSScalarEnumerator.h b/src/objective-c/RxLibrary/private/GRXNSScalarEnumerator.h new file mode 100644 index 00000000..12aa51e2 --- /dev/null +++ b/src/objective-c/RxLibrary/private/GRXNSScalarEnumerator.h @@ -0,0 +1,41 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import + +// Concrete subclass of NSEnumerator whose instances return a single object before finishing. +@interface GRXNSScalarEnumerator : NSEnumerator +// Param value: the single object this instance will produce. After the first invocation of +// nextObject, the value is released. +- (instancetype)initWithValue:(id)value; +@end diff --git a/src/objective-c/RxLibrary/private/GRXNSScalarEnumerator.m b/src/objective-c/RxLibrary/private/GRXNSScalarEnumerator.m new file mode 100644 index 00000000..f323ea14 --- /dev/null +++ b/src/objective-c/RxLibrary/private/GRXNSScalarEnumerator.m @@ -0,0 +1,57 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "GRXNSScalarEnumerator.h" + +@implementation GRXNSScalarEnumerator { + id _value; +} + +- (instancetype)init { + return [self initWithValue:nil]; +} + +// Designated initializer. +- (instancetype)initWithValue:(id)value { + if ((self = [super init])) { + _value = value; + } + return self; +} + +- (id)nextObject { + id value = _value; + _value = nil; + return value; +} +@end diff --git a/src/objective-c/RxLibrary/transformations/GRXMappingWriter.h b/src/objective-c/RxLibrary/transformations/GRXMappingWriter.h new file mode 100644 index 00000000..43b87068 --- /dev/null +++ b/src/objective-c/RxLibrary/transformations/GRXMappingWriter.h @@ -0,0 +1,40 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "RxLibrary/GRXForwardingWriter.h" + +// A "proxy" writer that transforms all the values of its input writer by using a mapping function. +@interface GRXMappingWriter : GRXForwardingWriter +- (instancetype)initWithWriter:(GRXWriter *)writer map:(id (^)(id value))map + NS_DESIGNATED_INITIALIZER; +@end diff --git a/src/objective-c/RxLibrary/transformations/GRXMappingWriter.m b/src/objective-c/RxLibrary/transformations/GRXMappingWriter.m new file mode 100644 index 00000000..f3242e4f --- /dev/null +++ b/src/objective-c/RxLibrary/transformations/GRXMappingWriter.m @@ -0,0 +1,63 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "GRXMappingWriter.h" + +static id (^kIdentity)(id value) = ^id(id value) { + return value; +}; + +@interface GRXForwardingWriter () +@end + +@implementation GRXMappingWriter { + id (^_map)(id value); +} + +- (instancetype)initWithWriter:(GRXWriter *)writer { + return [self initWithWriter:writer map:nil]; +} + +// Designated initializer +- (instancetype)initWithWriter:(GRXWriter *)writer map:(id (^)(id value))map { + if ((self = [super initWithWriter:writer])) { + _map = map ?: kIdentity; + } + return self; +} + +// Override +- (void)writeValue:(id)value { + [super writeValue:_map(value)]; +} +@end diff --git a/src/objective-c/examples/RemoteTestClient/RemoteTest.podspec b/src/objective-c/examples/RemoteTestClient/RemoteTest.podspec new file mode 100644 index 00000000..d4f8084c --- /dev/null +++ b/src/objective-c/examples/RemoteTestClient/RemoteTest.podspec @@ -0,0 +1,28 @@ +Pod::Spec.new do |s| + s.name = "RemoteTest" + s.version = "0.0.1" + s.license = "New BSD" + + s.ios.deployment_target = "6.0" + s.osx.deployment_target = "10.8" + + # Run protoc with the Objective-C and gRPC plugins to generate protocol messages and gRPC clients. + s.prepare_command = <<-CMD + protoc --objc_out=. --objcgrpc_out=. *.proto + CMD + + s.subspec "Messages" do |ms| + ms.source_files = "*.pbobjc.{h,m}" + ms.header_mappings_dir = "." + ms.requires_arc = false + ms.dependency "Protobuf", "~> 3.0.0-alpha-4" + end + + s.subspec "Services" do |ss| + ss.source_files = "*.pbrpc.{h,m}" + ss.header_mappings_dir = "." + ss.requires_arc = true + ss.dependency "gRPC", "~> 0.7" + ss.dependency "#{s.name}/Messages" + end +end diff --git a/src/objective-c/examples/RemoteTestClient/empty.proto b/src/objective-c/examples/RemoteTestClient/empty.proto new file mode 100644 index 00000000..a6780482 --- /dev/null +++ b/src/objective-c/examples/RemoteTestClient/empty.proto @@ -0,0 +1,44 @@ +// Copyright 2015, 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. + +syntax = "proto3"; + +package grpc.testing; + +option objc_class_prefix = "RMT"; + +// An empty message that you can re-use to avoid defining duplicated empty +// messages in your project. A typical example is to use it as argument or the +// return value of a service API. For instance: +// +// service Foo { +// rpc Bar (grpc.testing.Empty) returns (grpc.testing.Empty) { }; +// }; +// +message Empty {} diff --git a/src/objective-c/examples/RemoteTestClient/messages.proto b/src/objective-c/examples/RemoteTestClient/messages.proto new file mode 100644 index 00000000..85d93c2f --- /dev/null +++ b/src/objective-c/examples/RemoteTestClient/messages.proto @@ -0,0 +1,133 @@ +// Copyright 2015, 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. + +// Message definitions to be used by integration test service definitions. + +syntax = "proto3"; + +package grpc.testing; + +option objc_class_prefix = "RMT"; + +// The type of payload that should be returned. +enum PayloadType { + // Compressable text format. + COMPRESSABLE = 0; + + // Uncompressable binary format. + UNCOMPRESSABLE = 1; + + // Randomly chosen from all other formats defined in this enum. + RANDOM = 2; +} + +// A block of data, to simply increase gRPC message size. +message Payload { + // The type of data in body. + PayloadType type = 1; + // Primary contents of payload. + bytes body = 2; +} + +// Unary request. +message SimpleRequest { + // Desired payload type in the response from the server. + // If response_type is RANDOM, server randomly chooses one from other formats. + PayloadType response_type = 1; + + // Desired payload size in the response from the server. + // If response_type is COMPRESSABLE, this denotes the size before compression. + int32 response_size = 2; + + // Optional input payload sent along with the request. + Payload payload = 3; + + // Whether SimpleResponse should include username. + bool fill_username = 4; + + // Whether SimpleResponse should include OAuth scope. + bool fill_oauth_scope = 5; +} + +// Unary response, as configured by the request. +message SimpleResponse { + // Payload to increase message size. + Payload payload = 1; + // The user the request came from, for verifying authentication was + // successful when the client expected it. + string username = 2; + // OAuth scope. + string oauth_scope = 3; +} + +// Client-streaming request. +message StreamingInputCallRequest { + // Optional input payload sent along with the request. + Payload payload = 1; + + // Not expecting any payload from the response. +} + +// Client-streaming response. +message StreamingInputCallResponse { + // Aggregated size of payloads received from the client. + int32 aggregated_payload_size = 1; +} + +// Configuration for a particular response. +message ResponseParameters { + // Desired payload sizes in responses from the server. + // If response_type is COMPRESSABLE, this denotes the size before compression. + int32 size = 1; + + // Desired interval between consecutive responses in the response stream in + // microseconds. + int32 interval_us = 2; +} + +// Server-streaming request. +message StreamingOutputCallRequest { + // Desired payload type in the response from the server. + // If response_type is RANDOM, the payload from each response in the stream + // might be of different types. This is to simulate a mixed type of payload + // stream. + PayloadType response_type = 1; + + // Configuration for each expected response message. + repeated ResponseParameters response_parameters = 2; + + // Optional input payload sent along with the request. + Payload payload = 3; +} + +// Server-streaming response, as configured by the request and parameters. +message StreamingOutputCallResponse { + // Payload to increase response size. + Payload payload = 1; +} diff --git a/src/objective-c/examples/RemoteTestClient/test.proto b/src/objective-c/examples/RemoteTestClient/test.proto new file mode 100644 index 00000000..2f5a5489 --- /dev/null +++ b/src/objective-c/examples/RemoteTestClient/test.proto @@ -0,0 +1,73 @@ +// Copyright 2015, 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. + +// An integration test service that covers all the method signature permutations +// of unary/streaming requests/responses. +syntax = "proto3"; + +import "empty.proto"; +import "messages.proto"; + +package grpc.testing; + +option objc_class_prefix = "RMT"; + +// A simple service to test the various types of RPCs and experiment with +// performance with various types of payload. +service TestService { + // One empty request followed by one empty response. + rpc EmptyCall(grpc.testing.Empty) returns (grpc.testing.Empty); + + // One request followed by one response. + // TODO(Issue 527): Describe required server behavior. + rpc UnaryCall(SimpleRequest) returns (SimpleResponse); + + // One request followed by a sequence of responses (streamed download). + // The server returns the payload with client desired type and sizes. + rpc StreamingOutputCall(StreamingOutputCallRequest) + returns (stream StreamingOutputCallResponse); + + // A sequence of requests followed by one response (streamed upload). + // The server returns the aggregated size of client payload as the result. + rpc StreamingInputCall(stream StreamingInputCallRequest) + returns (StreamingInputCallResponse); + + // A sequence of requests with each request served by the server immediately. + // As one request could lead to multiple responses, this interface + // demonstrates the idea of full duplexing. + rpc FullDuplexCall(stream StreamingOutputCallRequest) + returns (stream StreamingOutputCallResponse); + + // A sequence of requests followed by a sequence of responses. + // The server buffers all the client requests and then serves them in order. A + // stream of responses are returned to the client when the server starts with + // first request. + rpc HalfDuplexCall(stream StreamingOutputCallRequest) + returns (stream StreamingOutputCallResponse); +} diff --git a/src/objective-c/examples/Sample/Podfile b/src/objective-c/examples/Sample/Podfile new file mode 100644 index 00000000..72308c16 --- /dev/null +++ b/src/objective-c/examples/Sample/Podfile @@ -0,0 +1,8 @@ +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '8.0' + +pod 'gRPC', :path => "../../../.." +pod 'RemoteTest', :path => "../../generated_libraries/RemoteTestClient" + +target 'Sample' do +end diff --git a/src/objective-c/examples/Sample/README.md b/src/objective-c/examples/Sample/README.md new file mode 100644 index 00000000..716241bb --- /dev/null +++ b/src/objective-c/examples/Sample/README.md @@ -0,0 +1 @@ +This sample app requires the use of Cocoapods. After installing Cocoapods, run `pod install` in this directory to recreate its dependencies. (This will compile OpenSSL, which takes some time). diff --git a/src/objective-c/examples/Sample/Sample.xcodeproj/project.pbxproj b/src/objective-c/examples/Sample/Sample.xcodeproj/project.pbxproj new file mode 100644 index 00000000..611eb603 --- /dev/null +++ b/src/objective-c/examples/Sample/Sample.xcodeproj/project.pbxproj @@ -0,0 +1,351 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 6369A2701A9322E20015FC5C /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6369A26F1A9322E20015FC5C /* main.m */; }; + 6369A2731A9322E20015FC5C /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6369A2721A9322E20015FC5C /* AppDelegate.m */; }; + 6369A2761A9322E20015FC5C /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6369A2751A9322E20015FC5C /* ViewController.m */; }; + 6369A2791A9322E20015FC5C /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6369A2771A9322E20015FC5C /* Main.storyboard */; }; + 6369A27B1A9322E20015FC5C /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6369A27A1A9322E20015FC5C /* Images.xcassets */; }; + FC81FE63CA655031F3524EC0 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2DC7B7C4C0410F43B9621631 /* libPods.a */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 2DC7B7C4C0410F43B9621631 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 6369A26A1A9322E20015FC5C /* Sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 6369A26E1A9322E20015FC5C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 6369A26F1A9322E20015FC5C /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 6369A2711A9322E20015FC5C /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 6369A2721A9322E20015FC5C /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 6369A2741A9322E20015FC5C /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; + 6369A2751A9322E20015FC5C /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; + 6369A2781A9322E20015FC5C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 6369A27A1A9322E20015FC5C /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + AC29DD6FCDF962F519FEBB0D /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = ""; }; + C68330F8D451CC6ACEABA09F /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 6369A2671A9322E20015FC5C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + FC81FE63CA655031F3524EC0 /* libPods.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 6369A2611A9322E20015FC5C = { + isa = PBXGroup; + children = ( + 6369A26C1A9322E20015FC5C /* Sample */, + 6369A26B1A9322E20015FC5C /* Products */, + AB3331C9AE6488E61B2B094E /* Pods */, + C4C2C5219053E079C9EFB930 /* Frameworks */, + ); + sourceTree = ""; + }; + 6369A26B1A9322E20015FC5C /* Products */ = { + isa = PBXGroup; + children = ( + 6369A26A1A9322E20015FC5C /* Sample.app */, + ); + name = Products; + sourceTree = ""; + }; + 6369A26C1A9322E20015FC5C /* Sample */ = { + isa = PBXGroup; + children = ( + 6369A2711A9322E20015FC5C /* AppDelegate.h */, + 6369A2721A9322E20015FC5C /* AppDelegate.m */, + 6369A2741A9322E20015FC5C /* ViewController.h */, + 6369A2751A9322E20015FC5C /* ViewController.m */, + 6369A2771A9322E20015FC5C /* Main.storyboard */, + 6369A27A1A9322E20015FC5C /* Images.xcassets */, + 6369A26D1A9322E20015FC5C /* Supporting Files */, + ); + path = Sample; + sourceTree = ""; + }; + 6369A26D1A9322E20015FC5C /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 6369A26E1A9322E20015FC5C /* Info.plist */, + 6369A26F1A9322E20015FC5C /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + AB3331C9AE6488E61B2B094E /* Pods */ = { + isa = PBXGroup; + children = ( + AC29DD6FCDF962F519FEBB0D /* Pods.debug.xcconfig */, + C68330F8D451CC6ACEABA09F /* Pods.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; + C4C2C5219053E079C9EFB930 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 2DC7B7C4C0410F43B9621631 /* libPods.a */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 6369A2691A9322E20015FC5C /* Sample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6369A28D1A9322E20015FC5C /* Build configuration list for PBXNativeTarget "Sample" */; + buildPhases = ( + 41F7486D8F66994B0BFB84AF /* Check Pods Manifest.lock */, + 6369A2661A9322E20015FC5C /* Sources */, + 6369A2671A9322E20015FC5C /* Frameworks */, + 6369A2681A9322E20015FC5C /* Resources */, + 04554623324BE4A838846086 /* Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Sample; + productName = Sample; + productReference = 6369A26A1A9322E20015FC5C /* Sample.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 6369A2621A9322E20015FC5C /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0610; + ORGANIZATIONNAME = gRPC; + TargetAttributes = { + 6369A2691A9322E20015FC5C = { + CreatedOnToolsVersion = 6.1.1; + }; + }; + }; + buildConfigurationList = 6369A2651A9322E20015FC5C /* Build configuration list for PBXProject "Sample" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 6369A2611A9322E20015FC5C; + productRefGroup = 6369A26B1A9322E20015FC5C /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 6369A2691A9322E20015FC5C /* Sample */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 6369A2681A9322E20015FC5C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6369A2791A9322E20015FC5C /* Main.storyboard in Resources */, + 6369A27B1A9322E20015FC5C /* Images.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 04554623324BE4A838846086 /* Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + 41F7486D8F66994B0BFB84AF /* Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 6369A2661A9322E20015FC5C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6369A2761A9322E20015FC5C /* ViewController.m in Sources */, + 6369A2731A9322E20015FC5C /* AppDelegate.m in Sources */, + 6369A2701A9322E20015FC5C /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 6369A2771A9322E20015FC5C /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 6369A2781A9322E20015FC5C /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 6369A28B1A9322E20015FC5C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.1; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 6369A28C1A9322E20015FC5C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.1; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 6369A28E1A9322E20015FC5C /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = AC29DD6FCDF962F519FEBB0D /* Pods.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = Sample/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 6369A28F1A9322E20015FC5C /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = C68330F8D451CC6ACEABA09F /* Pods.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = Sample/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 6369A2651A9322E20015FC5C /* Build configuration list for PBXProject "Sample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6369A28B1A9322E20015FC5C /* Debug */, + 6369A28C1A9322E20015FC5C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 6369A28D1A9322E20015FC5C /* Build configuration list for PBXNativeTarget "Sample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6369A28E1A9322E20015FC5C /* Debug */, + 6369A28F1A9322E20015FC5C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 6369A2621A9322E20015FC5C /* Project object */; +} diff --git a/src/objective-c/examples/Sample/Sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/src/objective-c/examples/Sample/Sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..a80c0382 --- /dev/null +++ b/src/objective-c/examples/Sample/Sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/src/objective-c/examples/Sample/Sample/AppDelegate.h b/src/objective-c/examples/Sample/Sample/AppDelegate.h new file mode 100644 index 00000000..102e7f3a --- /dev/null +++ b/src/objective-c/examples/Sample/Sample/AppDelegate.h @@ -0,0 +1,38 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import + +@interface AppDelegate : UIResponder +@property (strong, nonatomic) UIWindow *window; +@end diff --git a/src/objective-c/examples/Sample/Sample/AppDelegate.m b/src/objective-c/examples/Sample/Sample/AppDelegate.m new file mode 100644 index 00000000..a38e3665 --- /dev/null +++ b/src/objective-c/examples/Sample/Sample/AppDelegate.m @@ -0,0 +1,37 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "AppDelegate.h" + +@implementation AppDelegate +@end diff --git a/src/objective-c/examples/Sample/Sample/Base.lproj/Main.storyboard b/src/objective-c/examples/Sample/Sample/Base.lproj/Main.storyboard new file mode 100644 index 00000000..8887b9e1 --- /dev/null +++ b/src/objective-c/examples/Sample/Sample/Base.lproj/Main.storyboard @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/objective-c/examples/Sample/Sample/Images.xcassets/AppIcon.appiconset/Contents.json b/src/objective-c/examples/Sample/Sample/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..36d2c80d --- /dev/null +++ b/src/objective-c/examples/Sample/Sample/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/src/objective-c/examples/Sample/Sample/Info.plist b/src/objective-c/examples/Sample/Sample/Info.plist new file mode 100644 index 00000000..4436635a --- /dev/null +++ b/src/objective-c/examples/Sample/Sample/Info.plist @@ -0,0 +1,47 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + org.grpc.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + Main + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/src/objective-c/examples/Sample/Sample/ViewController.h b/src/objective-c/examples/Sample/Sample/ViewController.h new file mode 100644 index 00000000..c0b4aca7 --- /dev/null +++ b/src/objective-c/examples/Sample/Sample/ViewController.h @@ -0,0 +1,37 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import + +@interface ViewController : UIViewController +@end diff --git a/src/objective-c/examples/Sample/Sample/ViewController.m b/src/objective-c/examples/Sample/Sample/ViewController.m new file mode 100644 index 00000000..05bd6fa2 --- /dev/null +++ b/src/objective-c/examples/Sample/Sample/ViewController.m @@ -0,0 +1,93 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "ViewController.h" + +#import +#import +#import +#import +#import +#import + +@implementation ViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + NSString * const kRemoteHost = @"grpc-test.sandbox.google.com"; + + RMTSimpleRequest *request = [[RMTSimpleRequest alloc] init]; + request.responseSize = 10; + request.fillUsername = YES; + request.fillOauthScope = YES; + + // Example gRPC call using a generated proto client library: + + RMTTestService *service = [[RMTTestService alloc] initWithHost:kRemoteHost]; + [service unaryCallWithRequest:request handler:^(RMTSimpleResponse *response, NSError *error) { + if (response) { + NSLog(@"Finished successfully with response:\n%@", response); + } else if (error) { + NSLog(@"Finished with error: %@", error); + } + }]; + + + // Same example call using the generic gRPC client library: + + GRPCMethodName *method = [[GRPCMethodName alloc] initWithPackage:@"grpc.testing" + interface:@"TestService" + method:@"UnaryCall"]; + + id requestsWriter = [GRXWriter writerWithValue:[request data]]; + + GRPCCall *call = [[GRPCCall alloc] initWithHost:kRemoteHost + method:method + requestsWriter:requestsWriter]; + + id responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) { + RMTSimpleResponse *response = [RMTSimpleResponse parseFromData:value error:NULL]; + NSLog(@"Received response:\n%@", response); + } completionHandler:^(NSError *errorOrNil) { + if (errorOrNil) { + NSLog(@"Finished with error: %@", errorOrNil); + } else { + NSLog(@"Finished successfully."); + } + }]; + + [call startWithWriteable:responsesWriteable]; +} + +@end diff --git a/src/objective-c/examples/Sample/Sample/main.m b/src/objective-c/examples/Sample/Sample/main.m new file mode 100644 index 00000000..81e9d44e --- /dev/null +++ b/src/objective-c/examples/Sample/Sample/main.m @@ -0,0 +1,41 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import +#import "AppDelegate.h" + +int main(int argc, char * argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/src/objective-c/examples/SwiftSample/AppDelegate.swift b/src/objective-c/examples/SwiftSample/AppDelegate.swift new file mode 100644 index 00000000..88027d99 --- /dev/null +++ b/src/objective-c/examples/SwiftSample/AppDelegate.swift @@ -0,0 +1,39 @@ +/* + * + * Copyright 2015, 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. + * + */ + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + var window: UIWindow? +} diff --git a/src/objective-c/examples/SwiftSample/Base.lproj/Main.storyboard b/src/objective-c/examples/SwiftSample/Base.lproj/Main.storyboard new file mode 100644 index 00000000..3a2a49ba --- /dev/null +++ b/src/objective-c/examples/SwiftSample/Base.lproj/Main.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/objective-c/examples/SwiftSample/Bridging-Header.h b/src/objective-c/examples/SwiftSample/Bridging-Header.h new file mode 100644 index 00000000..65f768a7 --- /dev/null +++ b/src/objective-c/examples/SwiftSample/Bridging-Header.h @@ -0,0 +1,45 @@ +/* + * + * Copyright 2015, 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 SwiftSample_Bridging_Header_h +#define SwiftSample_Bridging_Header_h + +#import +#import +#import +#import +#import +#import +#import + +#endif diff --git a/src/objective-c/examples/SwiftSample/Images.xcassets/AppIcon.appiconset/Contents.json b/src/objective-c/examples/SwiftSample/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..36d2c80d --- /dev/null +++ b/src/objective-c/examples/SwiftSample/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/src/objective-c/examples/SwiftSample/Info.plist b/src/objective-c/examples/SwiftSample/Info.plist new file mode 100644 index 00000000..10f0450b --- /dev/null +++ b/src/objective-c/examples/SwiftSample/Info.plist @@ -0,0 +1,47 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + io.grpc.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + Main + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/src/objective-c/examples/SwiftSample/Podfile b/src/objective-c/examples/SwiftSample/Podfile new file mode 100644 index 00000000..3611b008 --- /dev/null +++ b/src/objective-c/examples/SwiftSample/Podfile @@ -0,0 +1,9 @@ +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '8.0' + +pod 'Protobuf', :path => "../../../../third_party/protobuf" +pod 'gRPC', :path => "../../../.." +pod 'RemoteTest', :path => "../RemoteTestClient" + +target 'SwiftSample' do +end diff --git a/src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/project.pbxproj b/src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/project.pbxproj new file mode 100644 index 00000000..cfccdd45 --- /dev/null +++ b/src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/project.pbxproj @@ -0,0 +1,354 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 253D3A297105CA46DA960A11 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DC58ACA18DCCB1553531B885 /* libPods.a */; }; + 633BFFC81B950B210007E424 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 633BFFC71B950B210007E424 /* AppDelegate.swift */; }; + 633BFFCA1B950B210007E424 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 633BFFC91B950B210007E424 /* ViewController.swift */; }; + 633BFFCD1B950B210007E424 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 633BFFCB1B950B210007E424 /* Main.storyboard */; }; + 633BFFCF1B950B210007E424 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 633BFFCE1B950B210007E424 /* Images.xcassets */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 12C7B447AA80E624D93B5C54 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = ""; }; + 633BFFC21B950B210007E424 /* SwiftSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftSample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 633BFFC61B950B210007E424 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 633BFFC71B950B210007E424 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 633BFFC91B950B210007E424 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + 633BFFCC1B950B210007E424 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 633BFFCE1B950B210007E424 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + 6367AD231B951655007FD3A4 /* Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Bridging-Header.h"; sourceTree = ""; }; + C335CBC4C160E0D9EDEE646B /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = ""; }; + DC58ACA18DCCB1553531B885 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 633BFFBF1B950B210007E424 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 253D3A297105CA46DA960A11 /* libPods.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 31F283C976AE97586C17CCD9 /* Pods */ = { + isa = PBXGroup; + children = ( + 12C7B447AA80E624D93B5C54 /* Pods.debug.xcconfig */, + C335CBC4C160E0D9EDEE646B /* Pods.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; + 633BFFB91B950B210007E424 = { + isa = PBXGroup; + children = ( + 633BFFC41B950B210007E424 /* SwiftSample */, + 633BFFC31B950B210007E424 /* Products */, + 31F283C976AE97586C17CCD9 /* Pods */, + 9D63A7F6423989BA306810CA /* Frameworks */, + ); + sourceTree = ""; + }; + 633BFFC31B950B210007E424 /* Products */ = { + isa = PBXGroup; + children = ( + 633BFFC21B950B210007E424 /* SwiftSample.app */, + ); + name = Products; + sourceTree = ""; + }; + 633BFFC41B950B210007E424 /* SwiftSample */ = { + isa = PBXGroup; + children = ( + 633BFFC71B950B210007E424 /* AppDelegate.swift */, + 633BFFC91B950B210007E424 /* ViewController.swift */, + 633BFFCB1B950B210007E424 /* Main.storyboard */, + 633BFFCE1B950B210007E424 /* Images.xcassets */, + 633BFFC51B950B210007E424 /* Supporting Files */, + 6367AD231B951655007FD3A4 /* Bridging-Header.h */, + ); + name = SwiftSample; + sourceTree = SOURCE_ROOT; + }; + 633BFFC51B950B210007E424 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 633BFFC61B950B210007E424 /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 9D63A7F6423989BA306810CA /* Frameworks */ = { + isa = PBXGroup; + children = ( + DC58ACA18DCCB1553531B885 /* libPods.a */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 633BFFC11B950B210007E424 /* SwiftSample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 633BFFE11B950B210007E424 /* Build configuration list for PBXNativeTarget "SwiftSample" */; + buildPhases = ( + 6BEEB33CA2705D7D2F2210E6 /* Check Pods Manifest.lock */, + 633BFFBE1B950B210007E424 /* Sources */, + 633BFFBF1B950B210007E424 /* Frameworks */, + 633BFFC01B950B210007E424 /* Resources */, + AC2F6F9AB1C090BB0BEE6E4D /* Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = SwiftSample; + productName = SwiftSample; + productReference = 633BFFC21B950B210007E424 /* SwiftSample.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 633BFFBA1B950B210007E424 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0640; + ORGANIZATIONNAME = gRPC; + TargetAttributes = { + 633BFFC11B950B210007E424 = { + CreatedOnToolsVersion = 6.4; + }; + }; + }; + buildConfigurationList = 633BFFBD1B950B210007E424 /* Build configuration list for PBXProject "SwiftSample" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 633BFFB91B950B210007E424; + productRefGroup = 633BFFC31B950B210007E424 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 633BFFC11B950B210007E424 /* SwiftSample */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 633BFFC01B950B210007E424 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 633BFFCD1B950B210007E424 /* Main.storyboard in Resources */, + 633BFFCF1B950B210007E424 /* Images.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 6BEEB33CA2705D7D2F2210E6 /* Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; + AC2F6F9AB1C090BB0BEE6E4D /* Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 633BFFBE1B950B210007E424 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 633BFFCA1B950B210007E424 /* ViewController.swift in Sources */, + 633BFFC81B950B210007E424 /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 633BFFCB1B950B210007E424 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 633BFFCC1B950B210007E424 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 633BFFDF1B950B210007E424 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.4; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 633BFFE01B950B210007E424 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.4; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 633BFFE21B950B210007E424 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 12C7B447AA80E624D93B5C54 /* Pods.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Bridging-Header.h"; + USER_HEADER_SEARCH_PATHS = "Pods/**"; + }; + name = Debug; + }; + 633BFFE31B950B210007E424 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = C335CBC4C160E0D9EDEE646B /* Pods.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Bridging-Header.h"; + USER_HEADER_SEARCH_PATHS = "Pods/**"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 633BFFBD1B950B210007E424 /* Build configuration list for PBXProject "SwiftSample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 633BFFDF1B950B210007E424 /* Debug */, + 633BFFE01B950B210007E424 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 633BFFE11B950B210007E424 /* Build configuration list for PBXNativeTarget "SwiftSample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 633BFFE21B950B210007E424 /* Debug */, + 633BFFE31B950B210007E424 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 633BFFBA1B950B210007E424 /* Project object */; +} diff --git a/src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..3b0f1c15 --- /dev/null +++ b/src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/src/objective-c/examples/SwiftSample/ViewController.swift b/src/objective-c/examples/SwiftSample/ViewController.swift new file mode 100644 index 00000000..76dad9e1 --- /dev/null +++ b/src/objective-c/examples/SwiftSample/ViewController.swift @@ -0,0 +1,99 @@ +/* + * + * Copyright 2015, 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. + * + */ + +import UIKit + +class ViewController: UIViewController { + + override func viewDidLoad() { + super.viewDidLoad() + + let RemoteHost = "grpc-test.sandbox.google.com" + + let request = RMTSimpleRequest() + request.responseSize = 10 + request.fillUsername = true + request.fillOauthScope = true + + + // Example gRPC call using a generated proto client library: + + let service = RMTTestService(host: RemoteHost) + service.unaryCallWithRequest(request) { response, error in + if let response = response { + NSLog("1. Finished successfully with response:\n\(response)") + } else { + NSLog("1. Finished with error: \(error!)") + } + } + + + // Same but manipulating headers: + + var RPC : ProtoRPC! // Needed to convince Swift to capture by reference (__block) + RPC = service.RPCToUnaryCallWithRequest(request) { response, error in + if let response = response { + NSLog("2. Finished successfully with response:\n\(response)") + } else { + NSLog("2. Finished with error: \(error!)") + } + NSLog("2. Response headers: \(RPC.responseHeaders)") + NSLog("2. Response trailers: \(RPC.responseTrailers)") + } + + RPC.requestHeaders["My-Header"] = "My value" + + RPC.start() + + + // Same example call using the generic gRPC client library: + + let method = ProtoMethod(package: "grpc.testing", service: "TestService", method: "UnaryCall") + + let requestsWriter = GRXWriter(value: request.data()) + + let call = GRPCCall(host: RemoteHost, path: method.HTTPPath, requestsWriter: requestsWriter) + + call.requestHeaders["My-Header"] = "My value" + + call.startWithWriteable(GRXWriteable { response, error in + if let response = response as? NSData { + NSLog("3. Received response:\n\(RMTSimpleResponse(data: response, error: nil))") + } else { + NSLog("3. Finished with error: \(error!)") + } + NSLog("3. Response headers: \(call.responseHeaders)") + NSLog("3. Response trailers: \(call.responseTrailers)") + }) + } +} diff --git a/src/objective-c/generated_libraries/RemoteTestClient/RemoteTest.podspec b/src/objective-c/generated_libraries/RemoteTestClient/RemoteTest.podspec new file mode 100644 index 00000000..8710753e --- /dev/null +++ b/src/objective-c/generated_libraries/RemoteTestClient/RemoteTest.podspec @@ -0,0 +1,31 @@ +Pod::Spec.new do |s| + s.name = "RemoteTest" + s.version = "0.0.1" + s.license = "New BSD" + + s.ios.deployment_target = "6.0" + s.osx.deployment_target = "10.8" + + # Run protoc with the Objective-C and gRPC plugins to generate protocol messages and gRPC clients. + s.prepare_command = <<-CMD + BINDIR=../../../../bins/$CONFIG + PROTOC=$BINDIR/protobuf/protoc + PLUGIN=$BINDIR/grpc_objective_c_plugin + $PROTOC --plugin=protoc-gen-grpc=$PLUGIN --objc_out=. --grpc_out=. *.proto + CMD + + s.subspec "Messages" do |ms| + ms.source_files = "*.pbobjc.{h,m}" + ms.header_mappings_dir = "." + ms.requires_arc = false + ms.dependency "Protobuf", "~> 3.0.0-alpha-3" + end + + s.subspec "Services" do |ss| + ss.source_files = "*.pbrpc.{h,m}" + ss.header_mappings_dir = "." + ss.requires_arc = true + ss.dependency "gRPC", "~> 0.5" + ss.dependency "#{s.name}/Messages" + end +end diff --git a/src/objective-c/generated_libraries/RemoteTestClient/empty.proto b/src/objective-c/generated_libraries/RemoteTestClient/empty.proto new file mode 100644 index 00000000..a6780482 --- /dev/null +++ b/src/objective-c/generated_libraries/RemoteTestClient/empty.proto @@ -0,0 +1,44 @@ +// Copyright 2015, 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. + +syntax = "proto3"; + +package grpc.testing; + +option objc_class_prefix = "RMT"; + +// An empty message that you can re-use to avoid defining duplicated empty +// messages in your project. A typical example is to use it as argument or the +// return value of a service API. For instance: +// +// service Foo { +// rpc Bar (grpc.testing.Empty) returns (grpc.testing.Empty) { }; +// }; +// +message Empty {} diff --git a/src/objective-c/generated_libraries/RemoteTestClient/messages.proto b/src/objective-c/generated_libraries/RemoteTestClient/messages.proto new file mode 100644 index 00000000..85d93c2f --- /dev/null +++ b/src/objective-c/generated_libraries/RemoteTestClient/messages.proto @@ -0,0 +1,133 @@ +// Copyright 2015, 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. + +// Message definitions to be used by integration test service definitions. + +syntax = "proto3"; + +package grpc.testing; + +option objc_class_prefix = "RMT"; + +// The type of payload that should be returned. +enum PayloadType { + // Compressable text format. + COMPRESSABLE = 0; + + // Uncompressable binary format. + UNCOMPRESSABLE = 1; + + // Randomly chosen from all other formats defined in this enum. + RANDOM = 2; +} + +// A block of data, to simply increase gRPC message size. +message Payload { + // The type of data in body. + PayloadType type = 1; + // Primary contents of payload. + bytes body = 2; +} + +// Unary request. +message SimpleRequest { + // Desired payload type in the response from the server. + // If response_type is RANDOM, server randomly chooses one from other formats. + PayloadType response_type = 1; + + // Desired payload size in the response from the server. + // If response_type is COMPRESSABLE, this denotes the size before compression. + int32 response_size = 2; + + // Optional input payload sent along with the request. + Payload payload = 3; + + // Whether SimpleResponse should include username. + bool fill_username = 4; + + // Whether SimpleResponse should include OAuth scope. + bool fill_oauth_scope = 5; +} + +// Unary response, as configured by the request. +message SimpleResponse { + // Payload to increase message size. + Payload payload = 1; + // The user the request came from, for verifying authentication was + // successful when the client expected it. + string username = 2; + // OAuth scope. + string oauth_scope = 3; +} + +// Client-streaming request. +message StreamingInputCallRequest { + // Optional input payload sent along with the request. + Payload payload = 1; + + // Not expecting any payload from the response. +} + +// Client-streaming response. +message StreamingInputCallResponse { + // Aggregated size of payloads received from the client. + int32 aggregated_payload_size = 1; +} + +// Configuration for a particular response. +message ResponseParameters { + // Desired payload sizes in responses from the server. + // If response_type is COMPRESSABLE, this denotes the size before compression. + int32 size = 1; + + // Desired interval between consecutive responses in the response stream in + // microseconds. + int32 interval_us = 2; +} + +// Server-streaming request. +message StreamingOutputCallRequest { + // Desired payload type in the response from the server. + // If response_type is RANDOM, the payload from each response in the stream + // might be of different types. This is to simulate a mixed type of payload + // stream. + PayloadType response_type = 1; + + // Configuration for each expected response message. + repeated ResponseParameters response_parameters = 2; + + // Optional input payload sent along with the request. + Payload payload = 3; +} + +// Server-streaming response, as configured by the request and parameters. +message StreamingOutputCallResponse { + // Payload to increase response size. + Payload payload = 1; +} diff --git a/src/objective-c/generated_libraries/RemoteTestClient/test.proto b/src/objective-c/generated_libraries/RemoteTestClient/test.proto new file mode 100644 index 00000000..2f5a5489 --- /dev/null +++ b/src/objective-c/generated_libraries/RemoteTestClient/test.proto @@ -0,0 +1,73 @@ +// Copyright 2015, 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. + +// An integration test service that covers all the method signature permutations +// of unary/streaming requests/responses. +syntax = "proto3"; + +import "empty.proto"; +import "messages.proto"; + +package grpc.testing; + +option objc_class_prefix = "RMT"; + +// A simple service to test the various types of RPCs and experiment with +// performance with various types of payload. +service TestService { + // One empty request followed by one empty response. + rpc EmptyCall(grpc.testing.Empty) returns (grpc.testing.Empty); + + // One request followed by one response. + // TODO(Issue 527): Describe required server behavior. + rpc UnaryCall(SimpleRequest) returns (SimpleResponse); + + // One request followed by a sequence of responses (streamed download). + // The server returns the payload with client desired type and sizes. + rpc StreamingOutputCall(StreamingOutputCallRequest) + returns (stream StreamingOutputCallResponse); + + // A sequence of requests followed by one response (streamed upload). + // The server returns the aggregated size of client payload as the result. + rpc StreamingInputCall(stream StreamingInputCallRequest) + returns (StreamingInputCallResponse); + + // A sequence of requests with each request served by the server immediately. + // As one request could lead to multiple responses, this interface + // demonstrates the idea of full duplexing. + rpc FullDuplexCall(stream StreamingOutputCallRequest) + returns (stream StreamingOutputCallResponse); + + // A sequence of requests followed by a sequence of responses. + // The server buffers all the client requests and then serves them in order. A + // stream of responses are returned to the client when the server starts with + // first request. + rpc HalfDuplexCall(stream StreamingOutputCallRequest) + returns (stream StreamingOutputCallResponse); +} diff --git a/src/objective-c/generated_libraries/RouteGuideClient/RouteGuide.podspec b/src/objective-c/generated_libraries/RouteGuideClient/RouteGuide.podspec new file mode 100644 index 00000000..23ccffe6 --- /dev/null +++ b/src/objective-c/generated_libraries/RouteGuideClient/RouteGuide.podspec @@ -0,0 +1,31 @@ +Pod::Spec.new do |s| + s.name = "RouteGuide" + s.version = "0.0.1" + s.license = "New BSD" + + s.ios.deployment_target = "6.0" + s.osx.deployment_target = "10.8" + + # Run protoc with the Objective-C and gRPC plugins to generate protocol messages and gRPC clients. + s.prepare_command = <<-CMD + BINDIR=../../../../bins/$CONFIG + PROTOC=$BINDIR/protobuf/protoc + PLUGIN=$BINDIR/grpc_objective_c_plugin + $PROTOC --plugin=protoc-gen-grpc=$PLUGIN --objc_out=. --grpc_out=. *.proto + CMD + + s.subspec "Messages" do |ms| + ms.source_files = "*.pbobjc.{h,m}" + ms.header_mappings_dir = "." + ms.requires_arc = false + ms.dependency "Protobuf", "~> 3.0.0-alpha-3" + end + + s.subspec "Services" do |ss| + ss.source_files = "*.pbrpc.{h,m}" + ss.header_mappings_dir = "." + ss.requires_arc = true + ss.dependency "gRPC", "~> 0.5" + ss.dependency "#{s.name}/Messages" + end +end diff --git a/src/objective-c/generated_libraries/RouteGuideClient/route_guide.proto b/src/objective-c/generated_libraries/RouteGuideClient/route_guide.proto new file mode 100644 index 00000000..19592e2e --- /dev/null +++ b/src/objective-c/generated_libraries/RouteGuideClient/route_guide.proto @@ -0,0 +1,120 @@ +// Copyright 2015, 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. + +syntax = "proto3"; + +package routeguide; + +option objc_class_prefix = "RGD"; + +// Interface exported by the server. +service RouteGuide { + // A simple RPC. + // + // Obtains the feature at a given position. + rpc GetFeature(Point) returns (Feature) {} + + // A server-to-client streaming RPC. + // + // Obtains the Features available within the given Rectangle. Results are + // streamed rather than returned at once (e.g. in a response message with a + // repeated field), as the rectangle may cover a large area and contain a + // huge number of features. + rpc ListFeatures(Rectangle) returns (stream Feature) {} + + // A client-to-server streaming RPC. + // + // Accepts a stream of Points on a route being traversed, returning a + // RouteSummary when traversal is completed. + rpc RecordRoute(stream Point) returns (RouteSummary) {} + + // A Bidirectional streaming RPC. + // + // Accepts a stream of RouteNotes sent while a route is being traversed, + // while receiving other RouteNotes (e.g. from other users). + rpc RouteChat(stream RouteNote) returns (stream RouteNote) {} +} + +// Points are represented as latitude-longitude pairs in the E7 representation +// (degrees multiplied by 10**7 and rounded to the nearest integer). +// Latitudes should be in the range +/- 90 degrees and longitude should be in +// the range +/- 180 degrees (inclusive). +message Point { + int32 latitude = 1; + int32 longitude = 2; +} + +// A latitude-longitude rectangle, represented as two diagonally opposite +// points "lo" and "hi". +message Rectangle { + // One corner of the rectangle. + Point lo = 1; + + // The other corner of the rectangle. + Point hi = 2; +} + +// A feature names something at a given point. +// +// If a feature could not be named, the name is empty. +message Feature { + // The name of the feature. + string name = 1; + + // The point where the feature is detected. + Point location = 2; +} + +// A RouteNote is a message sent while at a given point. +message RouteNote { + // The location from which the message is sent. + Point location = 1; + + // The message to be sent. + string message = 2; +} + +// A RouteSummary is received in response to a RecordRoute rpc. +// +// It contains the number of individual points received, the number of +// detected features, and the total distance covered as the cumulative sum of +// the distance between each point. +message RouteSummary { + // The number of points received. + int32 point_count = 1; + + // The number of known features passed while traversing the route. + int32 feature_count = 2; + + // The distance covered in metres. + int32 distance = 3; + + // The duration of the traversal in seconds. + int32 elapsed_time = 4; +} diff --git a/src/objective-c/tests/GRPCClientTests.m b/src/objective-c/tests/GRPCClientTests.m new file mode 100644 index 00000000..09a55e07 --- /dev/null +++ b/src/objective-c/tests/GRPCClientTests.m @@ -0,0 +1,248 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import +#import + +#import +#import +#import +#import +#import +#import +#import + +// These are a few tests similar to InteropTests, but which use the generic gRPC client (GRPCCall) +// rather than a generated proto library on top of it. + +static NSString * const kHostAddress = @"localhost:5050"; +static NSString * const kPackage = @"grpc.testing"; +static NSString * const kService = @"TestService"; + +static ProtoMethod *kInexistentMethod; +static ProtoMethod *kEmptyCallMethod; +static ProtoMethod *kUnaryCallMethod; + +// This is an observer class for testing that responseMetadata is KVO-compliant + +@interface PassthroughObserver : NSObject + +- (instancetype) initWithCallback:(void (^)(NSString*, id, NSDictionary*))callback; + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change + context:(void *)context; +@end + +@implementation PassthroughObserver { + void (^_callback)(NSString*, id, NSDictionary*); +} + +- (instancetype)initWithCallback:(void (^)(NSString *, id, NSDictionary *))callback { + self = [super init]; + if (self) { + _callback = callback; + } + return self; + +} + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ + _callback(keyPath, object, change); + [object removeObserver:self forKeyPath:keyPath]; +} + +@end + +@interface GRPCClientTests : XCTestCase +@end + +@implementation GRPCClientTests + +- (void)setUp { + // Register test server as non-SSL. + [GRPCCall useInsecureConnectionsForHost:kHostAddress]; + + // This method isn't implemented by the remote server. + kInexistentMethod = [[ProtoMethod alloc] initWithPackage:kPackage + service:kService + method:@"Inexistent"]; + kEmptyCallMethod = [[ProtoMethod alloc] initWithPackage:kPackage + service:kService + method:@"EmptyCall"]; + kUnaryCallMethod = [[ProtoMethod alloc] initWithPackage:kPackage + service:kService + method:@"UnaryCall"]; +} + +- (void)testConnectionToRemoteServer { + __weak XCTestExpectation *expectation = [self expectationWithDescription:@"Server reachable."]; + + GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress + path:kInexistentMethod.HTTPPath + requestsWriter:[GRXWriter writerWithValue:[NSData data]]]; + + id responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) { + XCTFail(@"Received unexpected response: %@", value); + } completionHandler:^(NSError *errorOrNil) { + XCTAssertNotNil(errorOrNil, @"Finished without error!"); + // TODO(jcanizales): The server should return code 12 UNIMPLEMENTED, not 5 NOT FOUND. + XCTAssertEqual(errorOrNil.code, 5, @"Finished with unexpected error: %@", errorOrNil); + [expectation fulfill]; + }]; + + [call startWithWriteable:responsesWriteable]; + + [self waitForExpectationsWithTimeout:4 handler:nil]; +} + +- (void)testEmptyRPC { + __weak XCTestExpectation *response = [self expectationWithDescription:@"Empty response received."]; + __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."]; + + GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress + path:kEmptyCallMethod.HTTPPath + requestsWriter:[GRXWriter writerWithValue:[NSData data]]]; + + id responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) { + XCTAssertNotNil(value, @"nil value received as response."); + XCTAssertEqual([value length], 0, @"Non-empty response received: %@", value); + [response fulfill]; + } completionHandler:^(NSError *errorOrNil) { + XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil); + [completion fulfill]; + }]; + + [call startWithWriteable:responsesWriteable]; + + [self waitForExpectationsWithTimeout:8 handler:nil]; +} + +- (void)testSimpleProtoRPC { + __weak XCTestExpectation *response = [self expectationWithDescription:@"Expected response."]; + __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."]; + + RMTSimpleRequest *request = [RMTSimpleRequest message]; + request.responseSize = 100; + request.fillUsername = YES; + request.fillOauthScope = YES; + GRXWriter *requestsWriter = [GRXWriter writerWithValue:[request data]]; + + GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress + path:kUnaryCallMethod.HTTPPath + requestsWriter:requestsWriter]; + + id responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) { + XCTAssertNotNil(value, @"nil value received as response."); + XCTAssertGreaterThan(value.length, 0, @"Empty response received."); + RMTSimpleResponse *responseProto = [RMTSimpleResponse parseFromData:value error:NULL]; + // We expect empty strings, not nil: + XCTAssertNotNil(responseProto.username, @"Response's username is nil."); + XCTAssertNotNil(responseProto.oauthScope, @"Response's OAuth scope is nil."); + [response fulfill]; + } completionHandler:^(NSError *errorOrNil) { + XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil); + [completion fulfill]; + }]; + + [call startWithWriteable:responsesWriteable]; + + [self waitForExpectationsWithTimeout:8 handler:nil]; +} + +- (void)testMetadata { + __weak XCTestExpectation *expectation = [self expectationWithDescription:@"RPC unauthorized."]; + + RMTSimpleRequest *request = [RMTSimpleRequest message]; + request.fillUsername = YES; + request.fillOauthScope = YES; + GRXWriter *requestsWriter = [GRXWriter writerWithValue:[request data]]; + + GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress + path:kUnaryCallMethod.HTTPPath + requestsWriter:requestsWriter]; + + call.oauth2AccessToken = @"bogusToken"; + + id responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) { + XCTFail(@"Received unexpected response: %@", value); + } completionHandler:^(NSError *errorOrNil) { + XCTAssertNotNil(errorOrNil, @"Finished without error!"); + XCTAssertEqual(errorOrNil.code, 16, @"Finished with unexpected error: %@", errorOrNil); + XCTAssertEqualObjects(call.responseHeaders, errorOrNil.userInfo[kGRPCHeadersKey], + @"Headers in the NSError object and call object differ."); + XCTAssertEqualObjects(call.responseTrailers, errorOrNil.userInfo[kGRPCTrailersKey], + @"Trailers in the NSError object and call object differ."); + NSString *challengeHeader = call.oauth2ChallengeHeader; + XCTAssertGreaterThan(challengeHeader.length, 0, + @"No challenge in response headers %@", call.responseHeaders); + [expectation fulfill]; + }]; + + [call startWithWriteable:responsesWriteable]; + + [self waitForExpectationsWithTimeout:4 handler:nil]; +} + +- (void)testResponseMetadataKVO { + __weak XCTestExpectation *response = [self expectationWithDescription:@"Empty response received."]; + __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."]; + __weak XCTestExpectation *metadata = [self expectationWithDescription:@"Metadata changed."]; + + GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress + path:kEmptyCallMethod.HTTPPath + requestsWriter:[GRXWriter writerWithValue:[NSData data]]]; + + PassthroughObserver *observer = [[PassthroughObserver alloc] initWithCallback:^(NSString *keypath, id object, NSDictionary * change) { + if ([keypath isEqual: @"responseHeaders"]) { + [metadata fulfill]; + } + }]; + + [call addObserver:observer forKeyPath:@"responseHeaders" options:0 context:NULL]; + + id responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) { + XCTAssertNotNil(value, @"nil value received as response."); + XCTAssertEqual([value length], 0, @"Non-empty response received: %@", value); + [response fulfill]; + } completionHandler:^(NSError *errorOrNil) { + XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil); + [completion fulfill]; + }]; + + [call startWithWriteable:responsesWriteable]; + + [self waitForExpectationsWithTimeout:8 handler:nil]; +} + +@end diff --git a/src/objective-c/tests/Info.plist b/src/objective-c/tests/Info.plist new file mode 100644 index 00000000..fbeeb96b --- /dev/null +++ b/src/objective-c/tests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + gRPC.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/src/objective-c/tests/InteropTests.h b/src/objective-c/tests/InteropTests.h new file mode 100644 index 00000000..1045c3d1 --- /dev/null +++ b/src/objective-c/tests/InteropTests.h @@ -0,0 +1,43 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import + +// Implements tests as described here: +// https://github.com/grpc/grpc/blob/master/doc/interop-test-descriptions.md + +@interface InteropTests : XCTestCase +// Returns @"grpc-test.sandbox.google.com". +// Override in a subclass to perform the same tests against a different address. ++ (NSString *)host; +@end diff --git a/src/objective-c/tests/InteropTests.m b/src/objective-c/tests/InteropTests.m new file mode 100644 index 00000000..1b63fe20 --- /dev/null +++ b/src/objective-c/tests/InteropTests.m @@ -0,0 +1,309 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import "InteropTests.h" + +#include + +#import +#import +#import +#import +#import +#import +#import +#import + +// Convenience constructors for the generated proto messages: + +@interface RMTStreamingOutputCallRequest (Constructors) ++ (instancetype)messageWithPayloadSize:(NSNumber *)payloadSize + requestedResponseSize:(NSNumber *)responseSize; +@end + +@implementation RMTStreamingOutputCallRequest (Constructors) ++ (instancetype)messageWithPayloadSize:(NSNumber *)payloadSize + requestedResponseSize:(NSNumber *)responseSize { + RMTStreamingOutputCallRequest *request = [self message]; + RMTResponseParameters *parameters = [RMTResponseParameters message]; + parameters.size = responseSize.integerValue; + [request.responseParametersArray addObject:parameters]; + request.payload.body = [NSMutableData dataWithLength:payloadSize.unsignedIntegerValue]; + return request; +} +@end + +@interface RMTStreamingOutputCallResponse (Constructors) ++ (instancetype)messageWithPayloadSize:(NSNumber *)payloadSize; +@end + +@implementation RMTStreamingOutputCallResponse (Constructors) ++ (instancetype)messageWithPayloadSize:(NSNumber *)payloadSize { + RMTStreamingOutputCallResponse * response = [self message]; + response.payload.type = RMTPayloadType_Compressable; + response.payload.body = [NSMutableData dataWithLength:payloadSize.unsignedIntegerValue]; + return response; +} +@end + +#pragma mark Tests + +static NSString * const kRemoteSSLHost = @"grpc-test.sandbox.google.com"; + +@implementation InteropTests { + RMTTestService *_service; +} + ++ (NSString *)host { + return kRemoteSSLHost; +} + +- (void)setUp { + _service = [[RMTTestService alloc] initWithHost:self.class.host]; +} + +- (void)testEmptyUnaryRPC { + __weak XCTestExpectation *expectation = [self expectationWithDescription:@"EmptyUnary"]; + + RMTEmpty *request = [RMTEmpty message]; + + [_service emptyCallWithRequest:request handler:^(RMTEmpty *response, NSError *error) { + XCTAssertNil(error, @"Finished with unexpected error: %@", error); + + id expectedResponse = [RMTEmpty message]; + XCTAssertEqualObjects(response, expectedResponse); + + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:4 handler:nil]; +} + +- (void)testLargeUnaryRPC { + __weak XCTestExpectation *expectation = [self expectationWithDescription:@"LargeUnary"]; + + RMTSimpleRequest *request = [RMTSimpleRequest message]; + request.responseType = RMTPayloadType_Compressable; + request.responseSize = 314159; + request.payload.body = [NSMutableData dataWithLength:271828]; + + [_service unaryCallWithRequest:request handler:^(RMTSimpleResponse *response, NSError *error) { + XCTAssertNil(error, @"Finished with unexpected error: %@", error); + + RMTSimpleResponse *expectedResponse = [RMTSimpleResponse message]; + expectedResponse.payload.type = RMTPayloadType_Compressable; + expectedResponse.payload.body = [NSMutableData dataWithLength:314159]; + XCTAssertEqualObjects(response, expectedResponse); + + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:16 handler:nil]; +} + +- (void)testClientStreamingRPC { + __weak XCTestExpectation *expectation = [self expectationWithDescription:@"ClientStreaming"]; + + RMTStreamingInputCallRequest *request1 = [RMTStreamingInputCallRequest message]; + request1.payload.body = [NSMutableData dataWithLength:27182]; + + RMTStreamingInputCallRequest *request2 = [RMTStreamingInputCallRequest message]; + request2.payload.body = [NSMutableData dataWithLength:8]; + + RMTStreamingInputCallRequest *request3 = [RMTStreamingInputCallRequest message]; + request3.payload.body = [NSMutableData dataWithLength:1828]; + + RMTStreamingInputCallRequest *request4 = [RMTStreamingInputCallRequest message]; + request4.payload.body = [NSMutableData dataWithLength:45904]; + + GRXWriter *writer = [GRXWriter writerWithContainer:@[request1, request2, request3, request4]]; + + [_service streamingInputCallWithRequestsWriter:writer + handler:^(RMTStreamingInputCallResponse *response, + NSError *error) { + XCTAssertNil(error, @"Finished with unexpected error: %@", error); + + RMTStreamingInputCallResponse *expectedResponse = [RMTStreamingInputCallResponse message]; + expectedResponse.aggregatedPayloadSize = 74922; + XCTAssertEqualObjects(response, expectedResponse); + + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:8 handler:nil]; +} + +- (void)testServerStreamingRPC { + __weak XCTestExpectation *expectation = [self expectationWithDescription:@"ServerStreaming"]; + + NSArray *expectedSizes = @[@31415, @9, @2653, @58979]; + + RMTStreamingOutputCallRequest *request = [RMTStreamingOutputCallRequest message]; + for (NSNumber *size in expectedSizes) { + RMTResponseParameters *parameters = [RMTResponseParameters message]; + parameters.size = [size integerValue]; + [request.responseParametersArray addObject:parameters]; + } + + __block int index = 0; + [_service streamingOutputCallWithRequest:request + eventHandler:^(BOOL done, + RMTStreamingOutputCallResponse *response, + NSError *error){ + XCTAssertNil(error, @"Finished with unexpected error: %@", error); + XCTAssertTrue(done || response, @"Event handler called without an event."); + + if (response) { + XCTAssertLessThan(index, 4, @"More than 4 responses received."); + id expected = [RMTStreamingOutputCallResponse messageWithPayloadSize:expectedSizes[index]]; + XCTAssertEqualObjects(response, expected); + index += 1; + } + + if (done) { + XCTAssertEqual(index, 4, @"Received %i responses instead of 4.", index); + [expectation fulfill]; + } + }]; + + [self waitForExpectationsWithTimeout:8 handler:nil]; +} + +- (void)testPingPongRPC { + __weak XCTestExpectation *expectation = [self expectationWithDescription:@"PingPong"]; + + NSArray *requests = @[@27182, @8, @1828, @45904]; + NSArray *responses = @[@31415, @9, @2653, @58979]; + + GRXBufferedPipe *requestsBuffer = [[GRXBufferedPipe alloc] init]; + + __block int index = 0; + + id request = [RMTStreamingOutputCallRequest messageWithPayloadSize:requests[index] + requestedResponseSize:responses[index]]; + [requestsBuffer writeValue:request]; + + [_service fullDuplexCallWithRequestsWriter:requestsBuffer + eventHandler:^(BOOL done, + RMTStreamingOutputCallResponse *response, + NSError *error) { + XCTAssertNil(error, @"Finished with unexpected error: %@", error); + XCTAssertTrue(done || response, @"Event handler called without an event."); + + if (response) { + XCTAssertLessThan(index, 4, @"More than 4 responses received."); + id expected = [RMTStreamingOutputCallResponse messageWithPayloadSize:responses[index]]; + XCTAssertEqualObjects(response, expected); + index += 1; + if (index < 4) { + id request = [RMTStreamingOutputCallRequest messageWithPayloadSize:requests[index] + requestedResponseSize:responses[index]]; + [requestsBuffer writeValue:request]; + } else { + [requestsBuffer writesFinishedWithError:nil]; + } + } + + if (done) { + XCTAssertEqual(index, 4, @"Received %i responses instead of 4.", index); + [expectation fulfill]; + } + }]; + [self waitForExpectationsWithTimeout:4 handler:nil]; +} + +- (void)testEmptyStreamRPC { + __weak XCTestExpectation *expectation = [self expectationWithDescription:@"EmptyStream"]; + [_service fullDuplexCallWithRequestsWriter:[GRXWriter emptyWriter] + eventHandler:^(BOOL done, + RMTStreamingOutputCallResponse *response, + NSError *error) { + XCTAssertNil(error, @"Finished with unexpected error: %@", error); + XCTAssert(done, @"Unexpected response: %@", response); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:2 handler:nil]; +} + +- (void)testCancelAfterBeginRPC { + __weak XCTestExpectation *expectation = [self expectationWithDescription:@"CancelAfterBegin"]; + + // A buffered pipe to which we never write any value acts as a writer that just hangs. + GRXBufferedPipe *requestsBuffer = [[GRXBufferedPipe alloc] init]; + + ProtoRPC *call = [_service RPCToStreamingInputCallWithRequestsWriter:requestsBuffer + handler:^(RMTStreamingInputCallResponse *response, + NSError *error) { + XCTAssertEqual(error.code, GRPC_STATUS_CANCELLED); + [expectation fulfill]; + }]; + [call start]; + [call cancel]; + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +- (void)testCancelAfterFirstResponseRPC { + __weak XCTestExpectation *expectation = [self expectationWithDescription:@"CancelAfterFirstResponse"]; + + // A buffered pipe to which we write a single value but never close + GRXBufferedPipe *requestsBuffer = [[GRXBufferedPipe alloc] init]; + + __block BOOL receivedResponse = NO; + + id request = [RMTStreamingOutputCallRequest messageWithPayloadSize:@21782 + requestedResponseSize:@31415]; + + [requestsBuffer writeValue:request]; + + __block ProtoRPC *call = + [_service RPCToFullDuplexCallWithRequestsWriter:requestsBuffer + eventHandler:^(BOOL done, + RMTStreamingOutputCallResponse *response, + NSError *error) { + if (receivedResponse) { + XCTAssert(done, @"Unexpected extra response %@", response); + XCTAssertEqual(error.code, GRPC_STATUS_CANCELLED); + [expectation fulfill]; + } else { + XCTAssertNil(error, @"Finished with unexpected error: %@", error); + XCTAssertFalse(done, @"Finished without response"); + XCTAssertNotNil(response); + receivedResponse = YES; + [call cancel]; + } + }]; + [call start]; + [self waitForExpectationsWithTimeout:8 handler:nil]; +} + +@end diff --git a/src/objective-c/tests/InteropTestsLocalCleartext.m b/src/objective-c/tests/InteropTestsLocalCleartext.m new file mode 100644 index 00000000..2d7d3c4b --- /dev/null +++ b/src/objective-c/tests/InteropTestsLocalCleartext.m @@ -0,0 +1,59 @@ +/* + * + * Copyright 2015, 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. + * + */ + +// Repeat of the tests in InteropTests.m, but sending the RPCs to a local cleartext server instead +// of the remote SSL one. + +#import + +#import "InteropTests.h" + +static NSString * const kLocalCleartextHost = @"localhost:5050"; + +@interface InteropTestsLocalCleartext : InteropTests +@end + +@implementation InteropTestsLocalCleartext + ++ (NSString *)host { + return kLocalCleartextHost; +} + +- (void)setUp { + // Register test server as non-SSL. + [GRPCCall useInsecureConnectionsForHost:kLocalCleartextHost]; + + [super setUp]; +} + +@end diff --git a/src/objective-c/tests/InteropTestsLocalSSL.m b/src/objective-c/tests/InteropTestsLocalSSL.m new file mode 100644 index 00000000..f69f806d --- /dev/null +++ b/src/objective-c/tests/InteropTestsLocalSSL.m @@ -0,0 +1,62 @@ +/* + * + * Copyright 2015, 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. + * + */ + +// Repeat of the tests in InteropTests.m, but sending the RPCs to a local SSL server instead of the +// remote one. + +#import + +#import "InteropTests.h" + +static NSString * const kLocalSSLHost = @"localhost:5051"; + +@interface InteropTestsLocalSSL : InteropTests +@end + +@implementation InteropTestsLocalSSL + ++ (NSString *)host { + return kLocalSSLHost; +} + +- (void)setUp { + // Register test server certificates and name. + NSBundle *bundle = [NSBundle bundleForClass:self.class]; + NSString *certsPath = [bundle pathForResource:@"TestCertificates.bundle/test-certificates" + ofType:@"pem"]; + [GRPCCall useTestCertsPath:certsPath testName:@"foo.test.google.fr" forHost:kLocalSSLHost]; + + [super setUp]; +} + +@end diff --git a/src/objective-c/tests/LocalClearTextTests.m b/src/objective-c/tests/LocalClearTextTests.m new file mode 100644 index 00000000..976fff55 --- /dev/null +++ b/src/objective-c/tests/LocalClearTextTests.m @@ -0,0 +1,164 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import +#import + +#import +#import +#import +#import +#import +#import + +// These tests require a gRPC "RouteGuide" sample server to be running locally. You can compile and +// run one by following the instructions here: https://github.com/grpc/grpc/blob/master/examples/cpp/cpptutorial.md#try-it-out +// Be sure to have the C gRPC library installed in your system (for example, by having followed the +// instructions at https://github.com/grpc/homebrew-grpc + +static NSString * const kRouteGuideHost = @"http://localhost:50051"; +static NSString * const kPackage = @"routeguide"; +static NSString * const kService = @"RouteGuide"; + +@interface LocalClearTextTests : XCTestCase +@end + +@implementation LocalClearTextTests + +// This test currently fails: see Issue #1907. +//- (void)testConnectionToLocalServer { +// __weak XCTestExpectation *expectation = [self expectationWithDescription:@"Server reachable."]; +// +// // This method isn't implemented by the local server. +// GRPCMethodName *method = [[GRPCMethodName alloc] initWithPackage:kPackage +// interface:kService +// method:@"EmptyCall"]; +// +// GRXWriter *requestsWriter = [GRXWriter writerWithValue:[NSData data]]; +// +// GRPCCall *call = [[GRPCCall alloc] initWithHost:kRouteGuideHost +// method:method +// requestsWriter:requestsWriter]; +// +// id responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) { +// XCTFail(@"Received unexpected response: %@", value); +// } completionHandler:^(NSError *errorOrNil) { +// XCTAssertNotNil(errorOrNil, @"Finished without error!"); +// XCTAssertEqual(errorOrNil.code, 12, @"Finished with unexpected error: %@", errorOrNil); +// [expectation fulfill]; +// }]; +// +// [call startWithWriteable:responsesWriteable]; +// +// [self waitForExpectationsWithTimeout:8.0 handler:nil]; +//} + +- (void)testEmptyRPC { + __weak XCTestExpectation *response = [self expectationWithDescription:@"Empty response received."]; + __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."]; + + ProtoMethod *method = [[ProtoMethod alloc] initWithPackage:kPackage + service:kService + method:@"RecordRoute"]; + + GRXWriter *requestsWriter = [GRXWriter emptyWriter]; + + GRPCCall *call = [[GRPCCall alloc] initWithHost:kRouteGuideHost + path:method.HTTPPath + requestsWriter:requestsWriter]; + + id responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) { + XCTAssertNotNil(value, @"nil value received as response."); + XCTAssertEqual([value length], 0, @"Non-empty response received: %@", value); + [response fulfill]; + } completionHandler:^(NSError *errorOrNil) { + XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil); + [completion fulfill]; + }]; + + [call startWithWriteable:responsesWriteable]; + + [self waitForExpectationsWithTimeout:2.0 handler:nil]; +} + +- (void)testSimpleProtoRPC { + __weak XCTestExpectation *response = [self expectationWithDescription:@"Response received."]; + __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."]; + + ProtoMethod *method = [[ProtoMethod alloc] initWithPackage:kPackage + service:kService + method:@"GetFeature"]; + + RGDPoint *point = [RGDPoint message]; + point.latitude = 28E7; + point.longitude = -15E7; + GRXWriter *requestsWriter = [GRXWriter writerWithValue:[point data]]; + + GRPCCall *call = [[GRPCCall alloc] initWithHost:kRouteGuideHost + path:method.HTTPPath + requestsWriter:requestsWriter]; + + id responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) { + XCTAssertNotNil(value, @"nil value received as response."); + RGDFeature *feature = [RGDFeature parseFromData:value error:NULL]; + XCTAssertEqualObjects(point, feature.location); + XCTAssertNotNil(feature.name, @"Response's name is nil."); + [response fulfill]; + } completionHandler:^(NSError *errorOrNil) { + XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil); + [completion fulfill]; + }]; + + [call startWithWriteable:responsesWriteable]; + + [self waitForExpectationsWithTimeout:2.0 handler:nil]; +} + +- (void)testSimpleProtoRPCUsingGeneratedService { + __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."]; + + RGDPoint *point = [RGDPoint message]; + point.latitude = 28E7; + point.longitude = -15E7; + + RGDRouteGuide *service = [[RGDRouteGuide alloc] initWithHost:kRouteGuideHost]; + [service getFeatureWithRequest:point handler:^(RGDFeature *response, NSError *error) { + XCTAssertNil(error, @"Finished with unexpected error: %@", error); + XCTAssertEqualObjects(point, response.location); + XCTAssertNotNil(response.name, @"Response's name is nil."); + [completion fulfill]; + }]; + + [self waitForExpectationsWithTimeout:2.0 handler:nil]; +} +@end diff --git a/src/objective-c/tests/Podfile b/src/objective-c/tests/Podfile new file mode 100644 index 00000000..2aa837f7 --- /dev/null +++ b/src/objective-c/tests/Podfile @@ -0,0 +1,15 @@ +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '8.0' + +pod 'Protobuf', :path => "../../../third_party/protobuf" +pod 'gRPC', :path => "../../.." +pod 'RemoteTest', :path => "../generated_libraries/RemoteTestClient" +pod 'RouteGuide', :path => "../generated_libraries/RouteGuideClient" + +link_with 'AllTests' + +target 'Tests' do +end + +target 'AllTests' do +end diff --git a/src/objective-c/tests/RxLibraryUnitTests.m b/src/objective-c/tests/RxLibraryUnitTests.m new file mode 100644 index 00000000..a67a4c6c --- /dev/null +++ b/src/objective-c/tests/RxLibraryUnitTests.m @@ -0,0 +1,140 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import +#import + +#import +#import +#import + +// A mock of a GRXSingleValueHandler block that can be queried for how many times it was called and +// what were the last values passed to it. +// +// TODO(jcanizales): Move this to a test util library, and add tests for it. +@interface CapturingSingleValueHandler : NSObject +@property (nonatomic, readonly) void (^block)(id value, NSError *errorOrNil); +@property (nonatomic, readonly) NSUInteger timesCalled; +@property (nonatomic, readonly) id value; +@property (nonatomic, readonly) NSError *errorOrNil; ++ (instancetype)handler; +@end + +@implementation CapturingSingleValueHandler ++ (instancetype)handler { + return [[self alloc] init]; +} + +- (GRXSingleHandler)block { + return ^(id value, NSError *errorOrNil) { + ++_timesCalled; + _value = value; + _errorOrNil = errorOrNil; + }; +} +@end + +@interface RxLibraryUnitTests : XCTestCase +@end + +@implementation RxLibraryUnitTests + +#pragma mark Writeable + +- (void)testWriteableSingleHandlerIsCalledForValue { + // Given: + CapturingSingleValueHandler *handler = [CapturingSingleValueHandler handler]; + id anyValue = @7; + + // If: + id writeable = [GRXWriteable writeableWithSingleHandler:handler.block]; + [writeable writeValue:anyValue]; + + // Then: + XCTAssertEqual(handler.timesCalled, 1); + XCTAssertEqualObjects(handler.value, anyValue); + XCTAssertEqualObjects(handler.errorOrNil, nil); +} + +- (void)testWriteableSingleHandlerIsCalledForError { + // Given: + CapturingSingleValueHandler *handler = [CapturingSingleValueHandler handler]; + NSError *anyError = [NSError errorWithDomain:@"domain" code:7 userInfo:nil]; + + // If: + id writeable = [GRXWriteable writeableWithSingleHandler:handler.block]; + [writeable writesFinishedWithError:anyError]; + + // Then: + XCTAssertEqual(handler.timesCalled, 1); + XCTAssertEqualObjects(handler.value, nil); + XCTAssertEqualObjects(handler.errorOrNil, anyError); +} + +#pragma mark BufferedPipe + +- (void)testBufferedPipePropagatesValue { + // Given: + CapturingSingleValueHandler *handler = [CapturingSingleValueHandler handler]; + id writeable = [GRXWriteable writeableWithSingleHandler:handler.block]; + id anyValue = @7; + + // If: + GRXBufferedPipe *pipe = [GRXBufferedPipe pipe]; + [pipe startWithWriteable:writeable]; + [pipe writeValue:anyValue]; + + // Then: + XCTAssertEqual(handler.timesCalled, 1); + XCTAssertEqualObjects(handler.value, anyValue); + XCTAssertEqualObjects(handler.errorOrNil, nil); +} + +- (void)testBufferedPipePropagatesError { + // Given: + CapturingSingleValueHandler *handler = [CapturingSingleValueHandler handler]; + id writeable = [GRXWriteable writeableWithSingleHandler:handler.block]; + NSError *anyError = [NSError errorWithDomain:@"domain" code:7 userInfo:nil]; + + // If: + GRXBufferedPipe *pipe = [GRXBufferedPipe pipe]; + [pipe startWithWriteable:writeable]; + [pipe writesFinishedWithError:anyError]; + + // Then: + XCTAssertEqual(handler.timesCalled, 1); + XCTAssertEqualObjects(handler.value, nil); + XCTAssertEqualObjects(handler.errorOrNil, anyError); +} + +@end diff --git a/src/objective-c/tests/TestCertificates.bundle/test-certificates.pem b/src/objective-c/tests/TestCertificates.bundle/test-certificates.pem new file mode 100644 index 00000000..6c8511a7 --- /dev/null +++ b/src/objective-c/tests/TestCertificates.bundle/test-certificates.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICSjCCAbOgAwIBAgIJAJHGGR4dGioHMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQxDzANBgNVBAMTBnRlc3RjYTAeFw0xNDExMTEyMjMxMjla +Fw0yNDExMDgyMjMxMjlaMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0 +YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMT +BnRlc3RjYTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwEDfBV5MYdlHVHJ7 ++L4nxrZy7mBfAVXpOc5vMYztssUI7mL2/iYujiIXM+weZYNTEpLdjyJdu7R5gGUu +g1jSVK/EPHfc74O7AyZU34PNIP4Sh33N+/A5YexrNgJlPY+E3GdVYi4ldWJjgkAd +Qah2PH5ACLrIIC6tRka9hcaBlIECAwEAAaMgMB4wDAYDVR0TBAUwAwEB/zAOBgNV +HQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQELBQADgYEAHzC7jdYlzAVmddi/gdAeKPau +sPBG/C2HCWqHzpCUHcKuvMzDVkY/MP2o6JIW2DBbY64bO/FceExhjcykgaYtCH/m +oIU63+CFOTtR7otyQAWHqXa7q4SbCDlG7DyRFxqG0txPtGvy12lgldA2+RgcigQG +Dfcog5wrJytaQ6UA0wE= +-----END CERTIFICATE----- diff --git a/src/objective-c/tests/Tests.m b/src/objective-c/tests/Tests.m new file mode 100644 index 00000000..b821d387 --- /dev/null +++ b/src/objective-c/tests/Tests.m @@ -0,0 +1,40 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#import + +@interface Tests : NSObject +@end + +@implementation Tests +@end diff --git a/src/objective-c/tests/Tests.xcodeproj/project.pbxproj b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj new file mode 100644 index 00000000..3a1c3d94 --- /dev/null +++ b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj @@ -0,0 +1,454 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 6312AE4E1B1BF49B00341DEE /* GRPCClientTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6312AE4D1B1BF49B00341DEE /* GRPCClientTests.m */; }; + 63175DFF1B1B9FAF00027841 /* LocalClearTextTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 63175DFE1B1B9FAF00027841 /* LocalClearTextTests.m */; }; + 63423F4A1B150A5F006CF63C /* libTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 635697C71B14FC11007A7283 /* libTests.a */; }; + 63423F511B151B77006CF63C /* RxLibraryUnitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 63423F501B151B77006CF63C /* RxLibraryUnitTests.m */; }; + 635697CD1B14FC11007A7283 /* Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 635697CC1B14FC11007A7283 /* Tests.m */; }; + 635ED2EC1B1A3BC400FDE5C3 /* InteropTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 635ED2EB1B1A3BC400FDE5C3 /* InteropTests.m */; }; + 63715F561B780C020029CB0B /* InteropTestsLocalCleartext.m in Sources */ = {isa = PBXBuildFile; fileRef = 63715F551B780C020029CB0B /* InteropTestsLocalCleartext.m */; }; + 63E240CE1B6C4E2B005F3B0E /* InteropTestsLocalSSL.m in Sources */ = {isa = PBXBuildFile; fileRef = 63E240CD1B6C4E2B005F3B0E /* InteropTestsLocalSSL.m */; }; + 63E240D01B6C63DC005F3B0E /* TestCertificates.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 63E240CF1B6C63DC005F3B0E /* TestCertificates.bundle */; }; + 7D8A186224D39101F90230F6 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 35F2B6BF3BAE8F0DC4AFD76E /* libPods.a */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 63423F4B1B150A5F006CF63C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 635697BF1B14FC11007A7283 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 635697C61B14FC11007A7283; + remoteInfo = Tests; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 635697C51B14FC11007A7283 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "include/$(PRODUCT_NAME)"; + dstSubfolderSpec = 16; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 0A4F89D9C90E9C30990218F0 /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = ""; }; + 35F2B6BF3BAE8F0DC4AFD76E /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 6312AE4D1B1BF49B00341DEE /* GRPCClientTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GRPCClientTests.m; sourceTree = ""; }; + 63175DFE1B1B9FAF00027841 /* LocalClearTextTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LocalClearTextTests.m; sourceTree = ""; }; + 63423F441B150A5F006CF63C /* AllTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AllTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 63423F501B151B77006CF63C /* RxLibraryUnitTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RxLibraryUnitTests.m; sourceTree = ""; }; + 635697C71B14FC11007A7283 /* libTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libTests.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 635697CC1B14FC11007A7283 /* Tests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Tests.m; sourceTree = ""; }; + 635697D81B14FC11007A7283 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 635ED2EB1B1A3BC400FDE5C3 /* InteropTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InteropTests.m; sourceTree = ""; }; + 63715F551B780C020029CB0B /* InteropTestsLocalCleartext.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InteropTestsLocalCleartext.m; sourceTree = ""; }; + 63E240CC1B6C4D3A005F3B0E /* InteropTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InteropTests.h; sourceTree = ""; }; + 63E240CD1B6C4E2B005F3B0E /* InteropTestsLocalSSL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InteropTestsLocalSSL.m; sourceTree = ""; }; + 63E240CF1B6C63DC005F3B0E /* TestCertificates.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = TestCertificates.bundle; sourceTree = ""; }; + FF7B5489BCFE40111D768DD0 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 63423F411B150A5F006CF63C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 63423F4A1B150A5F006CF63C /* libTests.a in Frameworks */, + 7D8A186224D39101F90230F6 /* libPods.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 635697C41B14FC11007A7283 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 136D535E19727099B941D7B1 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 35F2B6BF3BAE8F0DC4AFD76E /* libPods.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + 51E4650F34F854F41FF053B3 /* Pods */ = { + isa = PBXGroup; + children = ( + FF7B5489BCFE40111D768DD0 /* Pods.debug.xcconfig */, + 0A4F89D9C90E9C30990218F0 /* Pods.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; + 635697BE1B14FC11007A7283 = { + isa = PBXGroup; + children = ( + 635697C91B14FC11007A7283 /* Tests */, + 63E240CF1B6C63DC005F3B0E /* TestCertificates.bundle */, + 635697C81B14FC11007A7283 /* Products */, + 51E4650F34F854F41FF053B3 /* Pods */, + 136D535E19727099B941D7B1 /* Frameworks */, + ); + sourceTree = ""; + }; + 635697C81B14FC11007A7283 /* Products */ = { + isa = PBXGroup; + children = ( + 635697C71B14FC11007A7283 /* libTests.a */, + 63423F441B150A5F006CF63C /* AllTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 635697C91B14FC11007A7283 /* Tests */ = { + isa = PBXGroup; + children = ( + 6312AE4D1B1BF49B00341DEE /* GRPCClientTests.m */, + 63E240CC1B6C4D3A005F3B0E /* InteropTests.h */, + 635ED2EB1B1A3BC400FDE5C3 /* InteropTests.m */, + 63E240CD1B6C4E2B005F3B0E /* InteropTestsLocalSSL.m */, + 63715F551B780C020029CB0B /* InteropTestsLocalCleartext.m */, + 63423F501B151B77006CF63C /* RxLibraryUnitTests.m */, + 63175DFE1B1B9FAF00027841 /* LocalClearTextTests.m */, + 635697CC1B14FC11007A7283 /* Tests.m */, + 635697D71B14FC11007A7283 /* Supporting Files */, + ); + name = Tests; + sourceTree = SOURCE_ROOT; + }; + 635697D71B14FC11007A7283 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 635697D81B14FC11007A7283 /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 63423F431B150A5F006CF63C /* AllTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 63423F4D1B150A5F006CF63C /* Build configuration list for PBXNativeTarget "AllTests" */; + buildPhases = ( + 914ADDD7106BA9BB8A7E569F /* Check Pods Manifest.lock */, + 63423F401B150A5F006CF63C /* Sources */, + 63423F411B150A5F006CF63C /* Frameworks */, + 63423F421B150A5F006CF63C /* Resources */, + A441F71824DCB9D0CA297748 /* Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + 63423F4C1B150A5F006CF63C /* PBXTargetDependency */, + ); + name = AllTests; + productName = AllTests; + productReference = 63423F441B150A5F006CF63C /* AllTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 635697C61B14FC11007A7283 /* Tests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 635697DB1B14FC11007A7283 /* Build configuration list for PBXNativeTarget "Tests" */; + buildPhases = ( + 635697C31B14FC11007A7283 /* Sources */, + 635697C41B14FC11007A7283 /* Frameworks */, + 635697C51B14FC11007A7283 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Tests; + productName = Tests; + productReference = 635697C71B14FC11007A7283 /* libTests.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 635697BF1B14FC11007A7283 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0630; + ORGANIZATIONNAME = gRPC; + TargetAttributes = { + 63423F431B150A5F006CF63C = { + CreatedOnToolsVersion = 6.3.1; + }; + 635697C61B14FC11007A7283 = { + CreatedOnToolsVersion = 6.3.1; + }; + }; + }; + buildConfigurationList = 635697C21B14FC11007A7283 /* Build configuration list for PBXProject "Tests" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 635697BE1B14FC11007A7283; + productRefGroup = 635697C81B14FC11007A7283 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 635697C61B14FC11007A7283 /* Tests */, + 63423F431B150A5F006CF63C /* AllTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 63423F421B150A5F006CF63C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 63E240D01B6C63DC005F3B0E /* TestCertificates.bundle in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 914ADDD7106BA9BB8A7E569F /* Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; + A441F71824DCB9D0CA297748 /* Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 63423F401B150A5F006CF63C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 63715F561B780C020029CB0B /* InteropTestsLocalCleartext.m in Sources */, + 63175DFF1B1B9FAF00027841 /* LocalClearTextTests.m in Sources */, + 63423F511B151B77006CF63C /* RxLibraryUnitTests.m in Sources */, + 63E240CE1B6C4E2B005F3B0E /* InteropTestsLocalSSL.m in Sources */, + 6312AE4E1B1BF49B00341DEE /* GRPCClientTests.m in Sources */, + 635ED2EC1B1A3BC400FDE5C3 /* InteropTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 635697C31B14FC11007A7283 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 635697CD1B14FC11007A7283 /* Tests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 63423F4C1B150A5F006CF63C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 635697C61B14FC11007A7283 /* Tests */; + targetProxy = 63423F4B1B150A5F006CF63C /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 63423F4E1B150A5F006CF63C /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = FF7B5489BCFE40111D768DD0 /* Pods.debug.xcconfig */; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + ); + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 63423F4F1B150A5F006CF63C /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 0A4F89D9C90E9C30990218F0 /* Pods.release.xcconfig */; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + ); + INFOPLIST_FILE = Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 635697D91B14FC11007A7283 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.3; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 635697DA1B14FC11007A7283 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.3; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 635697DC1B14FC11007A7283 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + 635697DD1B14FC11007A7283 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 63423F4D1B150A5F006CF63C /* Build configuration list for PBXNativeTarget "AllTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 63423F4E1B150A5F006CF63C /* Debug */, + 63423F4F1B150A5F006CF63C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 635697C21B14FC11007A7283 /* Build configuration list for PBXProject "Tests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 635697D91B14FC11007A7283 /* Debug */, + 635697DA1B14FC11007A7283 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 635697DB1B14FC11007A7283 /* Build configuration list for PBXNativeTarget "Tests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 635697DC1B14FC11007A7283 /* Debug */, + 635697DD1B14FC11007A7283 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 635697BF1B14FC11007A7283 /* Project object */; +} diff --git a/src/objective-c/tests/Tests.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/src/objective-c/tests/Tests.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..6c0ea849 --- /dev/null +++ b/src/objective-c/tests/Tests.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/AllTests.xcscheme b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/AllTests.xcscheme new file mode 100644 index 00000000..a7e0ed11 --- /dev/null +++ b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/AllTests.xcscheme @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/objective-c/tests/build_tests.sh b/src/objective-c/tests/build_tests.sh new file mode 100755 index 00000000..e7ad31e4 --- /dev/null +++ b/src/objective-c/tests/build_tests.sh @@ -0,0 +1,66 @@ +#!/bin/bash +# Copyright 2015, 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. + +# Don't run this script standalone. Instead, run from the repository root: +# ./tools/run_tests/run_tests.py -l objc + +set -e + +cd $(dirname $0) + +hash pod 2>/dev/null || { echo >&2 "Cocoapods needs to be installed."; exit 1; } +hash xcodebuild 2>/dev/null || { + echo >&2 "XCode command-line tools need to be installed." + exit 1 +} + +BINDIR=../../../bins/$CONFIG + +if [ ! -f $BINDIR/protobuf/protoc ]; then + hash protoc 2>/dev/null || { + echo >&2 "Can't find protoc. Make sure run_tests.py is making" \ + "grpc_objective_c_plugin before calling this script." + exit 1 + } + # When protoc is already installed, make doesn't compile one. Put a link + # there so the podspecs can do codegen using that path. + mkdir -p $BINDIR/protobuf + ln -s `which protoc` $BINDIR/protobuf/protoc +fi + +[ -f $BINDIR/interop_server ] || { + echo >&2 "Can't find the test server. Make sure run_tests.py is making" \ + "interop_server before calling this script. It needs to be done" \ + "before because pod install of gRPC renames some C gRPC files" \ + "and not the server's code references to them." + exit 1 +} + +pod install diff --git a/src/objective-c/tests/run_tests.sh b/src/objective-c/tests/run_tests.sh new file mode 100755 index 00000000..7b133c17 --- /dev/null +++ b/src/objective-c/tests/run_tests.sh @@ -0,0 +1,54 @@ +#!/bin/bash +# Copyright 2015, 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. + +# Don't run this script standalone. Instead, run from the repository root: +# ./tools/run_tests/run_tests.py -l objc + +set -e + +cd $(dirname $0) + +# Run the tests server. +../../../bins/$CONFIG/interop_server --port=5050 & +../../../bins/$CONFIG/interop_server --port=5051 --enable_ssl & +# Kill them when this script exits. +trap 'kill -9 `jobs -p`' EXIT + +# xcodebuild is very verbose. We filter its output and tell Bash to fail if any +# element of the pipe fails. +# TODO(jcanizales): Use xctool instead? Issue #2540. +set -o pipefail +XCODEBUILD_FILTER='(^===|^\*\*|\bfatal\b|\berror\b|\bwarning\b|\bfail)' +xcodebuild \ + -workspace Tests.xcworkspace \ + -scheme AllTests \ + -destination name="iPhone 6" \ + test \ + | egrep "$XCODEBUILD_FILTER" - diff --git a/src/php/.gitignore b/src/php/.gitignore new file mode 100644 index 00000000..ecde2ca4 --- /dev/null +++ b/src/php/.gitignore @@ -0,0 +1,22 @@ +.libs/ +build/ +modules/ +autom4te.cache/ +*.lo +*.la +.deps +acinclude.m4 +aclocal.m4 +config.* +configure* +Makefile* +run-tests.php + +install-sh +libtool +missing +mkinstalldirs + +ext/grpc/ltmain.sh +composer.lock +vendor/ diff --git a/src/php/README.md b/src/php/README.md new file mode 100644 index 00000000..a0542587 --- /dev/null +++ b/src/php/README.md @@ -0,0 +1,300 @@ + +#Overview + +This directory contains source code for PHP implementation of gRPC layered on shared C library. + +#Status + +Beta + +## Environment + +Prerequisite: PHP 5.5 or later, `phpunit`, `pecl` + +**Linux:** + +```sh +$ sudo apt-get install php5 php5-dev phpunit php-pear +``` + +**Mac OS X:** + +```sh +$ curl https://phar.phpunit.de/phpunit.phar -o phpunit.phar +$ chmod +x phpunit.phar +$ sudo mv phpunit.phar /usr/local/bin/phpunit + +$ curl -O http://pear.php.net/go-pear.phar +$ sudo php -d detect_unicode=0 go-pear.phar +``` + +## Quick Install + +**Linux (Debian):** + +Add [Debian jessie-backports][] to your `sources.list` file. Example: + +```sh +echo "deb http://http.debian.net/debian jessie-backports main" | \ +sudo tee -a /etc/apt/sources.list +``` + +Install the gRPC Debian package + +```sh +sudo apt-get update +sudo apt-get install libgrpc-dev +``` + +Install the gRPC PHP extension + +```sh +sudo pecl install grpc-beta +``` + +**Mac OS X:** + +Install [homebrew][]. Example: + +```sh +ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" +``` + +Install the gRPC core library and the PHP extension in one step + +```sh +$ curl -fsSL https://goo.gl/getgrpc | bash -s php +``` + +This will download and run the [gRPC install script][] and compile the gRPC PHP extension. + + +## Build from Source + +Clone this repository + +```sh +$ git clone https://github.com/grpc/grpc.git +``` + +Build and install the gRPC C core libraries + +```sh +$ cd grpc +$ git pull --recurse-submodules && git submodule update --init --recursive +$ make +$ sudo make install +``` + +Note: you may encounter a warning about the Protobuf compiler `protoc` 3.0.0+ not being installed. The following might help, and will be useful later on when we need to compile the `protoc-gen-php` tool. + +```sh +$ cd grpc/third_party/protobuf +$ sudo make install # 'make' should have been run by core grpc +``` + +Install the gRPC PHP extension + +```sh +$ sudo pecl install grpc-beta +``` + +OR + +```sh +$ cd grpc/src/php/ext/grpc +$ phpize +$ ./configure +$ make +$ sudo make install +``` + +Add this line to your `php.ini` file, e.g. `/etc/php5/cli/php.ini` + +```sh +extension=grpc.so +``` + +Install Composer + +```sh +$ cd grpc/src/php +$ curl -sS https://getcomposer.org/installer | php +$ sudo mv composer.phar /usr/local/bin/composer +$ composer install +``` + +## Unit Tests + +Run unit tests + +```sh +$ cd grpc/src/php +$ ./bin/run_tests.sh +``` + +## Generated Code Tests + +Install `protoc-gen-php` + +```sh +$ cd grpc/src/php/vendor/datto/protobuf-php +$ gem install rake ronn +$ rake pear:package version=1.0 +$ sudo pear install Protobuf-1.0.tgz +``` + +Generate client stub code + +```sh +$ cd grpc/src/php +$ ./bin/generate_proto_php.sh +``` + +Run a local server serving the math services + + - Please see [Node][] on how to run an example server + +```sh +$ cd grpc/src/node +$ npm install +$ nodejs examples/math_server.js +``` + +Run the generated code tests + +```sh +$ cd grpc/src/php +$ ./bin/run_gen_code_test.sh +``` + +## Use the gRPC PHP extension with Apache + +Install `apache2`, in addition to `php5` above + +```sh +$ sudo apt-get install apache2 +``` + +Add this line to your `php.ini` file, e.g. `/etc/php5/apache2/php.ini` + +```sh +extension=grpc.so +``` + +Restart apache + +```sh +$ sudo service apache2 restart +``` + +Make sure the Node math server is still running, as above. + +```sh +$ cd grpc/src/node +$ nodejs examples/math_server.js +``` + +Make sure you have run `composer install` to generate the `vendor/autoload.php` file + +```sh +$ composer install +``` + +Make sure you have generated the client stub `math.php` + +```sh +$ ./bin/generate_proto_php.sh +``` + +Copy the `math_client.php` file into your Apache document root, e.g. + +```sh +$ cp tests/generated_code/math_client.php /var/www/html +``` + +You may have to fix the first two lines to point the includes to your installation: + +```php +include 'vendor/autoload.php'; +include 'tests/generated_code/math.php'; +``` + +Connect to `localhost/math_client.php` in your browser, or run this from command line: + +```sh +$ curl localhost/math_client.php +``` + +## Use the gRPC PHP extension with Nginx/PHP-FPM + +Install `nginx` and `php5-fpm`, in addition to `php5` above + +```sh +$ sudo apt-get install nginx php5-fpm +``` + +Add this line to your `php.ini` file, e.g. `/etc/php5/fpm/php.ini` + +```sh +extension=grpc.so +``` + +Uncomment the following lines in your `/etc/nginx/sites-available/default` file: + +``` +location ~ \.php$ { + include snippets/fastcgi-php.conf; + fastcgi_pass unix:/var/run/php5-fpm.sock; +} +``` + +Restart nginx and php-fpm + +```sh +$ sudo service nginx restart +$ sudo service php5-fpm restart +``` + +Make sure the Node math server is still running, as above. + +```sh +$ cd grpc/src/node +$ nodejs examples/math_server.js +``` + +Make sure you have run `composer install` to generate the `vendor/autoload.php` file + +```sh +$ composer install +``` + +Make sure you have generated the client stub `math.php` + +```sh +$ ./bin/generate_proto_php.sh +``` + +Copy the `math_client.php` file into your Nginx document root, e.g. + +```sh +$ cp tests/generated_code/math_client.php /var/www/html +``` + +You may have to fix the first two lines to point the includes to your installation: + +```php +include 'vendor/autoload.php'; +include 'tests/generated_code/math.php'; +``` + +Connect to `localhost/math_client.php` in your browser, or run this from command line: + +```sh +$ curl localhost/math_client.php +``` + +[homebrew]:http://brew.sh +[gRPC install script]:https://raw.githubusercontent.com/grpc/homebrew-grpc/master/scripts/install +[Node]:https://github.com/grpc/grpc/tree/master/src/node/examples +[Debian jessie-backports]:http://backports.debian.org/Instructions/ diff --git a/src/php/bin/determine_extension_dir.sh b/src/php/bin/determine_extension_dir.sh new file mode 100755 index 00000000..b4342ac8 --- /dev/null +++ b/src/php/bin/determine_extension_dir.sh @@ -0,0 +1,51 @@ +#!/bin/bash +# Copyright 2015, 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. +set -e +default_extension_dir=$(php-config --extension-dir) +if command -v brew > /dev/null && \ + brew ls --versions | grep php5[56]-grpc > /dev/null; then + # the grpc php extension was installed by homebrew + : +elif [ ! -e $default_extension_dir/grpc.so ]; then + # the grpc extension is not found in the default PHP extension dir + # try the source modules directory + module_dir=../ext/grpc/modules + if [ ! -e $module_dir/grpc.so ]; then + echo "Please run 'phpize && ./configure && make' from ext/grpc first" + exit 1 + fi + # sym-link in system supplied extensions + for f in $default_extension_dir/*.so; do + ln -s $f $module_dir/$(basename $f) &> /dev/null || true + done + extension_dir="-d extension_dir=${module_dir} -d extension=grpc.so" +else + extension_dir="-d extension=grpc.so" +fi diff --git a/src/php/bin/generate_proto_php.sh b/src/php/bin/generate_proto_php.sh new file mode 100755 index 00000000..16f93747 --- /dev/null +++ b/src/php/bin/generate_proto_php.sh @@ -0,0 +1,40 @@ +#!/bin/sh +# Copyright 2015, 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. + + +set +e +cd $(dirname $0) + +gen_code='../tests/generated_code' +interop='../tests/interop' + +protoc-gen-php -i $gen_code -o $gen_code $gen_code/math.proto + +protoc-gen-php -i $interop -o $interop $interop/test.proto diff --git a/src/php/bin/interop_client.sh b/src/php/bin/interop_client.sh new file mode 100755 index 00000000..17b888dd --- /dev/null +++ b/src/php/bin/interop_client.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# Copyright 2015, 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. + +set -e +cd $(dirname $0) +source ./determine_extension_dir.sh +php $extension_dir \ + ../tests/interop/interop_client.php $@ 1>&2 diff --git a/src/php/bin/run_gen_code_test.sh b/src/php/bin/run_gen_code_test.sh new file mode 100755 index 00000000..6e56c720 --- /dev/null +++ b/src/php/bin/run_gen_code_test.sh @@ -0,0 +1,38 @@ +#!/bin/bash +# Copyright 2015, 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. + +set -e +cd $(dirname $0) +source ./determine_extension_dir.sh +export GRPC_TEST_HOST=localhost:50051 +php $extension_dir $(which phpunit) -v --debug --strict \ + ../tests/generated_code/GeneratedCodeTest.php +php $extension_dir $(which phpunit) -v --debug --strict \ + ../tests/generated_code/GeneratedCodeWithCallbackTest.php diff --git a/src/php/bin/run_tests.sh b/src/php/bin/run_tests.sh new file mode 100755 index 00000000..1fe68cda --- /dev/null +++ b/src/php/bin/run_tests.sh @@ -0,0 +1,41 @@ +#!/bin/bash +# Copyright 2015, 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. + +# Loads the local shared library, and runs all of the test cases in tests/ +# against it +set -e +cd $(dirname $0)/../../.. +root=$(pwd) +cd src/php/bin +source ./determine_extension_dir.sh +# in some jenkins macos machine, somehow the PHP build script can't find libgrpc.dylib +export DYLD_LIBRARY_PATH=$root/libs/$config +php $extension_dir $(which phpunit) -v --debug --strict \ + ../tests/unit_tests diff --git a/src/php/composer.json b/src/php/composer.json new file mode 100644 index 00000000..1d41f847 --- /dev/null +++ b/src/php/composer.json @@ -0,0 +1,23 @@ +{ + "name": "grpc/grpc", + "description": "gRPC library for PHP", + "version": "0.6.0", + "homepage": "http://grpc.io", + "license": "BSD-3-Clause", + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/stanley-cheung/Protobuf-PHP" + } + ], + "require": { + "php": ">=5.5.0", + "datto/protobuf-php": "dev-master", + "google/auth": "dev-master" + }, + "autoload": { + "psr-4": { + "Grpc\\": "lib/Grpc/" + } + } +} diff --git a/src/php/ext/grpc/CREDITS b/src/php/ext/grpc/CREDITS new file mode 100644 index 00000000..17b94fed --- /dev/null +++ b/src/php/ext/grpc/CREDITS @@ -0,0 +1,3 @@ +Michael Lumish (mlumish@google.com) +Tim Emiola (temiola@google.com) +Stanley Cheung (stanleycheung@google.com) diff --git a/src/php/ext/grpc/LICENSE b/src/php/ext/grpc/LICENSE new file mode 100644 index 00000000..704b5239 --- /dev/null +++ b/src/php/ext/grpc/LICENSE @@ -0,0 +1,32 @@ +/* + * + * Copyright 2015, 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. + * + */ diff --git a/src/php/ext/grpc/README.md b/src/php/ext/grpc/README.md new file mode 100644 index 00000000..6e1cb200 --- /dev/null +++ b/src/php/ext/grpc/README.md @@ -0,0 +1,67 @@ +gRPC PHP Extension +================== + +# Requirements + + * PHP 5.5+ + * [gRPC core library](https://github.com/grpc/grpc) 0.11.0 + +# Installation + +## Install PHP 5 + +``` +$ sudo apt-get install git php5 php5-dev php-pear unzip +``` + +## Compile gRPC Core Library + +Clone the gRPC source code repository + +``` +$ git clone https://github.com/grpc/grpc.git +``` + +Build and install the gRPC C core libraries + +```sh +$ cd grpc +$ git checkout --track origin/release-0_11 +$ git pull --recurse-submodules && git submodule update --init --recursive +$ make +$ sudo make install +``` + +Note: you may encounter a warning about the Protobuf compiler `protoc` 3.0.0+ not being installed. The following might help, and will be useful later on when we need to compile the `protoc-gen-php` tool. + +```sh +$ cd grpc/third_party/protobuf +$ sudo make install # 'make' should have been run by core grpc +``` + +## Install the gRPC PHP extension + +Quick install + +```sh +$ sudo pecl install grpc +``` + +Note: before a stable release, you may need to do + +```sh +$ sudo pecl install grpc-beta +``` + +OR + +Compile from source + +```sh +$ # from grpc +$ cd src/php/ext/grpc +$ phpize +$ ./configure +$ make +$ sudo make install +``` diff --git a/src/php/ext/grpc/byte_buffer.c b/src/php/ext/grpc/byte_buffer.c new file mode 100644 index 00000000..8be0a206 --- /dev/null +++ b/src/php/ext/grpc/byte_buffer.c @@ -0,0 +1,78 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include "php_grpc.h" + +#include + +#include "byte_buffer.h" + +#include +#include +#include + +grpc_byte_buffer *string_to_byte_buffer(char *string, size_t length) { + gpr_slice slice = gpr_slice_from_copied_buffer(string, length); + grpc_byte_buffer *buffer = grpc_raw_byte_buffer_create(&slice, 1); + gpr_slice_unref(slice); + return buffer; +} + +void byte_buffer_to_string(grpc_byte_buffer *buffer, char **out_string, + size_t *out_length) { + if (buffer == NULL) { + *out_string = NULL; + *out_length = 0; + return; + } + size_t length = grpc_byte_buffer_length(buffer); + char *string = ecalloc(length + 1, sizeof(char)); + size_t offset = 0; + grpc_byte_buffer_reader reader; + grpc_byte_buffer_reader_init(&reader, buffer); + gpr_slice next; + while (grpc_byte_buffer_reader_next(&reader, &next) != 0) { + memcpy(string + offset, GPR_SLICE_START_PTR(next), GPR_SLICE_LENGTH(next)); + offset += GPR_SLICE_LENGTH(next); + } + *out_string = string; + *out_length = length; +} diff --git a/src/php/ext/grpc/byte_buffer.h b/src/php/ext/grpc/byte_buffer.h new file mode 100644 index 00000000..0e9d1e71 --- /dev/null +++ b/src/php/ext/grpc/byte_buffer.h @@ -0,0 +1,44 @@ +/* + * + * Copyright 2015, 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 NET_GRPC_PHP_GRPC_BYTE_BUFFER_H_ +#define NET_GRPC_PHP_GRPC_BYTE_BUFFER_H_ + +#include + +grpc_byte_buffer *string_to_byte_buffer(char *string, size_t length); + +void byte_buffer_to_string(grpc_byte_buffer *buffer, char **out_string, + size_t *out_length); + +#endif /* NET_GRPC_PHP_GRPC_BYTE_BUFFER_H_ */ diff --git a/src/php/ext/grpc/call.c b/src/php/ext/grpc/call.c new file mode 100644 index 00000000..3b99de75 --- /dev/null +++ b/src/php/ext/grpc/call.c @@ -0,0 +1,530 @@ +/* + * + * Copyright 2015, 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 "call.h" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include "php_grpc.h" + +#include +#include + +#include + +#include +#include +#include + +#include "completion_queue.h" +#include "timeval.h" +#include "channel.h" +#include "byte_buffer.h" + +zend_class_entry *grpc_ce_call; + +/* Frees and destroys an instance of wrapped_grpc_call */ +void free_wrapped_grpc_call(void *object TSRMLS_DC) { + wrapped_grpc_call *call = (wrapped_grpc_call *)object; + if (call->owned && call->wrapped != NULL) { + grpc_call_destroy(call->wrapped); + } + efree(call); +} + +/* Initializes an instance of wrapped_grpc_call to be associated with an object + * of a class specified by class_type */ +zend_object_value create_wrapped_grpc_call(zend_class_entry *class_type + TSRMLS_DC) { + zend_object_value retval; + wrapped_grpc_call *intern; + + intern = (wrapped_grpc_call *)emalloc(sizeof(wrapped_grpc_call)); + memset(intern, 0, sizeof(wrapped_grpc_call)); + + zend_object_std_init(&intern->std, class_type TSRMLS_CC); + object_properties_init(&intern->std, class_type); + retval.handle = zend_objects_store_put( + intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, + free_wrapped_grpc_call, NULL TSRMLS_CC); + retval.handlers = zend_get_std_object_handlers(); + return retval; +} + +/* Wraps a grpc_call struct in a PHP object. Owned indicates whether the struct + should be destroyed at the end of the object's lifecycle */ +zval *grpc_php_wrap_call(grpc_call *wrapped, bool owned) { + zval *call_object; + MAKE_STD_ZVAL(call_object); + object_init_ex(call_object, grpc_ce_call); + wrapped_grpc_call *call = + (wrapped_grpc_call *)zend_object_store_get_object(call_object TSRMLS_CC); + call->wrapped = wrapped; + return call_object; +} + +/* Creates and returns a PHP array object with the data in a + * grpc_metadata_array. Returns NULL on failure */ +zval *grpc_parse_metadata_array(grpc_metadata_array *metadata_array) { + int count = metadata_array->count; + grpc_metadata *elements = metadata_array->metadata; + int i; + zval *array; + zval **data = NULL; + HashTable *array_hash; + zval *inner_array; + char *str_key; + char *str_val; + size_t key_len; + MAKE_STD_ZVAL(array); + array_init(array); + array_hash = Z_ARRVAL_P(array); + grpc_metadata *elem; + for (i = 0; i < count; i++) { + elem = &elements[i]; + key_len = strlen(elem->key); + str_key = ecalloc(key_len + 1, sizeof(char)); + memcpy(str_key, elem->key, key_len); + str_val = ecalloc(elem->value_length + 1, sizeof(char)); + memcpy(str_val, elem->value, elem->value_length); + if (zend_hash_find(array_hash, str_key, key_len, (void **)data) == + SUCCESS) { + if (Z_TYPE_P(*data) != IS_ARRAY) { + zend_throw_exception(zend_exception_get_default(), + "Metadata hash somehow contains wrong types.", + 1 TSRMLS_CC); + efree(str_key); + efree(str_val); + return NULL; + } + add_next_index_stringl(*data, str_val, elem->value_length, false); + } else { + MAKE_STD_ZVAL(inner_array); + array_init(inner_array); + add_next_index_stringl(inner_array, str_val, elem->value_length, false); + add_assoc_zval(array, str_key, inner_array); + } + } + return array; +} + +/* Populates a grpc_metadata_array with the data in a PHP array object. + Returns true on success and false on failure */ +bool create_metadata_array(zval *array, grpc_metadata_array *metadata) { + zval **inner_array; + zval **value; + HashTable *array_hash; + HashPosition array_pointer; + HashTable *inner_array_hash; + HashPosition inner_array_pointer; + char *key; + uint key_len; + ulong index; + if (Z_TYPE_P(array) != IS_ARRAY) { + return false; + } + grpc_metadata_array_init(metadata); + array_hash = Z_ARRVAL_P(array); + for (zend_hash_internal_pointer_reset_ex(array_hash, &array_pointer); + zend_hash_get_current_data_ex(array_hash, (void**)&inner_array, + &array_pointer) == SUCCESS; + zend_hash_move_forward_ex(array_hash, &array_pointer)) { + if (zend_hash_get_current_key_ex(array_hash, &key, &key_len, &index, 0, + &array_pointer) != HASH_KEY_IS_STRING) { + return false; + } + if (Z_TYPE_P(*inner_array) != IS_ARRAY) { + return false; + } + inner_array_hash = Z_ARRVAL_P(*inner_array); + metadata->capacity += zend_hash_num_elements(inner_array_hash); + } + metadata->metadata = gpr_malloc(metadata->capacity * sizeof(grpc_metadata)); + for (zend_hash_internal_pointer_reset_ex(array_hash, &array_pointer); + zend_hash_get_current_data_ex(array_hash, (void**)&inner_array, + &array_pointer) == SUCCESS; + zend_hash_move_forward_ex(array_hash, &array_pointer)) { + if (zend_hash_get_current_key_ex(array_hash, &key, &key_len, &index, 0, + &array_pointer) != HASH_KEY_IS_STRING) { + return false; + } + inner_array_hash = Z_ARRVAL_P(*inner_array); + for (zend_hash_internal_pointer_reset_ex(inner_array_hash, + &inner_array_pointer); + zend_hash_get_current_data_ex(inner_array_hash, (void**)&value, + &inner_array_pointer) == SUCCESS; + zend_hash_move_forward_ex(inner_array_hash, &inner_array_pointer)) { + if (Z_TYPE_P(*value) != IS_STRING) { + return false; + } + metadata->metadata[metadata->count].key = key; + metadata->metadata[metadata->count].value = Z_STRVAL_P(*value); + metadata->metadata[metadata->count].value_length = Z_STRLEN_P(*value); + metadata->count += 1; + } + } + return true; +} + +/** + * Constructs a new instance of the Call class. + * @param Channel $channel The channel to associate the call with. Must not be + * closed. + * @param string $method The method to call + * @param Timeval $absolute_deadline The deadline for completing the call + */ +PHP_METHOD(Call, __construct) { + wrapped_grpc_call *call = + (wrapped_grpc_call *)zend_object_store_get_object(getThis() TSRMLS_CC); + zval *channel_obj; + char *method; + int method_len; + zval *deadline_obj; + char *host_override = NULL; + int host_override_len = 0; + /* "OsO|s" == 1 Object, 1 string, 1 Object, 1 optional string */ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "OsO|s", + &channel_obj, grpc_ce_channel, + &method, &method_len, + &deadline_obj, grpc_ce_timeval, + &host_override, &host_override_len) + == FAILURE) { + zend_throw_exception( + spl_ce_InvalidArgumentException, + "Call expects a Channel, a String, a Timeval and an optional String", + 1 TSRMLS_CC); + return; + } + wrapped_grpc_channel *channel = + (wrapped_grpc_channel *)zend_object_store_get_object( + channel_obj TSRMLS_CC); + if (channel->wrapped == NULL) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "Call cannot be constructed from a closed Channel", + 1 TSRMLS_CC); + return; + } + add_property_zval(getThis(), "channel", channel_obj); + wrapped_grpc_timeval *deadline = + (wrapped_grpc_timeval *)zend_object_store_get_object( + deadline_obj TSRMLS_CC); + call->wrapped = grpc_channel_create_call( + channel->wrapped, NULL, GRPC_PROPAGATE_DEFAULTS, completion_queue, method, + host_override, deadline->wrapped, NULL); +} + +/** + * Start a batch of RPC actions. + * @param array batch Array of actions to take + * @return object Object with results of all actions + */ +PHP_METHOD(Call, startBatch) { + wrapped_grpc_call *call = + (wrapped_grpc_call *)zend_object_store_get_object(getThis() TSRMLS_CC); + grpc_op ops[8]; + size_t op_num = 0; + zval *array; + zval **value; + zval **inner_value; + HashTable *array_hash; + HashPosition array_pointer; + HashTable *status_hash; + HashTable *message_hash; + zval **message_value; + zval **message_flags; + char *key; + uint key_len; + ulong index; + grpc_metadata_array metadata; + grpc_metadata_array trailing_metadata; + grpc_metadata_array recv_metadata; + grpc_metadata_array recv_trailing_metadata; + grpc_status_code status; + char *status_details = NULL; + size_t status_details_capacity = 0; + grpc_byte_buffer *message; + int cancelled; + grpc_call_error error; + zval *result; + char *message_str; + size_t message_len; + zval *recv_status; + grpc_metadata_array_init(&metadata); + grpc_metadata_array_init(&trailing_metadata); + grpc_metadata_array_init(&recv_metadata); + grpc_metadata_array_init(&recv_trailing_metadata); + MAKE_STD_ZVAL(result); + object_init(result); + /* "a" == 1 array */ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array) == + FAILURE) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "start_batch expects an array", 1 TSRMLS_CC); + goto cleanup; + } + array_hash = Z_ARRVAL_P(array); + for (zend_hash_internal_pointer_reset_ex(array_hash, &array_pointer); + zend_hash_get_current_data_ex(array_hash, (void**)&value, + &array_pointer) == SUCCESS; + zend_hash_move_forward_ex(array_hash, &array_pointer)) { + if (zend_hash_get_current_key_ex(array_hash, &key, &key_len, &index, 0, + &array_pointer) != HASH_KEY_IS_LONG) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "batch keys must be integers", 1 TSRMLS_CC); + goto cleanup; + } + switch(index) { + case GRPC_OP_SEND_INITIAL_METADATA: + if (!create_metadata_array(*value, &metadata)) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "Bad metadata value given", 1 TSRMLS_CC); + goto cleanup; + } + ops[op_num].data.send_initial_metadata.count = + metadata.count; + ops[op_num].data.send_initial_metadata.metadata = + metadata.metadata; + break; + case GRPC_OP_SEND_MESSAGE: + if (Z_TYPE_PP(value) != IS_ARRAY) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "Expected an array for send message", + 1 TSRMLS_CC); + goto cleanup; + } + message_hash = Z_ARRVAL_PP(value); + if (zend_hash_find(message_hash, "flags", sizeof("flags"), + (void **)&message_flags) == SUCCESS) { + if (Z_TYPE_PP(message_flags) != IS_LONG) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "Expected an int for message flags", + 1 TSRMLS_CC); + } + ops[op_num].flags = Z_LVAL_PP(message_flags) & GRPC_WRITE_USED_MASK; + } + if (zend_hash_find(message_hash, "message", sizeof("message"), + (void **)&message_value) != SUCCESS || + Z_TYPE_PP(message_value) != IS_STRING) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "Expected a string for send message", + 1 TSRMLS_CC); + goto cleanup; + } + ops[op_num].data.send_message = + string_to_byte_buffer(Z_STRVAL_PP(message_value), + Z_STRLEN_PP(message_value)); + break; + case GRPC_OP_SEND_CLOSE_FROM_CLIENT: + break; + case GRPC_OP_SEND_STATUS_FROM_SERVER: + status_hash = Z_ARRVAL_PP(value); + if (zend_hash_find(status_hash, "metadata", sizeof("metadata"), + (void **)&inner_value) == SUCCESS) { + if (!create_metadata_array(*inner_value, &trailing_metadata)) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "Bad trailing metadata value given", + 1 TSRMLS_CC); + goto cleanup; + } + ops[op_num].data.send_status_from_server.trailing_metadata = + trailing_metadata.metadata; + ops[op_num].data.send_status_from_server.trailing_metadata_count = + trailing_metadata.count; + } + if (zend_hash_find(status_hash, "code", sizeof("code"), + (void**)&inner_value) == SUCCESS) { + if (Z_TYPE_PP(inner_value) != IS_LONG) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "Status code must be an integer", + 1 TSRMLS_CC); + goto cleanup; + } + ops[op_num].data.send_status_from_server.status = + Z_LVAL_PP(inner_value); + } else { + zend_throw_exception(spl_ce_InvalidArgumentException, + "Integer status code is required", + 1 TSRMLS_CC); + goto cleanup; + } + if (zend_hash_find(status_hash, "details", sizeof("details"), + (void**)&inner_value) == SUCCESS) { + if (Z_TYPE_PP(inner_value) != IS_STRING) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "Status details must be a string", + 1 TSRMLS_CC); + goto cleanup; + } + ops[op_num].data.send_status_from_server.status_details = + Z_STRVAL_PP(inner_value); + } else { + zend_throw_exception(spl_ce_InvalidArgumentException, + "String status details is required", + 1 TSRMLS_CC); + goto cleanup; + } + break; + case GRPC_OP_RECV_INITIAL_METADATA: + ops[op_num].data.recv_initial_metadata = &recv_metadata; + break; + case GRPC_OP_RECV_MESSAGE: + ops[op_num].data.recv_message = &message; + break; + case GRPC_OP_RECV_STATUS_ON_CLIENT: + ops[op_num].data.recv_status_on_client.trailing_metadata = + &recv_trailing_metadata; + ops[op_num].data.recv_status_on_client.status = &status; + ops[op_num].data.recv_status_on_client.status_details = + &status_details; + ops[op_num].data.recv_status_on_client.status_details_capacity = + &status_details_capacity; + break; + case GRPC_OP_RECV_CLOSE_ON_SERVER: + ops[op_num].data.recv_close_on_server.cancelled = &cancelled; + break; + default: + zend_throw_exception(spl_ce_InvalidArgumentException, + "Unrecognized key in batch", 1 TSRMLS_CC); + goto cleanup; + } + ops[op_num].op = (grpc_op_type)index; + ops[op_num].flags = 0; + ops[op_num].reserved = NULL; + op_num++; + } + error = grpc_call_start_batch(call->wrapped, ops, op_num, call->wrapped, + NULL); + if (error != GRPC_CALL_OK) { + zend_throw_exception(spl_ce_LogicException, + "start_batch was called incorrectly", + (long)error TSRMLS_CC); + goto cleanup; + } + grpc_completion_queue_pluck(completion_queue, call->wrapped, + gpr_inf_future(GPR_CLOCK_REALTIME), NULL); + for (int i = 0; i < op_num; i++) { + switch(ops[i].op) { + case GRPC_OP_SEND_INITIAL_METADATA: + add_property_bool(result, "send_metadata", true); + break; + case GRPC_OP_SEND_MESSAGE: + add_property_bool(result, "send_message", true); + break; + case GRPC_OP_SEND_CLOSE_FROM_CLIENT: + add_property_bool(result, "send_close", true); + break; + case GRPC_OP_SEND_STATUS_FROM_SERVER: + add_property_bool(result, "send_status", true); + break; + case GRPC_OP_RECV_INITIAL_METADATA: + array = grpc_parse_metadata_array(&recv_metadata); + add_property_zval(result, "metadata", array); + Z_DELREF_P(array); + break; + case GRPC_OP_RECV_MESSAGE: + byte_buffer_to_string(message, &message_str, &message_len); + if (message_str == NULL) { + add_property_null(result, "message"); + } else { + add_property_stringl(result, "message", message_str, message_len, + false); + } + break; + case GRPC_OP_RECV_STATUS_ON_CLIENT: + MAKE_STD_ZVAL(recv_status); + object_init(recv_status); + array = grpc_parse_metadata_array(&recv_trailing_metadata); + add_property_zval(recv_status, "metadata", array); + Z_DELREF_P(array); + add_property_long(recv_status, "code", status); + add_property_string(recv_status, "details", status_details, true); + add_property_zval(result, "status", recv_status); + Z_DELREF_P(recv_status); + break; + case GRPC_OP_RECV_CLOSE_ON_SERVER: + add_property_bool(result, "cancelled", cancelled); + break; + default: + break; + } + } +cleanup: + grpc_metadata_array_destroy(&metadata); + grpc_metadata_array_destroy(&trailing_metadata); + grpc_metadata_array_destroy(&recv_metadata); + grpc_metadata_array_destroy(&recv_trailing_metadata); + if (status_details != NULL) { + gpr_free(status_details); + } + RETURN_DESTROY_ZVAL(result); +} + +/** + * Get the endpoint this call/stream is connected to + * @return string The URI of the endpoint + */ +PHP_METHOD(Call, getPeer) { + wrapped_grpc_call *call = + (wrapped_grpc_call *)zend_object_store_get_object(getThis() TSRMLS_CC); + RETURN_STRING(grpc_call_get_peer(call->wrapped), 1); +} + +/** + * Cancel the call. This will cause the call to end with STATUS_CANCELLED if it + * has not already ended with another status. + */ +PHP_METHOD(Call, cancel) { + wrapped_grpc_call *call = + (wrapped_grpc_call *)zend_object_store_get_object(getThis() TSRMLS_CC); + grpc_call_cancel(call->wrapped, NULL); +} + +static zend_function_entry call_methods[] = { + PHP_ME(Call, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) + PHP_ME(Call, startBatch, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Call, getPeer, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Call, cancel, NULL, ZEND_ACC_PUBLIC) + PHP_FE_END}; + +void grpc_init_call(TSRMLS_D) { + zend_class_entry ce; + INIT_CLASS_ENTRY(ce, "Grpc\\Call", call_methods); + ce.create_object = create_wrapped_grpc_call; + grpc_ce_call = zend_register_internal_class(&ce TSRMLS_CC); +} diff --git a/src/php/ext/grpc/call.h b/src/php/ext/grpc/call.h new file mode 100644 index 00000000..f3ef89dc --- /dev/null +++ b/src/php/ext/grpc/call.h @@ -0,0 +1,69 @@ +/* + * + * Copyright 2015, 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 NET_GRPC_PHP_GRPC_CALL_H_ +#define NET_GRPC_PHP_GRPC_CALL_H_ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include "php_grpc.h" + +#include + +/* Class entry for the Call PHP class */ +extern zend_class_entry *grpc_ce_call; + +/* Wrapper struct for grpc_call that can be associated with a PHP object */ +typedef struct wrapped_grpc_call { + zend_object std; + + bool owned; + grpc_call *wrapped; +} wrapped_grpc_call; + +/* Initializes the Call PHP class */ +void grpc_init_call(TSRMLS_D); + +/* Creates a Call object that wraps the given grpc_call struct */ +zval *grpc_php_wrap_call(grpc_call *wrapped, bool owned); + +/* Creates and returns a PHP associative array of metadata from a C array of + * call metadata */ +zval *grpc_parse_metadata_array(grpc_metadata_array *metadata_array); + +#endif /* NET_GRPC_PHP_GRPC_CHANNEL_H_ */ diff --git a/src/php/ext/grpc/channel.c b/src/php/ext/grpc/channel.c new file mode 100644 index 00000000..a4313b6b --- /dev/null +++ b/src/php/ext/grpc/channel.c @@ -0,0 +1,266 @@ +/* + * + * Copyright 2015, 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 "channel.h" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include "php_grpc.h" + +#include + +#include + +#include +#include +#include + +#include "completion_queue.h" +#include "credentials.h" +#include "server.h" +#include "timeval.h" + +zend_class_entry *grpc_ce_channel; + +/* Frees and destroys an instance of wrapped_grpc_channel */ +void free_wrapped_grpc_channel(void *object TSRMLS_DC) { + wrapped_grpc_channel *channel = (wrapped_grpc_channel *)object; + if (channel->wrapped != NULL) { + grpc_channel_destroy(channel->wrapped); + } + efree(channel); +} + +/* Initializes an instance of wrapped_grpc_channel to be associated with an + * object of a class specified by class_type */ +zend_object_value create_wrapped_grpc_channel(zend_class_entry *class_type + TSRMLS_DC) { + zend_object_value retval; + wrapped_grpc_channel *intern; + intern = (wrapped_grpc_channel *)emalloc(sizeof(wrapped_grpc_channel)); + memset(intern, 0, sizeof(wrapped_grpc_channel)); + zend_object_std_init(&intern->std, class_type TSRMLS_CC); + object_properties_init(&intern->std, class_type); + retval.handle = zend_objects_store_put( + intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, + free_wrapped_grpc_channel, NULL TSRMLS_CC); + retval.handlers = zend_get_std_object_handlers(); + return retval; +} + +void php_grpc_read_args_array(zval *args_array, grpc_channel_args *args) { + HashTable *array_hash; + HashPosition array_pointer; + int args_index; + zval **data; + char *key; + uint key_len; + ulong index; + array_hash = Z_ARRVAL_P(args_array); + args->num_args = zend_hash_num_elements(array_hash); + args->args = ecalloc(args->num_args, sizeof(grpc_arg)); + args_index = 0; + for (zend_hash_internal_pointer_reset_ex(array_hash, &array_pointer); + zend_hash_get_current_data_ex(array_hash, (void **)&data, + &array_pointer) == SUCCESS; + zend_hash_move_forward_ex(array_hash, &array_pointer)) { + if (zend_hash_get_current_key_ex(array_hash, &key, &key_len, &index, 0, + &array_pointer) != HASH_KEY_IS_STRING) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "args keys must be strings", 1 TSRMLS_CC); + return; + } + args->args[args_index].key = key; + switch (Z_TYPE_P(*data)) { + case IS_LONG: + args->args[args_index].value.integer = (int)Z_LVAL_P(*data); + break; + case IS_STRING: + args->args[args_index].value.string = Z_STRVAL_P(*data); + break; + default: + zend_throw_exception(spl_ce_InvalidArgumentException, + "args values must be int or string", 1 TSRMLS_CC); + return; + } + args_index++; + } +} + +/** + * Construct an instance of the Channel class. If the $args array contains a + * "credentials" key mapping to a Credentials object, a secure channel will be + * created with those credentials. + * @param string $target The hostname to associate with this channel + * @param array $args The arguments to pass to the Channel (optional) + */ +PHP_METHOD(Channel, __construct) { + wrapped_grpc_channel *channel = + (wrapped_grpc_channel *)zend_object_store_get_object(getThis() TSRMLS_CC); + char *target; + int target_length; + zval *args_array = NULL; + grpc_channel_args args; + HashTable *array_hash; + zval **creds_obj = NULL; + wrapped_grpc_credentials *creds = NULL; + /* "s|a" == 1 string, 1 optional array */ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|a", &target, + &target_length, &args_array) == FAILURE) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "Channel expects a string and an array", 1 TSRMLS_CC); + return; + } + if (args_array == NULL) { + channel->wrapped = grpc_insecure_channel_create(target, NULL, NULL); + } else { + array_hash = Z_ARRVAL_P(args_array); + if (zend_hash_find(array_hash, "credentials", sizeof("credentials"), + (void **)&creds_obj) == SUCCESS) { + if (zend_get_class_entry(*creds_obj TSRMLS_CC) != grpc_ce_credentials) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "credentials must be a Credentials object", + 1 TSRMLS_CC); + return; + } + creds = (wrapped_grpc_credentials *)zend_object_store_get_object( + *creds_obj TSRMLS_CC); + zend_hash_del(array_hash, "credentials", 12); + } + php_grpc_read_args_array(args_array, &args); + if (creds == NULL) { + channel->wrapped = grpc_insecure_channel_create(target, &args, NULL); + } else { + gpr_log(GPR_DEBUG, "Initialized secure channel"); + channel->wrapped = + grpc_secure_channel_create(creds->wrapped, target, &args, NULL); + } + efree(args.args); + } +} + +/** + * Get the endpoint this call/stream is connected to + * @return string The URI of the endpoint + */ +PHP_METHOD(Channel, getTarget) { + wrapped_grpc_channel *channel = + (wrapped_grpc_channel *)zend_object_store_get_object(getThis() TSRMLS_CC); + RETURN_STRING(grpc_channel_get_target(channel->wrapped), 1); +} + +/** + * Get the connectivity state of the channel + * @param bool (optional) try to connect on the channel + * @return long The grpc connectivity state + */ +PHP_METHOD(Channel, getConnectivityState) { + wrapped_grpc_channel *channel = + (wrapped_grpc_channel *)zend_object_store_get_object(getThis() TSRMLS_CC); + bool try_to_connect; + /* "|b" == 1 optional bool */ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &try_to_connect) == + FAILURE) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "getConnectivityState expects a bool", 1 TSRMLS_CC); + return; + } + RETURN_LONG(grpc_channel_check_connectivity_state(channel->wrapped, + (int)try_to_connect)); +} + +/** + * Watch the connectivity state of the channel until it changed + * @param long The previous connectivity state of the channel + * @param Timeval The deadline this function should wait until + * @return bool If the connectivity state changes from last_state + * before deadline + */ +PHP_METHOD(Channel, watchConnectivityState) { + wrapped_grpc_channel *channel = + (wrapped_grpc_channel *)zend_object_store_get_object(getThis() TSRMLS_CC); + long last_state; + zval *deadline_obj; + /* "lO" == 1 long 1 object */ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lO", + &last_state, &deadline_obj, grpc_ce_timeval) == FAILURE) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "watchConnectivityState expects 1 long 1 timeval", + 1 TSRMLS_CC); + return; + } + + wrapped_grpc_timeval *deadline = + (wrapped_grpc_timeval *)zend_object_store_get_object( + deadline_obj TSRMLS_CC); + grpc_channel_watch_connectivity_state( + channel->wrapped, (grpc_connectivity_state)last_state, + deadline->wrapped, completion_queue, NULL); + grpc_event event = grpc_completion_queue_pluck( + completion_queue, NULL, + gpr_inf_future(GPR_CLOCK_REALTIME), NULL); + RETURN_BOOL(event.success); +} + +/** + * Close the channel + */ +PHP_METHOD(Channel, close) { + wrapped_grpc_channel *channel = + (wrapped_grpc_channel *)zend_object_store_get_object(getThis() TSRMLS_CC); + if (channel->wrapped != NULL) { + grpc_channel_destroy(channel->wrapped); + channel->wrapped = NULL; + } +} + +static zend_function_entry channel_methods[] = { + PHP_ME(Channel, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) + PHP_ME(Channel, getTarget, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Channel, getConnectivityState, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Channel, watchConnectivityState, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Channel, close, NULL, ZEND_ACC_PUBLIC) + PHP_FE_END}; + +void grpc_init_channel(TSRMLS_D) { + zend_class_entry ce; + INIT_CLASS_ENTRY(ce, "Grpc\\Channel", channel_methods); + ce.create_object = create_wrapped_grpc_channel; + grpc_ce_channel = zend_register_internal_class(&ce TSRMLS_CC); +} diff --git a/src/php/ext/grpc/channel.h b/src/php/ext/grpc/channel.h new file mode 100755 index 00000000..78a16ed0 --- /dev/null +++ b/src/php/ext/grpc/channel.h @@ -0,0 +1,64 @@ +/* + * + * Copyright 2015, 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 NET_GRPC_PHP_GRPC_CHANNEL_H_ +#define NET_GRPC_PHP_GRPC_CHANNEL_H_ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include "php_grpc.h" + +#include + +/* Class entry for the PHP Channel class */ +extern zend_class_entry *grpc_ce_channel; + +/* Wrapper struct for grpc_channel that can be associated with a PHP object */ +typedef struct wrapped_grpc_channel { + zend_object std; + + grpc_channel *wrapped; +} wrapped_grpc_channel; + +/* Initializes the Channel class */ +void grpc_init_channel(TSRMLS_D); + +/* Iterates through a PHP array and populates args with the contents */ +void php_grpc_read_args_array(zval *args_array, grpc_channel_args *args); + +#endif /* NET_GRPC_PHP_GRPC_CHANNEL_H_ */ diff --git a/src/php/ext/grpc/completion_queue.c b/src/php/ext/grpc/completion_queue.c new file mode 100644 index 00000000..741204b0 --- /dev/null +++ b/src/php/ext/grpc/completion_queue.c @@ -0,0 +1,50 @@ +/* + * + * Copyright 2015, 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 "completion_queue.h" + +#include + +grpc_completion_queue *completion_queue; + +void grpc_php_init_completion_queue(TSRMLS_D) { + completion_queue = grpc_completion_queue_create(NULL); +} + +void grpc_php_shutdown_completion_queue(TSRMLS_D) { + grpc_completion_queue_shutdown(completion_queue); + while (grpc_completion_queue_next(completion_queue, + gpr_inf_future(GPR_CLOCK_REALTIME), + NULL).type != GRPC_QUEUE_SHUTDOWN); + grpc_completion_queue_destroy(completion_queue); +} diff --git a/src/php/ext/grpc/completion_queue.h b/src/php/ext/grpc/completion_queue.h new file mode 100644 index 00000000..d5dac4f0 --- /dev/null +++ b/src/php/ext/grpc/completion_queue.h @@ -0,0 +1,50 @@ +/* + * + * Copyright 2015, 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_PHP_GRPC_COMPLETION_QUEUE_H_ +#define GRPC_PHP_GRPC_COMPLETION_QUEUE_H_ + +#include + +#include + +/* The global completion queue for all operations */ +extern grpc_completion_queue *completion_queue; + +/* Initializes the completion queue */ +void grpc_php_init_completion_queue(TSRMLS_D); + +/* Shut down the completion queue */ +void grpc_php_shutdown_completion_queue(TSRMLS_D); + +#endif /* GRPC_PHP_GRPC_COMPLETION_QUEUE_H_ */ diff --git a/src/php/ext/grpc/config.m4 b/src/php/ext/grpc/config.m4 new file mode 100755 index 00000000..8bacdfbf --- /dev/null +++ b/src/php/ext/grpc/config.m4 @@ -0,0 +1,75 @@ +PHP_ARG_ENABLE(grpc, whether to enable grpc support, +[ --enable-grpc Enable grpc support]) + +if test "$PHP_GRPC" != "no"; then + dnl Write more examples of tests here... + + dnl # --with-grpc -> check with-path + SEARCH_PATH="/usr/local /usr" # you might want to change this + SEARCH_FOR="include/grpc/grpc.h" # you most likely want to change this + if test -r $PHP_GRPC/$SEARCH_FOR; then # path given as parameter + GRPC_DIR=$PHP_GRPC + else # search default path list + AC_MSG_CHECKING([for grpc files in default path]) + for i in $SEARCH_PATH ; do + if test -r $i/$SEARCH_FOR; then + GRPC_DIR=$i + AC_MSG_RESULT(found in $i) + fi + done + fi + if test -z "$GRPC_DIR"; then + AC_MSG_RESULT([not found]) + AC_MSG_ERROR([Please reinstall the grpc distribution]) + fi + + dnl # --with-grpc -> add include path + PHP_ADD_INCLUDE($GRPC_DIR/include) + + LIBS="-lpthread $LIBS" + + dnl PHP_ADD_LIBRARY(pthread,,GRPC_SHARED_LIBADD) + GRPC_SHARED_LIBADD="-lpthread $GRPC_SHARED_LIBADD" + PHP_ADD_LIBRARY(pthread) + + PHP_ADD_LIBRARY(dl,,GRPC_SHARED_LIBADD) + PHP_ADD_LIBRARY(dl) + + case $host in + *darwin*) ;; + *) + PHP_ADD_LIBRARY(rt,,GRPC_SHARED_LIBADD) + PHP_ADD_LIBRARY(rt) + ;; + esac + + GRPC_LIBDIR=$GRPC_DIR/${GRPC_LIB_SUBDIR-lib} + + PHP_ADD_LIBPATH($GRPC_LIBDIR) + + PHP_CHECK_LIBRARY(gpr,gpr_now, + [ + PHP_ADD_LIBRARY(gpr,,GRPC_SHARED_LIBADD) + PHP_ADD_LIBRARY(gpr) + AC_DEFINE(HAVE_GPRLIB,1,[ ]) + ],[ + AC_MSG_ERROR([wrong gpr lib version or lib not found]) + ],[ + -L$GRPC_LIBDIR + ]) + + PHP_CHECK_LIBRARY(grpc,grpc_channel_destroy, + [ + PHP_ADD_LIBRARY(grpc,,GRPC_SHARED_LIBADD) + dnl PHP_ADD_LIBRARY_WITH_PATH(grpc, $GRPC_DIR/lib, GRPC_SHARED_LIBADD) + AC_DEFINE(HAVE_GRPCLIB,1,[ ]) + ],[ + AC_MSG_ERROR([wrong grpc lib version or lib not found]) + ],[ + -L$GRPC_LIBDIR + ]) + + PHP_SUBST(GRPC_SHARED_LIBADD) + + PHP_NEW_EXTENSION(grpc, byte_buffer.c call.c channel.c completion_queue.c credentials.c timeval.c server.c server_credentials.c php_grpc.c, $ext_shared, , -Wall -Werror -std=c11) +fi diff --git a/src/php/ext/grpc/credentials.c b/src/php/ext/grpc/credentials.c new file mode 100644 index 00000000..8e3b7ff2 --- /dev/null +++ b/src/php/ext/grpc/credentials.c @@ -0,0 +1,191 @@ +/* + * + * Copyright 2015, 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 "credentials.h" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include "php_grpc.h" + +#include +#include + +#include +#include + +zend_class_entry *grpc_ce_credentials; + +/* Frees and destroys an instance of wrapped_grpc_credentials */ +void free_wrapped_grpc_credentials(void *object TSRMLS_DC) { + wrapped_grpc_credentials *creds = (wrapped_grpc_credentials *)object; + if (creds->wrapped != NULL) { + grpc_credentials_release(creds->wrapped); + } + efree(creds); +} + +/* Initializes an instance of wrapped_grpc_credentials to be associated with an + * object of a class specified by class_type */ +zend_object_value create_wrapped_grpc_credentials(zend_class_entry *class_type + TSRMLS_DC) { + zend_object_value retval; + wrapped_grpc_credentials *intern; + + intern = + (wrapped_grpc_credentials *)emalloc(sizeof(wrapped_grpc_credentials)); + memset(intern, 0, sizeof(wrapped_grpc_credentials)); + + zend_object_std_init(&intern->std, class_type TSRMLS_CC); + object_properties_init(&intern->std, class_type); + retval.handle = zend_objects_store_put( + intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, + free_wrapped_grpc_credentials, NULL TSRMLS_CC); + retval.handlers = zend_get_std_object_handlers(); + return retval; +} + +zval *grpc_php_wrap_credentials(grpc_credentials *wrapped) { + zval *credentials_object; + MAKE_STD_ZVAL(credentials_object); + object_init_ex(credentials_object, grpc_ce_credentials); + wrapped_grpc_credentials *credentials = + (wrapped_grpc_credentials *)zend_object_store_get_object( + credentials_object TSRMLS_CC); + credentials->wrapped = wrapped; + return credentials_object; +} + +/** + * Create a default credentials object. + * @return Credentials The new default credentials object + */ +PHP_METHOD(Credentials, createDefault) { + grpc_credentials *creds = grpc_google_default_credentials_create(); + zval *creds_object = grpc_php_wrap_credentials(creds); + RETURN_DESTROY_ZVAL(creds_object); +} + +/** + * Create SSL credentials. + * @param string pem_root_certs PEM encoding of the server root certificates + * @param string pem_private_key PEM encoding of the client's private key + * (optional) + * @param string pem_cert_chain PEM encoding of the client's certificate chain + * (optional) + * @return Credentials The new SSL credentials object + */ +PHP_METHOD(Credentials, createSsl) { + char *pem_root_certs; + grpc_ssl_pem_key_cert_pair pem_key_cert_pair; + + int root_certs_length, private_key_length = 0, cert_chain_length = 0; + + pem_key_cert_pair.private_key = pem_key_cert_pair.cert_chain = NULL; + + /* "s|s!s! == 1 string, 2 optional nullable strings */ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s!s!", + &pem_root_certs, &root_certs_length, + &pem_key_cert_pair.private_key, &private_key_length, + &pem_key_cert_pair.cert_chain, + &cert_chain_length) == FAILURE) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "createSsl expects 1 to 3 strings", 1 TSRMLS_CC); + return; + } + grpc_credentials *creds = grpc_ssl_credentials_create( + pem_root_certs, + pem_key_cert_pair.private_key == NULL ? NULL : &pem_key_cert_pair, NULL); + zval *creds_object = grpc_php_wrap_credentials(creds); + RETURN_DESTROY_ZVAL(creds_object); +} + +/** + * Create composite credentials from two existing credentials. + * @param Credentials cred1 The first credential + * @param Credentials cred2 The second credential + * @return Credentials The new composite credentials object + */ +PHP_METHOD(Credentials, createComposite) { + zval *cred1_obj; + zval *cred2_obj; + + /* "OO" == 3 Objects */ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "OO", &cred1_obj, + grpc_ce_credentials, &cred2_obj, + grpc_ce_credentials) == FAILURE) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "createComposite expects 2 Credentials", 1 TSRMLS_CC); + return; + } + wrapped_grpc_credentials *cred1 = + (wrapped_grpc_credentials *)zend_object_store_get_object( + cred1_obj TSRMLS_CC); + wrapped_grpc_credentials *cred2 = + (wrapped_grpc_credentials *)zend_object_store_get_object( + cred2_obj TSRMLS_CC); + grpc_credentials *creds = + grpc_composite_credentials_create(cred1->wrapped, cred2->wrapped, NULL); + zval *creds_object = grpc_php_wrap_credentials(creds); + RETURN_DESTROY_ZVAL(creds_object); +} + +/** + * Create Google Compute Engine credentials + * @return Credentials The new GCE credentials object + */ +PHP_METHOD(Credentials, createGce) { + grpc_credentials *creds = grpc_google_compute_engine_credentials_create(NULL); + zval *creds_object = grpc_php_wrap_credentials(creds); + RETURN_DESTROY_ZVAL(creds_object); +} + +static zend_function_entry credentials_methods[] = { + PHP_ME(Credentials, createDefault, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(Credentials, createSsl, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(Credentials, createComposite, NULL, + ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(Credentials, createGce, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_FE_END}; + +void grpc_init_credentials(TSRMLS_D) { + zend_class_entry ce; + INIT_CLASS_ENTRY(ce, "Grpc\\Credentials", credentials_methods); + ce.create_object = create_wrapped_grpc_credentials; + grpc_ce_credentials = zend_register_internal_class(&ce TSRMLS_CC); +} diff --git a/src/php/ext/grpc/credentials.h b/src/php/ext/grpc/credentials.h new file mode 100755 index 00000000..86d7ae5b --- /dev/null +++ b/src/php/ext/grpc/credentials.h @@ -0,0 +1,63 @@ +/* + * + * Copyright 2015, 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 NET_GRPC_PHP_GRPC_CREDENTIALS_H_ +#define NET_GRPC_PHP_GRPC_CREDENTIALS_H_ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "php_grpc.h" + +#include "grpc/grpc.h" +#include "grpc/grpc_security.h" + +/* Class entry for the Credentials PHP class */ +extern zend_class_entry *grpc_ce_credentials; + +/* Wrapper struct for grpc_credentials that can be associated with a PHP + * object */ +typedef struct wrapped_grpc_credentials { + zend_object std; + + grpc_credentials *wrapped; +} wrapped_grpc_credentials; + +/* Initializes the Credentials PHP class */ +void grpc_init_credentials(TSRMLS_D); + +#endif /* NET_GRPC_PHP_GRPC_CREDENTIALS_H_ */ diff --git a/src/php/ext/grpc/package.xml b/src/php/ext/grpc/package.xml new file mode 100644 index 00000000..381ae23b --- /dev/null +++ b/src/php/ext/grpc/package.xml @@ -0,0 +1,122 @@ + + + grpc + pecl.php.net + A high performance, open source, general RPC framework that puts mobile and HTTP/2 first. + Remote Procedure Calls (RPCs) provide a useful abstraction for building distributed applications and services. The libraries in this repository provide a concrete implementation of the gRPC protocol, layered over HTTP/2. These libraries enable communication between clients and servers using any combination of the supported languages. + + Stanley Cheung + stanleycheung + grpc-packages@google.com + yes + + 2015-09-01 + + + 0.6.0 + 0.6.0 + + + beta + beta + + BSD + + - support per message compression disable + - expose per-call host override option + - expose connectivity API + - expose channel target and call peer + - add user-agent + - update to wrap gRPC C core library beta version 0.11.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5.5.0 + + + 1.4.0 + + + + grpc + + + + + 0.5.0 + 0.5.0 + + + alpha + alpha + + 2015-06-16 + BSD + +First alpha release + + + + + 0.5.1 + 0.5.1 + + + alpha + alpha + + 2015-07-09 + BSD + +Update to wrap gRPC C Core version 0.10.0 + + + + + 0.6.0 + 0.6.0 + + + beta + beta + + 2015-09-01 + BSD + + - support per message compression disable + - expose per-call host override option + - expose connectivity API + - expose channel target and call peer + - add user-agent + - update to wrap gRPC C core library beta version 0.11.0 + + + + diff --git a/src/php/ext/grpc/php_grpc.c b/src/php/ext/grpc/php_grpc.c new file mode 100644 index 00000000..0f730ea7 --- /dev/null +++ b/src/php/ext/grpc/php_grpc.c @@ -0,0 +1,247 @@ +/* + * + * Copyright 2015, 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 "call.h" +#include "channel.h" +#include "server.h" +#include "timeval.h" +#include "credentials.h" +#include "server_credentials.h" +#include "completion_queue.h" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include "php_grpc.h" + +// ZEND_DECLARE_MODULE_GLOBALS(grpc) + +/* {{{ grpc_functions[] + * + * Every user visible function must have an entry in grpc_functions[]. + */ +const zend_function_entry grpc_functions[] = { + PHP_FE_END /* Must be the last line in grpc_functions[] */ +}; +/* }}} */ + +/* {{{ grpc_module_entry + */ +zend_module_entry grpc_module_entry = { +#if ZEND_MODULE_API_NO >= 20010901 + STANDARD_MODULE_HEADER, +#endif + "grpc", grpc_functions, PHP_MINIT(grpc), + PHP_MSHUTDOWN(grpc), NULL, NULL, + PHP_MINFO(grpc), +#if ZEND_MODULE_API_NO >= 20010901 + PHP_GRPC_VERSION, +#endif + STANDARD_MODULE_PROPERTIES}; +/* }}} */ + +#ifdef COMPILE_DL_GRPC +ZEND_GET_MODULE(grpc) +#endif + +/* {{{ PHP_INI + */ +/* Remove comments and fill if you need to have entries in php.ini +PHP_INI_BEGIN() + STD_PHP_INI_ENTRY("grpc.global_value", "42", PHP_INI_ALL, OnUpdateLong, +global_value, zend_grpc_globals, grpc_globals) + STD_PHP_INI_ENTRY("grpc.global_string", "foobar", PHP_INI_ALL, +OnUpdateString, global_string, zend_grpc_globals, grpc_globals) +PHP_INI_END() +*/ +/* }}} */ + +/* {{{ php_grpc_init_globals + */ +/* Uncomment this function if you have INI entries +static void php_grpc_init_globals(zend_grpc_globals *grpc_globals) +{ + grpc_globals->global_value = 0; + grpc_globals->global_string = NULL; +} +*/ +/* }}} */ + +/* {{{ PHP_MINIT_FUNCTION + */ +PHP_MINIT_FUNCTION(grpc) { + /* If you have INI entries, uncomment these lines + REGISTER_INI_ENTRIES(); + */ + /* Register call error constants */ + grpc_init(); + REGISTER_LONG_CONSTANT("Grpc\\CALL_OK", GRPC_CALL_OK, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR", GRPC_CALL_ERROR, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_NOT_ON_SERVER", + GRPC_CALL_ERROR_NOT_ON_SERVER, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_NOT_ON_CLIENT", + GRPC_CALL_ERROR_NOT_ON_CLIENT, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_ALREADY_INVOKED", + GRPC_CALL_ERROR_ALREADY_INVOKED, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_NOT_INVOKED", + GRPC_CALL_ERROR_NOT_INVOKED, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_ALREADY_FINISHED", + GRPC_CALL_ERROR_ALREADY_FINISHED, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_TOO_MANY_OPERATIONS", + GRPC_CALL_ERROR_TOO_MANY_OPERATIONS, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_INVALID_FLAGS", + GRPC_CALL_ERROR_INVALID_FLAGS, CONST_CS); + + /* Register flag constants */ + REGISTER_LONG_CONSTANT("Grpc\\WRITE_BUFFER_HINT", GRPC_WRITE_BUFFER_HINT, + CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\WRITE_NO_COMPRESS", GRPC_WRITE_NO_COMPRESS, + CONST_CS); + + /* Register status constants */ + REGISTER_LONG_CONSTANT("Grpc\\STATUS_OK", GRPC_STATUS_OK, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\STATUS_CANCELLED", GRPC_STATUS_CANCELLED, + CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNKNOWN", GRPC_STATUS_UNKNOWN, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\STATUS_INVALID_ARGUMENT", + GRPC_STATUS_INVALID_ARGUMENT, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\STATUS_DEADLINE_EXCEEDED", + GRPC_STATUS_DEADLINE_EXCEEDED, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\STATUS_NOT_FOUND", GRPC_STATUS_NOT_FOUND, + CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\STATUS_ALREADY_EXISTS", + GRPC_STATUS_ALREADY_EXISTS, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\STATUS_PERMISSION_DENIED", + GRPC_STATUS_PERMISSION_DENIED, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNAUTHENTICATED", + GRPC_STATUS_UNAUTHENTICATED, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\STATUS_RESOURCE_EXHAUSTED", + GRPC_STATUS_RESOURCE_EXHAUSTED, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\STATUS_FAILED_PRECONDITION", + GRPC_STATUS_FAILED_PRECONDITION, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\STATUS_ABORTED", GRPC_STATUS_ABORTED, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\STATUS_OUT_OF_RANGE", GRPC_STATUS_OUT_OF_RANGE, + CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNIMPLEMENTED", + GRPC_STATUS_UNIMPLEMENTED, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\STATUS_INTERNAL", GRPC_STATUS_INTERNAL, + CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNAVAILABLE", GRPC_STATUS_UNAVAILABLE, + CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\STATUS_DATA_LOSS", GRPC_STATUS_DATA_LOSS, + CONST_CS); + + /* Register op type constants */ + REGISTER_LONG_CONSTANT("Grpc\\OP_SEND_INITIAL_METADATA", + GRPC_OP_SEND_INITIAL_METADATA, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\OP_SEND_MESSAGE", + GRPC_OP_SEND_MESSAGE, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\OP_SEND_CLOSE_FROM_CLIENT", + GRPC_OP_SEND_CLOSE_FROM_CLIENT, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\OP_SEND_STATUS_FROM_SERVER", + GRPC_OP_SEND_STATUS_FROM_SERVER, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\OP_RECV_INITIAL_METADATA", + GRPC_OP_RECV_INITIAL_METADATA, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\OP_RECV_MESSAGE", + GRPC_OP_RECV_MESSAGE, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\OP_RECV_STATUS_ON_CLIENT", + GRPC_OP_RECV_STATUS_ON_CLIENT, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\OP_RECV_CLOSE_ON_SERVER", + GRPC_OP_RECV_CLOSE_ON_SERVER, CONST_CS); + + /* Register connectivity state constants */ + REGISTER_LONG_CONSTANT("Grpc\\CHANNEL_IDLE", + GRPC_CHANNEL_IDLE, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\CHANNEL_CONNECTING", + GRPC_CHANNEL_CONNECTING, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\CHANNEL_READY", + GRPC_CHANNEL_READY, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\CHANNEL_TRANSIENT_FAILURE", + GRPC_CHANNEL_TRANSIENT_FAILURE, CONST_CS); + REGISTER_LONG_CONSTANT("Grpc\\CHANNEL_FATAL_FAILURE", + GRPC_CHANNEL_FATAL_FAILURE, CONST_CS); + + grpc_init_call(TSRMLS_C); + grpc_init_channel(TSRMLS_C); + grpc_init_server(TSRMLS_C); + grpc_init_timeval(TSRMLS_C); + grpc_init_credentials(TSRMLS_C); + grpc_init_server_credentials(TSRMLS_C); + grpc_php_init_completion_queue(TSRMLS_C); + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MSHUTDOWN_FUNCTION + */ +PHP_MSHUTDOWN_FUNCTION(grpc) { + /* uncomment this line if you have INI entries + UNREGISTER_INI_ENTRIES(); + */ + grpc_shutdown_timeval(TSRMLS_C); + grpc_php_shutdown_completion_queue(TSRMLS_C); + grpc_shutdown(); + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MINFO_FUNCTION + */ +PHP_MINFO_FUNCTION(grpc) { + php_info_print_table_start(); + php_info_print_table_header(2, "grpc support", "enabled"); + php_info_print_table_end(); + + /* Remove comments if you have entries in php.ini + DISPLAY_INI_ENTRIES(); + */ +} +/* }}} */ +/* The previous line is meant for vim and emacs, so it can correctly fold and + unfold functions in source code. See the corresponding marks just before + function definition, where the functions purpose is also documented. Please + follow this convention for the convenience of others editing your code. +*/ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/src/php/ext/grpc/php_grpc.h b/src/php/ext/grpc/php_grpc.h new file mode 100644 index 00000000..1d4834c5 --- /dev/null +++ b/src/php/ext/grpc/php_grpc.h @@ -0,0 +1,98 @@ +/* + * + * Copyright 2015, 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 PHP_GRPC_H +#define PHP_GRPC_H + +#include + +extern zend_module_entry grpc_module_entry; +#define phpext_grpc_ptr &grpc_module_entry + +#define PHP_GRPC_VERSION \ + "0.1.0" /* Replace with version number for your extension */ + +#ifdef PHP_WIN32 +#define PHP_GRPC_API __declspec(dllexport) +#elif defined(__GNUC__) && __GNUC__ >= 4 +#define PHP_GRPC_API __attribute__((visibility("default"))) +#else +#define PHP_GRPC_API +#endif + +#ifdef ZTS +#include "TSRM.h" +#endif + +#include "php.h" + +#include "grpc/grpc.h" + +#define RETURN_DESTROY_ZVAL(val) \ + RETURN_ZVAL(val, false /* Don't execute copy constructor */, \ + true /* Dealloc original before returning */) + +/* These are all function declarations */ +/* Code that runs at module initialization */ +PHP_MINIT_FUNCTION(grpc); +/* Code that runs at module shutdown */ +PHP_MSHUTDOWN_FUNCTION(grpc); +/* Displays information about the module */ +PHP_MINFO_FUNCTION(grpc); + +/* + Declare any global variables you may need between the BEGIN + and END macros here: + +ZEND_BEGIN_MODULE_GLOBALS(grpc) +ZEND_END_MODULE_GLOBALS(grpc) +*/ + +/* In every utility function you add that needs to use variables + in php_grpc_globals, call TSRMLS_FETCH(); after declaring other + variables used by that function, or better yet, pass in TSRMLS_CC + after the last function argument and declare your utility function + with TSRMLS_DC after the last declared argument. Always refer to + the globals in your function as GRPC_G(variable). You are + encouraged to rename these macros something shorter, see + examples in any other php module directory. +*/ + +#ifdef ZTS +#define GRPC_G(v) TSRMG(grpc_globals_id, zend_grpc_globals *, v) +#else +#define GRPC_G(v) (grpc_globals.v) +#endif + +#endif /* PHP_GRPC_H */ diff --git a/src/php/ext/grpc/server.c b/src/php/ext/grpc/server.c new file mode 100644 index 00000000..ca129e76 --- /dev/null +++ b/src/php/ext/grpc/server.c @@ -0,0 +1,233 @@ +/* + * + * Copyright 2015, 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 "call.h" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include "php_grpc.h" + +#include + +#include + +#include +#include +#include + +#include "completion_queue.h" +#include "server.h" +#include "channel.h" +#include "server_credentials.h" +#include "timeval.h" + +zend_class_entry *grpc_ce_server; + +/* Frees and destroys an instance of wrapped_grpc_server */ +void free_wrapped_grpc_server(void *object TSRMLS_DC) { + wrapped_grpc_server *server = (wrapped_grpc_server *)object; + if (server->wrapped != NULL) { + grpc_server_shutdown_and_notify(server->wrapped, completion_queue, NULL); + grpc_server_cancel_all_calls(server->wrapped); + grpc_completion_queue_pluck(completion_queue, NULL, + gpr_inf_future(GPR_CLOCK_REALTIME), NULL); + grpc_server_destroy(server->wrapped); + } + efree(server); +} + +/* Initializes an instance of wrapped_grpc_call to be associated with an object + * of a class specified by class_type */ +zend_object_value create_wrapped_grpc_server(zend_class_entry *class_type + TSRMLS_DC) { + zend_object_value retval; + wrapped_grpc_server *intern; + + intern = (wrapped_grpc_server *)emalloc(sizeof(wrapped_grpc_server)); + memset(intern, 0, sizeof(wrapped_grpc_server)); + + zend_object_std_init(&intern->std, class_type TSRMLS_CC); + object_properties_init(&intern->std, class_type); + retval.handle = zend_objects_store_put( + intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, + free_wrapped_grpc_server, NULL TSRMLS_CC); + retval.handlers = zend_get_std_object_handlers(); + return retval; +} + +/** + * Constructs a new instance of the Server class + * @param array $args The arguments to pass to the server (optional) + */ +PHP_METHOD(Server, __construct) { + wrapped_grpc_server *server = + (wrapped_grpc_server *)zend_object_store_get_object(getThis() TSRMLS_CC); + zval *args_array = NULL; + grpc_channel_args args; + /* "|a" == 1 optional array */ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a", &args_array) == + FAILURE) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "Server expects an array", + 1 TSRMLS_CC); + return; + } + if (args_array == NULL) { + server->wrapped = grpc_server_create(NULL, NULL); + } else { + php_grpc_read_args_array(args_array, &args); + server->wrapped = grpc_server_create(&args, NULL); + efree(args.args); + } + grpc_server_register_completion_queue(server->wrapped, completion_queue, + NULL); +} + +/** + * Request a call on a server. Creates a single GRPC_SERVER_RPC_NEW event. + * @param long $tag_new The tag to associate with the new request + * @param long $tag_cancel The tag to use if the call is cancelled + * @return Void + */ +PHP_METHOD(Server, requestCall) { + grpc_call_error error_code; + wrapped_grpc_server *server = + (wrapped_grpc_server *)zend_object_store_get_object(getThis() TSRMLS_CC); + grpc_call *call; + grpc_call_details details; + grpc_metadata_array metadata; + zval *result; + grpc_event event; + MAKE_STD_ZVAL(result); + object_init(result); + grpc_call_details_init(&details); + grpc_metadata_array_init(&metadata); + error_code = + grpc_server_request_call(server->wrapped, &call, &details, &metadata, + completion_queue, completion_queue, NULL); + if (error_code != GRPC_CALL_OK) { + zend_throw_exception(spl_ce_LogicException, "request_call failed", + (long)error_code TSRMLS_CC); + goto cleanup; + } + event = grpc_completion_queue_pluck(completion_queue, NULL, + gpr_inf_future(GPR_CLOCK_REALTIME), NULL); + if (!event.success) { + zend_throw_exception(spl_ce_LogicException, + "Failed to request a call for some reason", + 1 TSRMLS_CC); + goto cleanup; + } + add_property_zval(result, "call", grpc_php_wrap_call(call, true)); + add_property_string(result, "method", details.method, true); + add_property_string(result, "host", details.host, true); + add_property_zval(result, "absolute_deadline", + grpc_php_wrap_timeval(details.deadline)); + add_property_zval(result, "metadata", grpc_parse_metadata_array(&metadata)); +cleanup: + grpc_call_details_destroy(&details); + grpc_metadata_array_destroy(&metadata); + RETURN_DESTROY_ZVAL(result); +} + +/** + * Add a http2 over tcp listener. + * @param string $addr The address to add + * @return true on success, false on failure + */ +PHP_METHOD(Server, addHttp2Port) { + wrapped_grpc_server *server = + (wrapped_grpc_server *)zend_object_store_get_object(getThis() TSRMLS_CC); + const char *addr; + int addr_len; + /* "s" == 1 string */ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &addr, &addr_len) == + FAILURE) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "add_http2_port expects a string", 1 TSRMLS_CC); + return; + } + RETURN_LONG(grpc_server_add_insecure_http2_port(server->wrapped, addr)); +} + +PHP_METHOD(Server, addSecureHttp2Port) { + wrapped_grpc_server *server = + (wrapped_grpc_server *)zend_object_store_get_object(getThis() TSRMLS_CC); + const char *addr; + int addr_len; + zval *creds_obj; + /* "sO" == 1 string, 1 object */ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sO", &addr, &addr_len, + &creds_obj, grpc_ce_server_credentials) == + FAILURE) { + zend_throw_exception( + spl_ce_InvalidArgumentException, + "add_http2_port expects a string and a ServerCredentials", 1 TSRMLS_CC); + return; + } + wrapped_grpc_server_credentials *creds = + (wrapped_grpc_server_credentials *)zend_object_store_get_object( + creds_obj TSRMLS_CC); + RETURN_LONG(grpc_server_add_secure_http2_port(server->wrapped, addr, + creds->wrapped)); +} + +/** + * Start a server - tells all listeners to start listening + * @return Void + */ +PHP_METHOD(Server, start) { + wrapped_grpc_server *server = + (wrapped_grpc_server *)zend_object_store_get_object(getThis() TSRMLS_CC); + grpc_server_start(server->wrapped); +} + +static zend_function_entry server_methods[] = { + PHP_ME(Server, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) + PHP_ME(Server, requestCall, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Server, addHttp2Port, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Server, addSecureHttp2Port, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Server, start, NULL, ZEND_ACC_PUBLIC) PHP_FE_END}; + +void grpc_init_server(TSRMLS_D) { + zend_class_entry ce; + INIT_CLASS_ENTRY(ce, "Grpc\\Server", server_methods); + ce.create_object = create_wrapped_grpc_server; + grpc_ce_server = zend_register_internal_class(&ce TSRMLS_CC); +} diff --git a/src/php/ext/grpc/server.h b/src/php/ext/grpc/server.h new file mode 100755 index 00000000..022257f3 --- /dev/null +++ b/src/php/ext/grpc/server.h @@ -0,0 +1,61 @@ +/* + * + * Copyright 2015, 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 NET_GRPC_PHP_GRPC_SERVER_H_ +#define NET_GRPC_PHP_GRPC_SERVER_H_ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include "php_grpc.h" + +#include + +/* Class entry for the Server PHP class */ +extern zend_class_entry *grpc_ce_server; + +/* Wrapper struct for grpc_server that can be associated with a PHP object */ +typedef struct wrapped_grpc_server { + zend_object std; + + grpc_server *wrapped; +} wrapped_grpc_server; + +/* Initializes the Server class */ +void grpc_init_server(TSRMLS_D); + +#endif /* NET_GRPC_PHP_GRPC_SERVER_H_ */ diff --git a/src/php/ext/grpc/server_credentials.c b/src/php/ext/grpc/server_credentials.c new file mode 100644 index 00000000..79188246 --- /dev/null +++ b/src/php/ext/grpc/server_credentials.c @@ -0,0 +1,135 @@ +/* + * + * Copyright 2015, 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 "server_credentials.h" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include "php_grpc.h" + +#include +#include + +#include +#include + +zend_class_entry *grpc_ce_server_credentials; + +/* Frees and destroys an instace of wrapped_grpc_server_credentials */ +void free_wrapped_grpc_server_credentials(void *object TSRMLS_DC) { + wrapped_grpc_server_credentials *creds = + (wrapped_grpc_server_credentials *)object; + if (creds->wrapped != NULL) { + grpc_server_credentials_release(creds->wrapped); + } + efree(creds); +} + +/* Initializes an instace of wrapped_grpc_server_credentials to be associated + * with an object of a class specified by class_type */ +zend_object_value create_wrapped_grpc_server_credentials( + zend_class_entry *class_type TSRMLS_DC) { + zend_object_value retval; + wrapped_grpc_server_credentials *intern; + + intern = (wrapped_grpc_server_credentials *)emalloc( + sizeof(wrapped_grpc_server_credentials)); + memset(intern, 0, sizeof(wrapped_grpc_server_credentials)); + + zend_object_std_init(&intern->std, class_type TSRMLS_CC); + object_properties_init(&intern->std, class_type); + retval.handle = zend_objects_store_put( + intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, + free_wrapped_grpc_server_credentials, NULL TSRMLS_CC); + retval.handlers = zend_get_std_object_handlers(); + return retval; +} + +zval *grpc_php_wrap_server_credentials(grpc_server_credentials *wrapped) { + zval *server_credentials_object; + MAKE_STD_ZVAL(server_credentials_object); + object_init_ex(server_credentials_object, grpc_ce_server_credentials); + wrapped_grpc_server_credentials *server_credentials = + (wrapped_grpc_server_credentials *)zend_object_store_get_object( + server_credentials_object TSRMLS_CC); + server_credentials->wrapped = wrapped; + return server_credentials_object; +} + +/** + * Create SSL credentials. + * @param string pem_root_certs PEM encoding of the server root certificates + * @param string pem_private_key PEM encoding of the client's private key + * @param string pem_cert_chain PEM encoding of the client's certificate chain + * @return Credentials The new SSL credentials object + */ +PHP_METHOD(ServerCredentials, createSsl) { + char *pem_root_certs = 0; + grpc_ssl_pem_key_cert_pair pem_key_cert_pair; + + int root_certs_length = 0, private_key_length, cert_chain_length; + + /* "s!ss" == 1 nullable string, 2 strings */ + /* TODO: support multiple key cert pairs. */ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!ss", &pem_root_certs, + &root_certs_length, &pem_key_cert_pair.private_key, + &private_key_length, &pem_key_cert_pair.cert_chain, + &cert_chain_length) == FAILURE) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "createSsl expects 3 strings", 1 TSRMLS_CC); + return; + } + /* TODO: add a force_client_auth field in ServerCredentials and pass it as + * the last parameter. */ + grpc_server_credentials *creds = grpc_ssl_server_credentials_create( + pem_root_certs, &pem_key_cert_pair, 1, 0, NULL); + zval *creds_object = grpc_php_wrap_server_credentials(creds); + RETURN_DESTROY_ZVAL(creds_object); +} + +static zend_function_entry server_credentials_methods[] = { + PHP_ME(ServerCredentials, createSsl, NULL, + ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END}; + +void grpc_init_server_credentials(TSRMLS_D) { + zend_class_entry ce; + INIT_CLASS_ENTRY(ce, "Grpc\\ServerCredentials", server_credentials_methods); + ce.create_object = create_wrapped_grpc_server_credentials; + grpc_ce_server_credentials = zend_register_internal_class(&ce TSRMLS_CC); +} diff --git a/src/php/ext/grpc/server_credentials.h b/src/php/ext/grpc/server_credentials.h new file mode 100755 index 00000000..7101d650 --- /dev/null +++ b/src/php/ext/grpc/server_credentials.h @@ -0,0 +1,63 @@ +/* + * + * Copyright 2015, 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 NET_GRPC_PHP_GRPC_SERVER_CREDENTIALS_H_ +#define NET_GRPC_PHP_GRPC_SERVER_CREDENTIALS_H_ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include "php_grpc.h" + +#include +#include + +/* Class entry for the Server_Credentials PHP class */ +extern zend_class_entry *grpc_ce_server_credentials; + +/* Wrapper struct for grpc_server_credentials that can be associated with a PHP + * object */ +typedef struct wrapped_grpc_server_credentials { + zend_object std; + + grpc_server_credentials *wrapped; +} wrapped_grpc_server_credentials; + +/* Initializes the Server_Credentials PHP class */ +void grpc_init_server_credentials(TSRMLS_D); + +#endif /* NET_GRPC_PHP_GRPC_SERVER_CREDENTIALS_H_ */ diff --git a/src/php/ext/grpc/timeval.c b/src/php/ext/grpc/timeval.c new file mode 100644 index 00000000..4fd069e1 --- /dev/null +++ b/src/php/ext/grpc/timeval.c @@ -0,0 +1,276 @@ +/* + * + * Copyright 2015, 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 "timeval.h" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include "php_grpc.h" + +#include + +#include + +#include +#include + +zend_class_entry *grpc_ce_timeval; + +/* Frees and destroys an instance of wrapped_grpc_call */ +void free_wrapped_grpc_timeval(void *object TSRMLS_DC) { efree(object); } + +/* Initializes an instance of wrapped_grpc_timeval to be associated with an + * object of a class specified by class_type */ +zend_object_value create_wrapped_grpc_timeval(zend_class_entry *class_type + TSRMLS_DC) { + zend_object_value retval; + wrapped_grpc_timeval *intern; + intern = (wrapped_grpc_timeval *)emalloc(sizeof(wrapped_grpc_timeval)); + memset(intern, 0, sizeof(wrapped_grpc_timeval)); + zend_object_std_init(&intern->std, class_type TSRMLS_CC); + object_properties_init(&intern->std, class_type); + retval.handle = zend_objects_store_put( + intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, + free_wrapped_grpc_timeval, NULL TSRMLS_CC); + retval.handlers = zend_get_std_object_handlers(); + return retval; +} + +zval *grpc_php_wrap_timeval(gpr_timespec wrapped) { + zval *timeval_object; + MAKE_STD_ZVAL(timeval_object); + object_init_ex(timeval_object, grpc_ce_timeval); + wrapped_grpc_timeval *timeval = + (wrapped_grpc_timeval *)zend_object_store_get_object( + timeval_object TSRMLS_CC); + memcpy(&timeval->wrapped, &wrapped, sizeof(gpr_timespec)); + return timeval_object; +} + +/** + * Constructs a new instance of the Timeval class + * @param long $usec The number of microseconds in the interval + */ +PHP_METHOD(Timeval, __construct) { + wrapped_grpc_timeval *timeval = + (wrapped_grpc_timeval *)zend_object_store_get_object(getThis() TSRMLS_CC); + long microseconds; + /* "l" == 1 long */ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", µseconds) == + FAILURE) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "Timeval expects a long", 1 TSRMLS_CC); + return; + } + gpr_timespec time = gpr_time_from_micros(microseconds, GPR_TIMESPAN); + memcpy(&timeval->wrapped, &time, sizeof(gpr_timespec)); +} + +/** + * Adds another Timeval to this one and returns the sum. Calculations saturate + * at infinities. + * @param Timeval $other The other Timeval object to add + * @return Timeval A new Timeval object containing the sum + */ +PHP_METHOD(Timeval, add) { + zval *other_obj; + /* "O" == 1 Object */ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &other_obj, + grpc_ce_timeval) == FAILURE) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "add expects a Timeval", 1 TSRMLS_CC); + return; + } + wrapped_grpc_timeval *self = + (wrapped_grpc_timeval *)zend_object_store_get_object(getThis() TSRMLS_CC); + wrapped_grpc_timeval *other = + (wrapped_grpc_timeval *)zend_object_store_get_object(other_obj TSRMLS_CC); + zval *sum = + grpc_php_wrap_timeval(gpr_time_add(self->wrapped, other->wrapped)); + RETURN_DESTROY_ZVAL(sum); +} + +/** + * Subtracts another Timeval from this one and returns the difference. + * Calculations saturate at infinities. + * @param Timeval $other The other Timeval object to subtract + * @param Timeval A new Timeval object containing the sum + */ +PHP_METHOD(Timeval, subtract) { + zval *other_obj; + /* "O" == 1 Object */ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &other_obj, + grpc_ce_timeval) == FAILURE) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "subtract expects a Timeval", 1 TSRMLS_CC); + return; + } + wrapped_grpc_timeval *self = + (wrapped_grpc_timeval *)zend_object_store_get_object(getThis() TSRMLS_CC); + wrapped_grpc_timeval *other = + (wrapped_grpc_timeval *)zend_object_store_get_object(other_obj TSRMLS_CC); + zval *diff = + grpc_php_wrap_timeval(gpr_time_sub(self->wrapped, other->wrapped)); + RETURN_DESTROY_ZVAL(diff); +} + +/** + * Return negative, 0, or positive according to whether a < b, a == b, or a > b + * respectively. + * @param Timeval $a The first time to compare + * @param Timeval $b The second time to compare + * @return long + */ +PHP_METHOD(Timeval, compare) { + zval *a_obj, *b_obj; + /* "OO" == 2 Objects */ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "OO", &a_obj, + grpc_ce_timeval, &b_obj, + grpc_ce_timeval) == FAILURE) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "compare expects two Timevals", 1 TSRMLS_CC); + return; + } + wrapped_grpc_timeval *a = + (wrapped_grpc_timeval *)zend_object_store_get_object(a_obj TSRMLS_CC); + wrapped_grpc_timeval *b = + (wrapped_grpc_timeval *)zend_object_store_get_object(b_obj TSRMLS_CC); + long result = gpr_time_cmp(a->wrapped, b->wrapped); + RETURN_LONG(result); +} + +/** + * Checks whether the two times are within $threshold of each other + * @param Timeval $a The first time to compare + * @param Timeval $b The second time to compare + * @param Timeval $threshold The threshold to check against + * @return bool True if $a and $b are within $threshold, False otherwise + */ +PHP_METHOD(Timeval, similar) { + zval *a_obj, *b_obj, *thresh_obj; + /* "OOO" == 3 Objects */ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "OOO", &a_obj, + grpc_ce_timeval, &b_obj, grpc_ce_timeval, + &thresh_obj, grpc_ce_timeval) == FAILURE) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "compare expects three Timevals", 1 TSRMLS_CC); + return; + } + wrapped_grpc_timeval *a = + (wrapped_grpc_timeval *)zend_object_store_get_object(a_obj TSRMLS_CC); + wrapped_grpc_timeval *b = + (wrapped_grpc_timeval *)zend_object_store_get_object(b_obj TSRMLS_CC); + wrapped_grpc_timeval *thresh = + (wrapped_grpc_timeval *)zend_object_store_get_object( + thresh_obj TSRMLS_CC); + int result = gpr_time_similar(a->wrapped, b->wrapped, thresh->wrapped); + RETURN_BOOL(result); +} + +/** + * Returns the current time as a timeval object + * @return Timeval The current time + */ +PHP_METHOD(Timeval, now) { + zval *now = grpc_php_wrap_timeval(gpr_now(GPR_CLOCK_REALTIME)); + RETURN_DESTROY_ZVAL(now); +} + +/** + * Returns the zero time interval as a timeval object + * @return Timeval Zero length time interval + */ +PHP_METHOD(Timeval, zero) { + zval *grpc_php_timeval_zero = + grpc_php_wrap_timeval(gpr_time_0(GPR_CLOCK_REALTIME)); + RETURN_ZVAL(grpc_php_timeval_zero, + false, /* Copy original before returning? */ + true /* Destroy original before returning */); +} + +/** + * Returns the infinite future time value as a timeval object + * @return Timeval Infinite future time value + */ +PHP_METHOD(Timeval, infFuture) { + zval *grpc_php_timeval_inf_future = + grpc_php_wrap_timeval(gpr_inf_future(GPR_CLOCK_REALTIME)); + RETURN_DESTROY_ZVAL(grpc_php_timeval_inf_future); +} + +/** + * Returns the infinite past time value as a timeval object + * @return Timeval Infinite past time value + */ +PHP_METHOD(Timeval, infPast) { + zval *grpc_php_timeval_inf_past = + grpc_php_wrap_timeval(gpr_inf_past(GPR_CLOCK_REALTIME)); + RETURN_DESTROY_ZVAL(grpc_php_timeval_inf_past); +} + +/** + * Sleep until this time, interpreted as an absolute timeout + * @return void + */ +PHP_METHOD(Timeval, sleepUntil) { + wrapped_grpc_timeval *this = + (wrapped_grpc_timeval *)zend_object_store_get_object(getThis() TSRMLS_CC); + gpr_sleep_until(this->wrapped); +} + +static zend_function_entry timeval_methods[] = { + PHP_ME(Timeval, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) + PHP_ME(Timeval, add, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Timeval, compare, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(Timeval, infFuture, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(Timeval, infPast, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(Timeval, now, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(Timeval, similar, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(Timeval, sleepUntil, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Timeval, subtract, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Timeval, zero, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END}; + +void grpc_init_timeval(TSRMLS_D) { + zend_class_entry ce; + INIT_CLASS_ENTRY(ce, "Grpc\\Timeval", timeval_methods); + ce.create_object = create_wrapped_grpc_timeval; + grpc_ce_timeval = zend_register_internal_class(&ce TSRMLS_CC); +} + +void grpc_shutdown_timeval(TSRMLS_D) {} diff --git a/src/php/ext/grpc/timeval.h b/src/php/ext/grpc/timeval.h new file mode 100755 index 00000000..07cef037 --- /dev/null +++ b/src/php/ext/grpc/timeval.h @@ -0,0 +1,68 @@ +/* + * + * Copyright 2015, 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 NET_GRPC_PHP_GRPC_TIMEVAL_H_ +#define NET_GRPC_PHP_GRPC_TIMEVAL_H_ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include "php_grpc.h" + +#include +#include + +/* Class entry for the Timeval PHP Class */ +extern zend_class_entry *grpc_ce_timeval; + +/* Wrapper struct for timeval that can be associated with a PHP object */ +typedef struct wrapped_grpc_timeval { + zend_object std; + + gpr_timespec wrapped; +} wrapped_grpc_timeval; + +/* Initialize the Timeval PHP class */ +void grpc_init_timeval(TSRMLS_D); + +/* Shutdown the Timeval PHP class */ +void grpc_shutdown_timeval(TSRMLS_D); + +/* Creates a Timeval object that wraps the given timeval struct */ +zval *grpc_php_wrap_timeval(gpr_timespec wrapped); + +#endif /* NET_GRPC_PHP_GRPC_TIMEVAL_H_ */ diff --git a/src/php/lib/Grpc/AbstractCall.php b/src/php/lib/Grpc/AbstractCall.php new file mode 100644 index 00000000..a3c7a9e0 --- /dev/null +++ b/src/php/lib/Grpc/AbstractCall.php @@ -0,0 +1,95 @@ +add($delta); + } else { + $deadline = Timeval::infFuture(); + } + $this->call = new Call($channel, $method, $deadline); + $this->deserialize = $deserialize; + $this->metadata = null; + } + + /** + * @return The metadata sent by the server. + */ + public function getMetadata() { + return $this->metadata; + } + + /** + * @return string The URI of the endpoint. + */ + public function getPeer() { + return $this->call->getPeer(); + } + + /** + * Cancels the call + */ + public function cancel() { + $this->call->cancel(); + } + + /** + * Deserialize a response value to an object. + * @param string $value The binary value to deserialize + * @return The deserialized value + */ + protected function deserializeResponse($value) { + if ($value === null) { + return null; + } + return call_user_func($this->deserialize, $value); + } +} diff --git a/src/php/lib/Grpc/BaseStub.php b/src/php/lib/Grpc/BaseStub.php new file mode 100755 index 00000000..381b1143 --- /dev/null +++ b/src/php/lib/Grpc/BaseStub.php @@ -0,0 +1,260 @@ +hostname = $hostname; + $this->update_metadata = null; + if (isset($opts['update_metadata'])) { + if (is_callable($opts['update_metadata'])) { + $this->update_metadata = $opts['update_metadata']; + } + unset($opts['update_metadata']); + } + $package_config = json_decode( + file_get_contents(dirname(__FILE__) . '/../../composer.json'), true); + $opts['grpc.primary_user_agent'] = + 'grpc-php/' . $package_config['version']; + $this->channel = new Channel($hostname, $opts); + } + + /** + * @return string The URI of the endpoint. + */ + public function getTarget() { + return $this->channel->getTarget(); + } + + /** + * @param $try_to_connect bool + * @return int The grpc connectivity state + */ + public function getConnectivityState($try_to_connect = false) { + return $this->channel->getConnectivityState($try_to_connect); + } + + /** + * @param $timeout in microseconds + * @return bool true if channel is ready + * @throw Exception if channel is in FATAL_ERROR state + */ + public function waitForReady($timeout) { + $new_state = $this->getConnectivityState(true); + if ($this->_checkConnectivityState($new_state)) { + return true; + } + + $now = Timeval::now(); + $delta = new Timeval($timeout); + $deadline = $now->add($delta); + + while ($this->channel->watchConnectivityState($new_state, $deadline)) { + // state has changed before deadline + $new_state = $this->getConnectivityState(); + if ($this->_checkConnectivityState($new_state)) { + return true; + } + } + // deadline has passed + $new_state = $this->getConnectivityState(); + return $this->_checkConnectivityState($new_state); + } + + private function _checkConnectivityState($new_state) { + if ($new_state == \Grpc\CHANNEL_READY) { + return true; + } + if ($new_state == \Grpc\CHANNEL_FATAL_FAILURE) { + throw new Exception('Failed to connect to server'); + } + return false; + } + + /** + * Close the communication channel associated with this stub + */ + public function close() { + $channel->close(); + } + + /** + * constructs the auth uri for the jwt + */ + private function _get_jwt_aud_uri($method) { + $last_slash_idx = strrpos($method, '/'); + if ($last_slash_idx === false) { + return false; + } + $service_name = substr($method, 0, $last_slash_idx); + return "https://" . $this->hostname . $service_name; + } + + /** + * extract $timeout from $metadata + * @param $metadata The metadata map + * @return list($metadata_copy, $timeout) + */ + private function _extract_timeout_from_metadata($metadata) { + $timeout = false; + $metadata_copy = $metadata; + if (isset($metadata['timeout'])) { + $timeout = $metadata['timeout']; + unset($metadata_copy['timeout']); + } + return array($metadata_copy, $timeout); + } + + /* This class is intended to be subclassed by generated code, so all functions + begin with "_" to avoid name collisions. */ + + /** + * Call a remote method that takes a single argument and has a single output + * + * @param string $method The name of the method to call + * @param $argument The argument to the method + * @param callable $deserialize A function that deserializes the response + * @param array $metadata A metadata map to send to the server + * @return SimpleSurfaceActiveCall The active call object + */ + public function _simpleRequest($method, + $argument, + callable $deserialize, + $metadata = array(), + $options = array()) { + list($actual_metadata, $timeout) = $this->_extract_timeout_from_metadata($metadata); + $call = new UnaryCall($this->channel, $method, $deserialize, $timeout); + $jwt_aud_uri = $this->_get_jwt_aud_uri($method); + if (is_callable($this->update_metadata)) { + $actual_metadata = call_user_func($this->update_metadata, + $actual_metadata, + $jwt_aud_uri); + } + $call->start($argument, $actual_metadata, $options); + return $call; + } + + /** + * Call a remote method that takes a stream of arguments and has a single + * output + * + * @param string $method The name of the method to call + * @param $arguments An array or Traversable of arguments to stream to the + * server + * @param callable $deserialize A function that deserializes the response + * @param array $metadata A metadata map to send to the server + * @return ClientStreamingSurfaceActiveCall The active call object + */ + public function _clientStreamRequest($method, + callable $deserialize, + $metadata = array()) { + list($actual_metadata, $timeout) = $this->_extract_timeout_from_metadata($metadata); + $call = new ClientStreamingCall($this->channel, $method, $deserialize, $timeout); + $jwt_aud_uri = $this->_get_jwt_aud_uri($method); + if (is_callable($this->update_metadata)) { + $actual_metadata = call_user_func($this->update_metadata, + $actual_metadata, + $jwt_aud_uri); + } + $call->start($actual_metadata); + return $call; + } + + /** + * Call a remote method that takes a single argument and returns a stream of + * responses + * + * @param string $method The name of the method to call + * @param $argument The argument to the method + * @param callable $deserialize A function that deserializes the responses + * @param array $metadata A metadata map to send to the server + * @return ServerStreamingSurfaceActiveCall The active call object + */ + public function _serverStreamRequest($method, + $argument, + callable $deserialize, + $metadata = array(), + $options = array()) { + list($actual_metadata, $timeout) = $this->_extract_timeout_from_metadata($metadata); + $call = new ServerStreamingCall($this->channel, $method, $deserialize, $timeout); + $jwt_aud_uri = $this->_get_jwt_aud_uri($method); + if (is_callable($this->update_metadata)) { + $actual_metadata = call_user_func($this->update_metadata, + $actual_metadata, + $jwt_aud_uri); + } + $call->start($argument, $actual_metadata, $options); + return $call; + } + + /** + * Call a remote method with messages streaming in both directions + * + * @param string $method The name of the method to call + * @param callable $deserialize A function that deserializes the responses + * @param array $metadata A metadata map to send to the server + * @return BidiStreamingSurfaceActiveCall The active call object + */ + public function _bidiRequest($method, + callable $deserialize, + $metadata = array()) { + list($actual_metadata, $timeout) = $this->_extract_timeout_from_metadata($metadata); + $call = new BidiStreamingCall($this->channel, $method, $deserialize, $timeout); + $jwt_aud_uri = $this->_get_jwt_aud_uri($method); + if (is_callable($this->update_metadata)) { + $actual_metadata = call_user_func($this->update_metadata, + $actual_metadata, + $jwt_aud_uri); + } + $call->start($actual_metadata); + return $call; + } +} diff --git a/src/php/lib/Grpc/BidiStreamingCall.php b/src/php/lib/Grpc/BidiStreamingCall.php new file mode 100644 index 00000000..c432fd52 --- /dev/null +++ b/src/php/lib/Grpc/BidiStreamingCall.php @@ -0,0 +1,98 @@ +call->startBatch([OP_SEND_INITIAL_METADATA => $metadata]); + } + + /** + * Reads the next value from the server. + * @return The next value from the server, or null if there is none + */ + public function read() { + $batch = [OP_RECV_MESSAGE => true]; + if ($this->metadata === null) { + $batch[OP_RECV_INITIAL_METADATA] = true; + } + $read_event = $this->call->startBatch($batch); + if ($this->metadata === null) { + $this->metadata = $read_event->metadata; + } + return $this->deserializeResponse($read_event->message); + } + + /** + * Write a single message to the server. This cannot be called after + * writesDone is called. + * @param ByteBuffer $data The data to write + * @param array $options an array of options, possible keys: + * 'flags' => a number + */ + public function write($data, $options = array()) { + $message_array = ['message' => $data->serialize()]; + if (isset($options['flags'])) { + $message_array['flags'] = $options['flags']; + } + $this->call->startBatch([OP_SEND_MESSAGE => $message_array]); + } + + /** + * Indicate that no more writes will be sent. + */ + public function writesDone() { + $this->call->startBatch([OP_SEND_CLOSE_FROM_CLIENT => true]); + } + + /** + * Wait for the server to send the status, and return it. + * @return object The status object, with integer $code, string $details, + * and array $metadata members + */ + public function getStatus() { + $status_event = $this->call->startBatch([ + OP_RECV_STATUS_ON_CLIENT => true + ]); + return $status_event->status; + } +} \ No newline at end of file diff --git a/src/php/lib/Grpc/ClientStreamingCall.php b/src/php/lib/Grpc/ClientStreamingCall.php new file mode 100644 index 00000000..b96c17e7 --- /dev/null +++ b/src/php/lib/Grpc/ClientStreamingCall.php @@ -0,0 +1,77 @@ +call->startBatch([OP_SEND_INITIAL_METADATA => $metadata]); + } + + /** + * Write a single message to the server. This cannot be called after + * wait is called. + * @param ByteBuffer $data The data to write + * @param array $options an array of options, possible keys: + * 'flags' => a number + */ + public function write($data, $options = array()) { + $message_array = ['message' => $data->serialize()]; + if (isset($options['flags'])) { + $message_array['flags'] = $options['flags']; + } + $this->call->startBatch([OP_SEND_MESSAGE => $message_array]); + } + + /** + * Wait for the server to respond with data and a status + * @return [response data, status] + */ + public function wait() { + $event = $this->call->startBatch([ + OP_SEND_CLOSE_FROM_CLIENT => true, + OP_RECV_INITIAL_METADATA => true, + OP_RECV_MESSAGE => true, + OP_RECV_STATUS_ON_CLIENT => true]); + $this->metadata = $event->metadata; + return array($this->deserializeResponse($event->message), $event->status); + } +} \ No newline at end of file diff --git a/src/php/lib/Grpc/ServerStreamingCall.php b/src/php/lib/Grpc/ServerStreamingCall.php new file mode 100644 index 00000000..a93c1a5d --- /dev/null +++ b/src/php/lib/Grpc/ServerStreamingCall.php @@ -0,0 +1,83 @@ + a number + */ + public function start($data, $metadata = array(), $options = array()) { + $message_array = ['message' => $data->serialize()]; + if (isset($options['flags'])) { + $message_array['flags'] = $options['flags']; + } + $event = $this->call->startBatch([ + OP_SEND_INITIAL_METADATA => $metadata, + OP_RECV_INITIAL_METADATA => true, + OP_SEND_MESSAGE => $message_array, + OP_SEND_CLOSE_FROM_CLIENT => true]); + $this->metadata = $event->metadata; + } + + /** + * @return An iterator of response values + */ + public function responses() { + $response = $this->call->startBatch([OP_RECV_MESSAGE => true])->message; + while($response !== null) { + yield $this->deserializeResponse($response); + $response = $this->call->startBatch([OP_RECV_MESSAGE => true])->message; + } + } + + /** + * Wait for the server to send the status, and return it. + * @return object The status object, with integer $code, string $details, + * and array $metadata members + */ + public function getStatus() { + $status_event = $this->call->startBatch([ + OP_RECV_STATUS_ON_CLIENT => true + ]); + return $status_event->status; + } +} diff --git a/src/php/lib/Grpc/UnaryCall.php b/src/php/lib/Grpc/UnaryCall.php new file mode 100644 index 00000000..38af6b1d --- /dev/null +++ b/src/php/lib/Grpc/UnaryCall.php @@ -0,0 +1,71 @@ + a number + */ + public function start($data, $metadata = array(), $options = array()) { + $message_array = ['message' => $data->serialize()]; + if (isset($options['flags'])) { + $message_array['flags'] = $options['flags']; + } + $event = $this->call->startBatch([ + OP_SEND_INITIAL_METADATA => $metadata, + OP_RECV_INITIAL_METADATA => true, + OP_SEND_MESSAGE => $message_array, + OP_SEND_CLOSE_FROM_CLIENT => true]); + $this->metadata = $event->metadata; + } + + /** + * Wait for the server to respond with data and a status + * @return [response data, status] + */ + public function wait() { + $event = $this->call->startBatch([ + OP_RECV_MESSAGE => true, + OP_RECV_STATUS_ON_CLIENT => true]); + return array($this->deserializeResponse($event->message), $event->status); + } +} diff --git a/src/php/tests/data/README b/src/php/tests/data/README new file mode 100644 index 00000000..888d95b9 --- /dev/null +++ b/src/php/tests/data/README @@ -0,0 +1 @@ +CONFIRMEDTESTKEY diff --git a/src/php/tests/data/ca.pem b/src/php/tests/data/ca.pem new file mode 100755 index 00000000..6c8511a7 --- /dev/null +++ b/src/php/tests/data/ca.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICSjCCAbOgAwIBAgIJAJHGGR4dGioHMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQxDzANBgNVBAMTBnRlc3RjYTAeFw0xNDExMTEyMjMxMjla +Fw0yNDExMDgyMjMxMjlaMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0 +YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMT +BnRlc3RjYTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwEDfBV5MYdlHVHJ7 ++L4nxrZy7mBfAVXpOc5vMYztssUI7mL2/iYujiIXM+weZYNTEpLdjyJdu7R5gGUu +g1jSVK/EPHfc74O7AyZU34PNIP4Sh33N+/A5YexrNgJlPY+E3GdVYi4ldWJjgkAd +Qah2PH5ACLrIIC6tRka9hcaBlIECAwEAAaMgMB4wDAYDVR0TBAUwAwEB/zAOBgNV +HQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQELBQADgYEAHzC7jdYlzAVmddi/gdAeKPau +sPBG/C2HCWqHzpCUHcKuvMzDVkY/MP2o6JIW2DBbY64bO/FceExhjcykgaYtCH/m +oIU63+CFOTtR7otyQAWHqXa7q4SbCDlG7DyRFxqG0txPtGvy12lgldA2+RgcigQG +Dfcog5wrJytaQ6UA0wE= +-----END CERTIFICATE----- diff --git a/src/php/tests/data/server1.key b/src/php/tests/data/server1.key new file mode 100755 index 00000000..143a5b87 --- /dev/null +++ b/src/php/tests/data/server1.key @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAOHDFScoLCVJpYDD +M4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1BgzkWF+slf +3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd9N8YwbBY +AckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAECgYAn7qGnM2vbjJNBm0VZCkOkTIWm +V10okw7EPJrdL2mkre9NasghNXbE1y5zDshx5Nt3KsazKOxTT8d0Jwh/3KbaN+YY +tTCbKGW0pXDRBhwUHRcuRzScjli8Rih5UOCiZkhefUTcRb6xIhZJuQy71tjaSy0p +dHZRmYyBYO2YEQ8xoQJBAPrJPhMBkzmEYFtyIEqAxQ/o/A6E+E4w8i+KM7nQCK7q +K4JXzyXVAjLfyBZWHGM2uro/fjqPggGD6QH1qXCkI4MCQQDmdKeb2TrKRh5BY1LR +81aJGKcJ2XbcDu6wMZK4oqWbTX2KiYn9GB0woM6nSr/Y6iy1u145YzYxEV/iMwff +DJULAkB8B2MnyzOg0pNFJqBJuH29bKCcHa8gHJzqXhNO5lAlEbMK95p/P2Wi+4Hd +aiEIAF1BF326QJcvYKmwSmrORp85AkAlSNxRJ50OWrfMZnBgzVjDx3xG6KsFQVk2 +ol6VhqL6dFgKUORFUWBvnKSyhjJxurlPEahV6oo6+A+mPhFY8eUvAkAZQyTdupP3 +XEFQKctGz+9+gKkemDp7LBBMEMBXrGTLPhpEfcjv/7KPdnFHYmhYeBTBnuVmTVWe +F98XJ7tIFfJq +-----END PRIVATE KEY----- diff --git a/src/php/tests/data/server1.pem b/src/php/tests/data/server1.pem new file mode 100755 index 00000000..8e582e57 --- /dev/null +++ b/src/php/tests/data/server1.pem @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICmzCCAgSgAwIBAgIBAzANBgkqhkiG9w0BAQUFADBWMQswCQYDVQQGEwJBVTET +MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ +dHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0Y2EwHhcNMTQwNzIyMDYwMDU3WhcNMjQwNzE5 +MDYwMDU3WjBkMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV +BAcTB0NoaWNhZ28xFDASBgNVBAoTC0dvb2dsZSBJbmMuMRowGAYDVQQDFBEqLnRl +c3QuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA4cMVJygs +JUmlgMMzgdi0h1XoCR7+ww1pop04OMMyy7H/i0PJ2W6Y35+b4CM8QrkYeEafUGDO +RYX6yV/cHGGsD/x02ye6ey1UDtkGAD/mpDEx8YCrjAc1Vfvt8Fk6Cn1WVIxV/J30 +3xjBsFgByQ55RBp1OLZfVLo6AleBDSbcxaECAwEAAaNrMGkwCQYDVR0TBAIwADAL +BgNVHQ8EBAMCBeAwTwYDVR0RBEgwRoIQKi50ZXN0Lmdvb2dsZS5mcoIYd2F0ZXJ6 +b29pLnRlc3QuZ29vZ2xlLmJlghIqLnRlc3QueW91dHViZS5jb22HBMCoAQMwDQYJ +KoZIhvcNAQEFBQADgYEAM2Ii0LgTGbJ1j4oqX9bxVcxm+/R5Yf8oi0aZqTJlnLYS +wXcBykxTx181s7WyfJ49WwrYXo78zTDAnf1ma0fPq3e4mpspvyndLh1a+OarHa1e +aT0DIIYk7qeEa1YcVljx2KyLd0r1BBAfrwyGaEPVeJQVYWaOJRU2we/KD4ojf9s= +-----END CERTIFICATE----- diff --git a/src/php/tests/generated_code/AbstractGeneratedCodeTest.php b/src/php/tests/generated_code/AbstractGeneratedCodeTest.php new file mode 100644 index 00000000..9cee1886 --- /dev/null +++ b/src/php/tests/generated_code/AbstractGeneratedCodeTest.php @@ -0,0 +1,122 @@ +assertFalse(self::$client->waitForReady(1)); + } + + public function testWaitForReady() { + $this->assertTrue(self::$client->waitForReady(250000)); + } + + public function testGetTarget() { + $this->assertTrue(is_string(self::$client->getTarget())); + } + + public function testWriteFlags() { + $div_arg = new math\DivArgs(); + $div_arg->setDividend(7); + $div_arg->setDivisor(4); + $call = self::$client->Div($div_arg, array(), array('flags' => Grpc\WRITE_NO_COMPRESS)); + $this->assertTrue(is_string($call->getPeer())); + list($response, $status) = $call->wait(); + $this->assertSame(1, $response->getQuotient()); + $this->assertSame(3, $response->getRemainder()); + $this->assertSame(\Grpc\STATUS_OK, $status->code); + } + + public function testSimpleRequest() { + $div_arg = new math\DivArgs(); + $div_arg->setDividend(7); + $div_arg->setDivisor(4); + $call = self::$client->Div($div_arg); + $this->assertTrue(is_string($call->getPeer())); + list($response, $status) = $call->wait(); + $this->assertSame(1, $response->getQuotient()); + $this->assertSame(3, $response->getRemainder()); + $this->assertSame(\Grpc\STATUS_OK, $status->code); + } + + public function testServerStreaming() { + $fib_arg = new math\FibArgs(); + $fib_arg->setLimit(7); + $call = self::$client->Fib($fib_arg); + $this->assertTrue(is_string($call->getPeer())); + $result_array = iterator_to_array($call->responses()); + $extract_num = function($num){ + return $num->getNum(); + }; + $values = array_map($extract_num, $result_array); + $this->assertSame([1, 1, 2, 3, 5, 8, 13], $values); + $status = $call->getStatus(); + $this->assertSame(\Grpc\STATUS_OK, $status->code); + } + + public function testClientStreaming() { + $call = self::$client->Sum(); + $this->assertTrue(is_string($call->getPeer())); + for ($i = 0; $i < 7; $i++) { + $num = new math\Num(); + $num->setNum($i); + $call->write($num); + } + list($response, $status) = $call->wait(); + $this->assertSame(21, $response->getNum()); + $this->assertSame(\Grpc\STATUS_OK, $status->code); + } + + public function testBidiStreaming() { + $call = self::$client->DivMany(); + $this->assertTrue(is_string($call->getPeer())); + for ($i = 0; $i < 7; $i++) { + $div_arg = new math\DivArgs(); + $div_arg->setDividend(2 * $i + 1); + $div_arg->setDivisor(2); + $call->write($div_arg); + $response = $call->read(); + $this->assertSame($i, $response->getQuotient()); + $this->assertSame(1, $response->getRemainder()); + } + $call->writesDone(); + $status = $call->getStatus(); + $this->assertSame(\Grpc\STATUS_OK, $status->code); + } +} diff --git a/src/php/tests/generated_code/GeneratedCodeTest.php b/src/php/tests/generated_code/GeneratedCodeTest.php new file mode 100755 index 00000000..a1a2ce81 --- /dev/null +++ b/src/php/tests/generated_code/GeneratedCodeTest.php @@ -0,0 +1,41 @@ + + function($a_hash, + $client = array()) { + $a_copy = $a_hash; + $a_copy['foo'] = ['bar']; + return $a_copy; + }]); + } +} diff --git a/src/php/tests/generated_code/math.proto b/src/php/tests/generated_code/math.proto new file mode 100644 index 00000000..1de7d0b8 --- /dev/null +++ b/src/php/tests/generated_code/math.proto @@ -0,0 +1,80 @@ + +// Copyright 2015, 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. + +syntax = "proto2"; + +package math; + +message DivArgs { + optional int64 dividend = 1 [default = 0]; + optional int64 divisor = 2 [default = 0]; +} + +message DivReply { + optional int64 quotient = 1 [default = 0]; + optional int64 remainder = 2 [default = 0]; +} + +message FibArgs { + optional int64 limit = 1 [default = 0]; +} + +message Num { + optional int64 num = 1 [default = 0]; +} + +message FibReply { + optional int64 count = 1 [default = 0]; +} + +service Math { + // Div divides args.dividend by args.divisor and returns the quotient and + // remainder. + rpc Div (DivArgs) returns (DivReply) { + } + + // DivMany accepts an arbitrary number of division args from the client stream + // and sends back the results in the reply stream. The stream continues until + // the client closes its end; the server does the same after sending all the + // replies. The stream ends immediately if either end aborts. + rpc DivMany (stream DivArgs) returns (stream DivReply) { + } + + // Fib generates numbers in the Fibonacci sequence. If args.limit > 0, Fib + // generates up to limit numbers; otherwise it continues until the call is + // canceled. Unlike Fib above, Fib has no final FibReply. + rpc Fib (FibArgs) returns (stream Num) { + } + + // Sum sums a stream of numbers, returning the final result once the stream + // is closed. + rpc Sum (stream Num) returns (Num) { + } +} diff --git a/src/php/tests/generated_code/math_client.php b/src/php/tests/generated_code/math_client.php new file mode 100644 index 00000000..7bc78287 --- /dev/null +++ b/src/php/tests/generated_code/math_client.php @@ -0,0 +1,102 @@ +\n"); +} + +$host = "localhost:50051"; +p("Connecting to host: $host"); +$client = new math\MathClient($host, []); +p("Client class: ".get_class($client)); +p(''); + +p("Running unary call test:"); +$dividend = 7; +$divisor = 4; +$div_arg = new math\DivArgs(); +$div_arg->setDividend($dividend); +$div_arg->setDivisor($divisor); +$call = $client->Div($div_arg); +p("Call peer: ".$call->getPeer()); +p("Dividing $dividend by $divisor"); +list($response, $status) = $call->wait(); +p("quotient = ".$response->getQuotient()); +p("remainder = ".$response->getRemainder()); +p(''); + +p("Running server streaming test:"); +$limit = 7; +$fib_arg = new math\FibArgs(); +$fib_arg->setLimit($limit); +$call = $client->Fib($fib_arg); +$result_array = iterator_to_array($call->responses()); +$result = ''; +foreach ($result_array as $num) { + $result .= ' '.$num->getNum(); +} +p("The first $limit Fibonacci numbers are:".$result); +p(''); + +p("Running client streaming test:"); +$call = $client->Sum(); +for ($i = 0; $i <= $limit; $i++) { + $num = new math\Num(); + $num->setNum($i); + $call->write($num); +} +list($response, $status) = $call->wait(); +p(sprintf("The first %d positive integers sum to: %d", + $limit, $response->getNum())); +p(''); + +p("Running bidi-streaming test:"); +$call = $client->DivMany(); +for ($i = 0; $i < 7; $i++) { + $div_arg = new math\DivArgs(); + $dividend = 2 * $i + 1; + $divisor = 3; + $div_arg->setDividend($dividend); + $div_arg->setDivisor($divisor); + $call->write($div_arg); + p("client writing: $dividend / $divisor"); + $response = $call->read(); + p(sprintf("server writing: quotient = %d, remainder = %d", + $response->getQuotient(), $response->getRemainder())); +} +$call->writesDone(); diff --git a/src/php/tests/interop/empty.proto b/src/php/tests/interop/empty.proto new file mode 100644 index 00000000..4200d7b5 --- /dev/null +++ b/src/php/tests/interop/empty.proto @@ -0,0 +1,43 @@ + +// Copyright 2015, 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. + +syntax = "proto2"; + +package grpc.testing; + +// An empty message that you can re-use to avoid defining duplicated empty +// messages in your project. A typical example is to use it as argument or the +// return value of a service API. For instance: +// +// service Foo { +// rpc Bar (grpc.testing.EmptyMessage) returns (grpc.testing.EmptyMessage) { }; +// }; +// +message EmptyMessage {} diff --git a/src/php/tests/interop/interop_client.php b/src/php/tests/interop/interop_client.php new file mode 100755 index 00000000..d55d5629 --- /dev/null +++ b/src/php/tests/interop/interop_client.php @@ -0,0 +1,394 @@ +EmptyCall(new grpc\testing\EmptyMessage())->wait(); + hardAssert($status->code === Grpc\STATUS_OK, 'Call did not complete successfully'); + hardAssert($result !== null, 'Call completed with a null response'); +} + +/** + * Run the large_unary test. + * Passes when run against the C++/Node server as of 2015-04-30 + * @param $stub Stub object that has service methods + */ +function largeUnary($stub) { + performLargeUnary($stub); +} + +/** + * Shared code between large unary test and auth test + * @param $stub Stub object that has service methods + * @param $fillUsername boolean whether to fill result with username + * @param $fillOauthScope boolean whether to fill result with oauth scope + */ +function performLargeUnary($stub, $fillUsername = false, $fillOauthScope = false) { + $request_len = 271828; + $response_len = 314159; + + $request = new grpc\testing\SimpleRequest(); + $request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE); + $request->setResponseSize($response_len); + $payload = new grpc\testing\Payload(); + $payload->setType(grpc\testing\PayloadType::COMPRESSABLE); + $payload->setBody(str_repeat("\0", $request_len)); + $request->setPayload($payload); + $request->setFillUsername($fillUsername); + $request->setFillOauthScope($fillOauthScope); + + list($result, $status) = $stub->UnaryCall($request)->wait(); + hardAssert($status->code === Grpc\STATUS_OK, 'Call did not complete successfully'); + hardAssert($result !== null, 'Call returned a null response'); + $payload = $result->getPayload(); + hardAssert($payload->getType() === grpc\testing\PayloadType::COMPRESSABLE, + 'Payload had the wrong type'); + hardAssert(strlen($payload->getBody()) === $response_len, + 'Payload had the wrong length'); + hardAssert($payload->getBody() === str_repeat("\0", $response_len), + 'Payload had the wrong content'); + return $result; +} + +/** + * Run the service account credentials auth test. + * Passes when run against the cloud server as of 2015-04-30 + * @param $stub Stub object that has service methods + * @param $args array command line args + */ +function serviceAccountCreds($stub, $args) { + if (!array_key_exists('oauth_scope', $args)) { + throw new Exception('Missing oauth scope'); + } + $jsonKey = json_decode( + file_get_contents(getenv(Google\Auth\CredentialsLoader::ENV_VAR)), + true); + $result = performLargeUnary($stub, $fillUsername=true, $fillOauthScope=true); + hardAssert($result->getUsername() == $jsonKey['client_email'], + 'invalid email returned'); + hardAssert(strpos($args['oauth_scope'], $result->getOauthScope()) !== false, + 'invalid oauth scope returned'); +} + +/** + * Run the compute engine credentials auth test. + * Has not been run from gcloud as of 2015-05-05 + * @param $stub Stub object that has service methods + * @param $args array command line args + */ +function computeEngineCreds($stub, $args) { + if (!array_key_exists('oauth_scope', $args)) { + throw new Exception('Missing oauth scope'); + } + if (!array_key_exists('default_service_account', $args)) { + throw new Exception('Missing default_service_account'); + } + $result = performLargeUnary($stub, $fillUsername=true, $fillOauthScope=true); + hardAssert($args['default_service_account'] == $result->getUsername(), + 'invalid email returned'); +} + +/** + * Run the jwt token credentials auth test. + * Passes when run against the cloud server as of 2015-05-12 + * @param $stub Stub object that has service methods + * @param $args array command line args + */ +function jwtTokenCreds($stub, $args) { + $jsonKey = json_decode( + file_get_contents(getenv(Google\Auth\CredentialsLoader::ENV_VAR)), + true); + $result = performLargeUnary($stub, $fillUsername=true, $fillOauthScope=true); + hardAssert($result->getUsername() == $jsonKey['client_email'], + 'invalid email returned'); +} + +/** + * Run the client_streaming test. + * Passes when run against the Node server as of 2015-04-30 + * @param $stub Stub object that has service methods + */ +function clientStreaming($stub) { + $request_lengths = array(27182, 8, 1828, 45904); + + $requests = array_map( + function($length) { + $request = new grpc\testing\StreamingInputCallRequest(); + $payload = new grpc\testing\Payload(); + $payload->setBody(str_repeat("\0", $length)); + $request->setPayload($payload); + return $request; + }, $request_lengths); + + $call = $stub->StreamingInputCall(); + foreach ($requests as $request) { + $call->write($request); + } + list($result, $status) = $call->wait(); + hardAssert($status->code === Grpc\STATUS_OK, 'Call did not complete successfully'); + hardAssert($result->getAggregatedPayloadSize() === 74922, + 'aggregated_payload_size was incorrect'); +} + +/** + * Run the server_streaming test. + * Passes when run against the Node server as of 2015-04-30 + * @param $stub Stub object that has service methods. + */ +function serverStreaming($stub) { + $sizes = array(31415, 9, 2653, 58979); + + $request = new grpc\testing\StreamingOutputCallRequest(); + $request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE); + foreach($sizes as $size) { + $response_parameters = new grpc\testing\ResponseParameters(); + $response_parameters->setSize($size); + $request->addResponseParameters($response_parameters); + } + + $call = $stub->StreamingOutputCall($request); + $i = 0; + foreach($call->responses() as $value) { + hardAssert($i < 4, 'Too many responses'); + $payload = $value->getPayload(); + hardAssert($payload->getType() === grpc\testing\PayloadType::COMPRESSABLE, + 'Payload ' . $i . ' had the wrong type'); + hardAssert(strlen($payload->getBody()) === $sizes[$i], + 'Response ' . $i . ' had the wrong length'); + $i += 1; + } + hardAssert($call->getStatus()->code === Grpc\STATUS_OK, + 'Call did not complete successfully'); +} + +/** + * Run the ping_pong test. + * Passes when run against the Node server as of 2015-04-30 + * @param $stub Stub object that has service methods. + */ +function pingPong($stub) { + $request_lengths = array(27182, 8, 1828, 45904); + $response_lengths = array(31415, 9, 2653, 58979); + + $call = $stub->FullDuplexCall(); + for($i = 0; $i < 4; $i++) { + $request = new grpc\testing\StreamingOutputCallRequest(); + $request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE); + $response_parameters = new grpc\testing\ResponseParameters(); + $response_parameters->setSize($response_lengths[$i]); + $request->addResponseParameters($response_parameters); + $payload = new grpc\testing\Payload(); + $payload->setBody(str_repeat("\0", $request_lengths[$i])); + $request->setPayload($payload); + + $call->write($request); + $response = $call->read(); + + hardAssert($response !== null, 'Server returned too few responses'); + $payload = $response->getPayload(); + hardAssert($payload->getType() === grpc\testing\PayloadType::COMPRESSABLE, + 'Payload ' . $i . ' had the wrong type'); + hardAssert(strlen($payload->getBody()) === $response_lengths[$i], + 'Payload ' . $i . ' had the wrong length'); + } + $call->writesDone(); + hardAssert($call->read() === null, 'Server returned too many responses'); + hardAssert($call->getStatus()->code === Grpc\STATUS_OK, + 'Call did not complete successfully'); +} + +/** + * Run the cancel_after_begin test. + * Passes when run against the Node server as of 2015-08-28 + * @param $stub Stub object that has service methods. + */ +function cancelAfterBegin($stub) { + $call = $stub->StreamingInputCall(); + $call->cancel(); + list($result, $status) = $call->wait(); + hardAssert($status->code === Grpc\STATUS_CANCELLED, + 'Call status was not CANCELLED'); +} + +/** + * Run the cancel_after_first_response test. + * Passes when run against the Node server as of 2015-04-30 + * @param $stub Stub object that has service methods. + */ +function cancelAfterFirstResponse($stub) { + $call = $stub->FullDuplexCall(); + $request = new grpc\testing\StreamingOutputCallRequest(); + $request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE); + $response_parameters = new grpc\testing\ResponseParameters(); + $response_parameters->setSize(31415); + $request->addResponseParameters($response_parameters); + $payload = new grpc\testing\Payload(); + $payload->setBody(str_repeat("\0", 27182)); + $request->setPayload($payload); + + $call->write($request); + $response = $call->read(); + + $call->cancel(); + hardAssert($call->getStatus()->code === Grpc\STATUS_CANCELLED, + 'Call status was not CANCELLED'); +} + +function timeoutOnSleepingServer($stub) { + $call = $stub->FullDuplexCall(array('timeout' => 1000)); + $request = new grpc\testing\StreamingOutputCallRequest(); + $request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE); + $response_parameters = new grpc\testing\ResponseParameters(); + $response_parameters->setSize(8); + $request->addResponseParameters($response_parameters); + $payload = new grpc\testing\Payload(); + $payload->setBody(str_repeat("\0", 9)); + $request->setPayload($payload); + + $call->write($request); + $response = $call->read(); + + hardAssert($call->getStatus()->code === Grpc\STATUS_DEADLINE_EXCEEDED, + 'Call status was not DEADLINE_EXCEEDED'); +} + +$args = getopt('', array('server_host:', 'server_port:', 'test_case:', + 'server_host_override:', 'oauth_scope:', + 'default_service_account:')); +if (!array_key_exists('server_host', $args) || + !array_key_exists('server_port', $args) || + !array_key_exists('test_case', $args)) { + throw new Exception('Missing argument'); +} + +if ($args['server_port'] == 443) { + $server_address = $args['server_host']; +} else { + $server_address = $args['server_host'] . ':' . $args['server_port']; +} + +if (!array_key_exists('server_host_override', $args)) { + $args['server_host_override'] = 'foo.test.google.fr'; +} + +$ssl_cert_file = getenv('SSL_CERT_FILE'); +if (!$ssl_cert_file) { + $ssl_cert_file = dirname(__FILE__) . '/../data/ca.pem'; +} + +$credentials = Grpc\Credentials::createSsl(file_get_contents($ssl_cert_file)); + +$opts = [ + 'grpc.ssl_target_name_override' => $args['server_host_override'], + 'credentials' => $credentials, + ]; + +if (in_array($args['test_case'], array( + 'service_account_creds', + 'compute_engine_creds', + 'jwt_token_creds'))) { + if ($args['test_case'] == 'jwt_token_creds') { + $auth = Google\Auth\ApplicationDefaultCredentials::getCredentials(); + } else { + $auth = Google\Auth\ApplicationDefaultCredentials::getCredentials( + $args['oauth_scope']); + } + $opts['update_metadata'] = $auth->getUpdateMetadataFunc(); +} + +$stub = new grpc\testing\TestServiceClient($server_address, $opts); + +echo "Connecting to $server_address\n"; +echo "Running test case $args[test_case]\n"; + +switch ($args['test_case']) { + case 'empty_unary': + emptyUnary($stub); + break; + case 'large_unary': + largeUnary($stub); + break; + case 'client_streaming': + clientStreaming($stub); + break; + case 'server_streaming': + serverStreaming($stub); + break; + case 'ping_pong': + pingPong($stub); + break; + case 'cancel_after_begin': + cancelAfterBegin($stub); + break; + case 'cancel_after_first_response': + cancelAfterFirstResponse($stub); + break; + case 'timeout_on_sleeping_server': + timeoutOnSleepingServer($stub); + break; + case 'service_account_creds': + serviceAccountCreds($stub, $args); + break; + case 'compute_engine_creds': + computeEngineCreds($stub, $args); + break; + case 'jwt_token_creds': + jwtTokenCreds($stub, $args); + break; + default: + echo "Unsupported test case $args[test_case]\n"; + exit(1); +} diff --git a/src/php/tests/interop/message_set.php b/src/php/tests/interop/message_set.php new file mode 100755 index 00000000..c35c6d74 --- /dev/null +++ b/src/php/tests/interop/message_set.php @@ -0,0 +1,26 @@ +addField($cb(), true); + } + + return $descriptor; + } + } +} + diff --git a/src/php/tests/interop/messages.proto b/src/php/tests/interop/messages.proto new file mode 100644 index 00000000..de0b1a23 --- /dev/null +++ b/src/php/tests/interop/messages.proto @@ -0,0 +1,132 @@ + +// Copyright 2015, 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. + +// Message definitions to be used by integration test service definitions. + +syntax = "proto2"; + +package grpc.testing; + +// The type of payload that should be returned. +enum PayloadType { + // Compressable text format. + COMPRESSABLE = 0; + + // Uncompressable binary format. + UNCOMPRESSABLE = 1; + + // Randomly chosen from all other formats defined in this enum. + RANDOM = 2; +} + +// A block of data, to simply increase gRPC message size. +message Payload { + // The type of data in body. + optional PayloadType type = 1 [default = COMPRESSABLE]; + // Primary contents of payload. + optional bytes body = 2; +} + +// Unary request. +message SimpleRequest { + // Desired payload type in the response from the server. + // If response_type is RANDOM, server randomly chooses one from other formats. + optional PayloadType response_type = 1 [default = COMPRESSABLE]; + + // Desired payload size in the response from the server. + // If response_type is COMPRESSABLE, this denotes the size before compression. + optional int32 response_size = 2; + + // Optional input payload sent along with the request. + optional Payload payload = 3; + + // Whether SimpleResponse should include username. + optional bool fill_username = 4; + + // Whether SimpleResponse should include OAuth scope. + optional bool fill_oauth_scope = 5; +} + +// Unary response, as configured by the request. +message SimpleResponse { + // Payload to increase message size. + optional Payload payload = 1; + // The user the request came from, for verifying authentication was + // successful when the client expected it. + optional string username = 2; + // OAuth scope. + optional string oauth_scope = 3; +} + +// Client-streaming request. +message StreamingInputCallRequest { + // Optional input payload sent along with the request. + optional Payload payload = 1; + + // Not expecting any payload from the response. +} + +// Client-streaming response. +message StreamingInputCallResponse { + // Aggregated size of payloads received from the client. + optional int32 aggregated_payload_size = 1; +} + +// Configuration for a particular response. +message ResponseParameters { + // Desired payload sizes in responses from the server. + // If response_type is COMPRESSABLE, this denotes the size before compression. + optional int32 size = 1; + + // Desired interval between consecutive responses in the response stream in + // microseconds. + optional int32 interval_us = 2; +} + +// Server-streaming request. +message StreamingOutputCallRequest { + // Desired payload type in the response from the server. + // If response_type is RANDOM, the payload from each response in the stream + // might be of different types. This is to simulate a mixed type of payload + // stream. + optional PayloadType response_type = 1 [default = COMPRESSABLE]; + + // Configuration for each expected response message. + repeated ResponseParameters response_parameters = 2; + + // Optional input payload sent along with the request. + optional Payload payload = 3; +} + +// Server-streaming response, as configured by the request and parameters. +message StreamingOutputCallResponse { + // Payload to increase response size. + optional Payload payload = 1; +} diff --git a/src/php/tests/interop/test.proto b/src/php/tests/interop/test.proto new file mode 100644 index 00000000..39c08f35 --- /dev/null +++ b/src/php/tests/interop/test.proto @@ -0,0 +1,72 @@ + +// Copyright 2015, 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. + +// An integration test service that covers all the method signature permutations +// of unary/streaming requests/responses. +syntax = "proto2"; + +import "empty.proto"; +import "messages.proto"; + +package grpc.testing; + +// A simple service to test the various types of RPCs and experiment with +// performance with various types of payload. +service TestService { + // One empty request followed by one empty response. + rpc EmptyCall(grpc.testing.EmptyMessage) returns (grpc.testing.EmptyMessage); + + // One request followed by one response. + // TODO(Issue 527): Describe required server behavior. + rpc UnaryCall(SimpleRequest) returns (SimpleResponse); + + // One request followed by a sequence of responses (streamed download). + // The server returns the payload with client desired type and sizes. + rpc StreamingOutputCall(StreamingOutputCallRequest) + returns (stream StreamingOutputCallResponse); + + // A sequence of requests followed by one response (streamed upload). + // The server returns the aggregated size of client payload as the result. + rpc StreamingInputCall(stream StreamingInputCallRequest) + returns (StreamingInputCallResponse); + + // A sequence of requests with each request served by the server immediately. + // As one request could lead to multiple responses, this interface + // demonstrates the idea of full duplexing. + rpc FullDuplexCall(stream StreamingOutputCallRequest) + returns (stream StreamingOutputCallResponse); + + // A sequence of requests followed by a sequence of responses. + // The server buffers all the client requests and then serves them in order. A + // stream of responses are returned to the client when the server starts with + // first request. + rpc HalfDuplexCall(stream StreamingOutputCallRequest) + returns (stream StreamingOutputCallResponse); +} diff --git a/src/php/tests/unit_tests/CallTest.php b/src/php/tests/unit_tests/CallTest.php new file mode 100755 index 00000000..caff15ee --- /dev/null +++ b/src/php/tests/unit_tests/CallTest.php @@ -0,0 +1,86 @@ +addHttp2Port('0.0.0.0:0'); + } + + public function setUp() { + $this->channel = new Grpc\Channel('localhost:' . self::$port, []); + $this->call = new Grpc\Call($this->channel, + '/foo', + Grpc\Timeval::infFuture()); + } + + public function testAddEmptyMetadata() { + $batch = [ + Grpc\OP_SEND_INITIAL_METADATA => [] + ]; + $result = $this->call->startBatch($batch); + $this->assertTrue($result->send_metadata); + } + + public function testAddSingleMetadata() { + $batch = [ + Grpc\OP_SEND_INITIAL_METADATA => ['key' => ['value']] + ]; + $result = $this->call->startBatch($batch); + $this->assertTrue($result->send_metadata); + } + + public function testAddMultiValueMetadata() { + $batch = [ + Grpc\OP_SEND_INITIAL_METADATA => ['key' => ['value1', 'value2']] + ]; + $result = $this->call->startBatch($batch); + $this->assertTrue($result->send_metadata); + } + + public function testAddSingleAndMultiValueMetadata() { + $batch = [ + Grpc\OP_SEND_INITIAL_METADATA => ['key1' => ['value1'], + 'key2' => ['value2', 'value3']] + ]; + $result = $this->call->startBatch($batch); + $this->assertTrue($result->send_metadata); + } + + public function testGetPeer() { + $this->assertTrue(is_string($this->call->getPeer())); + } +} diff --git a/src/php/tests/unit_tests/EndToEndTest.php b/src/php/tests/unit_tests/EndToEndTest.php new file mode 100755 index 00000000..bd464f93 --- /dev/null +++ b/src/php/tests/unit_tests/EndToEndTest.php @@ -0,0 +1,247 @@ +server = new Grpc\Server([]); + $this->port = $this->server->addHttp2Port('0.0.0.0:0'); + $this->channel = new Grpc\Channel('localhost:' . $this->port, []); + $this->server->start(); + } + + public function tearDown() { + unset($this->channel); + unset($this->server); + } + + public function testSimpleRequestBody() { + $deadline = Grpc\Timeval::infFuture(); + $status_text = 'xyz'; + $call = new Grpc\Call($this->channel, + 'dummy_method', + $deadline); + + $event = $call->startBatch([ + Grpc\OP_SEND_INITIAL_METADATA => [], + Grpc\OP_SEND_CLOSE_FROM_CLIENT => true + ]); + + $this->assertTrue($event->send_metadata); + $this->assertTrue($event->send_close); + + $event = $this->server->requestCall(); + $this->assertSame('dummy_method', $event->method); + $server_call = $event->call; + + $event = $server_call->startBatch([ + Grpc\OP_SEND_INITIAL_METADATA => [], + Grpc\OP_SEND_STATUS_FROM_SERVER => [ + 'metadata' => [], + 'code' => Grpc\STATUS_OK, + 'details' => $status_text + ], + Grpc\OP_RECV_CLOSE_ON_SERVER => true + ]); + + $this->assertTrue($event->send_metadata); + $this->assertTrue($event->send_status); + $this->assertFalse($event->cancelled); + + $event = $call->startBatch([ + Grpc\OP_RECV_INITIAL_METADATA => true, + Grpc\OP_RECV_STATUS_ON_CLIENT => true + ]); + + $status = $event->status; + $this->assertSame([], $status->metadata); + $this->assertSame(Grpc\STATUS_OK, $status->code); + $this->assertSame($status_text, $status->details); + + unset($call); + unset($server_call); + } + + public function testMessageWriteFlags() { + $deadline = Grpc\Timeval::infFuture(); + $req_text = 'message_write_flags_test'; + $status_text = 'xyz'; + $call = new Grpc\Call($this->channel, + 'dummy_method', + $deadline); + + $event = $call->startBatch([ + Grpc\OP_SEND_INITIAL_METADATA => [], + Grpc\OP_SEND_MESSAGE => ['message' => $req_text, + 'flags' => Grpc\WRITE_NO_COMPRESS], + Grpc\OP_SEND_CLOSE_FROM_CLIENT => true + ]); + + $this->assertTrue($event->send_metadata); + $this->assertTrue($event->send_close); + + $event = $this->server->requestCall(); + $this->assertSame('dummy_method', $event->method); + $server_call = $event->call; + + $event = $server_call->startBatch([ + Grpc\OP_SEND_INITIAL_METADATA => [], + Grpc\OP_SEND_STATUS_FROM_SERVER => [ + 'metadata' => [], + 'code' => Grpc\STATUS_OK, + 'details' => $status_text + ], + ]); + + $event = $call->startBatch([ + Grpc\OP_RECV_INITIAL_METADATA => true, + Grpc\OP_RECV_STATUS_ON_CLIENT => true + ]); + + $status = $event->status; + $this->assertSame([], $status->metadata); + $this->assertSame(Grpc\STATUS_OK, $status->code); + $this->assertSame($status_text, $status->details); + + unset($call); + unset($server_call); + } + + public function testClientServerFullRequestResponse() { + $deadline = Grpc\Timeval::infFuture(); + $req_text = 'client_server_full_request_response'; + $reply_text = 'reply:client_server_full_request_response'; + $status_text = 'status:client_server_full_response_text'; + + $call = new Grpc\Call($this->channel, + 'dummy_method', + $deadline); + + $event = $call->startBatch([ + Grpc\OP_SEND_INITIAL_METADATA => [], + Grpc\OP_SEND_CLOSE_FROM_CLIENT => true, + Grpc\OP_SEND_MESSAGE => ['message' => $req_text], + ]); + + $this->assertTrue($event->send_metadata); + $this->assertTrue($event->send_close); + $this->assertTrue($event->send_message); + + $event = $this->server->requestCall(); + $this->assertSame('dummy_method', $event->method); + $server_call = $event->call; + + $event = $server_call->startBatch([ + Grpc\OP_SEND_INITIAL_METADATA => [], + Grpc\OP_SEND_MESSAGE => ['message' => $reply_text], + Grpc\OP_SEND_STATUS_FROM_SERVER => [ + 'metadata' => [], + 'code' => Grpc\STATUS_OK, + 'details' => $status_text + ], + Grpc\OP_RECV_MESSAGE => true, + Grpc\OP_RECV_CLOSE_ON_SERVER => true, + ]); + + $this->assertTrue($event->send_metadata); + $this->assertTrue($event->send_status); + $this->assertTrue($event->send_message); + $this->assertFalse($event->cancelled); + $this->assertSame($req_text, $event->message); + + $event = $call->startBatch([ + Grpc\OP_RECV_INITIAL_METADATA => true, + Grpc\OP_RECV_MESSAGE => true, + Grpc\OP_RECV_STATUS_ON_CLIENT => true, + ]); + + $this->assertSame([], $event->metadata); + $this->assertSame($reply_text, $event->message); + $status = $event->status; + $this->assertSame([], $status->metadata); + $this->assertSame(Grpc\STATUS_OK, $status->code); + $this->assertSame($status_text, $status->details); + + unset($call); + unset($server_call); + } + + public function testGetTarget() { + $this->assertTrue(is_string($this->channel->getTarget())); + } + + public function testGetConnectivityState() { + $this->assertTrue($this->channel->getConnectivityState() == Grpc\CHANNEL_IDLE); + } + + public function testWatchConnectivityStateFailed() { + $idle_state = $this->channel->getConnectivityState(true); + $this->assertTrue($idle_state == Grpc\CHANNEL_IDLE); + + $now = Grpc\Timeval::now(); + $delta = new Grpc\Timeval(1); + $deadline = $now->add($delta); + + $this->assertFalse($this->channel->watchConnectivityState( + $idle_state, $deadline)); + } + + public function testWatchConnectivityStateSuccess() { + $idle_state = $this->channel->getConnectivityState(true); + $this->assertTrue($idle_state == Grpc\CHANNEL_IDLE); + + $now = Grpc\Timeval::now(); + $delta = new Grpc\Timeval(3000000); // should finish well before + $deadline = $now->add($delta); + + $this->assertTrue($this->channel->watchConnectivityState( + $idle_state, $deadline)); + + $new_state = $this->channel->getConnectivityState(); + $this->assertTrue($idle_state != $new_state); + } + + public function testWatchConnectivityStateDoNothing() { + $idle_state = $this->channel->getConnectivityState(); + $this->assertTrue($idle_state == Grpc\CHANNEL_IDLE); + + $now = Grpc\Timeval::now(); + $delta = new Grpc\Timeval(100000); + $deadline = $now->add($delta); + + $this->assertFalse($this->channel->watchConnectivityState( + $idle_state, $deadline)); + + $new_state = $this->channel->getConnectivityState(); + $this->assertTrue($new_state == Grpc\CHANNEL_IDLE); + } +} diff --git a/src/php/tests/unit_tests/SecureEndToEndTest.php b/src/php/tests/unit_tests/SecureEndToEndTest.php new file mode 100755 index 00000000..d7fca14a --- /dev/null +++ b/src/php/tests/unit_tests/SecureEndToEndTest.php @@ -0,0 +1,216 @@ +server = new Grpc\Server(); + $this->port = $this->server->addSecureHttp2Port('0.0.0.0:0', + $server_credentials); + $this->server->start(); + $this->host_override = 'foo.test.google.fr'; + $this->channel = new Grpc\Channel( + 'localhost:' . $this->port, + [ + 'grpc.ssl_target_name_override' => $this->host_override, + 'grpc.default_authority' => $this->host_override, + 'credentials' => $credentials + ]); + } + + public function tearDown() { + unset($this->channel); + unset($this->server); + } + + public function testSimpleRequestBody() { + $deadline = Grpc\Timeval::infFuture(); + $status_text = 'xyz'; + $call = new Grpc\Call($this->channel, + 'dummy_method', + $deadline, + $this->host_override); + + $event = $call->startBatch([ + Grpc\OP_SEND_INITIAL_METADATA => [], + Grpc\OP_SEND_CLOSE_FROM_CLIENT => true + ]); + + $this->assertTrue($event->send_metadata); + $this->assertTrue($event->send_close); + + $event = $this->server->requestCall(); + $this->assertSame('dummy_method', $event->method); + $server_call = $event->call; + + $event = $server_call->startBatch([ + Grpc\OP_SEND_INITIAL_METADATA => [], + Grpc\OP_SEND_STATUS_FROM_SERVER => [ + 'metadata' => [], + 'code' => Grpc\STATUS_OK, + 'details' => $status_text + ], + Grpc\OP_RECV_CLOSE_ON_SERVER => true + ]); + + $this->assertTrue($event->send_metadata); + $this->assertTrue($event->send_status); + $this->assertFalse($event->cancelled); + + $event = $call->startBatch([ + Grpc\OP_RECV_INITIAL_METADATA => true, + Grpc\OP_RECV_STATUS_ON_CLIENT => true + ]); + + $this->assertSame([], $event->metadata); + $status = $event->status; + $this->assertSame([], $status->metadata); + $this->assertSame(Grpc\STATUS_OK, $status->code); + $this->assertSame($status_text, $status->details); + + unset($call); + unset($server_call); + } + + public function testMessageWriteFlags() { + $deadline = Grpc\Timeval::infFuture(); + $req_text = 'message_write_flags_test'; + $status_text = 'xyz'; + $call = new Grpc\Call($this->channel, + 'dummy_method', + $deadline, + $this->host_override); + + $event = $call->startBatch([ + Grpc\OP_SEND_INITIAL_METADATA => [], + Grpc\OP_SEND_MESSAGE => ['message' => $req_text, + 'flags' => Grpc\WRITE_NO_COMPRESS], + Grpc\OP_SEND_CLOSE_FROM_CLIENT => true + ]); + + $this->assertTrue($event->send_metadata); + $this->assertTrue($event->send_close); + + $event = $this->server->requestCall(); + $this->assertSame('dummy_method', $event->method); + $server_call = $event->call; + + $event = $server_call->startBatch([ + Grpc\OP_SEND_INITIAL_METADATA => [], + Grpc\OP_SEND_STATUS_FROM_SERVER => [ + 'metadata' => [], + 'code' => Grpc\STATUS_OK, + 'details' => $status_text + ], + ]); + + $event = $call->startBatch([ + Grpc\OP_RECV_INITIAL_METADATA => true, + Grpc\OP_RECV_STATUS_ON_CLIENT => true + ]); + + $this->assertSame([], $event->metadata); + $status = $event->status; + $this->assertSame([], $status->metadata); + $this->assertSame(Grpc\STATUS_OK, $status->code); + $this->assertSame($status_text, $status->details); + + unset($call); + unset($server_call); + } + + public function testClientServerFullRequestResponse() { + $deadline = Grpc\Timeval::infFuture(); + $req_text = 'client_server_full_request_response'; + $reply_text = 'reply:client_server_full_request_response'; + $status_text = 'status:client_server_full_response_text'; + + $call = new Grpc\Call($this->channel, + 'dummy_method', + $deadline, + $this->host_override); + + $event = $call->startBatch([ + Grpc\OP_SEND_INITIAL_METADATA => [], + Grpc\OP_SEND_CLOSE_FROM_CLIENT => true, + Grpc\OP_SEND_MESSAGE => ['message' => $req_text] + ]); + + $this->assertTrue($event->send_metadata); + $this->assertTrue($event->send_close); + $this->assertTrue($event->send_message); + + $event = $this->server->requestCall(); + $this->assertSame('dummy_method', $event->method); + $server_call = $event->call; + + $event = $server_call->startBatch([ + Grpc\OP_SEND_INITIAL_METADATA => [], + Grpc\OP_SEND_MESSAGE => ['message' => $reply_text], + Grpc\OP_SEND_STATUS_FROM_SERVER => [ + 'metadata' => [], + 'code' => Grpc\STATUS_OK, + 'details' => $status_text + ], + Grpc\OP_RECV_MESSAGE => true, + Grpc\OP_RECV_CLOSE_ON_SERVER => true, + ]); + + $this->assertTrue($event->send_metadata); + $this->assertTrue($event->send_status); + $this->assertTrue($event->send_message); + $this->assertFalse($event->cancelled); + $this->assertSame($req_text, $event->message); + + $event = $call->startBatch([ + Grpc\OP_RECV_INITIAL_METADATA => true, + Grpc\OP_RECV_MESSAGE => true, + Grpc\OP_RECV_STATUS_ON_CLIENT => true, + ]); + + $this->assertSame([], $event->metadata); + $this->assertSame($reply_text, $event->message); + $status = $event->status; + $this->assertSame([], $status->metadata); + $this->assertSame(Grpc\STATUS_OK, $status->code); + $this->assertSame($status_text, $status->details); + + unset($call); + unset($server_call); + } +} diff --git a/src/php/tests/unit_tests/TimevalTest.php b/src/php/tests/unit_tests/TimevalTest.php new file mode 100755 index 00000000..7b4925ca --- /dev/null +++ b/src/php/tests/unit_tests/TimevalTest.php @@ -0,0 +1,86 @@ +assertSame(0, Grpc\Timeval::compare($zero, $zero)); + } + + public function testPastIsLessThanZero() { + $zero = Grpc\Timeval::zero(); + $past = Grpc\Timeval::infPast(); + $this->assertLessThan(0, Grpc\Timeval::compare($past, $zero)); + $this->assertGreaterThan(0, Grpc\Timeval::compare($zero, $past)); + } + + public function testFutureIsGreaterThanZero() { + $zero = Grpc\Timeval::zero(); + $future = Grpc\Timeval::infFuture(); + $this->assertLessThan(0, Grpc\Timeval::compare($zero, $future)); + $this->assertGreaterThan(0, Grpc\Timeval::compare($future, $zero)); + } + + /** + * @depends testFutureIsGreaterThanZero + */ + public function testNowIsBetweenZeroAndFuture() { + $zero = Grpc\Timeval::zero(); + $future = Grpc\Timeval::infFuture(); + $now = Grpc\Timeval::now(); + $this->assertLessThan(0, Grpc\Timeval::compare($zero, $now)); + $this->assertLessThan(0, Grpc\Timeval::compare($now, $future)); + } + + public function testNowAndAdd() { + $now = Grpc\Timeval::now(); + $delta = new Grpc\Timeval(1000); + $deadline = $now->add($delta); + $this->assertGreaterThan(0, Grpc\Timeval::compare($deadline, $now)); + } + + public function testNowAndSubtract() { + $now = Grpc\Timeval::now(); + $delta = new Grpc\Timeval(1000); + $deadline = $now->subtract($delta); + $this->assertLessThan(0, Grpc\Timeval::compare($deadline, $now)); + } + + public function testAddAndSubtract() { + $now = Grpc\Timeval::now(); + $delta = new Grpc\Timeval(1000); + $deadline = $now->add($delta); + $back_to_now = $deadline->subtract($delta); + $this->assertSame(0, Grpc\Timeval::compare($back_to_now, $now)); + } +} diff --git a/src/python/README.md b/src/python/README.md new file mode 100644 index 00000000..afe7c731 --- /dev/null +++ b/src/python/README.md @@ -0,0 +1,95 @@ +gRPC Python +========= +The Python facility of gRPC. + +Status +------- +Beta : Core behavior well-used and proven; bugs lurk off the beaten path. + +PREREQUISITES +------------- +- Python 2.7, virtualenv, pip +- [homebrew][] on Mac OS X. These simplify the installation of the gRPC C core. + +INSTALLATION +------------- + +**Linux (Debian):** + +Add [Debian jessie-backports][] to your `sources.list` file. Example: + +```sh +echo "deb http://http.debian.net/debian jessie-backports main" | \ +sudo tee -a /etc/apt/sources.list +``` + +Install the gRPC Debian package + +```sh +sudo apt-get update +sudo apt-get install libgrpc-dev +``` + +Install the gRPC Python module + +```sh +sudo pip install grpcio +``` + +**Mac OS X** + +Install [homebrew][]. Run the following command to install gRPC Python. +```sh +$ curl -fsSL https://goo.gl/getgrpc | bash -s python +``` +This will download and run the [gRPC install script][], then install the latest version of the gRPC Python package. It also installs the Protocol Buffers compiler (_protoc_) and the gRPC _protoc_ plugin for python. + +EXAMPLES +-------- +Please read our online documentation for a [Quick Start][] and a [detailed example][] + +BUILDING FROM SOURCE +--------------------- +- Clone this repository + +- Initialize the git submodules +``` +$ git submodule update --init +``` + +- Make the libraries +``` +$ make +``` + +- Use build_python.sh to build the Python code and install it into a virtual environment +``` +$ CONFIG=opt tools/run_tests/build_python.sh 2.7 +``` + +TESTING +------- + +- Use run_python.sh to run gRPC as it was installed into the virtual environment +``` +$ CONFIG=opt PYVER=2.7 tools/run_tests/run_python.sh +``` + +PACKAGING +--------- + +- Install packaging dependencies +``` +$ pip install setuptools twine +``` + +- Push to PyPI +``` +$ ../../tools/distrib/python/submit.py +``` + +[homebrew]:http://brew.sh +[gRPC install script]:https://raw.githubusercontent.com/grpc/homebrew-grpc/master/scripts/install +[Quick Start]:http://www.grpc.io/docs/tutorials/basic/python.html +[detailed example]:http://www.grpc.io/docs/installation/python.html +[Debian jessie-backports]:http://backports.debian.org/Instructions/ diff --git a/src/python/grpcio/.gitignore b/src/python/grpcio/.gitignore new file mode 100644 index 00000000..4c02b8d1 --- /dev/null +++ b/src/python/grpcio/.gitignore @@ -0,0 +1,9 @@ +MANIFEST +*.egg-info/ +build/ +dist/ +*.egg +*.egg/ +*.eggs/ +doc/ +_grpcio_metadata.py diff --git a/src/python/grpcio/MANIFEST.in b/src/python/grpcio/MANIFEST.in new file mode 100644 index 00000000..9583dc77 --- /dev/null +++ b/src/python/grpcio/MANIFEST.in @@ -0,0 +1,3 @@ +graft grpc +include commands.py +include requirements.txt diff --git a/src/python/grpcio/README.rst b/src/python/grpcio/README.rst new file mode 100644 index 00000000..c7b5a3bd --- /dev/null +++ b/src/python/grpcio/README.rst @@ -0,0 +1,22 @@ +gRPC Python +=========== + +Package for GRPC Python. + +Dependencies +------------ + +Ensure you have installed the gRPC core. On Mac OS X, install homebrew_. +Run the following command to install gRPC Python. + +:: + + $ curl -fsSL https://goo.gl/getgrpc | bash -s python + +This will download and run the [gRPC install script][] to install grpc core. The script then uses pip to install this package. It also installs the Protocol Buffers compiler (_protoc_) and the gRPC _protoc_ plugin for python. + +Otherwise, `install from source`_ + +.. _`install from source`: https://github.com/grpc/grpc/blob/master/src/python/README.md#building-from-source +.. _homebrew: http://brew.sh +.. _`gRPC install script`: https://raw.githubusercontent.com/grpc/homebrew-grpc/master/scripts/install diff --git a/src/python/grpcio/commands.py b/src/python/grpcio/commands.py new file mode 100644 index 00000000..8a2f2d62 --- /dev/null +++ b/src/python/grpcio/commands.py @@ -0,0 +1,102 @@ +# Copyright 2015, 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. + +"""Provides distutils command classes for the GRPC Python setup process.""" + +import os +import os.path +import sys + +import setuptools +from setuptools.command import build_py + +_CONF_PY_ADDENDUM = """ +extensions.append('sphinx.ext.napoleon') +napoleon_google_docstring = True +napoleon_numpy_docstring = True + +html_theme = 'sphinx_rtd_theme' +""" + + +class SphinxDocumentation(setuptools.Command): + """Command to generate documentation via sphinx.""" + + description = '' + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + # We import here to ensure that setup.py has had a chance to install the + # relevant package eggs first. + import sphinx + import sphinx.apidoc + metadata = self.distribution.metadata + src_dir = os.path.join( + os.getcwd(), self.distribution.package_dir[''], 'grpc') + sys.path.append(src_dir) + sphinx.apidoc.main([ + '', '--force', '--full', '-H', metadata.name, '-A', metadata.author, + '-V', metadata.version, '-R', metadata.version, + '-o', os.path.join('doc', 'src'), src_dir]) + conf_filepath = os.path.join('doc', 'src', 'conf.py') + with open(conf_filepath, 'a') as conf_file: + conf_file.write(_CONF_PY_ADDENDUM) + sphinx.main(['', os.path.join('doc', 'src'), os.path.join('doc', 'build')]) + + +class BuildProjectMetadata(setuptools.Command): + """Command to generate project metadata in a module.""" + + description = '' + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + with open('grpc/_grpcio_metadata.py', 'w') as module_file: + module_file.write('__version__ = """{}"""'.format( + self.distribution.get_version())) + + +class BuildPy(build_py.build_py): + """Custom project build command.""" + + def run(self): + self.run_command('build_project_metadata') + build_py.build_py.run(self) diff --git a/src/python/grpcio/grpc/__init__.py b/src/python/grpcio/grpc/__init__.py new file mode 100644 index 00000000..70865191 --- /dev/null +++ b/src/python/grpcio/grpc/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, 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. + + diff --git a/src/python/grpcio/grpc/_adapter/.gitignore b/src/python/grpcio/grpc/_adapter/.gitignore new file mode 100644 index 00000000..a6f96cd6 --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/.gitignore @@ -0,0 +1,5 @@ +*.a +*.so +*.dll +*.pyc +*.pyd diff --git a/src/python/grpcio/grpc/_adapter/__init__.py b/src/python/grpcio/grpc/_adapter/__init__.py new file mode 100644 index 00000000..70865191 --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, 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. + + diff --git a/src/python/grpcio/grpc/_adapter/_c/module.c b/src/python/grpcio/grpc/_adapter/_c/module.c new file mode 100644 index 00000000..9b93b051 --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_c/module.c @@ -0,0 +1,67 @@ +/* + * + * Copyright 2015, 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 + +#define PY_SSIZE_T_CLEAN +#include +#include + +#include "grpc/_adapter/_c/types.h" + +static PyMethodDef c_methods[] = { + {NULL} +}; + +PyMODINIT_FUNC init_c(void) { + PyObject *module; + + module = Py_InitModule3("_c", c_methods, + "Wrappings of C structures and functions."); + + if (pygrpc_module_add_types(module) < 0) { + return; + } + + if (PyModule_AddStringConstant( + module, "PRIMARY_USER_AGENT_KEY", + GRPC_ARG_PRIMARY_USER_AGENT_STRING) < 0) { + return; + } + + /* GRPC maintains an internal counter of how many times it has been + initialized and handles multiple pairs of grpc_init()/grpc_shutdown() + invocations accordingly. */ + grpc_init(); + atexit(&grpc_shutdown); +} diff --git a/src/python/grpcio/grpc/_adapter/_c/types.c b/src/python/grpcio/grpc/_adapter/_c/types.c new file mode 100644 index 00000000..8855c32c --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_c/types.c @@ -0,0 +1,60 @@ +/* + * + * Copyright 2015, 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 "grpc/_adapter/_c/types.h" + +#define PY_SSIZE_T_CLEAN +#include +#include + +int pygrpc_module_add_types(PyObject *module) { + int i; + PyTypeObject *types[] = { + &pygrpc_ClientCredentials_type, + &pygrpc_ServerCredentials_type, + &pygrpc_CompletionQueue_type, + &pygrpc_Call_type, + &pygrpc_Channel_type, + &pygrpc_Server_type + }; + for (i = 0; i < sizeof(types)/sizeof(PyTypeObject *); ++i) { + if (PyType_Ready(types[i]) < 0) { + return -1; + } + } + for (i = 0; i < sizeof(types)/sizeof(PyTypeObject *); ++i) { + Py_INCREF(types[i]); + PyModule_AddObject(module, types[i]->tp_name, (PyObject *)types[i]); + } + return 0; +} diff --git a/src/python/grpcio/grpc/_adapter/_c/types.h b/src/python/grpcio/grpc/_adapter/_c/types.h new file mode 100644 index 00000000..31fd470d --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_c/types.h @@ -0,0 +1,278 @@ +/* + * + * Copyright 2015, 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__ADAPTER__C_TYPES_H_ +#define GRPC__ADAPTER__C_TYPES_H_ + +#define PY_SSIZE_T_CLEAN +#include +#include +#include + + +/*=========================*/ +/* Client-side credentials */ +/*=========================*/ + +typedef struct ClientCredentials { + PyObject_HEAD + grpc_credentials *c_creds; +} ClientCredentials; +void pygrpc_ClientCredentials_dealloc(ClientCredentials *self); +ClientCredentials *pygrpc_ClientCredentials_google_default( + PyTypeObject *type, PyObject *ignored); +ClientCredentials *pygrpc_ClientCredentials_ssl( + PyTypeObject *type, PyObject *args, PyObject *kwargs); +ClientCredentials *pygrpc_ClientCredentials_composite( + PyTypeObject *type, PyObject *args, PyObject *kwargs); +ClientCredentials *pygrpc_ClientCredentials_compute_engine( + PyTypeObject *type, PyObject *ignored); +ClientCredentials *pygrpc_ClientCredentials_jwt( + PyTypeObject *type, PyObject *args, PyObject *kwargs); +ClientCredentials *pygrpc_ClientCredentials_refresh_token( + PyTypeObject *type, PyObject *args, PyObject *kwargs); +ClientCredentials *pygrpc_ClientCredentials_iam( + PyTypeObject *type, PyObject *args, PyObject *kwargs); +extern PyTypeObject pygrpc_ClientCredentials_type; + + +/*=========================*/ +/* Server-side credentials */ +/*=========================*/ + +typedef struct ServerCredentials { + PyObject_HEAD + grpc_server_credentials *c_creds; +} ServerCredentials; +void pygrpc_ServerCredentials_dealloc(ServerCredentials *self); +ServerCredentials *pygrpc_ServerCredentials_ssl( + PyTypeObject *type, PyObject *args, PyObject *kwargs); +extern PyTypeObject pygrpc_ServerCredentials_type; + + +/*==================*/ +/* Completion queue */ +/*==================*/ + +typedef struct CompletionQueue { + PyObject_HEAD + grpc_completion_queue *c_cq; +} CompletionQueue; +CompletionQueue *pygrpc_CompletionQueue_new( + PyTypeObject *type, PyObject *args, PyObject *kwargs); +void pygrpc_CompletionQueue_dealloc(CompletionQueue *self); +PyObject *pygrpc_CompletionQueue_next( + CompletionQueue *self, PyObject *args, PyObject *kwargs); +PyObject *pygrpc_CompletionQueue_shutdown( + CompletionQueue *self, PyObject *ignored); +extern PyTypeObject pygrpc_CompletionQueue_type; + + +/*======*/ +/* Call */ +/*======*/ + +typedef struct Call { + PyObject_HEAD + grpc_call *c_call; + CompletionQueue *cq; +} Call; +Call *pygrpc_Call_new_empty(CompletionQueue *cq); +void pygrpc_Call_dealloc(Call *self); +PyObject *pygrpc_Call_start_batch(Call *self, PyObject *args, PyObject *kwargs); +PyObject *pygrpc_Call_cancel(Call *self, PyObject *args, PyObject *kwargs); +PyObject *pygrpc_Call_peer(Call *self); +PyObject *pygrpc_Call_set_credentials(Call *self, PyObject *args, + PyObject *kwargs); +extern PyTypeObject pygrpc_Call_type; + + +/*=========*/ +/* Channel */ +/*=========*/ + +typedef struct Channel { + PyObject_HEAD + grpc_channel *c_chan; +} Channel; +Channel *pygrpc_Channel_new( + PyTypeObject *type, PyObject *args, PyObject *kwargs); +void pygrpc_Channel_dealloc(Channel *self); +Call *pygrpc_Channel_create_call( + Channel *self, PyObject *args, PyObject *kwargs); +PyObject *pygrpc_Channel_check_connectivity_state(Channel *self, PyObject *args, + PyObject *kwargs); +PyObject *pygrpc_Channel_watch_connectivity_state(Channel *self, PyObject *args, + PyObject *kwargs); +PyObject *pygrpc_Channel_target(Channel *self); +extern PyTypeObject pygrpc_Channel_type; + + +/*========*/ +/* Server */ +/*========*/ + +typedef struct Server { + PyObject_HEAD + grpc_server *c_serv; + CompletionQueue *cq; + int shutdown_called; +} Server; +Server *pygrpc_Server_new(PyTypeObject *type, PyObject *args, PyObject *kwargs); +void pygrpc_Server_dealloc(Server *self); +PyObject *pygrpc_Server_request_call( + Server *self, PyObject *args, PyObject *kwargs); +PyObject *pygrpc_Server_add_http2_port( + Server *self, PyObject *args, PyObject *kwargs); +PyObject *pygrpc_Server_start(Server *self, PyObject *ignored); +PyObject *pygrpc_Server_shutdown( + Server *self, PyObject *args, PyObject *kwargs); +PyObject *pygrpc_Server_cancel_all_calls(Server *self, PyObject *unused); +extern PyTypeObject pygrpc_Server_type; + +/*=========*/ +/* Utility */ +/*=========*/ + +/* Every tag that passes from Python GRPC to GRPC core is of this type. */ +typedef struct pygrpc_tag { + PyObject *user_tag; + Call *call; + grpc_call_details request_call_details; + grpc_metadata_array request_metadata; + grpc_op *ops; + size_t nops; + int is_new_call; +} pygrpc_tag; + +/* Construct a tag associated with a batch call. Does not take ownership of the + resources in the elements of ops. */ +pygrpc_tag *pygrpc_produce_batch_tag(PyObject *user_tag, Call *call, + grpc_op *ops, size_t nops); + + +/* Construct a tag associated with a server request. The calling code should + use the appropriate fields of the produced tag in the invocation of + grpc_server_request_call. */ +pygrpc_tag *pygrpc_produce_request_tag(PyObject *user_tag, Call *empty_call); + +/* Construct a tag associated with a server shutdown. */ +pygrpc_tag *pygrpc_produce_server_shutdown_tag(PyObject *user_tag); + +/* Construct a tag associated with a channel state change. */ +pygrpc_tag *pygrpc_produce_channel_state_change_tag(PyObject *user_tag); + +/* Frees all resources owned by the tag and the tag itself. */ +void pygrpc_discard_tag(pygrpc_tag *tag); + +/* Consumes an event and its associated tag, providing a Python tuple of the + form `(type, tag, call, call_details, results)` (where type is an integer + corresponding to a grpc_completion_type, tag is an arbitrary PyObject, call + is the call object associated with the event [if any], call_details is a + tuple of form `(method, host, deadline)` [if such details are available], + and resultd is a list of tuples of form `(type, metadata, message, status, + cancelled)` [where type corresponds to a grpc_op_type, metadata is a + sequence of 2-sequences of strings, message is a byte string, and status is + a 2-tuple of an integer corresponding to grpc_status_code and a string of + status details]). + + Frees all resources associated with the event tag. */ +PyObject *pygrpc_consume_event(grpc_event event); + +/* Transliterate the Python tuple of form `(type, metadata, message, + status)` (where type is an integer corresponding to a grpc_op_type, metadata + is a sequence of 2-sequences of strings, message is a byte string, and + status is 2-tuple of an integer corresponding to grpc_status_code and a + string of status details) to a grpc_op suitable for use in a + grpc_call_start_batch invocation. The grpc_op is a 'directory' of resources + that must be freed after GRPC core is done with them. + + Calls gpr_malloc (or the appropriate type-specific grpc_*_create function) + to populate the appropriate union-discriminated members of the op. + + Returns true on success, false on failure. */ +int pygrpc_produce_op(PyObject *op, grpc_op *result); + +/* Discards all resources associated with the passed in op that was produced by + pygrpc_produce_op. */ +void pygrpc_discard_op(grpc_op op); + +/* Transliterate the grpc_ops (which have been sent through a + grpc_call_start_batch invocation and whose corresponding event has appeared + on a completion queue) to a Python tuple of form `(type, metadata, message, + status, cancelled)` (where type is an integer corresponding to a + grpc_op_type, metadata is a sequence of 2-sequences of strings, message is a + byte string, and status is 2-tuple of an integer corresponding to + grpc_status_code and a string of status details). + + Calls gpr_free (or the appropriate type-specific grpc_*_destroy function) on + the appropriate union-discriminated populated members of the ops. */ +PyObject *pygrpc_consume_ops(grpc_op *op, size_t nops); + +/* Transliterate from a gpr_timespec to a double (in units of seconds, either + from the epoch if interpreted absolutely or as a delta otherwise). */ +double pygrpc_cast_gpr_timespec_to_double(gpr_timespec timespec); + +/* Transliterate from a double (in units of seconds from the epoch if + interpreted absolutely or as a delta otherwise) to a gpr_timespec. */ +gpr_timespec pygrpc_cast_double_to_gpr_timespec(double seconds); + +/* Returns true on success, false on failure. */ +int pygrpc_cast_pyseq_to_send_metadata( + PyObject *pyseq, grpc_metadata **metadata, size_t *count); +/* Returns a metadata array as a Python object on success, else NULL. */ +PyObject *pygrpc_cast_metadata_array_to_pyseq(grpc_metadata_array metadata); + +/* Transliterate from a list of python channel arguments (2-tuples of string + and string|integer|None) to a grpc_channel_args object. The strings placed + in the grpc_channel_args object's grpc_arg elements are views of the Python + object. The Python object must live long enough for the grpc_channel_args + to be used. Arguments set to None are silently ignored. Returns true on + success, false on failure. */ +int pygrpc_produce_channel_args(PyObject *py_args, grpc_channel_args *c_args); +void pygrpc_discard_channel_args(grpc_channel_args args); + +/* Read the bytes from grpc_byte_buffer to a gpr_malloc'd array of bytes; + output to result and result_size. */ +void pygrpc_byte_buffer_to_bytes( + grpc_byte_buffer *buffer, char **result, size_t *result_size); + + +/*========*/ +/* Module */ +/*========*/ + +/* Returns 0 on success, -1 on failure. */ +int pygrpc_module_add_types(PyObject *module); + +#endif /* GRPC__ADAPTER__C_TYPES_H_ */ diff --git a/src/python/grpcio/grpc/_adapter/_c/types/call.c b/src/python/grpcio/grpc/_adapter/_c/types/call.c new file mode 100644 index 00000000..5604aba3 --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_c/types/call.c @@ -0,0 +1,186 @@ +/* + * + * Copyright 2015, 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 "grpc/_adapter/_c/types.h" + +#define PY_SSIZE_T_CLEAN +#include +#include +#include + + +PyMethodDef pygrpc_Call_methods[] = { + {"start_batch", (PyCFunction)pygrpc_Call_start_batch, METH_KEYWORDS, ""}, + {"cancel", (PyCFunction)pygrpc_Call_cancel, METH_KEYWORDS, ""}, + {"peer", (PyCFunction)pygrpc_Call_peer, METH_NOARGS, ""}, + {"set_credentials", (PyCFunction)pygrpc_Call_set_credentials, METH_KEYWORDS, + ""}, + {NULL} +}; +const char pygrpc_Call_doc[] = "See grpc._adapter._types.Call."; +PyTypeObject pygrpc_Call_type = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "Call", /* tp_name */ + sizeof(Call), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)pygrpc_Call_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + pygrpc_Call_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + pygrpc_Call_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0 /* tp_new */ +}; + +Call *pygrpc_Call_new_empty(CompletionQueue *cq) { + Call *call = (Call *)pygrpc_Call_type.tp_alloc(&pygrpc_Call_type, 0); + call->c_call = NULL; + call->cq = cq; + Py_XINCREF(call->cq); + return call; +} +void pygrpc_Call_dealloc(Call *self) { + if (self->c_call) { + grpc_call_destroy(self->c_call); + } + Py_XDECREF(self->cq); + self->ob_type->tp_free((PyObject *)self); +} +PyObject *pygrpc_Call_start_batch(Call *self, PyObject *args, PyObject *kwargs) { + PyObject *op_list; + PyObject *user_tag; + grpc_op *ops; + size_t nops; + size_t i; + size_t j; + pygrpc_tag *tag; + grpc_call_error errcode; + static char *keywords[] = {"ops", "tag", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO:start_batch", keywords, + &op_list, &user_tag)) { + return NULL; + } + if (!PyList_Check(op_list)) { + PyErr_SetString(PyExc_TypeError, "expected a list of OpArgs"); + return NULL; + } + nops = PyList_Size(op_list); + ops = gpr_malloc(sizeof(grpc_op) * nops); + for (i = 0; i < nops; ++i) { + PyObject *item = PyList_GET_ITEM(op_list, i); + if (!pygrpc_produce_op(item, &ops[i])) { + for (j = 0; j < i; ++j) { + pygrpc_discard_op(ops[j]); + } + return NULL; + } + } + tag = pygrpc_produce_batch_tag(user_tag, self, ops, nops); + errcode = grpc_call_start_batch(self->c_call, tag->ops, tag->nops, tag, NULL); + gpr_free(ops); + return PyInt_FromLong(errcode); +} +PyObject *pygrpc_Call_cancel(Call *self, PyObject *args, PyObject *kwargs) { + PyObject *py_code = NULL; + grpc_call_error errcode; + int code; + char *details = NULL; + static char *keywords[] = {"code", "details", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|Os:start_batch", keywords, + &py_code, &details)) { + return NULL; + } + if (py_code != NULL && details != NULL) { + if (!PyInt_Check(py_code)) { + PyErr_SetString(PyExc_TypeError, "expected integer code"); + return NULL; + } + code = PyInt_AsLong(py_code); + errcode = grpc_call_cancel_with_status(self->c_call, code, details, NULL); + } else if (py_code != NULL || details != NULL) { + PyErr_SetString(PyExc_ValueError, + "if `code` is specified, so must `details`"); + return NULL; + } else { + errcode = grpc_call_cancel(self->c_call, NULL); + } + return PyInt_FromLong(errcode); +} + +PyObject *pygrpc_Call_peer(Call *self) { + char *peer = grpc_call_get_peer(self->c_call); + PyObject *py_peer = PyString_FromString(peer); + gpr_free(peer); + return py_peer; +} +PyObject *pygrpc_Call_set_credentials(Call *self, PyObject *args, + PyObject *kwargs) { + ClientCredentials *creds; + grpc_call_error errcode; + static char *keywords[] = {"creds", NULL}; + if (!PyArg_ParseTupleAndKeywords( + args, kwargs, "O!:set_credentials", keywords, + &pygrpc_ClientCredentials_type, &creds)) { + return NULL; + } + errcode = grpc_call_set_credentials(self->c_call, creds->c_creds); + return PyInt_FromLong(errcode); +} diff --git a/src/python/grpcio/grpc/_adapter/_c/types/channel.c b/src/python/grpcio/grpc/_adapter/_c/types/channel.c new file mode 100644 index 00000000..79d39c43 --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_c/types/channel.c @@ -0,0 +1,187 @@ +/* + * + * Copyright 2015, 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 "grpc/_adapter/_c/types.h" + +#define PY_SSIZE_T_CLEAN +#include +#include +#include + + +PyMethodDef pygrpc_Channel_methods[] = { + {"create_call", (PyCFunction)pygrpc_Channel_create_call, METH_KEYWORDS, ""}, + {"check_connectivity_state", (PyCFunction)pygrpc_Channel_check_connectivity_state, METH_KEYWORDS, ""}, + {"watch_connectivity_state", (PyCFunction)pygrpc_Channel_watch_connectivity_state, METH_KEYWORDS, ""}, + {"target", (PyCFunction)pygrpc_Channel_target, METH_NOARGS, ""}, + {NULL} +}; +const char pygrpc_Channel_doc[] = "See grpc._adapter._types.Channel."; +PyTypeObject pygrpc_Channel_type = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "Channel", /* tp_name */ + sizeof(Channel), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)pygrpc_Channel_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + pygrpc_Channel_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + pygrpc_Channel_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + (newfunc)pygrpc_Channel_new /* tp_new */ +}; + +Channel *pygrpc_Channel_new( + PyTypeObject *type, PyObject *args, PyObject *kwargs) { + Channel *self; + const char *target; + PyObject *py_args; + ClientCredentials *creds = NULL; + grpc_channel_args c_args; + char *keywords[] = {"target", "args", "creds", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|O!:Channel", keywords, + &target, &py_args, &pygrpc_ClientCredentials_type, &creds)) { + return NULL; + } + if (!pygrpc_produce_channel_args(py_args, &c_args)) { + return NULL; + } + self = (Channel *)type->tp_alloc(type, 0); + if (creds) { + self->c_chan = + grpc_secure_channel_create(creds->c_creds, target, &c_args, NULL); + } else { + self->c_chan = grpc_insecure_channel_create(target, &c_args, NULL); + } + pygrpc_discard_channel_args(c_args); + return self; +} +void pygrpc_Channel_dealloc(Channel *self) { + grpc_channel_destroy(self->c_chan); + self->ob_type->tp_free((PyObject *)self); +} + +Call *pygrpc_Channel_create_call( + Channel *self, PyObject *args, PyObject *kwargs) { + Call *call; + CompletionQueue *cq; + const char *method; + const char *host; + double deadline; + char *keywords[] = {"cq", "method", "host", "deadline", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!szd:create_call", keywords, + &pygrpc_CompletionQueue_type, &cq, &method, &host, &deadline)) { + return NULL; + } + call = pygrpc_Call_new_empty(cq); + call->c_call = grpc_channel_create_call( + self->c_chan, NULL, GRPC_PROPAGATE_DEFAULTS, cq->c_cq, method, host, + pygrpc_cast_double_to_gpr_timespec(deadline), NULL); + return call; +} + +PyObject *pygrpc_Channel_check_connectivity_state( + Channel *self, PyObject *args, PyObject *kwargs) { + PyObject *py_try_to_connect; + int try_to_connect; + char *keywords[] = {"try_to_connect", NULL}; + grpc_connectivity_state state; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O:connectivity_state", keywords, + &py_try_to_connect)) { + return NULL; + } + if (!PyBool_Check(py_try_to_connect)) { + Py_XDECREF(py_try_to_connect); + return NULL; + } + try_to_connect = Py_True == py_try_to_connect; + Py_DECREF(py_try_to_connect); + state = grpc_channel_check_connectivity_state(self->c_chan, try_to_connect); + return PyInt_FromLong(state); +} + +PyObject *pygrpc_Channel_watch_connectivity_state( + Channel *self, PyObject *args, PyObject *kwargs) { + PyObject *tag; + double deadline; + int last_observed_state; + CompletionQueue *completion_queue; + char *keywords[] = {"last_observed_state", "deadline", + "completion_queue", "tag", NULL}; + if (!PyArg_ParseTupleAndKeywords( + args, kwargs, "idO!O:watch_connectivity_state", keywords, + &last_observed_state, &deadline, &pygrpc_CompletionQueue_type, + &completion_queue, &tag)) { + return NULL; + } + grpc_channel_watch_connectivity_state( + self->c_chan, (grpc_connectivity_state)last_observed_state, + pygrpc_cast_double_to_gpr_timespec(deadline), completion_queue->c_cq, + pygrpc_produce_channel_state_change_tag(tag)); + Py_RETURN_NONE; +} + +PyObject *pygrpc_Channel_target(Channel *self) { + char *target = grpc_channel_get_target(self->c_chan); + PyObject *py_target = PyString_FromString(target); + gpr_free(target); + return py_target; +} diff --git a/src/python/grpcio/grpc/_adapter/_c/types/client_credentials.c b/src/python/grpcio/grpc/_adapter/_c/types/client_credentials.c new file mode 100644 index 00000000..90652b7b --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_c/types/client_credentials.c @@ -0,0 +1,247 @@ +/* + * + * Copyright 2015, 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 "grpc/_adapter/_c/types.h" + +#define PY_SSIZE_T_CLEAN +#include +#include +#include + + +PyMethodDef pygrpc_ClientCredentials_methods[] = { + {"google_default", (PyCFunction)pygrpc_ClientCredentials_google_default, + METH_CLASS|METH_NOARGS, ""}, + {"ssl", (PyCFunction)pygrpc_ClientCredentials_ssl, + METH_CLASS|METH_KEYWORDS, ""}, + {"composite", (PyCFunction)pygrpc_ClientCredentials_composite, + METH_CLASS|METH_KEYWORDS, ""}, + {"compute_engine", (PyCFunction)pygrpc_ClientCredentials_compute_engine, + METH_CLASS|METH_NOARGS, ""}, + {"jwt", (PyCFunction)pygrpc_ClientCredentials_jwt, + METH_CLASS|METH_KEYWORDS, ""}, + {"refresh_token", (PyCFunction)pygrpc_ClientCredentials_refresh_token, + METH_CLASS|METH_KEYWORDS, ""}, + {"iam", (PyCFunction)pygrpc_ClientCredentials_iam, + METH_CLASS|METH_KEYWORDS, ""}, + {NULL} +}; +const char pygrpc_ClientCredentials_doc[] = ""; +PyTypeObject pygrpc_ClientCredentials_type = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "ClientCredentials", /* tp_name */ + sizeof(ClientCredentials), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)pygrpc_ClientCredentials_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + pygrpc_ClientCredentials_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + pygrpc_ClientCredentials_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0 /* tp_new */ +}; + +void pygrpc_ClientCredentials_dealloc(ClientCredentials *self) { + grpc_credentials_release(self->c_creds); + self->ob_type->tp_free((PyObject *)self); +} + +ClientCredentials *pygrpc_ClientCredentials_google_default( + PyTypeObject *type, PyObject *ignored) { + ClientCredentials *self = (ClientCredentials *)type->tp_alloc(type, 0); + self->c_creds = grpc_google_default_credentials_create(); + if (!self->c_creds) { + Py_DECREF(self); + PyErr_SetString(PyExc_RuntimeError, + "couldn't create Google default credentials"); + return NULL; + } + return self; +} + +ClientCredentials *pygrpc_ClientCredentials_ssl( + PyTypeObject *type, PyObject *args, PyObject *kwargs) { + ClientCredentials *self; + const char *root_certs; + const char *private_key = NULL; + const char *cert_chain = NULL; + grpc_ssl_pem_key_cert_pair key_cert_pair; + static char *keywords[] = {"root_certs", "private_key", "cert_chain", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "z|zz:ssl", keywords, + &root_certs, &private_key, &cert_chain)) { + return NULL; + } + self = (ClientCredentials *)type->tp_alloc(type, 0); + if (private_key && cert_chain) { + key_cert_pair.private_key = private_key; + key_cert_pair.cert_chain = cert_chain; + self->c_creds = + grpc_ssl_credentials_create(root_certs, &key_cert_pair, NULL); + } else { + self->c_creds = grpc_ssl_credentials_create(root_certs, NULL, NULL); + } + if (!self->c_creds) { + Py_DECREF(self); + PyErr_SetString(PyExc_RuntimeError, "couldn't create ssl credentials"); + return NULL; + } + return self; +} + +ClientCredentials *pygrpc_ClientCredentials_composite( + PyTypeObject *type, PyObject *args, PyObject *kwargs) { + ClientCredentials *self; + ClientCredentials *creds1; + ClientCredentials *creds2; + static char *keywords[] = {"creds1", "creds2", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!:composite", keywords, + &pygrpc_ClientCredentials_type, &creds1, + &pygrpc_ClientCredentials_type, &creds2)) { + return NULL; + } + self = (ClientCredentials *)type->tp_alloc(type, 0); + self->c_creds = + grpc_composite_credentials_create(creds1->c_creds, creds2->c_creds, NULL); + if (!self->c_creds) { + Py_DECREF(self); + PyErr_SetString(PyExc_RuntimeError, "couldn't create composite credentials"); + return NULL; + } + return self; +} + +ClientCredentials *pygrpc_ClientCredentials_compute_engine( + PyTypeObject *type, PyObject *ignored) { + ClientCredentials *self = (ClientCredentials *)type->tp_alloc(type, 0); + self->c_creds = grpc_google_compute_engine_credentials_create(NULL); + if (!self->c_creds) { + Py_DECREF(self); + PyErr_SetString(PyExc_RuntimeError, + "couldn't create compute engine credentials"); + return NULL; + } + return self; +} + +/* TODO: Rename this credentials to something like service_account_jwt_access */ +ClientCredentials *pygrpc_ClientCredentials_jwt( + PyTypeObject *type, PyObject *args, PyObject *kwargs) { + ClientCredentials *self; + const char *json_key; + double lifetime; + static char *keywords[] = {"json_key", "token_lifetime", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sd:jwt", keywords, + &json_key, &lifetime)) { + return NULL; + } + self = (ClientCredentials *)type->tp_alloc(type, 0); + self->c_creds = grpc_service_account_jwt_access_credentials_create( + json_key, pygrpc_cast_double_to_gpr_timespec(lifetime), NULL); + if (!self->c_creds) { + Py_DECREF(self); + PyErr_SetString(PyExc_RuntimeError, "couldn't create JWT credentials"); + return NULL; + } + return self; +} + +ClientCredentials *pygrpc_ClientCredentials_refresh_token( + PyTypeObject *type, PyObject *args, PyObject *kwargs) { + ClientCredentials *self; + const char *json_refresh_token; + static char *keywords[] = {"json_refresh_token", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s:refresh_token", keywords, + &json_refresh_token)) { + return NULL; + } + self = (ClientCredentials *)type->tp_alloc(type, 0); + self->c_creds = + grpc_google_refresh_token_credentials_create(json_refresh_token, NULL); + if (!self->c_creds) { + Py_DECREF(self); + PyErr_SetString(PyExc_RuntimeError, + "couldn't create credentials from refresh token"); + return NULL; + } + return self; +} + +ClientCredentials *pygrpc_ClientCredentials_iam( + PyTypeObject *type, PyObject *args, PyObject *kwargs) { + ClientCredentials *self; + const char *authorization_token; + const char *authority_selector; + static char *keywords[] = {"authorization_token", "authority_selector", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss:iam", keywords, + &authorization_token, &authority_selector)) { + return NULL; + } + self = (ClientCredentials *)type->tp_alloc(type, 0); + self->c_creds = grpc_google_iam_credentials_create(authorization_token, + authority_selector, NULL); + if (!self->c_creds) { + Py_DECREF(self); + PyErr_SetString(PyExc_RuntimeError, "couldn't create IAM credentials"); + return NULL; + } + return self; +} + diff --git a/src/python/grpcio/grpc/_adapter/_c/types/completion_queue.c b/src/python/grpcio/grpc/_adapter/_c/types/completion_queue.c new file mode 100644 index 00000000..d8bb89ca --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_c/types/completion_queue.c @@ -0,0 +1,124 @@ +/* + * + * Copyright 2015, 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 "grpc/_adapter/_c/types.h" + +#define PY_SSIZE_T_CLEAN +#include +#include + + +PyMethodDef pygrpc_CompletionQueue_methods[] = { + {"next", (PyCFunction)pygrpc_CompletionQueue_next, METH_KEYWORDS, ""}, + {"shutdown", (PyCFunction)pygrpc_CompletionQueue_shutdown, METH_NOARGS, ""}, + {NULL} +}; +const char pygrpc_CompletionQueue_doc[] = + "See grpc._adapter._types.CompletionQueue."; +PyTypeObject pygrpc_CompletionQueue_type = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "CompletionQueue", /* tp_name */ + sizeof(CompletionQueue), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)pygrpc_CompletionQueue_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + pygrpc_CompletionQueue_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + pygrpc_CompletionQueue_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + (newfunc)pygrpc_CompletionQueue_new /* tp_new */ +}; + +CompletionQueue *pygrpc_CompletionQueue_new( + PyTypeObject *type, PyObject *args, PyObject *kwargs) { + CompletionQueue *self = (CompletionQueue *)type->tp_alloc(type, 0); + self->c_cq = grpc_completion_queue_create(NULL); + return self; +} + +void pygrpc_CompletionQueue_dealloc(CompletionQueue *self) { + grpc_completion_queue_destroy(self->c_cq); + self->ob_type->tp_free((PyObject *)self); +} + +PyObject *pygrpc_CompletionQueue_next( + CompletionQueue *self, PyObject *args, PyObject *kwargs) { + double deadline; + grpc_event event; + PyObject *transliterated_event; + static char *keywords[] = {"deadline", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "d:next", keywords, + &deadline)) { + return NULL; + } + Py_BEGIN_ALLOW_THREADS; + event = grpc_completion_queue_next( + self->c_cq, pygrpc_cast_double_to_gpr_timespec(deadline), NULL); + Py_END_ALLOW_THREADS; + transliterated_event = pygrpc_consume_event(event); + return transliterated_event; +} + +PyObject *pygrpc_CompletionQueue_shutdown( + CompletionQueue *self, PyObject *ignored) { + grpc_completion_queue_shutdown(self->c_cq); + Py_RETURN_NONE; +} diff --git a/src/python/grpcio/grpc/_adapter/_c/types/server.c b/src/python/grpcio/grpc/_adapter/_c/types/server.c new file mode 100644 index 00000000..8feab8aa --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_c/types/server.c @@ -0,0 +1,196 @@ +/* + * + * Copyright 2015, 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 "grpc/_adapter/_c/types.h" + +#define PY_SSIZE_T_CLEAN +#include +#include + + +PyMethodDef pygrpc_Server_methods[] = { + {"request_call", (PyCFunction)pygrpc_Server_request_call, + METH_KEYWORDS, ""}, + {"add_http2_port", (PyCFunction)pygrpc_Server_add_http2_port, + METH_KEYWORDS, ""}, + {"start", (PyCFunction)pygrpc_Server_start, METH_NOARGS, ""}, + {"shutdown", (PyCFunction)pygrpc_Server_shutdown, METH_KEYWORDS, ""}, + {"cancel_all_calls", (PyCFunction)pygrpc_Server_cancel_all_calls, + METH_NOARGS, ""}, + {NULL} +}; +const char pygrpc_Server_doc[] = "See grpc._adapter._types.Server."; +PyTypeObject pygrpc_Server_type = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "Server", /* tp_name */ + sizeof(Server), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)pygrpc_Server_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + pygrpc_Server_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + pygrpc_Server_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + (newfunc)pygrpc_Server_new /* tp_new */ +}; + +Server *pygrpc_Server_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { + Server *self; + CompletionQueue *cq; + PyObject *py_args; + grpc_channel_args c_args; + char *keywords[] = {"cq", "args", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O:Server", keywords, + &pygrpc_CompletionQueue_type, &cq, &py_args)) { + return NULL; + } + if (!pygrpc_produce_channel_args(py_args, &c_args)) { + return NULL; + } + self = (Server *)type->tp_alloc(type, 0); + self->c_serv = grpc_server_create(&c_args, NULL); + grpc_server_register_completion_queue(self->c_serv, cq->c_cq, NULL); + pygrpc_discard_channel_args(c_args); + self->cq = cq; + Py_INCREF(self->cq); + self->shutdown_called = 0; + return self; +} + +void pygrpc_Server_dealloc(Server *self) { + grpc_server_destroy(self->c_serv); + Py_XDECREF(self->cq); + self->ob_type->tp_free((PyObject *)self); +} + +PyObject *pygrpc_Server_request_call( + Server *self, PyObject *args, PyObject *kwargs) { + CompletionQueue *cq; + PyObject *user_tag; + pygrpc_tag *tag; + Call *empty_call; + grpc_call_error errcode; + static char *keywords[] = {"cq", "tag", NULL}; + if (!PyArg_ParseTupleAndKeywords( + args, kwargs, "O!O", keywords, + &pygrpc_CompletionQueue_type, &cq, &user_tag)) { + return NULL; + } + empty_call = pygrpc_Call_new_empty(cq); + tag = pygrpc_produce_request_tag(user_tag, empty_call); + errcode = grpc_server_request_call( + self->c_serv, &tag->call->c_call, &tag->request_call_details, + &tag->request_metadata, tag->call->cq->c_cq, self->cq->c_cq, tag); + Py_DECREF(empty_call); + return PyInt_FromLong(errcode); +} + +PyObject *pygrpc_Server_add_http2_port( + Server *self, PyObject *args, PyObject *kwargs) { + const char *addr; + ServerCredentials *creds = NULL; + int port; + static char *keywords[] = {"addr", "creds", NULL}; + if (!PyArg_ParseTupleAndKeywords( + args, kwargs, "s|O!:add_http2_port", keywords, + &addr, &pygrpc_ServerCredentials_type, &creds)) { + return NULL; + } + if (creds) { + port = grpc_server_add_secure_http2_port( + self->c_serv, addr, creds->c_creds); + } else { + port = grpc_server_add_insecure_http2_port(self->c_serv, addr); + } + return PyInt_FromLong(port); + +} + +PyObject *pygrpc_Server_start(Server *self, PyObject *ignored) { + grpc_server_start(self->c_serv); + self->shutdown_called = 0; + Py_RETURN_NONE; +} + +PyObject *pygrpc_Server_shutdown( + Server *self, PyObject *args, PyObject *kwargs) { + PyObject *user_tag; + pygrpc_tag *tag; + static char *keywords[] = {"tag", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", keywords, &user_tag)) { + return NULL; + } + tag = pygrpc_produce_server_shutdown_tag(user_tag); + grpc_server_shutdown_and_notify(self->c_serv, self->cq->c_cq, tag); + self->shutdown_called = 1; + Py_RETURN_NONE; +} + +PyObject *pygrpc_Server_cancel_all_calls(Server *self, PyObject *unused) { + if (!self->shutdown_called) { + PyErr_SetString( + PyExc_RuntimeError, + "shutdown must have been called prior to calling cancel_all_calls!"); + return NULL; + } + grpc_server_cancel_all_calls(self->c_serv); + Py_RETURN_NONE; +} diff --git a/src/python/grpcio/grpc/_adapter/_c/types/server_credentials.c b/src/python/grpcio/grpc/_adapter/_c/types/server_credentials.c new file mode 100644 index 00000000..df51a99b --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_c/types/server_credentials.c @@ -0,0 +1,137 @@ +/* + * + * Copyright 2015, 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 "grpc/_adapter/_c/types.h" + +#define PY_SSIZE_T_CLEAN +#include +#include +#include +#include + + +PyMethodDef pygrpc_ServerCredentials_methods[] = { + {"ssl", (PyCFunction)pygrpc_ServerCredentials_ssl, + METH_CLASS|METH_KEYWORDS, ""}, + {NULL} +}; +const char pygrpc_ServerCredentials_doc[] = ""; +PyTypeObject pygrpc_ServerCredentials_type = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "ServerCredentials", /* tp_name */ + sizeof(ServerCredentials), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)pygrpc_ServerCredentials_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + pygrpc_ServerCredentials_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + pygrpc_ServerCredentials_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0 /* tp_new */ +}; + +void pygrpc_ServerCredentials_dealloc(ServerCredentials *self) { + grpc_server_credentials_release(self->c_creds); + self->ob_type->tp_free((PyObject *)self); +} + +ServerCredentials *pygrpc_ServerCredentials_ssl( + PyTypeObject *type, PyObject *args, PyObject *kwargs) { + ServerCredentials *self; + const char *root_certs; + PyObject *py_key_cert_pairs; + grpc_ssl_pem_key_cert_pair *key_cert_pairs; + int force_client_auth; + size_t num_key_cert_pairs; + size_t i; + static char *keywords[] = { + "root_certs", "key_cert_pairs", "force_client_auth", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "zOi:ssl", keywords, + &root_certs, &py_key_cert_pairs, &force_client_auth)) { + return NULL; + } + if (!PyList_Check(py_key_cert_pairs)) { + PyErr_SetString(PyExc_TypeError, "expected a list of 2-tuples of strings"); + return NULL; + } + num_key_cert_pairs = PyList_Size(py_key_cert_pairs); + key_cert_pairs = + gpr_malloc(sizeof(grpc_ssl_pem_key_cert_pair) * num_key_cert_pairs); + for (i = 0; i < num_key_cert_pairs; ++i) { + PyObject *item = PyList_GET_ITEM(py_key_cert_pairs, i); + const char *key; + const char *cert; + if (!PyArg_ParseTuple(item, "zz", &key, &cert)) { + gpr_free(key_cert_pairs); + PyErr_SetString(PyExc_TypeError, + "expected a list of 2-tuples of strings"); + return NULL; + } + key_cert_pairs[i].private_key = key; + key_cert_pairs[i].cert_chain = cert; + } + + self = (ServerCredentials *)type->tp_alloc(type, 0); + self->c_creds = grpc_ssl_server_credentials_create( + root_certs, key_cert_pairs, num_key_cert_pairs, force_client_auth, NULL); + gpr_free(key_cert_pairs); + return self; +} diff --git a/src/python/grpcio/grpc/_adapter/_c/utility.c b/src/python/grpcio/grpc/_adapter/_c/utility.c new file mode 100644 index 00000000..590f7e01 --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_c/utility.c @@ -0,0 +1,524 @@ +/* + * + * Copyright 2015, 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 +#include + +#define PY_SSIZE_T_CLEAN +#include +#include +#include +#include +#include +#include +#include + +#include "grpc/_adapter/_c/types.h" + +pygrpc_tag *pygrpc_produce_batch_tag( + PyObject *user_tag, Call *call, grpc_op *ops, size_t nops) { + pygrpc_tag *tag = gpr_malloc(sizeof(pygrpc_tag)); + tag->user_tag = user_tag; + Py_XINCREF(tag->user_tag); + tag->call = call; + Py_XINCREF(tag->call); + tag->ops = gpr_malloc(sizeof(grpc_op)*nops); + memcpy(tag->ops, ops, sizeof(grpc_op)*nops); + tag->nops = nops; + grpc_call_details_init(&tag->request_call_details); + grpc_metadata_array_init(&tag->request_metadata); + tag->is_new_call = 0; + return tag; +} + +pygrpc_tag *pygrpc_produce_request_tag(PyObject *user_tag, Call *empty_call) { + pygrpc_tag *tag = gpr_malloc(sizeof(pygrpc_tag)); + tag->user_tag = user_tag; + Py_XINCREF(tag->user_tag); + tag->call = empty_call; + Py_XINCREF(tag->call); + tag->ops = NULL; + tag->nops = 0; + grpc_call_details_init(&tag->request_call_details); + grpc_metadata_array_init(&tag->request_metadata); + tag->is_new_call = 1; + return tag; +} + +pygrpc_tag *pygrpc_produce_server_shutdown_tag(PyObject *user_tag) { + pygrpc_tag *tag = gpr_malloc(sizeof(pygrpc_tag)); + tag->user_tag = user_tag; + Py_XINCREF(tag->user_tag); + tag->call = NULL; + tag->ops = NULL; + tag->nops = 0; + grpc_call_details_init(&tag->request_call_details); + grpc_metadata_array_init(&tag->request_metadata); + tag->is_new_call = 0; + return tag; +} + +pygrpc_tag *pygrpc_produce_channel_state_change_tag(PyObject *user_tag) { + pygrpc_tag *tag = gpr_malloc(sizeof(pygrpc_tag)); + tag->user_tag = user_tag; + Py_XINCREF(tag->user_tag); + tag->call = NULL; + tag->ops = NULL; + tag->nops = 0; + grpc_call_details_init(&tag->request_call_details); + grpc_metadata_array_init(&tag->request_metadata); + tag->is_new_call = 0; + return tag; +} + +void pygrpc_discard_tag(pygrpc_tag *tag) { + if (!tag) { + return; + } + Py_XDECREF(tag->user_tag); + Py_XDECREF(tag->call); + gpr_free(tag->ops); + grpc_call_details_destroy(&tag->request_call_details); + grpc_metadata_array_destroy(&tag->request_metadata); + gpr_free(tag); +} + +PyObject *pygrpc_consume_event(grpc_event event) { + pygrpc_tag *tag; + PyObject *result; + if (event.type == GRPC_QUEUE_TIMEOUT) { + Py_RETURN_NONE; + } + tag = event.tag; + switch (event.type) { + case GRPC_QUEUE_SHUTDOWN: + result = Py_BuildValue("iOOOOO", GRPC_QUEUE_SHUTDOWN, + Py_None, Py_None, Py_None, Py_None, Py_True); + break; + case GRPC_OP_COMPLETE: + if (tag->is_new_call) { + result = Py_BuildValue( + "iOO(ssd)[(iNOOOO)]O", GRPC_OP_COMPLETE, tag->user_tag, tag->call, + tag->request_call_details.method, tag->request_call_details.host, + pygrpc_cast_gpr_timespec_to_double(tag->request_call_details.deadline), + GRPC_OP_RECV_INITIAL_METADATA, + pygrpc_cast_metadata_array_to_pyseq(tag->request_metadata), Py_None, + Py_None, Py_None, Py_None, + event.success ? Py_True : Py_False); + } else { + result = Py_BuildValue("iOOONO", GRPC_OP_COMPLETE, tag->user_tag, + tag->call ? (PyObject*)tag->call : Py_None, Py_None, + pygrpc_consume_ops(tag->ops, tag->nops), + event.success ? Py_True : Py_False); + } + break; + default: + PyErr_SetString(PyExc_ValueError, + "unknown completion type; could not translate event"); + return NULL; + } + pygrpc_discard_tag(tag); + return result; +} + +int pygrpc_produce_op(PyObject *op, grpc_op *result) { + static const int OP_TUPLE_SIZE = 6; + static const int STATUS_TUPLE_SIZE = 2; + static const int TYPE_INDEX = 0; + static const int INITIAL_METADATA_INDEX = 1; + static const int TRAILING_METADATA_INDEX = 2; + static const int MESSAGE_INDEX = 3; + static const int STATUS_INDEX = 4; + static const int STATUS_CODE_INDEX = 0; + static const int STATUS_DETAILS_INDEX = 1; + static const int WRITE_FLAGS_INDEX = 5; + int type; + Py_ssize_t message_size; + char *message; + char *status_details; + gpr_slice message_slice; + grpc_op c_op; + if (!PyTuple_Check(op)) { + PyErr_SetString(PyExc_TypeError, "expected tuple op"); + return 0; + } + if (PyTuple_Size(op) != OP_TUPLE_SIZE) { + char *buf; + gpr_asprintf(&buf, "expected tuple op of length %d", OP_TUPLE_SIZE); + PyErr_SetString(PyExc_ValueError, buf); + gpr_free(buf); + return 0; + } + type = PyInt_AsLong(PyTuple_GET_ITEM(op, TYPE_INDEX)); + if (PyErr_Occurred()) { + return 0; + } + c_op.op = type; + c_op.reserved = NULL; + c_op.flags = PyInt_AsLong(PyTuple_GET_ITEM(op, WRITE_FLAGS_INDEX)); + if (PyErr_Occurred()) { + return 0; + } + switch (type) { + case GRPC_OP_SEND_INITIAL_METADATA: + if (!pygrpc_cast_pyseq_to_send_metadata( + PyTuple_GetItem(op, INITIAL_METADATA_INDEX), + &c_op.data.send_initial_metadata.metadata, + &c_op.data.send_initial_metadata.count)) { + return 0; + } + break; + case GRPC_OP_SEND_MESSAGE: + PyString_AsStringAndSize( + PyTuple_GET_ITEM(op, MESSAGE_INDEX), &message, &message_size); + message_slice = gpr_slice_from_copied_buffer(message, message_size); + c_op.data.send_message = grpc_raw_byte_buffer_create(&message_slice, 1); + gpr_slice_unref(message_slice); + break; + case GRPC_OP_SEND_CLOSE_FROM_CLIENT: + /* Don't need to fill in any other fields. */ + break; + case GRPC_OP_SEND_STATUS_FROM_SERVER: + if (!pygrpc_cast_pyseq_to_send_metadata( + PyTuple_GetItem(op, TRAILING_METADATA_INDEX), + &c_op.data.send_status_from_server.trailing_metadata, + &c_op.data.send_status_from_server.trailing_metadata_count)) { + return 0; + } + if (!PyTuple_Check(PyTuple_GET_ITEM(op, STATUS_INDEX))) { + char *buf; + gpr_asprintf(&buf, "expected tuple status in op of length %d", + STATUS_TUPLE_SIZE); + PyErr_SetString(PyExc_ValueError, buf); + gpr_free(buf); + return 0; + } + c_op.data.send_status_from_server.status = PyInt_AsLong( + PyTuple_GET_ITEM(PyTuple_GET_ITEM(op, STATUS_INDEX), STATUS_CODE_INDEX)); + status_details = PyString_AsString( + PyTuple_GET_ITEM(PyTuple_GET_ITEM(op, STATUS_INDEX), STATUS_DETAILS_INDEX)); + if (PyErr_Occurred()) { + return 0; + } + c_op.data.send_status_from_server.status_details = + gpr_malloc(strlen(status_details) + 1); + strcpy((char *)c_op.data.send_status_from_server.status_details, + status_details); + break; + case GRPC_OP_RECV_INITIAL_METADATA: + c_op.data.recv_initial_metadata = gpr_malloc(sizeof(grpc_metadata_array)); + grpc_metadata_array_init(c_op.data.recv_initial_metadata); + break; + case GRPC_OP_RECV_MESSAGE: + c_op.data.recv_message = gpr_malloc(sizeof(grpc_byte_buffer *)); + break; + case GRPC_OP_RECV_STATUS_ON_CLIENT: + c_op.data.recv_status_on_client.trailing_metadata = + gpr_malloc(sizeof(grpc_metadata_array)); + grpc_metadata_array_init(c_op.data.recv_status_on_client.trailing_metadata); + c_op.data.recv_status_on_client.status = + gpr_malloc(sizeof(grpc_status_code *)); + c_op.data.recv_status_on_client.status_details = + gpr_malloc(sizeof(char *)); + *c_op.data.recv_status_on_client.status_details = NULL; + c_op.data.recv_status_on_client.status_details_capacity = + gpr_malloc(sizeof(size_t)); + *c_op.data.recv_status_on_client.status_details_capacity = 0; + break; + case GRPC_OP_RECV_CLOSE_ON_SERVER: + c_op.data.recv_close_on_server.cancelled = gpr_malloc(sizeof(int)); + break; + default: + return 0; + } + *result = c_op; + return 1; +} + +void pygrpc_discard_op(grpc_op op) { + size_t i; + switch(op.op) { + case GRPC_OP_SEND_INITIAL_METADATA: + /* Whenever we produce send-metadata, we allocate new strings (to handle + arbitrary sequence input as opposed to just lists or just tuples). We + thus must free those elements. */ + for (i = 0; i < op.data.send_initial_metadata.count; ++i) { + gpr_free((void *)op.data.send_initial_metadata.metadata[i].key); + gpr_free((void *)op.data.send_initial_metadata.metadata[i].value); + } + gpr_free(op.data.send_initial_metadata.metadata); + break; + case GRPC_OP_SEND_MESSAGE: + grpc_byte_buffer_destroy(op.data.send_message); + break; + case GRPC_OP_SEND_CLOSE_FROM_CLIENT: + /* Don't need to free any fields. */ + break; + case GRPC_OP_SEND_STATUS_FROM_SERVER: + /* Whenever we produce send-metadata, we allocate new strings (to handle + arbitrary sequence input as opposed to just lists or just tuples). We + thus must free those elements. */ + for (i = 0; i < op.data.send_status_from_server.trailing_metadata_count; + ++i) { + gpr_free( + (void *)op.data.send_status_from_server.trailing_metadata[i].key); + gpr_free( + (void *)op.data.send_status_from_server.trailing_metadata[i].value); + } + gpr_free(op.data.send_status_from_server.trailing_metadata); + gpr_free((char *)op.data.send_status_from_server.status_details); + break; + case GRPC_OP_RECV_INITIAL_METADATA: + grpc_metadata_array_destroy(op.data.recv_initial_metadata); + gpr_free(op.data.recv_initial_metadata); + break; + case GRPC_OP_RECV_MESSAGE: + grpc_byte_buffer_destroy(*op.data.recv_message); + gpr_free(op.data.recv_message); + break; + case GRPC_OP_RECV_STATUS_ON_CLIENT: + grpc_metadata_array_destroy(op.data.recv_status_on_client.trailing_metadata); + gpr_free(op.data.recv_status_on_client.trailing_metadata); + gpr_free(op.data.recv_status_on_client.status); + gpr_free(*op.data.recv_status_on_client.status_details); + gpr_free(op.data.recv_status_on_client.status_details); + gpr_free(op.data.recv_status_on_client.status_details_capacity); + break; + case GRPC_OP_RECV_CLOSE_ON_SERVER: + gpr_free(op.data.recv_close_on_server.cancelled); + break; + } +} + +PyObject *pygrpc_consume_ops(grpc_op *op, size_t nops) { + static const int TYPE_INDEX = 0; + static const int INITIAL_METADATA_INDEX = 1; + static const int TRAILING_METADATA_INDEX = 2; + static const int MESSAGE_INDEX = 3; + static const int STATUS_INDEX = 4; + static const int CANCELLED_INDEX = 5; + static const int OPRESULT_LENGTH = 6; + PyObject *list; + size_t i; + size_t j; + char *bytes; + size_t bytes_size; + PyObject *results = PyList_New(nops); + if (!results) { + return NULL; + } + for (i = 0; i < nops; ++i) { + PyObject *result = PyTuple_Pack(OPRESULT_LENGTH, Py_None, Py_None, Py_None, + Py_None, Py_None, Py_None); + PyTuple_SetItem(result, TYPE_INDEX, PyInt_FromLong(op[i].op)); + switch(op[i].op) { + case GRPC_OP_RECV_INITIAL_METADATA: + PyTuple_SetItem(result, INITIAL_METADATA_INDEX, + list=PyList_New(op[i].data.recv_initial_metadata->count)); + for (j = 0; j < op[i].data.recv_initial_metadata->count; ++j) { + grpc_metadata md = op[i].data.recv_initial_metadata->metadata[j]; + PyList_SetItem(list, j, Py_BuildValue("ss#", md.key, md.value, + (Py_ssize_t)md.value_length)); + } + break; + case GRPC_OP_RECV_MESSAGE: + if (*op[i].data.recv_message) { + pygrpc_byte_buffer_to_bytes( + *op[i].data.recv_message, &bytes, &bytes_size); + PyTuple_SetItem(result, MESSAGE_INDEX, + PyString_FromStringAndSize(bytes, bytes_size)); + gpr_free(bytes); + } else { + PyTuple_SetItem(result, MESSAGE_INDEX, Py_BuildValue("")); + } + break; + case GRPC_OP_RECV_STATUS_ON_CLIENT: + PyTuple_SetItem( + result, TRAILING_METADATA_INDEX, + list = PyList_New(op[i].data.recv_status_on_client.trailing_metadata->count)); + for (j = 0; j < op[i].data.recv_status_on_client.trailing_metadata->count; ++j) { + grpc_metadata md = + op[i].data.recv_status_on_client.trailing_metadata->metadata[j]; + PyList_SetItem(list, j, Py_BuildValue("ss#", md.key, md.value, + (Py_ssize_t)md.value_length)); + } + PyTuple_SetItem( + result, STATUS_INDEX, Py_BuildValue( + "is", *op[i].data.recv_status_on_client.status, + *op[i].data.recv_status_on_client.status_details)); + break; + case GRPC_OP_RECV_CLOSE_ON_SERVER: + PyTuple_SetItem( + result, CANCELLED_INDEX, + PyBool_FromLong(*op[i].data.recv_close_on_server.cancelled)); + break; + default: + break; + } + pygrpc_discard_op(op[i]); + PyList_SetItem(results, i, result); + } + return results; +} + +double pygrpc_cast_gpr_timespec_to_double(gpr_timespec timespec) { + timespec = gpr_convert_clock_type(timespec, GPR_CLOCK_REALTIME); + return timespec.tv_sec + 1e-9*timespec.tv_nsec; +} + +/* Because C89 doesn't have a way to check for infinity... */ +static int pygrpc_isinf(double x) { + return x * 0 != 0; +} + +gpr_timespec pygrpc_cast_double_to_gpr_timespec(double seconds) { + gpr_timespec result; + if (pygrpc_isinf(seconds)) { + result = seconds > 0.0 ? gpr_inf_future(GPR_CLOCK_REALTIME) + : gpr_inf_past(GPR_CLOCK_REALTIME); + } else { + result.tv_sec = (time_t)seconds; + result.tv_nsec = ((seconds - result.tv_sec) * 1e9); + result.clock_type = GPR_CLOCK_REALTIME; + } + return result; +} + +int pygrpc_produce_channel_args(PyObject *py_args, grpc_channel_args *c_args) { + size_t num_args = PyList_Size(py_args); + size_t i; + grpc_channel_args args; + args.num_args = num_args; + args.args = gpr_malloc(sizeof(grpc_arg) * num_args); + for (i = 0; i < args.num_args; ++i) { + char *key; + PyObject *value; + if (!PyArg_ParseTuple(PyList_GetItem(py_args, i), "zO", &key, &value)) { + gpr_free(args.args); + args.num_args = 0; + args.args = NULL; + PyErr_SetString(PyExc_TypeError, + "expected a list of 2-tuple of str and str|int|None"); + return 0; + } + args.args[i].key = key; + if (PyInt_Check(value)) { + args.args[i].type = GRPC_ARG_INTEGER; + args.args[i].value.integer = PyInt_AsLong(value); + } else if (PyString_Check(value)) { + args.args[i].type = GRPC_ARG_STRING; + args.args[i].value.string = PyString_AsString(value); + } else if (value == Py_None) { + --args.num_args; + --i; + continue; + } else { + gpr_free(args.args); + args.num_args = 0; + args.args = NULL; + PyErr_SetString(PyExc_TypeError, + "expected a list of 2-tuple of str and str|int|None"); + return 0; + } + } + *c_args = args; + return 1; +} + +void pygrpc_discard_channel_args(grpc_channel_args args) { + gpr_free(args.args); +} + +int pygrpc_cast_pyseq_to_send_metadata( + PyObject *pyseq, grpc_metadata **metadata, size_t *count) { + size_t i; + Py_ssize_t value_length; + char *key; + char *value; + if (!PySequence_Check(pyseq)) { + return 0; + } + *count = PySequence_Size(pyseq); + *metadata = gpr_malloc(sizeof(grpc_metadata) * *count); + for (i = 0; i < *count; ++i) { + PyObject *item = PySequence_GetItem(pyseq, i); + if (!PyArg_ParseTuple(item, "ss#", &key, &value, &value_length)) { + Py_DECREF(item); + gpr_free(*metadata); + *count = 0; + *metadata = NULL; + return 0; + } else { + (*metadata)[i].key = gpr_strdup(key); + (*metadata)[i].value = gpr_malloc(value_length); + memcpy((void *)(*metadata)[i].value, value, value_length); + Py_DECREF(item); + } + (*metadata)[i].value_length = value_length; + } + return 1; +} + +PyObject *pygrpc_cast_metadata_array_to_pyseq(grpc_metadata_array metadata) { + PyObject *result = PyTuple_New(metadata.count); + size_t i; + for (i = 0; i < metadata.count; ++i) { + PyTuple_SetItem( + result, i, Py_BuildValue( + "ss#", metadata.metadata[i].key, metadata.metadata[i].value, + (Py_ssize_t)metadata.metadata[i].value_length)); + if (PyErr_Occurred()) { + Py_DECREF(result); + return NULL; + } + } + return result; +} + +void pygrpc_byte_buffer_to_bytes( + grpc_byte_buffer *buffer, char **result, size_t *result_size) { + grpc_byte_buffer_reader reader; + gpr_slice slice; + char *read_result = NULL; + size_t size = 0; + grpc_byte_buffer_reader_init(&reader, buffer); + while (grpc_byte_buffer_reader_next(&reader, &slice)) { + read_result = gpr_realloc(read_result, size + GPR_SLICE_LENGTH(slice)); + memcpy(read_result + size, GPR_SLICE_START_PTR(slice), + GPR_SLICE_LENGTH(slice)); + size = size + GPR_SLICE_LENGTH(slice); + gpr_slice_unref(slice); + } + *result_size = size; + *result = read_result; +} diff --git a/src/python/grpcio/grpc/_adapter/_common.py b/src/python/grpcio/grpc/_adapter/_common.py new file mode 100644 index 00000000..492849f4 --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_common.py @@ -0,0 +1,76 @@ +# Copyright 2015, 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. + +"""State used by both invocation-side and service-side code.""" + +import enum + + +@enum.unique +class HighWrite(enum.Enum): + """The possible categories of high-level write state.""" + + OPEN = 'OPEN' + CLOSED = 'CLOSED' + + +class WriteState(object): + """A description of the state of writing to an RPC. + + Attributes: + low: A side-specific value describing the low-level state of writing. + high: A HighWrite value describing the high-level state of writing. + pending: A list of bytestrings for the RPC waiting to be written to the + other side of the RPC. + """ + + def __init__(self, low, high, pending): + self.low = low + self.high = high + self.pending = pending + + +class CommonRPCState(object): + """A description of an RPC's state. + + Attributes: + write: A WriteState describing the state of writing to the RPC. + sequence_number: The lowest-unused sequence number for use in generating + tickets locally describing the progress of the RPC. + deserializer: The behavior to be used to deserialize payload bytestreams + taken off the wire. + serializer: The behavior to be used to serialize payloads to be sent on the + wire. + """ + + def __init__(self, write, sequence_number, deserializer, serializer): + self.write = write + self.sequence_number = sequence_number + self.deserializer = deserializer + self.serializer = serializer diff --git a/src/python/grpcio/grpc/_adapter/_intermediary_low.py b/src/python/grpcio/grpc/_adapter/_intermediary_low.py new file mode 100644 index 00000000..e2feec6f --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_intermediary_low.py @@ -0,0 +1,267 @@ +# Copyright 2015, 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. + +"""Temporary old _low-like layer. + +Eases refactoring burden while we overhaul the Python framework. + +Plan: + The layers used to look like: + ... # outside _adapter + fore.py + rear.py # visible outside _adapter + _low + _c + The layers currently look like: + ... # outside _adapter + fore.py + rear.py # visible outside _adapter + _low_intermediary # adapter for new '_low' to old '_low' + _low # new '_low' + _c # new '_c' + We will later remove _low_intermediary after refactoring of fore.py and + rear.py according to the ticket system refactoring and get: + ... # outside _adapter, refactored + fore.py + rear.py # visible outside _adapter, refactored + _low # new '_low' + _c # new '_c' +""" + +import collections +import enum + +from grpc._adapter import _low +from grpc._adapter import _types + +_IGNORE_ME_TAG = object() +Code = _types.StatusCode +WriteFlags = _types.OpWriteFlags + + +class Status(collections.namedtuple('Status', ['code', 'details'])): + """Describes an RPC's overall status.""" + + +class ServiceAcceptance( + collections.namedtuple( + 'ServiceAcceptance', ['call', 'method', 'host', 'deadline'])): + """Describes an RPC on the service side at the start of service.""" + + +class Event( + collections.namedtuple( + 'Event', + ['kind', 'tag', 'write_accepted', 'complete_accepted', + 'service_acceptance', 'bytes', 'status', 'metadata'])): + """Describes an event emitted from a completion queue.""" + + @enum.unique + class Kind(enum.Enum): + """Describes the kind of an event.""" + + STOP = object() + WRITE_ACCEPTED = object() + COMPLETE_ACCEPTED = object() + SERVICE_ACCEPTED = object() + READ_ACCEPTED = object() + METADATA_ACCEPTED = object() + FINISH = object() + + +class _TagAdapter(collections.namedtuple('_TagAdapter', [ + 'user_tag', + 'kind' + ])): + pass + + +class Call(object): + """Adapter from old _low.Call interface to new _low.Call.""" + + def __init__(self, channel, completion_queue, method, host, deadline): + self._internal = channel._internal.create_call( + completion_queue._internal, method, host, deadline) + self._metadata = [] + + @staticmethod + def _from_internal(internal): + call = Call.__new__(Call) + call._internal = internal + call._metadata = [] + return call + + def invoke(self, completion_queue, metadata_tag, finish_tag): + err0 = self._internal.start_batch([ + _types.OpArgs.send_initial_metadata(self._metadata) + ], _IGNORE_ME_TAG) + err1 = self._internal.start_batch([ + _types.OpArgs.recv_initial_metadata() + ], _TagAdapter(metadata_tag, Event.Kind.METADATA_ACCEPTED)) + err2 = self._internal.start_batch([ + _types.OpArgs.recv_status_on_client() + ], _TagAdapter(finish_tag, Event.Kind.FINISH)) + return err0 if err0 != _types.CallError.OK else err1 if err1 != _types.CallError.OK else err2 if err2 != _types.CallError.OK else _types.CallError.OK + + def write(self, message, tag, flags): + return self._internal.start_batch([ + _types.OpArgs.send_message(message, flags) + ], _TagAdapter(tag, Event.Kind.WRITE_ACCEPTED)) + + def complete(self, tag): + return self._internal.start_batch([ + _types.OpArgs.send_close_from_client() + ], _TagAdapter(tag, Event.Kind.COMPLETE_ACCEPTED)) + + def accept(self, completion_queue, tag): + return self._internal.start_batch([ + _types.OpArgs.recv_close_on_server() + ], _TagAdapter(tag, Event.Kind.FINISH)) + + def add_metadata(self, key, value): + self._metadata.append((key, value)) + + def premetadata(self): + result = self._internal.start_batch([ + _types.OpArgs.send_initial_metadata(self._metadata) + ], _IGNORE_ME_TAG) + self._metadata = [] + return result + + def read(self, tag): + return self._internal.start_batch([ + _types.OpArgs.recv_message() + ], _TagAdapter(tag, Event.Kind.READ_ACCEPTED)) + + def status(self, status, tag): + return self._internal.start_batch([ + _types.OpArgs.send_status_from_server(self._metadata, status.code, status.details) + ], _TagAdapter(tag, Event.Kind.COMPLETE_ACCEPTED)) + + def cancel(self): + return self._internal.cancel() + + def peer(self): + return self._internal.peer() + + def set_credentials(self, creds): + return self._internal.set_credentials(creds._internal) + + +class Channel(object): + """Adapter from old _low.Channel interface to new _low.Channel.""" + + def __init__(self, hostport, client_credentials, server_host_override=None): + args = [] + if server_host_override: + args.append((_types.GrpcChannelArgumentKeys.SSL_TARGET_NAME_OVERRIDE.value, server_host_override)) + creds = None + if client_credentials: + creds = client_credentials._internal + self._internal = _low.Channel(hostport, args, creds) + + +class CompletionQueue(object): + """Adapter from old _low.CompletionQueue interface to new _low.CompletionQueue.""" + + def __init__(self): + self._internal = _low.CompletionQueue() + + def get(self, deadline=None): + if deadline is None: + ev = self._internal.next() + else: + ev = self._internal.next(deadline) + if ev is None: + return None + elif ev.tag is _IGNORE_ME_TAG: + return self.get(deadline) + elif ev.type == _types.EventType.QUEUE_SHUTDOWN: + kind = Event.Kind.STOP + tag = None + write_accepted = None + complete_accepted = None + service_acceptance = None + message_bytes = None + status = None + metadata = None + elif ev.type == _types.EventType.OP_COMPLETE: + kind = ev.tag.kind + tag = ev.tag.user_tag + write_accepted = ev.success if kind == Event.Kind.WRITE_ACCEPTED else None + complete_accepted = ev.success if kind == Event.Kind.COMPLETE_ACCEPTED else None + service_acceptance = ServiceAcceptance(Call._from_internal(ev.call), ev.call_details.method, ev.call_details.host, ev.call_details.deadline) if kind == Event.Kind.SERVICE_ACCEPTED else None + message_bytes = ev.results[0].message if kind == Event.Kind.READ_ACCEPTED else None + status = Status(ev.results[0].status.code, ev.results[0].status.details) if (kind == Event.Kind.FINISH and ev.results[0].status) else Status(_types.StatusCode.CANCELLED if ev.results[0].cancelled else _types.StatusCode.OK, '') if len(ev.results) > 0 and ev.results[0].cancelled is not None else None + metadata = ev.results[0].initial_metadata if (kind in [Event.Kind.SERVICE_ACCEPTED, Event.Kind.METADATA_ACCEPTED]) else (ev.results[0].trailing_metadata if kind == Event.Kind.FINISH else None) + else: + raise RuntimeError('unknown event') + result_ev = Event(kind=kind, tag=tag, write_accepted=write_accepted, complete_accepted=complete_accepted, service_acceptance=service_acceptance, bytes=message_bytes, status=status, metadata=metadata) + return result_ev + + def stop(self): + self._internal.shutdown() + + +class Server(object): + """Adapter from old _low.Server interface to new _low.Server.""" + + def __init__(self, completion_queue): + self._internal = _low.Server(completion_queue._internal, []) + self._internal_cq = completion_queue._internal + + def add_http2_addr(self, addr): + return self._internal.add_http2_port(addr) + + def add_secure_http2_addr(self, addr, server_credentials): + if server_credentials is None: + return self._internal.add_http2_port(addr, None) + else: + return self._internal.add_http2_port(addr, server_credentials._internal) + + def start(self): + return self._internal.start() + + def service(self, tag): + return self._internal.request_call(self._internal_cq, _TagAdapter(tag, Event.Kind.SERVICE_ACCEPTED)) + + def stop(self): + return self._internal.shutdown(_TagAdapter(None, Event.Kind.STOP)) + + +class ClientCredentials(object): + """Adapter from old _low.ClientCredentials interface to new _low.ClientCredentials.""" + + def __init__(self, root_certificates, private_key, certificate_chain): + self._internal = _low.ClientCredentials.ssl(root_certificates, private_key, certificate_chain) + + +class ServerCredentials(object): + """Adapter from old _low.ServerCredentials interface to new _low.ServerCredentials.""" + + def __init__(self, root_credentials, pair_sequence, force_client_auth): + self._internal = _low.ServerCredentials.ssl( + root_credentials, list(pair_sequence), force_client_auth) diff --git a/src/python/grpcio/grpc/_adapter/_low.py b/src/python/grpcio/grpc/_adapter/_low.py new file mode 100644 index 00000000..70ceb2a9 --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_low.py @@ -0,0 +1,132 @@ +# Copyright 2015, 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. + +from grpc import _grpcio_metadata +from grpc._adapter import _c +from grpc._adapter import _types + +_USER_AGENT = 'Python-gRPC-{}'.format(_grpcio_metadata.__version__) + +ClientCredentials = _c.ClientCredentials +ServerCredentials = _c.ServerCredentials + + +class CompletionQueue(_types.CompletionQueue): + + def __init__(self): + self.completion_queue = _c.CompletionQueue() + + def next(self, deadline=float('+inf')): + raw_event = self.completion_queue.next(deadline) + if raw_event is None: + return None + event = _types.Event(*raw_event) + if event.call is not None: + event = event._replace(call=Call(event.call)) + if event.call_details is not None: + event = event._replace(call_details=_types.CallDetails(*event.call_details)) + if event.results is not None: + new_results = [_types.OpResult(*r) for r in event.results] + new_results = [r if r.status is None else r._replace(status=_types.Status(_types.StatusCode(r.status[0]), r.status[1])) for r in new_results] + event = event._replace(results=new_results) + return event + + def shutdown(self): + self.completion_queue.shutdown() + + +class Call(_types.Call): + + def __init__(self, call): + self.call = call + + def start_batch(self, ops, tag): + return self.call.start_batch(ops, tag) + + def cancel(self, code=None, details=None): + if code is None and details is None: + return self.call.cancel() + else: + return self.call.cancel(code, details) + + def peer(self): + return self.call.peer() + + def set_credentials(self, creds): + return self.call.set_credentials(creds) + + +class Channel(_types.Channel): + + def __init__(self, target, args, creds=None): + args = list(args) + [(_c.PRIMARY_USER_AGENT_KEY, _USER_AGENT)] + if creds is None: + self.channel = _c.Channel(target, args) + else: + self.channel = _c.Channel(target, args, creds) + + def create_call(self, completion_queue, method, host, deadline=None): + return Call(self.channel.create_call(completion_queue.completion_queue, method, host, deadline)) + + def check_connectivity_state(self, try_to_connect): + return self.channel.check_connectivity_state(try_to_connect) + + def watch_connectivity_state(self, last_observed_state, deadline, + completion_queue, tag): + self.channel.watch_connectivity_state( + last_observed_state, deadline, completion_queue.completion_queue, tag) + + def target(self): + return self.channel.target() + + +_NO_TAG = object() + +class Server(_types.Server): + + def __init__(self, completion_queue, args): + self.server = _c.Server(completion_queue.completion_queue, args) + + def add_http2_port(self, addr, creds=None): + if creds is None: + return self.server.add_http2_port(addr) + else: + return self.server.add_http2_port(addr, creds) + + def start(self): + return self.server.start() + + def shutdown(self, tag=None): + return self.server.shutdown(tag) + + def request_call(self, completion_queue, tag): + return self.server.request_call(completion_queue.completion_queue, tag) + + def cancel_all_calls(self): + return self.server.cancel_all_calls() diff --git a/src/python/grpcio/grpc/_adapter/_types.py b/src/python/grpcio/grpc/_adapter/_types.py new file mode 100644 index 00000000..5470d2de --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_types.py @@ -0,0 +1,435 @@ +# Copyright 2015, 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. + +import abc +import collections +import enum + + +class GrpcChannelArgumentKeys(enum.Enum): + """Mirrors keys used in grpc_channel_args for GRPC-specific arguments.""" + SSL_TARGET_NAME_OVERRIDE = 'grpc.ssl_target_name_override' + + +@enum.unique +class CallError(enum.IntEnum): + """Mirrors grpc_call_error in the C core.""" + OK = 0 + ERROR = 1 + ERROR_NOT_ON_SERVER = 2 + ERROR_NOT_ON_CLIENT = 3 + ERROR_ALREADY_ACCEPTED = 4 + ERROR_ALREADY_INVOKED = 5 + ERROR_NOT_INVOKED = 6 + ERROR_ALREADY_FINISHED = 7 + ERROR_TOO_MANY_OPERATIONS = 8 + ERROR_INVALID_FLAGS = 9 + ERROR_INVALID_METADATA = 10 + + +@enum.unique +class StatusCode(enum.IntEnum): + """Mirrors grpc_status_code in the C core.""" + OK = 0 + CANCELLED = 1 + UNKNOWN = 2 + INVALID_ARGUMENT = 3 + DEADLINE_EXCEEDED = 4 + NOT_FOUND = 5 + ALREADY_EXISTS = 6 + PERMISSION_DENIED = 7 + RESOURCE_EXHAUSTED = 8 + FAILED_PRECONDITION = 9 + ABORTED = 10 + OUT_OF_RANGE = 11 + UNIMPLEMENTED = 12 + INTERNAL = 13 + UNAVAILABLE = 14 + DATA_LOSS = 15 + UNAUTHENTICATED = 16 + + +@enum.unique +class OpWriteFlags(enum.IntEnum): + """Mirrors defined write-flag constants in the C core.""" + WRITE_BUFFER_HINT = 1 + WRITE_NO_COMPRESS = 2 + + +@enum.unique +class OpType(enum.IntEnum): + """Mirrors grpc_op_type in the C core.""" + SEND_INITIAL_METADATA = 0 + SEND_MESSAGE = 1 + SEND_CLOSE_FROM_CLIENT = 2 + SEND_STATUS_FROM_SERVER = 3 + RECV_INITIAL_METADATA = 4 + RECV_MESSAGE = 5 + RECV_STATUS_ON_CLIENT = 6 + RECV_CLOSE_ON_SERVER = 7 + + +@enum.unique +class EventType(enum.IntEnum): + """Mirrors grpc_completion_type in the C core.""" + QUEUE_SHUTDOWN = 0 + QUEUE_TIMEOUT = 1 # if seen on the Python side, something went horridly wrong + OP_COMPLETE = 2 + + +@enum.unique +class ConnectivityState(enum.IntEnum): + """Mirrors grpc_connectivity_state in the C core.""" + IDLE = 0 + CONNECTING = 1 + READY = 2 + TRANSIENT_FAILURE = 3 + FATAL_FAILURE = 4 + + +class Status(collections.namedtuple( + 'Status', [ + 'code', + 'details', + ])): + """The end status of a GRPC call. + + Attributes: + code (StatusCode): ... + details (str): ... + """ + + +class CallDetails(collections.namedtuple( + 'CallDetails', [ + 'method', + 'host', + 'deadline', + ])): + """Provides information to the server about the client's call. + + Attributes: + method (str): ... + host (str): ... + deadline (float): ... + """ + + +class OpArgs(collections.namedtuple( + 'OpArgs', [ + 'type', + 'initial_metadata', + 'trailing_metadata', + 'message', + 'status', + 'write_flags', + ])): + """Arguments passed into a GRPC operation. + + Attributes: + type (OpType): ... + initial_metadata (sequence of 2-sequence of str): Only valid if type == + OpType.SEND_INITIAL_METADATA, else is None. + trailing_metadata (sequence of 2-sequence of str): Only valid if type == + OpType.SEND_STATUS_FROM_SERVER, else is None. + message (bytes): Only valid if type == OpType.SEND_MESSAGE, else is None. + status (Status): Only valid if type == OpType.SEND_STATUS_FROM_SERVER, else + is None. + write_flags (int): a bit OR'ing of 0 or more OpWriteFlags values. + """ + + @staticmethod + def send_initial_metadata(initial_metadata): + return OpArgs(OpType.SEND_INITIAL_METADATA, initial_metadata, None, None, None, 0) + + @staticmethod + def send_message(message, flags): + return OpArgs(OpType.SEND_MESSAGE, None, None, message, None, flags) + + @staticmethod + def send_close_from_client(): + return OpArgs(OpType.SEND_CLOSE_FROM_CLIENT, None, None, None, None, 0) + + @staticmethod + def send_status_from_server(trailing_metadata, status_code, status_details): + return OpArgs(OpType.SEND_STATUS_FROM_SERVER, None, trailing_metadata, None, Status(status_code, status_details), 0) + + @staticmethod + def recv_initial_metadata(): + return OpArgs(OpType.RECV_INITIAL_METADATA, None, None, None, None, 0); + + @staticmethod + def recv_message(): + return OpArgs(OpType.RECV_MESSAGE, None, None, None, None, 0) + + @staticmethod + def recv_status_on_client(): + return OpArgs(OpType.RECV_STATUS_ON_CLIENT, None, None, None, None, 0) + + @staticmethod + def recv_close_on_server(): + return OpArgs(OpType.RECV_CLOSE_ON_SERVER, None, None, None, None, 0) + + +class OpResult(collections.namedtuple( + 'OpResult', [ + 'type', + 'initial_metadata', + 'trailing_metadata', + 'message', + 'status', + 'cancelled', + ])): + """Results received from a GRPC operation. + + Attributes: + type (OpType): ... + initial_metadata (sequence of 2-sequence of str): Only valid if type == + OpType.RECV_INITIAL_METADATA, else is None. + trailing_metadata (sequence of 2-sequence of str): Only valid if type == + OpType.RECV_STATUS_ON_CLIENT, else is None. + message (bytes): Only valid if type == OpType.RECV_MESSAGE, else is None. + status (Status): Only valid if type == OpType.RECV_STATUS_ON_CLIENT, else + is None. + cancelled (bool): Only valid if type == OpType.RECV_CLOSE_ON_SERVER, else + is None. + """ + + +class Event(collections.namedtuple( + 'Event', [ + 'type', + 'tag', + 'call', + 'call_details', + 'results', + 'success', + ])): + """An event received from a GRPC completion queue. + + Attributes: + type (EventType): ... + tag (object): ... + call (Call): The Call object associated with this event (if there is one, + else None). + call_details (CallDetails): The call details associated with the + server-side call (if there is such information, else None). + results (list of OpResult): ... + success (bool): ... + """ + + +class CompletionQueue: + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def __init__(self): + pass + + def __iter__(self): + """This class may be iterated over. + + This is the equivalent of calling next() repeatedly with an absolute + deadline of None (i.e. no deadline). + """ + return self + + @abc.abstractmethod + def next(self, deadline=float('+inf')): + """Get the next event on this completion queue. + + Args: + deadline (float): absolute deadline in seconds from the Python epoch, or + None for no deadline. + + Returns: + Event: ... + """ + pass + + @abc.abstractmethod + def shutdown(self): + """Begin the shutdown process of this completion queue. + + Note that this does not immediately destroy the completion queue. + Nevertheless, user code should not pass it around after invoking this. + """ + return None + + +class Call: + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def start_batch(self, ops, tag): + """Start a batch of operations. + + Args: + ops (sequence of OpArgs): ... + tag (object): ... + + Returns: + CallError: ... + """ + return CallError.ERROR + + @abc.abstractmethod + def cancel(self, code=None, details=None): + """Cancel the call. + + Args: + code (int): Status code to cancel with (on the server side). If + specified, so must `details`. + details (str): Status details to cancel with (on the server side). If + specified, so must `code`. + + Returns: + CallError: ... + """ + return CallError.ERROR + + @abc.abstractmethod + def peer(self): + """Get the peer of this call. + + Returns: + str: the peer of this call. + """ + return None + + +class Channel: + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def __init__(self, target, args, credentials=None): + """Initialize a Channel. + + Args: + target (str): ... + args (sequence of 2-sequence of str, (str|integer)): ... + credentials (ClientCredentials): If None, create an insecure channel, + else create a secure channel using the client credentials. + """ + + @abc.abstractmethod + def create_call(self, completion_queue, method, host, deadline=float('+inf')): + """Create a call from this channel. + + Args: + completion_queue (CompletionQueue): ... + method (str): ... + host (str): ... + deadline (float): absolute deadline in seconds from the Python epoch, or + None for no deadline. + + Returns: + Call: call object associated with this Channel and passed parameters. + """ + return None + + @abc.abstractmethod + def check_connectivity_state(self, try_to_connect): + """Check and optionally repair the connectivity state of the channel. + + Args: + try_to_connect (bool): whether or not to try to connect the channel if + disconnected. + + Returns: + ConnectivityState: state of the channel at the time of this invocation. + """ + return None + + @abc.abstractmethod + def watch_connectivity_state(self, last_observed_state, deadline, + completion_queue, tag): + """Watch for connectivity state changes from the last_observed_state. + + Args: + last_observed_state (ConnectivityState): ... + deadline (float): ... + completion_queue (CompletionQueue): ... + tag (object) ... + """ + + @abc.abstractmethod + def target(self): + """Get the target of this channel. + + Returns: + str: the target of this channel. + """ + return None + + +class Server: + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def __init__(self, completion_queue, args): + """Initialize a server. + + Args: + completion_queue (CompletionQueue): ... + args (sequence of 2-sequence of str, (str|integer)): ... + """ + + @abc.abstractmethod + def add_http2_port(self, address, credentials=None): + """Adds an HTTP/2 address+port to the server. + + Args: + address (str): ... + credentials (ServerCredentials): If None, create an insecure port, else + create a secure port using the server credentials. + """ + + @abc.abstractmethod + def start(self): + """Starts the server.""" + + @abc.abstractmethod + def shutdown(self, tag=None): + """Shuts down the server. Does not immediately destroy the server. + + Args: + tag (object): if not None, have the server place an event on its + completion queue notifying it when this server has completely shut down. + """ + + @abc.abstractmethod + def request_call(self, completion_queue, tag): + """Requests a call from the server on the server's completion queue. + + Args: + completion_queue (CompletionQueue): Completion queue for the call. May be + the same as the server's completion queue. + tag (object) ... + """ diff --git a/src/python/grpcio/grpc/_adapter/fore.py b/src/python/grpcio/grpc/_adapter/fore.py new file mode 100644 index 00000000..acdd69c4 --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/fore.py @@ -0,0 +1,363 @@ +# Copyright 2015, 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. + +"""The RPC-service-side bridge between RPC Framework and GRPC-on-the-wire.""" + +import enum +import logging +import threading +import time + +from grpc._adapter import _common +from grpc._adapter import _intermediary_low as _low +from grpc.framework.base import interfaces as base_interfaces +from grpc.framework.base import null +from grpc.framework.foundation import activated +from grpc.framework.foundation import logging_pool + +_THREAD_POOL_SIZE = 10 + + +@enum.unique +class _LowWrite(enum.Enum): + """The possible categories of low-level write state.""" + + OPEN = 'OPEN' + ACTIVE = 'ACTIVE' + CLOSED = 'CLOSED' + + +def _write(call, rpc_state, payload): + serialized_payload = rpc_state.serializer(payload) + if rpc_state.write.low is _LowWrite.OPEN: + call.write(serialized_payload, call, 0) + rpc_state.write.low = _LowWrite.ACTIVE + else: + rpc_state.write.pending.append(serialized_payload) + + +def _status(call, rpc_state): + call.status(_low.Status(_low.Code.OK, ''), call) + rpc_state.write.low = _LowWrite.CLOSED + + +class ForeLink(base_interfaces.ForeLink, activated.Activated): + """A service-side bridge between RPC Framework and the C-ish _low code.""" + + def __init__( + self, pool, request_deserializers, response_serializers, + root_certificates, key_chain_pairs, port=None): + """Constructor. + + Args: + pool: A thread pool. + request_deserializers: A dict from RPC method names to request object + deserializer behaviors. + response_serializers: A dict from RPC method names to response object + serializer behaviors. + root_certificates: The PEM-encoded client root certificates as a + bytestring or None. + key_chain_pairs: A sequence of PEM-encoded private key-certificate chain + pairs. + port: The port on which to serve, or None to have a port selected + automatically. + """ + self._condition = threading.Condition() + self._pool = pool + self._request_deserializers = request_deserializers + self._response_serializers = response_serializers + self._root_certificates = root_certificates + self._key_chain_pairs = key_chain_pairs + self._requested_port = port + + self._rear_link = null.NULL_REAR_LINK + self._completion_queue = None + self._server = None + self._rpc_states = {} + self._spinning = False + self._port = None + + def _on_stop_event(self): + self._spinning = False + self._condition.notify_all() + + def _on_service_acceptance_event(self, event, server): + """Handle a service invocation event.""" + service_acceptance = event.service_acceptance + if service_acceptance is None: + return + + call = service_acceptance.call + call.accept(self._completion_queue, call) + # TODO(nathaniel): Metadata support. + call.premetadata() + call.read(call) + method = service_acceptance.method + + self._rpc_states[call] = _common.CommonRPCState( + _common.WriteState(_LowWrite.OPEN, _common.HighWrite.OPEN, []), 1, + self._request_deserializers[method], + self._response_serializers[method]) + + ticket = base_interfaces.FrontToBackTicket( + call, 0, base_interfaces.FrontToBackTicket.Kind.COMMENCEMENT, method, + base_interfaces.ServicedSubscription.Kind.FULL, None, None, + service_acceptance.deadline - time.time()) + self._rear_link.accept_front_to_back_ticket(ticket) + + server.service(None) + + def _on_read_event(self, event): + """Handle data arriving during an RPC.""" + call = event.tag + rpc_state = self._rpc_states.get(call, None) + if rpc_state is None: + return + + sequence_number = rpc_state.sequence_number + rpc_state.sequence_number += 1 + if event.bytes is None: + ticket = base_interfaces.FrontToBackTicket( + call, sequence_number, + base_interfaces.FrontToBackTicket.Kind.COMPLETION, None, None, None, + None, None) + else: + call.read(call) + ticket = base_interfaces.FrontToBackTicket( + call, sequence_number, + base_interfaces.FrontToBackTicket.Kind.CONTINUATION, None, None, + None, rpc_state.deserializer(event.bytes), None) + + self._rear_link.accept_front_to_back_ticket(ticket) + + def _on_write_event(self, event): + call = event.tag + rpc_state = self._rpc_states.get(call, None) + if rpc_state is None: + return + + if rpc_state.write.pending: + serialized_payload = rpc_state.write.pending.pop(0) + call.write(serialized_payload, call, 0) + elif rpc_state.write.high is _common.HighWrite.CLOSED: + _status(call, rpc_state) + else: + rpc_state.write.low = _LowWrite.OPEN + + def _on_complete_event(self, event): + if not event.complete_accepted: + logging.error('Complete not accepted! %s', (event,)) + call = event.tag + rpc_state = self._rpc_states.pop(call, None) + if rpc_state is None: + return + + sequence_number = rpc_state.sequence_number + rpc_state.sequence_number += 1 + ticket = base_interfaces.FrontToBackTicket( + call, sequence_number, + base_interfaces.FrontToBackTicket.Kind.TRANSMISSION_FAILURE, None, + None, None, None, None) + self._rear_link.accept_front_to_back_ticket(ticket) + + def _on_finish_event(self, event): + """Handle termination of an RPC.""" + call = event.tag + rpc_state = self._rpc_states.pop(call, None) + if rpc_state is None: + return + + code = event.status.code + if code is _low.Code.OK: + return + + sequence_number = rpc_state.sequence_number + rpc_state.sequence_number += 1 + if code is _low.Code.CANCELLED: + ticket = base_interfaces.FrontToBackTicket( + call, sequence_number, + base_interfaces.FrontToBackTicket.Kind.CANCELLATION, None, None, + None, None, None) + elif code is _low.Code.DEADLINE_EXCEEDED: + ticket = base_interfaces.FrontToBackTicket( + call, sequence_number, + base_interfaces.FrontToBackTicket.Kind.EXPIRATION, None, None, None, + None, None) + else: + # TODO(nathaniel): Better mapping of codes to ticket-categories + ticket = base_interfaces.FrontToBackTicket( + call, sequence_number, + base_interfaces.FrontToBackTicket.Kind.TRANSMISSION_FAILURE, None, + None, None, None, None) + self._rear_link.accept_front_to_back_ticket(ticket) + + def _spin(self, completion_queue, server): + while True: + event = completion_queue.get(None) + + with self._condition: + if event.kind is _low.Event.Kind.STOP: + self._on_stop_event() + return + elif self._server is None: + continue + elif event.kind is _low.Event.Kind.SERVICE_ACCEPTED: + self._on_service_acceptance_event(event, server) + elif event.kind is _low.Event.Kind.READ_ACCEPTED: + self._on_read_event(event) + elif event.kind is _low.Event.Kind.WRITE_ACCEPTED: + self._on_write_event(event) + elif event.kind is _low.Event.Kind.COMPLETE_ACCEPTED: + self._on_complete_event(event) + elif event.kind is _low.Event.Kind.FINISH: + self._on_finish_event(event) + else: + logging.error('Illegal event! %s', (event,)) + + def _continue(self, call, payload): + rpc_state = self._rpc_states.get(call, None) + if rpc_state is None: + return + + _write(call, rpc_state, payload) + + def _complete(self, call, payload): + """Handle completion of the writes of an RPC.""" + rpc_state = self._rpc_states.get(call, None) + if rpc_state is None: + return + + if rpc_state.write.low is _LowWrite.OPEN: + if payload is None: + _status(call, rpc_state) + else: + _write(call, rpc_state, payload) + elif rpc_state.write.low is _LowWrite.ACTIVE: + if payload is not None: + rpc_state.write.pending.append(rpc_state.serializer(payload)) + else: + raise ValueError('Called to complete after having already completed!') + rpc_state.write.high = _common.HighWrite.CLOSED + + def _cancel(self, call): + call.cancel() + self._rpc_states.pop(call, None) + + def join_rear_link(self, rear_link): + """See base_interfaces.ForeLink.join_rear_link for specification.""" + self._rear_link = null.NULL_REAR_LINK if rear_link is None else rear_link + + def _start(self): + """Starts this ForeLink. + + This method must be called before attempting to exchange tickets with this + object. + """ + with self._condition: + address = '[::]:%d' % ( + 0 if self._requested_port is None else self._requested_port) + self._completion_queue = _low.CompletionQueue() + if self._root_certificates is None and not self._key_chain_pairs: + self._server = _low.Server(self._completion_queue) + self._port = self._server.add_http2_addr(address) + else: + server_credentials = _low.ServerCredentials( + self._root_certificates, self._key_chain_pairs, False) + self._server = _low.Server(self._completion_queue) + self._port = self._server.add_secure_http2_addr( + address, server_credentials) + self._server.start() + + self._server.service(None) + + self._pool.submit(self._spin, self._completion_queue, self._server) + self._spinning = True + + return self + + # TODO(nathaniel): Expose graceful-shutdown semantics in which this object + # enters a state in which it finishes ongoing RPCs but refuses new ones. + def _stop(self): + """Stops this ForeLink. + + This method must be called for proper termination of this object, and no + attempts to exchange tickets with this object may be made after this method + has been called. + """ + with self._condition: + self._server.stop() + # TODO(nathaniel): Yep, this is weird. Deleting a server shouldn't have a + # behaviorally significant side-effect. + self._server = None + self._completion_queue.stop() + + while self._spinning: + self._condition.wait() + + self._port = None + + def __enter__(self): + """See activated.Activated.__enter__ for specification.""" + return self._start() + + def __exit__(self, exc_type, exc_val, exc_tb): + """See activated.Activated.__exit__ for specification.""" + self._stop() + return False + + def start(self): + """See activated.Activated.start for specification.""" + return self._start() + + def stop(self): + """See activated.Activated.stop for specification.""" + self._stop() + + def port(self): + """Identifies the port on which this ForeLink is servicing RPCs. + + Returns: + The number of the port on which this ForeLink is servicing RPCs, or None + if this ForeLink is not currently activated and servicing RPCs. + """ + with self._condition: + return self._port + + def accept_back_to_front_ticket(self, ticket): + """See base_interfaces.ForeLink.accept_back_to_front_ticket for spec.""" + with self._condition: + if self._server is None: + return + + if ticket.kind is base_interfaces.BackToFrontTicket.Kind.CONTINUATION: + self._continue(ticket.operation_id, ticket.payload) + elif ticket.kind is base_interfaces.BackToFrontTicket.Kind.COMPLETION: + self._complete(ticket.operation_id, ticket.payload) + else: + self._cancel(ticket.operation_id) diff --git a/src/python/grpcio/grpc/_adapter/rear.py b/src/python/grpcio/grpc/_adapter/rear.py new file mode 100644 index 00000000..17fa47f7 --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/rear.py @@ -0,0 +1,395 @@ +# Copyright 2015, 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. + +"""The RPC-invocation-side bridge between RPC Framework and GRPC-on-the-wire.""" + +import enum +import logging +import threading +import time + +from grpc._adapter import _common +from grpc._adapter import _intermediary_low as _low +from grpc.framework.base import interfaces as base_interfaces +from grpc.framework.base import null +from grpc.framework.foundation import activated +from grpc.framework.foundation import logging_pool + +_THREAD_POOL_SIZE = 10 + +_INVOCATION_EVENT_KINDS = ( + _low.Event.Kind.METADATA_ACCEPTED, + _low.Event.Kind.FINISH +) + + +@enum.unique +class _LowWrite(enum.Enum): + """The possible categories of low-level write state.""" + + OPEN = 'OPEN' + ACTIVE = 'ACTIVE' + CLOSED = 'CLOSED' + + +class _RPCState(object): + """The full state of any tracked RPC. + + Attributes: + call: The _low.Call object for the RPC. + outstanding: The set of Event.Kind values describing expected future events + for the RPC. + active: A boolean indicating whether or not the RPC is active. + common: An _common.RPCState describing additional state for the RPC. + """ + + def __init__(self, call, outstanding, active, common): + self.call = call + self.outstanding = outstanding + self.active = active + self.common = common + + +def _write(operation_id, call, outstanding, write_state, serialized_payload): + if write_state.low is _LowWrite.OPEN: + call.write(serialized_payload, operation_id, 0) + outstanding.add(_low.Event.Kind.WRITE_ACCEPTED) + write_state.low = _LowWrite.ACTIVE + elif write_state.low is _LowWrite.ACTIVE: + write_state.pending.append(serialized_payload) + else: + raise ValueError('Write attempted after writes completed!') + + +class RearLink(base_interfaces.RearLink, activated.Activated): + """An invocation-side bridge between RPC Framework and the C-ish _low code.""" + + def __init__( + self, host, port, pool, request_serializers, response_deserializers, + secure, root_certificates, private_key, certificate_chain, + metadata_transformer=None, server_host_override=None): + """Constructor. + + Args: + host: The host to which to connect for RPC service. + port: The port to which to connect for RPC service. + pool: A thread pool. + request_serializers: A dict from RPC method names to request object + serializer behaviors. + response_deserializers: A dict from RPC method names to response object + deserializer behaviors. + secure: A boolean indicating whether or not to use a secure connection. + root_certificates: The PEM-encoded root certificates or None to ask for + them to be retrieved from a default location. + private_key: The PEM-encoded private key to use or None if no private + key should be used. + certificate_chain: The PEM-encoded certificate chain to use or None if + no certificate chain should be used. + metadata_transformer: A function that given a metadata object produces + another metadata to be used in the underlying communication on the + wire. + server_host_override: (For testing only) the target name used for SSL + host name checking. + """ + self._condition = threading.Condition() + self._host = host + self._port = port + self._pool = pool + self._request_serializers = request_serializers + self._response_deserializers = response_deserializers + + self._fore_link = null.NULL_FORE_LINK + self._completion_queue = None + self._channel = None + self._rpc_states = {} + self._spinning = False + if secure: + self._client_credentials = _low.ClientCredentials( + root_certificates, private_key, certificate_chain) + else: + self._client_credentials = None + self._root_certificates = root_certificates + self._private_key = private_key + self._certificate_chain = certificate_chain + self._metadata_transformer = metadata_transformer + self._server_host_override = server_host_override + + def _on_write_event(self, operation_id, event, rpc_state): + if event.write_accepted: + if rpc_state.common.write.pending: + rpc_state.call.write( + rpc_state.common.write.pending.pop(0), operation_id, 0) + rpc_state.outstanding.add(_low.Event.Kind.WRITE_ACCEPTED) + elif rpc_state.common.write.high is _common.HighWrite.CLOSED: + rpc_state.call.complete(operation_id) + rpc_state.outstanding.add(_low.Event.Kind.COMPLETE_ACCEPTED) + rpc_state.common.write.low = _LowWrite.CLOSED + else: + rpc_state.common.write.low = _LowWrite.OPEN + else: + logging.error('RPC write not accepted! Event: %s', (event,)) + rpc_state.active = False + ticket = base_interfaces.BackToFrontTicket( + operation_id, rpc_state.common.sequence_number, + base_interfaces.BackToFrontTicket.Kind.TRANSMISSION_FAILURE, None) + rpc_state.common.sequence_number += 1 + self._fore_link.accept_back_to_front_ticket(ticket) + + def _on_read_event(self, operation_id, event, rpc_state): + if event.bytes is not None: + rpc_state.call.read(operation_id) + rpc_state.outstanding.add(_low.Event.Kind.READ_ACCEPTED) + + ticket = base_interfaces.BackToFrontTicket( + operation_id, rpc_state.common.sequence_number, + base_interfaces.BackToFrontTicket.Kind.CONTINUATION, + rpc_state.common.deserializer(event.bytes)) + rpc_state.common.sequence_number += 1 + self._fore_link.accept_back_to_front_ticket(ticket) + + def _on_complete_event(self, operation_id, event, rpc_state): + if not event.complete_accepted: + logging.error('RPC complete not accepted! Event: %s', (event,)) + rpc_state.active = False + ticket = base_interfaces.BackToFrontTicket( + operation_id, rpc_state.common.sequence_number, + base_interfaces.BackToFrontTicket.Kind.TRANSMISSION_FAILURE, None) + rpc_state.common.sequence_number += 1 + self._fore_link.accept_back_to_front_ticket(ticket) + + # TODO(nathaniel): Metadata support. + def _on_metadata_event(self, operation_id, event, rpc_state): # pylint: disable=unused-argument + rpc_state.call.read(operation_id) + rpc_state.outstanding.add(_low.Event.Kind.READ_ACCEPTED) + + def _on_finish_event(self, operation_id, event, rpc_state): + """Handle termination of an RPC.""" + # TODO(nathaniel): Cover all statuses. + if event.status.code is _low.Code.OK: + kind = base_interfaces.BackToFrontTicket.Kind.COMPLETION + elif event.status.code is _low.Code.CANCELLED: + kind = base_interfaces.BackToFrontTicket.Kind.CANCELLATION + elif event.status.code is _low.Code.DEADLINE_EXCEEDED: + kind = base_interfaces.BackToFrontTicket.Kind.EXPIRATION + else: + kind = base_interfaces.BackToFrontTicket.Kind.TRANSMISSION_FAILURE + ticket = base_interfaces.BackToFrontTicket( + operation_id, rpc_state.common.sequence_number, kind, None) + rpc_state.common.sequence_number += 1 + self._fore_link.accept_back_to_front_ticket(ticket) + + def _spin(self, completion_queue): + while True: + event = completion_queue.get(None) + operation_id = event.tag + + with self._condition: + rpc_state = self._rpc_states[operation_id] + rpc_state.outstanding.remove(event.kind) + if rpc_state.active and self._completion_queue is not None: + if event.kind is _low.Event.Kind.WRITE_ACCEPTED: + self._on_write_event(operation_id, event, rpc_state) + elif event.kind is _low.Event.Kind.METADATA_ACCEPTED: + self._on_metadata_event(operation_id, event, rpc_state) + elif event.kind is _low.Event.Kind.READ_ACCEPTED: + self._on_read_event(operation_id, event, rpc_state) + elif event.kind is _low.Event.Kind.COMPLETE_ACCEPTED: + self._on_complete_event(operation_id, event, rpc_state) + elif event.kind is _low.Event.Kind.FINISH: + self._on_finish_event(operation_id, event, rpc_state) + else: + logging.error('Illegal RPC event! %s', (event,)) + + if not rpc_state.outstanding: + self._rpc_states.pop(operation_id) + if not self._rpc_states: + self._spinning = False + self._condition.notify_all() + return + + def _invoke(self, operation_id, name, high_state, payload, timeout): + """Invoke an RPC. + + Args: + operation_id: Any object to be used as an operation ID for the RPC. + name: The RPC method name. + high_state: A _common.HighWrite value representing the "high write state" + of the RPC. + payload: A payload object for the RPC or None if no payload was given at + invocation-time. + timeout: A duration of time in seconds to allow for the RPC. + """ + request_serializer = self._request_serializers[name] + call = _low.Call(self._channel, self._completion_queue, name, self._host, time.time() + timeout) + if self._metadata_transformer is not None: + metadata = self._metadata_transformer([]) + for metadata_key, metadata_value in metadata: + call.add_metadata(metadata_key, metadata_value) + call.invoke(self._completion_queue, operation_id, operation_id) + outstanding = set(_INVOCATION_EVENT_KINDS) + + if payload is None: + if high_state is _common.HighWrite.CLOSED: + call.complete(operation_id) + low_state = _LowWrite.CLOSED + outstanding.add(_low.Event.Kind.COMPLETE_ACCEPTED) + else: + low_state = _LowWrite.OPEN + else: + serialized_payload = request_serializer(payload) + call.write(serialized_payload, operation_id, 0) + outstanding.add(_low.Event.Kind.WRITE_ACCEPTED) + low_state = _LowWrite.ACTIVE + + write_state = _common.WriteState(low_state, high_state, []) + common_state = _common.CommonRPCState( + write_state, 0, self._response_deserializers[name], request_serializer) + self._rpc_states[operation_id] = _RPCState( + call, outstanding, True, common_state) + + if not self._spinning: + self._pool.submit(self._spin, self._completion_queue) + self._spinning = True + + def _commence(self, operation_id, name, payload, timeout): + self._invoke(operation_id, name, _common.HighWrite.OPEN, payload, timeout) + + def _continue(self, operation_id, payload): + rpc_state = self._rpc_states.get(operation_id, None) + if rpc_state is None or not rpc_state.active: + return + + _write( + operation_id, rpc_state.call, rpc_state.outstanding, + rpc_state.common.write, rpc_state.common.serializer(payload)) + + def _complete(self, operation_id, payload): + """Close writes associated with an ongoing RPC. + + Args: + operation_id: Any object being use as an operation ID for the RPC. + payload: A payload object for the RPC (and thus the last payload object + for the RPC) or None if no payload was given along with the instruction + to indicate the end of writes for the RPC. + """ + rpc_state = self._rpc_states.get(operation_id, None) + if rpc_state is None or not rpc_state.active: + return + + write_state = rpc_state.common.write + if payload is None: + if write_state.low is _LowWrite.OPEN: + rpc_state.call.complete(operation_id) + rpc_state.outstanding.add(_low.Event.Kind.COMPLETE_ACCEPTED) + write_state.low = _LowWrite.CLOSED + else: + _write( + operation_id, rpc_state.call, rpc_state.outstanding, write_state, + rpc_state.common.serializer(payload)) + write_state.high = _common.HighWrite.CLOSED + + def _entire(self, operation_id, name, payload, timeout): + self._invoke(operation_id, name, _common.HighWrite.CLOSED, payload, timeout) + + def _cancel(self, operation_id): + rpc_state = self._rpc_states.get(operation_id, None) + if rpc_state is not None and rpc_state.active: + rpc_state.call.cancel() + rpc_state.active = False + + def join_fore_link(self, fore_link): + """See base_interfaces.RearLink.join_fore_link for specification.""" + with self._condition: + self._fore_link = null.NULL_FORE_LINK if fore_link is None else fore_link + + def _start(self): + """Starts this RearLink. + + This method must be called before attempting to exchange tickets with this + object. + """ + with self._condition: + self._completion_queue = _low.CompletionQueue() + self._channel = _low.Channel( + '%s:%d' % (self._host, self._port), self._client_credentials, + server_host_override=self._server_host_override) + return self + + def _stop(self): + """Stops this RearLink. + + This method must be called for proper termination of this object, and no + attempts to exchange tickets with this object may be made after this method + has been called. + """ + with self._condition: + self._completion_queue.stop() + self._completion_queue = None + + while self._spinning: + self._condition.wait() + + def __enter__(self): + """See activated.Activated.__enter__ for specification.""" + return self._start() + + def __exit__(self, exc_type, exc_val, exc_tb): + """See activated.Activated.__exit__ for specification.""" + self._stop() + return False + + def start(self): + """See activated.Activated.start for specification.""" + return self._start() + + def stop(self): + """See activated.Activated.stop for specification.""" + self._stop() + + def accept_front_to_back_ticket(self, ticket): + """See base_interfaces.RearLink.accept_front_to_back_ticket for spec.""" + with self._condition: + if self._completion_queue is None: + return + + if ticket.kind is base_interfaces.FrontToBackTicket.Kind.COMMENCEMENT: + self._commence( + ticket.operation_id, ticket.name, ticket.payload, ticket.timeout) + elif ticket.kind is base_interfaces.FrontToBackTicket.Kind.CONTINUATION: + self._continue(ticket.operation_id, ticket.payload) + elif ticket.kind is base_interfaces.FrontToBackTicket.Kind.COMPLETION: + self._complete(ticket.operation_id, ticket.payload) + elif ticket.kind is base_interfaces.FrontToBackTicket.Kind.ENTIRE: + self._entire( + ticket.operation_id, ticket.name, ticket.payload, ticket.timeout) + elif ticket.kind is base_interfaces.FrontToBackTicket.Kind.CANCELLATION: + self._cancel(ticket.operation_id) + else: + # NOTE(nathaniel): All other categories are treated as cancellation. + self._cancel(ticket.operation_id) diff --git a/src/python/grpcio/grpc/_cython/.gitignore b/src/python/grpcio/grpc/_cython/.gitignore new file mode 100644 index 00000000..c3150292 --- /dev/null +++ b/src/python/grpcio/grpc/_cython/.gitignore @@ -0,0 +1,7 @@ +*.h +*.c +*.a +*.so +*.dll +*.pyc +*.pyd diff --git a/src/python/grpcio/grpc/_cython/README.rst b/src/python/grpcio/grpc/_cython/README.rst new file mode 100644 index 00000000..c0e66734 --- /dev/null +++ b/src/python/grpcio/grpc/_cython/README.rst @@ -0,0 +1,52 @@ +GRPC Python Cython layer +======================== + +Package for the GRPC Python Cython layer. + +What is Cython? +--------------- + +Cython is both a superset of the Python language with extensions for dealing +with C types and a tool that transpiles this superset into C code. It provides +convenient means of statically typing expressions and of converting Python +strings to pointers (among other niceties), thus dramatically smoothing the +Python/C interop by allowing fluid use of APIs in both from the same source. +See the wonderful `Cython website`_. + +Why Cython? +----------- + +- **Python 2 and 3 support** + Cython generated C code has precompiler macros to target both Python 2 and + Python 3 C APIs, even while acting as a superset of just the Python 2 + language (e.g. using ``basestring``). +- **Significantly less semantic noise** + A lot of CPython code is just glue, especially human-error-prone + ``Py_INCREF``-ing and ``Py_DECREF``-ing around error handlers and such. + Cython takes care of that automagically. +- **Possible PyPy support** + One of the major developments in Cython over the past few years was the + addition of support for PyPy. We might soon be able to provide such support + ourselves through our use of Cython. +- **Less Python glue code** + There existed several adapter layers in and around the original CPython code + to smooth the surface exposed to Python due to how much trouble it was to + make such a smooth surface via the CPython API alone. Cython makes writing + such a surface incredibly easy, so these adapter layers may be removed. + +Implications for Users +---------------------- + +Nothing additional will be required for users. PyPI packages will contain +Cython generated C code and thus not necessitate a Cython installation. + +Implications for GRPC Developers +-------------------------------- + +A typical edit-compile-debug cycle now requires Cython. We install Cython in +the ``virtualenv`` generated for the Python tests in this repository, so +initial test runs may take an extra 2+ minutes to complete. Subsequent test +runs won't reinstall ``Cython`` (unless required versions change and the +``virtualenv`` doesn't have installed versions that satisfy the change). + +.. _`Cython website`: http://cython.org/ diff --git a/src/python/grpcio/grpc/_cython/__init__.py b/src/python/grpcio/grpc/_cython/__init__.py new file mode 100644 index 00000000..b8939880 --- /dev/null +++ b/src/python/grpcio/grpc/_cython/__init__.py @@ -0,0 +1,28 @@ +# Copyright 2015, 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. diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/__init__.py b/src/python/grpcio/grpc/_cython/_cygrpc/__init__.py new file mode 100644 index 00000000..b8939880 --- /dev/null +++ b/src/python/grpcio/grpc/_cython/_cygrpc/__init__.py @@ -0,0 +1,28 @@ +# Copyright 2015, 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. diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/call.pxd b/src/python/grpcio/grpc/_cython/_cygrpc/call.pxd new file mode 100644 index 00000000..fe9b81e3 --- /dev/null +++ b/src/python/grpcio/grpc/_cython/_cygrpc/call.pxd @@ -0,0 +1,37 @@ +# Copyright 2015, 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. + +from grpc._cython._cygrpc cimport grpc + + +cdef class Call: + + cdef grpc.grpc_call *c_call + cdef list references + diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/call.pyx b/src/python/grpcio/grpc/_cython/_cygrpc/call.pyx new file mode 100644 index 00000000..4349786b --- /dev/null +++ b/src/python/grpcio/grpc/_cython/_cygrpc/call.pyx @@ -0,0 +1,82 @@ +# Copyright 2015, 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. + +cimport cpython + +from grpc._cython._cygrpc cimport records + + +cdef class Call: + + def __cinit__(self): + # Create an *empty* call + self.c_call = NULL + self.references = [] + + def start_batch(self, operations, tag): + if not self.is_valid: + raise ValueError("invalid call object cannot be used from Python") + cdef records.Operations cy_operations = records.Operations(operations) + cdef records.OperationTag operation_tag = records.OperationTag(tag) + operation_tag.operation_call = self + operation_tag.batch_operations = cy_operations + cpython.Py_INCREF(operation_tag) + return grpc.grpc_call_start_batch( + self.c_call, cy_operations.c_ops, cy_operations.c_nops, + operation_tag) + + def cancel(self, + grpc.grpc_status_code error_code=grpc.GRPC_STATUS__DO_NOT_USE, + details=None): + if not self.is_valid: + raise ValueError("invalid call object cannot be used from Python") + if (details is None) != (error_code == grpc.GRPC_STATUS__DO_NOT_USE): + raise ValueError("if error_code is specified, so must details " + "(and vice-versa)") + if isinstance(details, bytes): + pass + elif isinstance(details, basestring): + details = details.encode() + else: + raise TypeError("expected details to be str or bytes") + if error_code != grpc.GRPC_STATUS__DO_NOT_USE: + self.references.append(details) + return grpc.grpc_call_cancel_with_status(self.c_call, error_code, details) + else: + return grpc.grpc_call_cancel(self.c_call) + + def __dealloc__(self): + if self.c_call != NULL: + grpc.grpc_call_destroy(self.c_call) + + # The object *should* always be valid from Python. Used for debugging. + @property + def is_valid(self): + return self.c_call != NULL + diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/channel.pxd b/src/python/grpcio/grpc/_cython/_cygrpc/channel.pxd new file mode 100644 index 00000000..3e341bf2 --- /dev/null +++ b/src/python/grpcio/grpc/_cython/_cygrpc/channel.pxd @@ -0,0 +1,36 @@ +# Copyright 2015, 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. + +from grpc._cython._cygrpc cimport grpc + + +cdef class Channel: + + cdef grpc.grpc_channel *c_channel + cdef list references diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx b/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx new file mode 100644 index 00000000..b2031381 --- /dev/null +++ b/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx @@ -0,0 +1,84 @@ +# Copyright 2015, 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. + +from grpc._cython._cygrpc cimport call +from grpc._cython._cygrpc cimport completion_queue +from grpc._cython._cygrpc cimport credentials +from grpc._cython._cygrpc cimport records + + +cdef class Channel: + + def __cinit__(self, target, records.ChannelArgs arguments=None, + credentials.ClientCredentials client_credentials=None): + cdef grpc.grpc_channel_args *c_arguments = NULL + self.c_channel = NULL + self.references = [] + if arguments is not None: + c_arguments = &arguments.c_args + if isinstance(target, bytes): + pass + elif isinstance(target, basestring): + target = target.encode() + else: + raise TypeError("expected target to be str or bytes") + if client_credentials is None: + self.c_channel = grpc.grpc_channel_create(target, c_arguments) + else: + self.c_channel = grpc.grpc_secure_channel_create( + client_credentials.c_credentials, target, c_arguments) + self.references.append(client_credentials) + self.references.append(target) + self.references.append(arguments) + + def create_call(self, completion_queue.CompletionQueue queue not None, + method, host, records.Timespec deadline not None): + if queue.is_shutting_down: + raise ValueError("queue must not be shutting down or shutdown") + if isinstance(method, bytes): + pass + elif isinstance(method, basestring): + method = method.encode() + else: + raise TypeError("expected method to be str or bytes") + if isinstance(host, bytes): + pass + elif isinstance(host, basestring): + host = host.encode() + else: + raise TypeError("expected host to be str or bytes") + cdef call.Call operation_call = call.Call() + operation_call.references = [self, method, host, queue] + operation_call.c_call = grpc.grpc_channel_create_call( + self.c_channel, queue.c_completion_queue, method, host, deadline.c_time) + return operation_call + + def __dealloc__(self): + if self.c_channel != NULL: + grpc.grpc_channel_destroy(self.c_channel) diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pxd b/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pxd new file mode 100644 index 00000000..fd562ad7 --- /dev/null +++ b/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pxd @@ -0,0 +1,39 @@ +# Copyright 2015, 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. + +from grpc._cython._cygrpc cimport grpc + + +cdef class CompletionQueue: + + cdef grpc.grpc_completion_queue *c_completion_queue + cdef object poll_condition + cdef bint is_polling + cdef bint is_shutting_down + cdef bint is_shutdown diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx b/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx new file mode 100644 index 00000000..886d8536 --- /dev/null +++ b/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx @@ -0,0 +1,117 @@ +# Copyright 2015, 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. + +cimport cpython + +from grpc._cython._cygrpc cimport call +from grpc._cython._cygrpc cimport records + +import threading +import time + + +cdef class CompletionQueue: + + def __cinit__(self): + self.c_completion_queue = grpc.grpc_completion_queue_create() + self.is_shutting_down = False + self.is_shutdown = False + self.poll_condition = threading.Condition() + self.is_polling = False + + def poll(self, records.Timespec deadline=None): + # We name this 'poll' to avoid problems with CPython's expectations for + # 'special' methods (like next and __next__). + cdef grpc.gpr_timespec c_deadline = grpc.gpr_inf_future + cdef records.OperationTag tag = None + cdef object user_tag = None + cdef call.Call operation_call = None + cdef records.CallDetails request_call_details = None + cdef records.Metadata request_metadata = None + cdef records.Operations batch_operations = None + if deadline is not None: + c_deadline = deadline.c_time + cdef grpc.grpc_event event + + # Poll within a critical section + with self.poll_condition: + while self.is_polling: + self.poll_condition.wait(float(deadline) - time.time()) + self.is_polling = True + with nogil: + event = grpc.grpc_completion_queue_next( + self.c_completion_queue, c_deadline) + with self.poll_condition: + self.is_polling = False + self.poll_condition.notify() + + if event.type == grpc.GRPC_QUEUE_TIMEOUT: + return records.Event(event.type, False, None, None, None, None, None) + elif event.type == grpc.GRPC_QUEUE_SHUTDOWN: + self.is_shutdown = True + return records.Event(event.type, True, None, None, None, None, None) + else: + if event.tag != NULL: + tag = event.tag + # We receive event tags only after they've been inc-ref'd elsewhere in + # the code. + cpython.Py_DECREF(tag) + if tag.shutting_down_server is not None: + tag.shutting_down_server.notify_shutdown_complete() + user_tag = tag.user_tag + operation_call = tag.operation_call + request_call_details = tag.request_call_details + request_metadata = tag.request_metadata + batch_operations = tag.batch_operations + if tag.is_new_request: + # Stuff in the tag not explicitly handled by us needs to live through + # the life of the call + operation_call.references.extend(tag.references) + return records.Event( + event.type, event.success, user_tag, operation_call, + request_call_details, request_metadata, batch_operations) + + def shutdown(self): + grpc.grpc_completion_queue_shutdown(self.c_completion_queue) + self.is_shutting_down = True + + def clear(self): + if not self.is_shutting_down: + raise ValueError('queue must be shutting down to be cleared') + while self.poll().type != grpc.GRPC_QUEUE_SHUTDOWN: + pass + + def __dealloc__(self): + if self.c_completion_queue != NULL: + # Ensure shutdown, pump the queue + if not self.is_shutting_down: + self.shutdown() + while not self.is_shutdown: + self.poll() + grpc.grpc_completion_queue_destroy(self.c_completion_queue) diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pxd b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pxd new file mode 100644 index 00000000..6b74a267 --- /dev/null +++ b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pxd @@ -0,0 +1,45 @@ +# Copyright 2015, 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. + +from grpc._cython._cygrpc cimport grpc + + +cdef class ClientCredentials: + + cdef grpc.grpc_credentials *c_credentials + cdef grpc.grpc_ssl_pem_key_cert_pair c_ssl_pem_key_cert_pair + cdef list references + + +cdef class ServerCredentials: + + cdef grpc.grpc_server_credentials *c_credentials + cdef grpc.grpc_ssl_pem_key_cert_pair *c_ssl_pem_key_cert_pairs + cdef size_t c_ssl_pem_key_cert_pairs_count + cdef list references diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx new file mode 100644 index 00000000..dc40a7a6 --- /dev/null +++ b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx @@ -0,0 +1,187 @@ +# Copyright 2015, 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. + +from grpc._cython._cygrpc cimport records + + +cdef class ClientCredentials: + + def __cinit__(self): + self.c_credentials = NULL + self.c_ssl_pem_key_cert_pair.private_key = NULL + self.c_ssl_pem_key_cert_pair.certificate_chain = NULL + self.references = [] + + # The object *can* be invalid in Python if we fail to make the credentials + # (and the core thus returns NULL credentials). Used primarily for debugging. + @property + def is_valid(self): + return self.c_credentials != NULL + + def __dealloc__(self): + if self.c_credentials != NULL: + grpc.grpc_credentials_release(self.c_credentials) + + +cdef class ServerCredentials: + + def __cinit__(self): + self.c_credentials = NULL + + def __dealloc__(self): + if self.c_credentials != NULL: + grpc.grpc_server_credentials_release(self.c_credentials) + + +def client_credentials_google_default(): + cdef ClientCredentials credentials = ClientCredentials(); + credentials.c_credentials = grpc.grpc_google_default_credentials_create() + return credentials + +def client_credentials_ssl(pem_root_certificates, + records.SslPemKeyCertPair ssl_pem_key_cert_pair): + if pem_root_certificates is None: + pass + elif isinstance(pem_root_certificates, bytes): + pass + elif isinstance(pem_root_certificates, basestring): + pem_root_certificates = pem_root_certificates.encode() + else: + raise TypeError("expected str or bytes for pem_root_certificates") + cdef ClientCredentials credentials = ClientCredentials() + cdef const char *c_pem_root_certificates = NULL + if pem_root_certificates is not None: + c_pem_root_certificates = pem_root_certificates + credentials.references.append(pem_root_certificates) + if ssl_pem_key_cert_pair is not None: + credentials.c_credentials = grpc.grpc_ssl_credentials_create( + c_pem_root_certificates, &ssl_pem_key_cert_pair.c_pair + ) + credentials.references.append(ssl_pem_key_cert_pair) + else: + credentials.c_credentials = grpc.grpc_ssl_credentials_create( + c_pem_root_certificates, NULL + ) + +def client_credentials_composite_credentials( + ClientCredentials credentials_1 not None, + ClientCredentials credentials_2 not None): + if not credentials_1.is_valid or not credentials_2.is_valid: + raise ValueError("passed credentials must both be valid") + cdef ClientCredentials credentials = ClientCredentials() + credentials.c_credentials = grpc.grpc_composite_credentials_create( + credentials_1.c_credentials, credentials_2.c_credentials) + credentials.references.append(credentials_1) + credentials.references.append(credentials_2) + return credentials + +def client_credentials_compute_engine(): + cdef ClientCredentials credentials = ClientCredentials() + credentials.c_credentials = grpc.grpc_compute_engine_credentials_create() + return credentials + +#TODO rename to something like client_credentials_service_account_jwt_access. +def client_credentials_jwt(json_key, records.Timespec token_lifetime not None): + if isinstance(json_key, bytes): + pass + elif isinstance(json_key, basestring): + json_key = json_key.encode() + else: + raise TypeError("expected json_key to be str or bytes") + cdef ClientCredentials credentials = ClientCredentials() + credentials.c_credentials = grpc.grpc_service_account_jwt_access_credentials_create( + json_key, token_lifetime.c_time) + credentials.references.append(json_key) + return credentials + +def client_credentials_refresh_token(json_refresh_token): + if isinstance(json_refresh_token, bytes): + pass + elif isinstance(json_refresh_token, basestring): + json_refresh_token = json_refresh_token.encode() + else: + raise TypeError("expected json_refresh_token to be str or bytes") + cdef ClientCredentials credentials = ClientCredentials() + credentials.c_credentials = grpc.grpc_refresh_token_credentials_create( + json_refresh_token) + credentials.references.append(json_refresh_token) + return credentials + +def client_credentials_iam(authorization_token, authority_selector): + if isinstance(authorization_token, bytes): + pass + elif isinstance(authorization_token, basestring): + authorization_token = authorization_token.encode() + else: + raise TypeError("expected authorization_token to be str or bytes") + if isinstance(authority_selector, bytes): + pass + elif isinstance(authority_selector, basestring): + authority_selector = authority_selector.encode() + else: + raise TypeError("expected authority_selector to be str or bytes") + cdef ClientCredentials credentials = ClientCredentials() + credentials.c_credentials = grpc.grpc_iam_credentials_create( + authorization_token, authority_selector) + credentials.references.append(authorization_token) + credentials.references.append(authority_selector) + return credentials + +def server_credentials_ssl(pem_root_certs, pem_key_cert_pairs): + if pem_root_certs is None: + pass + elif isinstance(pem_root_certs, bytes): + pass + elif isinstance(pem_root_certs, basestring): + pem_root_certs = pem_root_certs.encode() + else: + raise TypeError("expected pem_root_certs to be str or bytes") + pem_key_cert_pairs = list(pem_key_cert_pairs) + for pair in pem_key_cert_pairs: + if not isinstance(pair, records.SslPemKeyCertPair): + raise TypeError("expected pem_key_cert_pairs to be sequence of " + "records.SslPemKeyCertPair") + cdef ServerCredentials credentials = ServerCredentials() + credentials.references.append(pem_key_cert_pairs) + credentials.references.append(pem_root_certs) + credentials.c_ssl_pem_key_cert_pairs_count = len(pem_key_cert_pairs) + credentials.c_ssl_pem_key_cert_pairs = ( + grpc.gpr_malloc( + sizeof(grpc.grpc_ssl_pem_key_cert_pair) * + credentials.c_ssl_pem_key_cert_pairs_count + )) + for i in range(credentials.c_ssl_pem_key_cert_pairs_count): + credentials.c_ssl_pem_key_cert_pairs[i] = ( + (pem_key_cert_pairs[i]).c_pair) + credentials.c_credentials = grpc.grpc_ssl_server_credentials_create( + pem_root_certs, credentials.c_ssl_pem_key_cert_pairs, + credentials.c_ssl_pem_key_cert_pairs_count + ) + return credentials + diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxd b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxd new file mode 100644 index 00000000..8b469724 --- /dev/null +++ b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxd @@ -0,0 +1,340 @@ +# Copyright 2015, 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. + +cimport libc.time + + +cdef extern from "grpc/support/alloc.h": + void *gpr_malloc(size_t size) + void gpr_free(void *ptr) + void *gpr_realloc(void *p, size_t size) + +cdef extern from "grpc/support/slice.h": + ctypedef struct gpr_slice: + # don't worry about writing out the members of gpr_slice; we never access + # them directly. + pass + + gpr_slice gpr_slice_ref(gpr_slice s) + void gpr_slice_unref(gpr_slice s) + gpr_slice gpr_slice_new(void *p, size_t len, void (*destroy)(void *)) + gpr_slice gpr_slice_new_with_len( + void *p, size_t len, void (*destroy)(void *, size_t)) + gpr_slice gpr_slice_malloc(size_t length) + gpr_slice gpr_slice_from_copied_string(const char *source) + gpr_slice gpr_slice_from_copied_buffer(const char *source, size_t len) + + # Declare functions for function-like macros (because Cython)... + void *gpr_slice_start_ptr "GPR_SLICE_START_PTR" (gpr_slice s) + size_t gpr_slice_length "GPR_SLICE_LENGTH" (gpr_slice s) + + +cdef extern from "grpc/support/port_platform.h": + # As long as the header file gets this type right, we don't need to get this + # type exactly; just close enough that the operations will be supported in the + # underlying C layers. + ctypedef unsigned int gpr_uint32 + + +cdef extern from "grpc/support/time.h": + + ctypedef struct gpr_timespec: + libc.time.time_t seconds "tv_sec" + int nanoseconds "tv_nsec" + + cdef gpr_timespec gpr_time_0 + cdef gpr_timespec gpr_inf_future + cdef gpr_timespec gpr_inf_past + + gpr_timespec gpr_now() + + +cdef extern from "grpc/status.h": + ctypedef enum grpc_status_code: + GRPC_STATUS_OK + GRPC_STATUS_CANCELLED + GRPC_STATUS_UNKNOWN + GRPC_STATUS_INVALID_ARGUMENT + GRPC_STATUS_DEADLINE_EXCEEDED + GRPC_STATUS_NOT_FOUND + GRPC_STATUS_ALREADY_EXISTS + GRPC_STATUS_PERMISSION_DENIED + GRPC_STATUS_UNAUTHENTICATED + GRPC_STATUS_RESOURCE_EXHAUSTED + GRPC_STATUS_FAILED_PRECONDITION + GRPC_STATUS_ABORTED + GRPC_STATUS_OUT_OF_RANGE + GRPC_STATUS_UNIMPLEMENTED + GRPC_STATUS_INTERNAL + GRPC_STATUS_UNAVAILABLE + GRPC_STATUS_DATA_LOSS + GRPC_STATUS__DO_NOT_USE + + +cdef extern from "grpc/byte_buffer_reader.h": + struct grpc_byte_buffer_reader: + # We don't care about the internals + pass + + +cdef extern from "grpc/byte_buffer.h": + ctypedef struct grpc_byte_buffer: + # We don't care about the internals. + pass + + grpc_byte_buffer *grpc_raw_byte_buffer_create(gpr_slice *slices, + size_t nslices) + size_t grpc_byte_buffer_length(grpc_byte_buffer *bb) + void grpc_byte_buffer_destroy(grpc_byte_buffer *byte_buffer) + + void grpc_byte_buffer_reader_init(grpc_byte_buffer_reader *reader, + grpc_byte_buffer *buffer) + int grpc_byte_buffer_reader_next(grpc_byte_buffer_reader *reader, + gpr_slice *slice) + void grpc_byte_buffer_reader_destroy(grpc_byte_buffer_reader *reader) + + +cdef extern from "grpc/grpc.h": + + ctypedef struct grpc_completion_queue: + # We don't care about the internals (and in fact don't know them) + pass + + ctypedef struct grpc_channel: + # We don't care about the internals (and in fact don't know them) + pass + + ctypedef struct grpc_server: + # We don't care about the internals (and in fact don't know them) + pass + + ctypedef struct grpc_call: + # We don't care about the internals (and in fact don't know them) + pass + + ctypedef enum grpc_arg_type: + grpc_arg_string "GRPC_ARG_STRING" + grpc_arg_integer "GRPC_ARG_INTEGER" + grpc_arg_pointer "GRPC_ARG_POINTER" + + ctypedef struct grpc_arg_value_pointer: + void *address "p" + void *(*copy)(void *) + void (*destroy)(void *) + + union grpc_arg_value: + char *string + int integer + grpc_arg_value_pointer pointer + + ctypedef struct grpc_arg: + grpc_arg_type type + char *key + grpc_arg_value value + + ctypedef struct grpc_channel_args: + size_t arguments_length "num_args" + grpc_arg *arguments "args" + + ctypedef enum grpc_call_error: + GRPC_CALL_OK + GRPC_CALL_ERROR + GRPC_CALL_ERROR_NOT_ON_SERVER + GRPC_CALL_ERROR_NOT_ON_CLIENT + GRPC_CALL_ERROR_ALREADY_ACCEPTED + GRPC_CALL_ERROR_ALREADY_INVOKED + GRPC_CALL_ERROR_NOT_INVOKED + GRPC_CALL_ERROR_ALREADY_FINISHED + GRPC_CALL_ERROR_TOO_MANY_OPERATIONS + GRPC_CALL_ERROR_INVALID_FLAGS + GRPC_CALL_ERROR_INVALID_METADATA + + ctypedef struct grpc_metadata: + const char *key + const char *value + size_t value_length + # ignore the 'internal_data.obfuscated' fields. + + ctypedef enum grpc_completion_type: + GRPC_QUEUE_SHUTDOWN + GRPC_QUEUE_TIMEOUT + GRPC_OP_COMPLETE + + ctypedef struct grpc_event: + grpc_completion_type type + int success + void *tag + + ctypedef struct grpc_metadata_array: + size_t count + size_t capacity + grpc_metadata *metadata + + void grpc_metadata_array_init(grpc_metadata_array *array) + void grpc_metadata_array_destroy(grpc_metadata_array *array) + + ctypedef struct grpc_call_details: + char *method + size_t method_capacity + char *host + size_t host_capacity + gpr_timespec deadline + + void grpc_call_details_init(grpc_call_details *details) + void grpc_call_details_destroy(grpc_call_details *details) + + ctypedef enum grpc_op_type: + GRPC_OP_SEND_INITIAL_METADATA + GRPC_OP_SEND_MESSAGE + GRPC_OP_SEND_CLOSE_FROM_CLIENT + GRPC_OP_SEND_STATUS_FROM_SERVER + GRPC_OP_RECV_INITIAL_METADATA + GRPC_OP_RECV_MESSAGE + GRPC_OP_RECV_STATUS_ON_CLIENT + GRPC_OP_RECV_CLOSE_ON_SERVER + + ctypedef struct grpc_op_data_send_initial_metadata: + size_t count + grpc_metadata *metadata + + ctypedef struct grpc_op_data_send_status_from_server: + size_t trailing_metadata_count + grpc_metadata *trailing_metadata + grpc_status_code status + const char *status_details + + ctypedef struct grpc_op_data_recv_status_on_client: + grpc_metadata_array *trailing_metadata + grpc_status_code *status + char **status_details + size_t *status_details_capacity + + ctypedef struct grpc_op_data_recv_close_on_server: + int *cancelled + + union grpc_op_data: + grpc_op_data_send_initial_metadata send_initial_metadata + grpc_byte_buffer *send_message + grpc_op_data_send_status_from_server send_status_from_server + grpc_metadata_array *receive_initial_metadata "recv_initial_metadata" + grpc_byte_buffer **receive_message "recv_message" + grpc_op_data_recv_status_on_client receive_status_on_client "recv_status_on_client" + grpc_op_data_recv_close_on_server receive_close_on_server "recv_close_on_server" + + ctypedef struct grpc_op: + grpc_op_type type "op" + gpr_uint32 flags + grpc_op_data data + + void grpc_init() + void grpc_shutdown() + + grpc_completion_queue *grpc_completion_queue_create() + grpc_event grpc_completion_queue_next(grpc_completion_queue *cq, + gpr_timespec deadline) nogil + void grpc_completion_queue_shutdown(grpc_completion_queue *cq) + void grpc_completion_queue_destroy(grpc_completion_queue *cq) + + grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops, + size_t nops, void *tag) + grpc_call_error grpc_call_cancel(grpc_call *call) + grpc_call_error grpc_call_cancel_with_status(grpc_call *call, + grpc_status_code status, + const char *description) + void grpc_call_destroy(grpc_call *call) + + + grpc_channel *grpc_channel_create(const char *target, + const grpc_channel_args *args) + grpc_call *grpc_channel_create_call(grpc_channel *channel, + grpc_completion_queue *completion_queue, + const char *method, const char *host, + gpr_timespec deadline) + void grpc_channel_destroy(grpc_channel *channel) + + grpc_server *grpc_server_create(const grpc_channel_args *args) + grpc_call_error grpc_server_request_call( + grpc_server *server, grpc_call **call, grpc_call_details *details, + grpc_metadata_array *request_metadata, grpc_completion_queue + *cq_bound_to_call, grpc_completion_queue *cq_for_notification, void + *tag_new) + void grpc_server_register_completion_queue(grpc_server *server, + grpc_completion_queue *cq) + int grpc_server_add_http2_port(grpc_server *server, const char *addr) + void grpc_server_start(grpc_server *server) + void grpc_server_shutdown_and_notify( + grpc_server *server, grpc_completion_queue *cq, void *tag) + void grpc_server_cancel_all_calls(grpc_server *server) + void grpc_server_destroy(grpc_server *server) + + +cdef extern from "grpc/grpc_security.h": + + ctypedef struct grpc_ssl_pem_key_cert_pair: + const char *private_key + const char *certificate_chain "cert_chain" + + ctypedef struct grpc_credentials: + # We don't care about the internals (and in fact don't know them) + pass + + grpc_credentials *grpc_google_default_credentials_create() + grpc_credentials *grpc_ssl_credentials_create( + const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pair) + + grpc_credentials *grpc_composite_credentials_create(grpc_credentials *creds1, + grpc_credentials *creds2) + grpc_credentials *grpc_compute_engine_credentials_create() + grpc_credentials *grpc_service_account_jwt_access_credentials_create(const char *json_key, + gpr_timespec token_lifetime) + grpc_credentials *grpc_refresh_token_credentials_create( + const char *json_refresh_token) + grpc_credentials *grpc_iam_credentials_create(const char *authorization_token, + const char *authority_selector) + void grpc_credentials_release(grpc_credentials *creds) + + grpc_channel *grpc_secure_channel_create( + grpc_credentials *creds, const char *target, + const grpc_channel_args *args) + + ctypedef struct grpc_server_credentials: + # We don't care about the internals (and in fact don't know them) + pass + + grpc_server_credentials *grpc_ssl_server_credentials_create( + const char *pem_root_certs, + grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs, + size_t num_key_cert_pairs) + void grpc_server_credentials_release(grpc_server_credentials *creds) + + int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr, + grpc_server_credentials *creds) + + grpc_call_error grpc_call_set_credentials(grpc_call *call, + grpc_credentials *creds) diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/records.pxd b/src/python/grpcio/grpc/_cython/_cygrpc/records.pxd new file mode 100644 index 00000000..9ee48788 --- /dev/null +++ b/src/python/grpcio/grpc/_cython/_cygrpc/records.pxd @@ -0,0 +1,129 @@ +# Copyright 2015, 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. + +from grpc._cython._cygrpc cimport grpc +from grpc._cython._cygrpc cimport call +from grpc._cython._cygrpc cimport server + + +cdef class Timespec: + + cdef grpc.gpr_timespec c_time + + +cdef class CallDetails: + + cdef grpc.grpc_call_details c_details + + +cdef class OperationTag: + + cdef object user_tag + cdef list references + # This allows CompletionQueue to notify the Python Server object that the + # underlying GRPC core server has shutdown + cdef server.Server shutting_down_server + cdef call.Call operation_call + cdef CallDetails request_call_details + cdef Metadata request_metadata + cdef Operations batch_operations + cdef bint is_new_request + + +cdef class Event: + + cdef readonly grpc.grpc_completion_type type + cdef readonly bint success + cdef readonly object tag + + # For operations with calls + cdef readonly call.Call operation_call + + # For Server.request_call + cdef readonly CallDetails request_call_details + cdef readonly Metadata request_metadata + + # For Call.start_batch + cdef readonly Operations batch_operations + + +cdef class ByteBuffer: + + cdef grpc.grpc_byte_buffer *c_byte_buffer + + +cdef class SslPemKeyCertPair: + + cdef grpc.grpc_ssl_pem_key_cert_pair c_pair + cdef readonly object private_key, certificate_chain + + +cdef class ChannelArg: + + cdef grpc.grpc_arg c_arg + cdef readonly object key, value + + +cdef class ChannelArgs: + + cdef grpc.grpc_channel_args c_args + cdef list args + + +cdef class Metadatum: + + cdef grpc.grpc_metadata c_metadata + cdef object _key, _value + + +cdef class Metadata: + + cdef grpc.grpc_metadata_array c_metadata_array + cdef object metadata + + +cdef class Operation: + + cdef grpc.grpc_op c_op + cdef ByteBuffer _received_message + cdef Metadata _received_metadata + cdef grpc.grpc_status_code _received_status_code + cdef char *_received_status_details + cdef size_t _received_status_details_capacity + cdef int _received_cancelled + cdef readonly bint is_valid + cdef object references + + +cdef class Operations: + + cdef grpc.grpc_op *c_ops + cdef size_t c_nops + cdef list operations + diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx b/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx new file mode 100644 index 00000000..4814769f --- /dev/null +++ b/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx @@ -0,0 +1,575 @@ +# Copyright 2015, 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. + +from grpc._cython._cygrpc cimport grpc +from grpc._cython._cygrpc cimport call +from grpc._cython._cygrpc cimport server + + +class StatusCode: + ok = grpc.GRPC_STATUS_OK + cancelled = grpc.GRPC_STATUS_CANCELLED + unknown = grpc.GRPC_STATUS_UNKNOWN + invalid_argument = grpc.GRPC_STATUS_INVALID_ARGUMENT + deadline_exceeded = grpc.GRPC_STATUS_DEADLINE_EXCEEDED + not_found = grpc.GRPC_STATUS_NOT_FOUND + already_exists = grpc.GRPC_STATUS_ALREADY_EXISTS + permission_denied = grpc.GRPC_STATUS_PERMISSION_DENIED + unauthenticated = grpc.GRPC_STATUS_UNAUTHENTICATED + resource_exhausted = grpc.GRPC_STATUS_RESOURCE_EXHAUSTED + failed_precondition = grpc.GRPC_STATUS_FAILED_PRECONDITION + aborted = grpc.GRPC_STATUS_ABORTED + out_of_range = grpc.GRPC_STATUS_OUT_OF_RANGE + unimplemented = grpc.GRPC_STATUS_UNIMPLEMENTED + internal = grpc.GRPC_STATUS_INTERNAL + unavailable = grpc.GRPC_STATUS_UNAVAILABLE + data_loss = grpc.GRPC_STATUS_DATA_LOSS + + +class CallError: + ok = grpc.GRPC_CALL_OK + error = grpc.GRPC_CALL_ERROR + not_on_server = grpc.GRPC_CALL_ERROR_NOT_ON_SERVER + not_on_client = grpc.GRPC_CALL_ERROR_NOT_ON_CLIENT + already_accepted = grpc.GRPC_CALL_ERROR_ALREADY_ACCEPTED + already_invoked = grpc.GRPC_CALL_ERROR_ALREADY_INVOKED + not_invoked = grpc.GRPC_CALL_ERROR_NOT_INVOKED + already_finished = grpc.GRPC_CALL_ERROR_ALREADY_FINISHED + too_many_operations = grpc.GRPC_CALL_ERROR_TOO_MANY_OPERATIONS + invalid_flags = grpc.GRPC_CALL_ERROR_INVALID_FLAGS + invalid_metadata = grpc.GRPC_CALL_ERROR_INVALID_METADATA + + +class CompletionType: + queue_shutdown = grpc.GRPC_QUEUE_SHUTDOWN + queue_timeout = grpc.GRPC_QUEUE_TIMEOUT + operation_complete = grpc.GRPC_OP_COMPLETE + + +class OperationType: + send_initial_metadata = grpc.GRPC_OP_SEND_INITIAL_METADATA + send_message = grpc.GRPC_OP_SEND_MESSAGE + send_close_from_client = grpc.GRPC_OP_SEND_CLOSE_FROM_CLIENT + send_status_from_server = grpc.GRPC_OP_SEND_STATUS_FROM_SERVER + receive_initial_metadata = grpc.GRPC_OP_RECV_INITIAL_METADATA + receive_message = grpc.GRPC_OP_RECV_MESSAGE + receive_status_on_client = grpc.GRPC_OP_RECV_STATUS_ON_CLIENT + receive_close_on_server = grpc.GRPC_OP_RECV_CLOSE_ON_SERVER + + +cdef class Timespec: + + def __cinit__(self, time): + if time is None: + self.c_time = grpc.gpr_now() + elif isinstance(time, float): + if time == float("+inf"): + self.c_time = grpc.gpr_inf_future + elif time == float("-inf"): + self.c_time = grpc.gpr_inf_past + else: + self.c_time.seconds = time + self.c_time.nanoseconds = (time - float(self.c_time.seconds)) * 1e9 + else: + raise TypeError("expected time to be float") + + @property + def seconds(self): + return self.c_time.seconds + + @property + def nanoseconds(self): + return self.c_time.nanoseconds + + def __float__(self): + return self.c_time.seconds + self.c_time.nanoseconds / 1e9 + + infinite_future = Timespec(float("+inf")) + infinite_past = Timespec(float("-inf")) + + +cdef class CallDetails: + + def __cinit__(self): + grpc.grpc_call_details_init(&self.c_details) + + def __dealloc__(self): + grpc.grpc_call_details_destroy(&self.c_details) + + @property + def method(self): + if self.c_details.method != NULL: + return self.c_details.method + else: + return None + + @property + def host(self): + if self.c_details.host != NULL: + return self.c_details.host + else: + return None + + @property + def deadline(self): + timespec = Timespec(float("-inf")) + timespec.c_time = self.c_details.deadline + return timespec + + +cdef class OperationTag: + + def __cinit__(self, user_tag): + self.user_tag = user_tag + self.references = [] + + +cdef class Event: + + def __cinit__(self, grpc.grpc_completion_type type, bint success, + object tag, call.Call operation_call, + CallDetails request_call_details, + Metadata request_metadata, + Operations batch_operations): + self.type = type + self.success = success + self.tag = tag + self.operation_call = operation_call + self.request_call_details = request_call_details + self.request_metadata = request_metadata + self.batch_operations = batch_operations + + +cdef class ByteBuffer: + + def __cinit__(self, data): + if data is None: + self.c_byte_buffer = NULL + return + if isinstance(data, bytes): + pass + elif isinstance(data, basestring): + data = data.encode() + else: + raise TypeError("expected value to be of type str or bytes") + + cdef char *c_data = data + data_slice = grpc.gpr_slice_from_copied_buffer(c_data, len(data)) + self.c_byte_buffer = grpc.grpc_raw_byte_buffer_create( + &data_slice, 1) + grpc.gpr_slice_unref(data_slice) + + def bytes(self): + cdef grpc.grpc_byte_buffer_reader reader + cdef grpc.gpr_slice data_slice + cdef size_t data_slice_length + cdef void *data_slice_pointer + if self.c_byte_buffer != NULL: + grpc.grpc_byte_buffer_reader_init(&reader, self.c_byte_buffer) + result = b"" + while grpc.grpc_byte_buffer_reader_next(&reader, &data_slice): + data_slice_pointer = grpc.gpr_slice_start_ptr(data_slice) + data_slice_length = grpc.gpr_slice_length(data_slice) + result += (data_slice_pointer)[:data_slice_length] + grpc.grpc_byte_buffer_reader_destroy(&reader) + return result + else: + return None + + def __len__(self): + if self.c_byte_buffer != NULL: + return grpc.grpc_byte_buffer_length(self.c_byte_buffer) + else: + return 0 + + def __str__(self): + return self.bytes() + + def __dealloc__(self): + if self.c_byte_buffer != NULL: + grpc.grpc_byte_buffer_destroy(self.c_byte_buffer) + + +cdef class SslPemKeyCertPair: + + def __cinit__(self, private_key, certificate_chain): + if isinstance(private_key, bytes): + self.private_key = private_key + elif isinstance(private_key, basestring): + self.private_key = private_key.encode() + else: + raise TypeError("expected private_key to be of type str or bytes") + if isinstance(certificate_chain, bytes): + self.certificate_chain = certificate_chain + elif isinstance(certificate_chain, basestring): + self.certificate_chain = certificate_chain.encode() + else: + raise TypeError("expected certificate_chain to be of type str or bytes " + "or int") + self.c_pair.private_key = self.private_key + self.c_pair.certificate_chain = self.certificate_chain + + +cdef class ChannelArg: + + def __cinit__(self, key, value): + if isinstance(key, bytes): + self.key = key + elif isinstance(key, basestring): + self.key = key.encode() + else: + raise TypeError("expected key to be of type str or bytes") + if isinstance(value, bytes): + self.value = value + self.c_arg.type = grpc.GRPC_ARG_STRING + self.c_arg.value.string = self.value + elif isinstance(value, basestring): + self.value = value.encode() + self.c_arg.type = grpc.GRPC_ARG_STRING + self.c_arg.value.string = self.value + elif isinstance(value, int): + self.value = int(value) + self.c_arg.type = grpc.GRPC_ARG_INTEGER + self.c_arg.value.integer = self.value + else: + raise TypeError("expected value to be of type str or bytes or int") + self.c_arg.key = self.key + + +cdef class ChannelArgs: + + def __cinit__(self, args): + self.args = list(args) + for arg in self.args: + if not isinstance(arg, ChannelArg): + raise TypeError("expected list of ChannelArg") + self.c_args.arguments_length = len(self.args) + self.c_args.arguments = grpc.gpr_malloc( + self.c_args.arguments_length*sizeof(grpc.grpc_arg) + ) + for i in range(self.c_args.arguments_length): + self.c_args.arguments[i] = (self.args[i]).c_arg + + def __dealloc__(self): + grpc.gpr_free(self.c_args.arguments) + + def __len__(self): + # self.args is never stale; it's only updated from this file + return len(self.args) + + def __getitem__(self, size_t i): + # self.args is never stale; it's only updated from this file + return self.args[i] + + +cdef class Metadatum: + + def __cinit__(self, key, value): + if isinstance(key, bytes): + self._key = key + elif isinstance(key, basestring): + self._key = key.encode() + else: + raise TypeError("expected key to be of type str or bytes") + if isinstance(value, bytes): + self._value = value + elif isinstance(value, basestring): + self._value = value.encode() + else: + raise TypeError("expected value to be of type str or bytes") + self.c_metadata.key = self._key + self.c_metadata.value = self._value + self.c_metadata.value_length = len(self._value) + + @property + def key(self): + return self.c_metadata.key + + @property + def value(self): + return self.c_metadata.value[:self.c_metadata.value_length] + + def __len__(self): + return 2 + + def __getitem__(self, size_t i): + if i == 0: + return self.key + elif i == 1: + return self.value + else: + raise IndexError("index must be 0 (key) or 1 (value)") + + def __iter__(self): + return iter((self.key, self.value)) + + +cdef class _MetadataIterator: + + cdef size_t i + cdef Metadata metadata + + def __cinit__(self, Metadata metadata not None): + self.i = 0 + self.metadata = metadata + + def __next__(self): + if self.i < len(self.metadata): + result = self.metadata[self.i] + self.i = self.i + 1 + return result + else: + raise StopIteration() + + +cdef class Metadata: + + def __cinit__(self, metadata): + self.metadata = list(metadata) + for metadatum in metadata: + if not isinstance(metadatum, Metadatum): + raise TypeError("expected list of Metadatum") + grpc.grpc_metadata_array_init(&self.c_metadata_array) + self.c_metadata_array.count = len(self.metadata) + self.c_metadata_array.capacity = len(self.metadata) + self.c_metadata_array.metadata = grpc.gpr_malloc( + self.c_metadata_array.count*sizeof(grpc.grpc_metadata) + ) + for i in range(self.c_metadata_array.count): + self.c_metadata_array.metadata[i] = ( + (self.metadata[i]).c_metadata) + + def __dealloc__(self): + # this frees the allocated memory for the grpc_metadata_array (although + # it'd be nice if that were documented somewhere...) TODO(atash): document + # this in the C core + grpc.grpc_metadata_array_destroy(&self.c_metadata_array) + + def __len__(self): + return self.c_metadata_array.count + + def __getitem__(self, size_t i): + return Metadatum( + key=self.c_metadata_array.metadata[i].key, + value=self.c_metadata_array.metadata[i].value[ + :self.c_metadata_array.metadata[i].value_length]) + + def __iter__(self): + return _MetadataIterator(self) + + +cdef class Operation: + + def __cinit__(self): + self.references = [] + self._received_status_details = NULL + self._received_status_details_capacity = 0 + self.is_valid = False + + @property + def type(self): + return self.c_op.type + + @property + def received_message(self): + if self.c_op.type != grpc.GRPC_OP_RECV_MESSAGE: + raise TypeError("self must be an operation receiving a message") + return self._received_message + + @property + def received_metadata(self): + if (self.c_op.type != grpc.GRPC_OP_RECV_INITIAL_METADATA and + self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT): + raise TypeError("self must be an operation receiving metadata") + return self._received_metadata + + @property + def received_status_code(self): + if self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT: + raise TypeError("self must be an operation receiving a status code") + return self._received_status_code + + @property + def received_status_details(self): + if self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT: + raise TypeError("self must be an operation receiving status details") + if self._received_status_details: + return self._received_status_details + else: + return None + + @property + def received_cancelled(self): + if self.c_op.type != grpc.GRPC_OP_RECV_CLOSE_ON_SERVER: + raise TypeError("self must be an operation receiving cancellation " + "information") + return False if self._received_cancelled == 0 else True + + def __dealloc__(self): + # We *almost* don't need to do anything; most of the objects are handled by + # Python. The remaining one(s) are primitive fields filled in by GRPC core. + # This means that we need to clean up after receive_status_on_client. + if self.c_op.type == grpc.GRPC_OP_RECV_STATUS_ON_CLIENT: + grpc.gpr_free(self._received_status_details) + +def operation_send_initial_metadata(Metadata metadata): + cdef Operation op = Operation() + op.c_op.type = grpc.GRPC_OP_SEND_INITIAL_METADATA + op.c_op.data.send_initial_metadata.count = metadata.c_metadata_array.count + op.c_op.data.send_initial_metadata.metadata = ( + metadata.c_metadata_array.metadata) + op.references.append(metadata) + op.is_valid = True + return op + +def operation_send_message(data): + cdef Operation op = Operation() + op.c_op.type = grpc.GRPC_OP_SEND_MESSAGE + byte_buffer = ByteBuffer(data) + op.c_op.data.send_message = byte_buffer.c_byte_buffer + op.references.append(byte_buffer) + op.is_valid = True + return op + +def operation_send_close_from_client(): + cdef Operation op = Operation() + op.c_op.type = grpc.GRPC_OP_SEND_CLOSE_FROM_CLIENT + op.is_valid = True + return op + +def operation_send_status_from_server( + Metadata metadata, grpc.grpc_status_code code, details): + if isinstance(details, bytes): + pass + elif isinstance(details, basestring): + details = details.encode() + else: + raise TypeError("expected a str or bytes object for details") + cdef Operation op = Operation() + op.c_op.type = grpc.GRPC_OP_SEND_STATUS_FROM_SERVER + op.c_op.data.send_status_from_server.trailing_metadata_count = ( + metadata.c_metadata_array.count) + op.c_op.data.send_status_from_server.trailing_metadata = ( + metadata.c_metadata_array.metadata) + op.c_op.data.send_status_from_server.status = code + op.c_op.data.send_status_from_server.status_details = details + op.references.append(metadata) + op.references.append(details) + op.is_valid = True + return op + +def operation_receive_initial_metadata(): + cdef Operation op = Operation() + op.c_op.type = grpc.GRPC_OP_RECV_INITIAL_METADATA + op._received_metadata = Metadata([]) + op.c_op.data.receive_initial_metadata = ( + &op._received_metadata.c_metadata_array) + op.is_valid = True + return op + +def operation_receive_message(): + cdef Operation op = Operation() + op.c_op.type = grpc.GRPC_OP_RECV_MESSAGE + op._received_message = ByteBuffer(None) + # n.b. the c_op.data.receive_message field needs to be deleted by us, + # anyway, so we just let that be handled by the ByteBuffer() we allocated + # the line before. + op.c_op.data.receive_message = &op._received_message.c_byte_buffer + op.is_valid = True + return op + +def operation_receive_status_on_client(): + cdef Operation op = Operation() + op.c_op.type = grpc.GRPC_OP_RECV_STATUS_ON_CLIENT + op._received_metadata = Metadata([]) + op.c_op.data.receive_status_on_client.trailing_metadata = ( + &op._received_metadata.c_metadata_array) + op.c_op.data.receive_status_on_client.status = ( + &op._received_status_code) + op.c_op.data.receive_status_on_client.status_details = ( + &op._received_status_details) + op.c_op.data.receive_status_on_client.status_details_capacity = ( + &op._received_status_details_capacity) + op.is_valid = True + return op + +def operation_receive_close_on_server(): + cdef Operation op = Operation() + op.c_op.type = grpc.GRPC_OP_RECV_CLOSE_ON_SERVER + op.c_op.data.receive_close_on_server.cancelled = &op._received_cancelled + op.is_valid = True + return op + + +cdef class _OperationsIterator: + + cdef size_t i + cdef Operations operations + + def __cinit__(self, Operations operations not None): + self.i = 0 + self.operations = operations + + def __next__(self): + if self.i < len(self.operations): + result = self.operations[self.i] + self.i = self.i + 1 + return result + else: + raise StopIteration() + + +cdef class Operations: + + def __cinit__(self, operations): + self.operations = list(operations) # normalize iterable + self.c_ops = NULL + self.c_nops = 0 + for operation in self.operations: + if not isinstance(operation, Operation): + raise TypeError("expected operations to be iterable of Operation") + self.c_nops = len(self.operations) + self.c_ops = grpc.gpr_malloc( + sizeof(grpc.grpc_op)*self.c_nops) + for i in range(self.c_nops): + self.c_ops[i] = ((self.operations[i])).c_op + + def __len__(self): + return self.c_nops + + def __getitem__(self, size_t i): + # self.operations is never stale; it's only updated from this file + return self.operations[i] + + def __dealloc__(self): + grpc.gpr_free(self.c_ops) + + def __iter__(self): + return _OperationsIterator(self) + diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/server.pxd b/src/python/grpcio/grpc/_cython/_cygrpc/server.pxd new file mode 100644 index 00000000..0257542a --- /dev/null +++ b/src/python/grpcio/grpc/_cython/_cygrpc/server.pxd @@ -0,0 +1,45 @@ +# Copyright 2015, 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. + +from grpc._cython._cygrpc cimport grpc +from grpc._cython._cygrpc cimport completion_queue + + +cdef class Server: + + cdef grpc.grpc_server *c_server + cdef bint is_started # start has been called + cdef bint is_shutting_down # shutdown has been called + cdef bint is_shutdown # notification of complete shutdown received + # used at dealloc when user forgets to shutdown + cdef completion_queue.CompletionQueue backup_shutdown_queue + cdef list references + cdef list registered_completion_queues + + cdef notify_shutdown_complete(self) diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx b/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx new file mode 100644 index 00000000..dcf9d383 --- /dev/null +++ b/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx @@ -0,0 +1,158 @@ +# Copyright 2015, 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. + +cimport cpython + +from grpc._cython._cygrpc cimport call +from grpc._cython._cygrpc cimport completion_queue +from grpc._cython._cygrpc cimport credentials +from grpc._cython._cygrpc cimport records + +import time + + +cdef class Server: + + def __cinit__(self, records.ChannelArgs arguments=None): + cdef grpc.grpc_channel_args *c_arguments = NULL + self.references = [] + self.registered_completion_queues = [] + if arguments is not None: + c_arguments = &arguments.c_args + self.references.append(arguments) + self.c_server = grpc.grpc_server_create(c_arguments) + self.is_started = False + self.is_shutting_down = False + self.is_shutdown = False + + def request_call( + self, completion_queue.CompletionQueue call_queue not None, + completion_queue.CompletionQueue server_queue not None, tag): + if not self.is_started or self.is_shutting_down: + raise ValueError("server must be started and not shutting down") + if server_queue not in self.registered_completion_queues: + raise ValueError("server_queue must be a registered completion queue") + cdef records.OperationTag operation_tag = records.OperationTag(tag) + operation_tag.operation_call = call.Call() + operation_tag.request_call_details = records.CallDetails() + operation_tag.request_metadata = records.Metadata([]) + operation_tag.references.extend([self, call_queue, server_queue]) + operation_tag.is_new_request = True + operation_tag.batch_operations = records.Operations([]) + cpython.Py_INCREF(operation_tag) + return grpc.grpc_server_request_call( + self.c_server, &operation_tag.operation_call.c_call, + &operation_tag.request_call_details.c_details, + &operation_tag.request_metadata.c_metadata_array, + call_queue.c_completion_queue, server_queue.c_completion_queue, + operation_tag) + + def register_completion_queue( + self, completion_queue.CompletionQueue queue not None): + if self.is_started: + raise ValueError("cannot register completion queues after start") + grpc.grpc_server_register_completion_queue( + self.c_server, queue.c_completion_queue) + self.registered_completion_queues.append(queue) + + def start(self): + if self.is_started: + raise ValueError("the server has already started") + self.backup_shutdown_queue = completion_queue.CompletionQueue() + self.register_completion_queue(self.backup_shutdown_queue) + self.is_started = True + grpc.grpc_server_start(self.c_server) + + def add_http2_port(self, address, + credentials.ServerCredentials server_credentials=None): + if isinstance(address, bytes): + pass + elif isinstance(address, basestring): + address = address.encode() + else: + raise TypeError("expected address to be a str or bytes") + self.references.append(address) + if server_credentials is not None: + self.references.append(server_credentials) + return grpc.grpc_server_add_secure_http2_port( + self.c_server, address, server_credentials.c_credentials) + else: + return grpc.grpc_server_add_http2_port(self.c_server, address) + + def shutdown(self, completion_queue.CompletionQueue queue not None, tag): + cdef records.OperationTag operation_tag + if queue.is_shutting_down: + raise ValueError("queue must be live") + elif not self.is_started: + raise ValueError("the server hasn't started yet") + elif self.is_shutting_down: + return + elif queue not in self.registered_completion_queues: + raise ValueError("expected registered completion queue") + else: + self.is_shutting_down = True + operation_tag = records.OperationTag(tag) + operation_tag.shutting_down_server = self + operation_tag.references.extend([self, queue]) + cpython.Py_INCREF(operation_tag) + grpc.grpc_server_shutdown_and_notify( + self.c_server, queue.c_completion_queue, + operation_tag) + + cdef notify_shutdown_complete(self): + # called only by a completion queue on receiving our shutdown operation tag + self.is_shutdown = True + + def cancel_all_calls(self): + if not self.is_shutting_down: + raise ValueError("the server must be shutting down to cancel all calls") + elif self.is_shutdown: + return + else: + grpc.grpc_server_cancel_all_calls(self.c_server) + + def __dealloc__(self): + if self.c_server != NULL: + if not self.is_started: + pass + elif self.is_shutdown: + pass + elif not self.is_shutting_down: + # the user didn't call shutdown - use our backup queue + self.shutdown(self.backup_shutdown_queue, None) + # and now we wait + while not self.is_shutdown: + self.backup_shutdown_queue.poll() + else: + # We're in the process of shutting down, but have not shutdown; can't do + # much but repeatedly release the GIL and wait + while not self.is_shutdown: + time.sleep(0) + grpc.grpc_server_destroy(self.c_server) + diff --git a/src/python/grpcio/grpc/_cython/adapter_low.py b/src/python/grpcio/grpc/_cython/adapter_low.py new file mode 100644 index 00000000..4f24da33 --- /dev/null +++ b/src/python/grpcio/grpc/_cython/adapter_low.py @@ -0,0 +1,102 @@ +# Copyright 2015, 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. + + +# Adapter from grpc._cython.types to the surface expected by +# grpc._adapter._intermediary_low. +# +# TODO(atash): Once this is plugged into grpc._adapter._intermediary_low, remove +# both grpc._adapter._intermediary_low and this file. The fore and rear links in +# grpc._adapter should be able to use grpc._cython.types directly. + +from grpc._adapter import _types as type_interfaces +from grpc._cython import cygrpc + + +class ClientCredentials(object): + def __init__(self): + raise NotImplementedError() + + @staticmethod + def google_default(): + raise NotImplementedError() + + @staticmethod + def ssl(): + raise NotImplementedError() + + @staticmethod + def composite(): + raise NotImplementedError() + + @staticmethod + def compute_engine(): + raise NotImplementedError() + + @staticmethod + def jwt(): + raise NotImplementedError() + + @staticmethod + def refresh_token(): + raise NotImplementedError() + + @staticmethod + def iam(): + raise NotImplementedError() + + +class ServerCredentials(object): + def __init__(self): + raise NotImplementedError() + + @staticmethod + def ssl(): + raise NotImplementedError() + + +class CompletionQueue(type_interfaces.CompletionQueue): + def __init__(self): + raise NotImplementedError() + + +class Call(type_interfaces.Call): + def __init__(self): + raise NotImplementedError() + + +class Channel(type_interfaces.Channel): + def __init__(self): + raise NotImplementedError() + + +class Server(type_interfaces.Server): + def __init__(self): + raise NotImplementedError() + diff --git a/src/python/grpcio/grpc/_cython/cygrpc.pyx b/src/python/grpcio/grpc/_cython/cygrpc.pyx new file mode 100644 index 00000000..f4d96615 --- /dev/null +++ b/src/python/grpcio/grpc/_cython/cygrpc.pyx @@ -0,0 +1,107 @@ +# Copyright 2015, 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. + +cimport cpython + +from grpc._cython._cygrpc cimport grpc +from grpc._cython._cygrpc cimport call +from grpc._cython._cygrpc cimport channel +from grpc._cython._cygrpc cimport credentials +from grpc._cython._cygrpc cimport completion_queue +from grpc._cython._cygrpc cimport records +from grpc._cython._cygrpc cimport server + +from grpc._cython._cygrpc import call +from grpc._cython._cygrpc import channel +from grpc._cython._cygrpc import credentials +from grpc._cython._cygrpc import completion_queue +from grpc._cython._cygrpc import records +from grpc._cython._cygrpc import server + +StatusCode = records.StatusCode +CallError = records.CallError +CompletionType = records.CompletionType +OperationType = records.OperationType +Timespec = records.Timespec +CallDetails = records.CallDetails +Event = records.Event +ByteBuffer = records.ByteBuffer +SslPemKeyCertPair = records.SslPemKeyCertPair +ChannelArg = records.ChannelArg +ChannelArgs = records.ChannelArgs +Metadatum = records.Metadatum +Metadata = records.Metadata +Operation = records.Operation + +operation_send_initial_metadata = records.operation_send_initial_metadata +operation_send_message = records.operation_send_message +operation_send_close_from_client = records.operation_send_close_from_client +operation_send_status_from_server = records.operation_send_status_from_server +operation_receive_initial_metadata = records.operation_receive_initial_metadata +operation_receive_message = records.operation_receive_message +operation_receive_status_on_client = records.operation_receive_status_on_client +operation_receive_close_on_server = records.operation_receive_close_on_server + +Operations = records.Operations + +ClientCredentials = credentials.ClientCredentials +ServerCredentials = credentials.ServerCredentials + +client_credentials_google_default = ( + credentials.client_credentials_google_default) +client_credentials_ssl = credentials.client_credentials_ssl +client_credentials_composite_credentials = ( + credentials.client_credentials_composite_credentials) +client_credentials_compute_engine = ( + credentials.client_credentials_compute_engine) +client_credentials_jwt = credentials.client_credentials_jwt +client_credentials_refresh_token = credentials.client_credentials_refresh_token +client_credentials_iam = credentials.client_credentials_iam +server_credentials_ssl = credentials.server_credentials_ssl + +CompletionQueue = completion_queue.CompletionQueue +Channel = channel.Channel +Server = server.Server +Call = call.Call + + +# +# Global state +# + +cdef class _ModuleState: + + def __cinit__(self): + grpc.grpc_init() + + def __dealloc__(self): + grpc.grpc_shutdown() + +_module_state = _ModuleState() + diff --git a/src/python/grpcio/grpc/_links/__init__.py b/src/python/grpcio/grpc/_links/__init__.py new file mode 100644 index 00000000..70865191 --- /dev/null +++ b/src/python/grpcio/grpc/_links/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, 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. + + diff --git a/src/python/grpcio/grpc/_links/_constants.py b/src/python/grpcio/grpc/_links/_constants.py new file mode 100644 index 00000000..117fc5a6 --- /dev/null +++ b/src/python/grpcio/grpc/_links/_constants.py @@ -0,0 +1,42 @@ +# Copyright 2015, 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. + +"""Constants for use within this package.""" + +from grpc._adapter import _intermediary_low +from grpc.beta import interfaces as beta_interfaces + +LOW_STATUS_CODE_TO_HIGH_STATUS_CODE = { + low: high for low, high in zip( + _intermediary_low.Code, beta_interfaces.StatusCode) +} + +HIGH_STATUS_CODE_TO_LOW_STATUS_CODE = { + high: low for low, high in LOW_STATUS_CODE_TO_HIGH_STATUS_CODE.items() +} diff --git a/src/python/grpcio/grpc/_links/invocation.py b/src/python/grpcio/grpc/_links/invocation.py new file mode 100644 index 00000000..67ef86a1 --- /dev/null +++ b/src/python/grpcio/grpc/_links/invocation.py @@ -0,0 +1,452 @@ +# Copyright 2015, 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. + +"""The RPC-invocation-side bridge between RPC Framework and GRPC-on-the-wire.""" + +import abc +import enum +import logging +import threading +import time + +from grpc._adapter import _intermediary_low +from grpc._links import _constants +from grpc.beta import interfaces as beta_interfaces +from grpc.framework.foundation import activated +from grpc.framework.foundation import logging_pool +from grpc.framework.foundation import relay +from grpc.framework.interfaces.links import links + +_IDENTITY = lambda x: x + +_STOP = _intermediary_low.Event.Kind.STOP +_WRITE = _intermediary_low.Event.Kind.WRITE_ACCEPTED +_COMPLETE = _intermediary_low.Event.Kind.COMPLETE_ACCEPTED +_READ = _intermediary_low.Event.Kind.READ_ACCEPTED +_METADATA = _intermediary_low.Event.Kind.METADATA_ACCEPTED +_FINISH = _intermediary_low.Event.Kind.FINISH + + +@enum.unique +class _Read(enum.Enum): + AWAITING_METADATA = 'awaiting metadata' + READING = 'reading' + AWAITING_ALLOWANCE = 'awaiting allowance' + CLOSED = 'closed' + + +@enum.unique +class _HighWrite(enum.Enum): + OPEN = 'open' + CLOSED = 'closed' + + +@enum.unique +class _LowWrite(enum.Enum): + OPEN = 'OPEN' + ACTIVE = 'ACTIVE' + CLOSED = 'CLOSED' + + +class _Context(beta_interfaces.GRPCInvocationContext): + + def __init__(self): + self._lock = threading.Lock() + self._disable_next_compression = False + + def disable_next_request_compression(self): + with self._lock: + self._disable_next_compression = True + + def next_compression_disabled(self): + with self._lock: + disabled = self._disable_next_compression + self._disable_next_compression = False + return disabled + + +class _RPCState(object): + + def __init__( + self, call, request_serializer, response_deserializer, sequence_number, + read, allowance, high_write, low_write, due, context): + self.call = call + self.request_serializer = request_serializer + self.response_deserializer = response_deserializer + self.sequence_number = sequence_number + self.read = read + self.allowance = allowance + self.high_write = high_write + self.low_write = low_write + self.due = due + self.context = context + + +def _no_longer_due(kind, rpc_state, key, rpc_states): + rpc_state.due.remove(kind) + if not rpc_state.due: + del rpc_states[key] + + +class _Kernel(object): + + def __init__( + self, channel, host, metadata_transformer, request_serializers, + response_deserializers, ticket_relay): + self._lock = threading.Lock() + self._channel = channel + self._host = host + self._metadata_transformer = metadata_transformer + self._request_serializers = request_serializers + self._response_deserializers = response_deserializers + self._relay = ticket_relay + + self._completion_queue = None + self._rpc_states = {} + self._pool = None + + def _on_write_event(self, operation_id, unused_event, rpc_state): + if rpc_state.high_write is _HighWrite.CLOSED: + rpc_state.call.complete(operation_id) + rpc_state.due.add(_COMPLETE) + rpc_state.due.remove(_WRITE) + rpc_state.low_write = _LowWrite.CLOSED + else: + ticket = links.Ticket( + operation_id, rpc_state.sequence_number, None, None, None, None, 1, + None, None, None, None, None, None, None) + rpc_state.sequence_number += 1 + self._relay.add_value(ticket) + rpc_state.low_write = _LowWrite.OPEN + _no_longer_due(_WRITE, rpc_state, operation_id, self._rpc_states) + + def _on_read_event(self, operation_id, event, rpc_state): + if event.bytes is None or _FINISH not in rpc_state.due: + rpc_state.read = _Read.CLOSED + _no_longer_due(_READ, rpc_state, operation_id, self._rpc_states) + else: + if 0 < rpc_state.allowance: + rpc_state.allowance -= 1 + rpc_state.call.read(operation_id) + else: + rpc_state.read = _Read.AWAITING_ALLOWANCE + _no_longer_due(_READ, rpc_state, operation_id, self._rpc_states) + ticket = links.Ticket( + operation_id, rpc_state.sequence_number, None, None, None, None, None, + None, rpc_state.response_deserializer(event.bytes), None, None, None, + None, None) + rpc_state.sequence_number += 1 + self._relay.add_value(ticket) + + def _on_metadata_event(self, operation_id, event, rpc_state): + if _FINISH in rpc_state.due: + rpc_state.allowance -= 1 + rpc_state.call.read(operation_id) + rpc_state.read = _Read.READING + rpc_state.due.add(_READ) + rpc_state.due.remove(_METADATA) + ticket = links.Ticket( + operation_id, rpc_state.sequence_number, None, None, + links.Ticket.Subscription.FULL, None, None, event.metadata, None, + None, None, None, None, None) + rpc_state.sequence_number += 1 + self._relay.add_value(ticket) + else: + _no_longer_due(_METADATA, rpc_state, operation_id, self._rpc_states) + + def _on_finish_event(self, operation_id, event, rpc_state): + _no_longer_due(_FINISH, rpc_state, operation_id, self._rpc_states) + if event.status.code is _intermediary_low.Code.OK: + termination = links.Ticket.Termination.COMPLETION + elif event.status.code is _intermediary_low.Code.CANCELLED: + termination = links.Ticket.Termination.CANCELLATION + elif event.status.code is _intermediary_low.Code.DEADLINE_EXCEEDED: + termination = links.Ticket.Termination.EXPIRATION + elif event.status.code is _intermediary_low.Code.UNIMPLEMENTED: + termination = links.Ticket.Termination.REMOTE_FAILURE + elif event.status.code is _intermediary_low.Code.UNKNOWN: + termination = links.Ticket.Termination.LOCAL_FAILURE + else: + termination = links.Ticket.Termination.TRANSMISSION_FAILURE + code = _constants.LOW_STATUS_CODE_TO_HIGH_STATUS_CODE[event.status.code] + ticket = links.Ticket( + operation_id, rpc_state.sequence_number, None, None, None, None, None, + None, None, event.metadata, code, event.status.details, termination, + None) + rpc_state.sequence_number += 1 + self._relay.add_value(ticket) + + def _spin(self, completion_queue): + while True: + event = completion_queue.get(None) + with self._lock: + rpc_state = self._rpc_states.get(event.tag, None) + if event.kind is _STOP: + pass + elif event.kind is _WRITE: + self._on_write_event(event.tag, event, rpc_state) + elif event.kind is _METADATA: + self._on_metadata_event(event.tag, event, rpc_state) + elif event.kind is _READ: + self._on_read_event(event.tag, event, rpc_state) + elif event.kind is _FINISH: + self._on_finish_event(event.tag, event, rpc_state) + elif event.kind is _COMPLETE: + _no_longer_due(_COMPLETE, rpc_state, event.tag, self._rpc_states) + else: + logging.error('Illegal RPC event! %s', (event,)) + + if self._completion_queue is None and not self._rpc_states: + completion_queue.stop() + return + + def _invoke( + self, operation_id, group, method, initial_metadata, payload, termination, + timeout, allowance, options): + """Invoke an RPC. + + Args: + operation_id: Any object to be used as an operation ID for the RPC. + group: The group to which the RPC method belongs. + method: The RPC method name. + initial_metadata: The initial metadata object for the RPC. + payload: A payload object for the RPC or None if no payload was given at + invocation-time. + termination: A links.Ticket.Termination value or None indicated whether or + not more writes will follow from this side of the RPC. + timeout: A duration of time in seconds to allow for the RPC. + allowance: The number of payloads (beyond the free first one) that the + local ticket exchange mate has granted permission to be read. + options: A beta_interfaces.GRPCCallOptions value or None. + """ + if termination is links.Ticket.Termination.COMPLETION: + high_write = _HighWrite.CLOSED + elif termination is None: + high_write = _HighWrite.OPEN + else: + return + + transformed_initial_metadata = self._metadata_transformer(initial_metadata) + request_serializer = self._request_serializers.get( + (group, method), _IDENTITY) + response_deserializer = self._response_deserializers.get( + (group, method), _IDENTITY) + + call = _intermediary_low.Call( + self._channel, self._completion_queue, '/%s/%s' % (group, method), + self._host, time.time() + timeout) + if options is not None and options.credentials is not None: + call.set_credentials(options.credentials._intermediary_low_credentials) + if transformed_initial_metadata is not None: + for metadata_key, metadata_value in transformed_initial_metadata: + call.add_metadata(metadata_key, metadata_value) + call.invoke(self._completion_queue, operation_id, operation_id) + if payload is None: + if high_write is _HighWrite.CLOSED: + call.complete(operation_id) + low_write = _LowWrite.CLOSED + due = set((_METADATA, _COMPLETE, _FINISH,)) + else: + low_write = _LowWrite.OPEN + due = set((_METADATA, _FINISH,)) + else: + if options is not None and options.disable_compression: + flags = _intermediary_low.WriteFlags.WRITE_NO_COMPRESS + else: + flags = 0 + call.write(request_serializer(payload), operation_id, flags) + low_write = _LowWrite.ACTIVE + due = set((_WRITE, _METADATA, _FINISH,)) + context = _Context() + self._rpc_states[operation_id] = _RPCState( + call, request_serializer, response_deserializer, 1, + _Read.AWAITING_METADATA, 1 if allowance is None else (1 + allowance), + high_write, low_write, due, context) + protocol = links.Protocol(links.Protocol.Kind.INVOCATION_CONTEXT, context) + ticket = links.Ticket( + operation_id, 0, None, None, None, None, None, None, None, None, None, + None, None, protocol) + self._relay.add_value(ticket) + + def _advance(self, operation_id, rpc_state, payload, termination, allowance): + if payload is not None: + disable_compression = rpc_state.context.next_compression_disabled() + if disable_compression: + flags = _intermediary_low.WriteFlags.WRITE_NO_COMPRESS + else: + flags = 0 + rpc_state.call.write( + rpc_state.request_serializer(payload), operation_id, flags) + rpc_state.low_write = _LowWrite.ACTIVE + rpc_state.due.add(_WRITE) + + if allowance is not None: + if rpc_state.read is _Read.AWAITING_ALLOWANCE: + rpc_state.allowance += allowance - 1 + rpc_state.call.read(operation_id) + rpc_state.read = _Read.READING + rpc_state.due.add(_READ) + else: + rpc_state.allowance += allowance + + if termination is links.Ticket.Termination.COMPLETION: + rpc_state.high_write = _HighWrite.CLOSED + if rpc_state.low_write is _LowWrite.OPEN: + rpc_state.call.complete(operation_id) + rpc_state.due.add(_COMPLETE) + rpc_state.low_write = _LowWrite.CLOSED + elif termination is not None: + rpc_state.call.cancel() + + def add_ticket(self, ticket): + with self._lock: + if ticket.sequence_number == 0: + if self._completion_queue is None: + logging.error('Received invocation ticket %s after stop!', ticket) + else: + if (ticket.protocol is not None and + ticket.protocol.kind is links.Protocol.Kind.CALL_OPTION): + grpc_call_options = ticket.protocol.value + else: + grpc_call_options = None + self._invoke( + ticket.operation_id, ticket.group, ticket.method, + ticket.initial_metadata, ticket.payload, ticket.termination, + ticket.timeout, ticket.allowance, grpc_call_options) + else: + rpc_state = self._rpc_states.get(ticket.operation_id) + if rpc_state is not None: + self._advance( + ticket.operation_id, rpc_state, ticket.payload, + ticket.termination, ticket.allowance) + + def start(self): + """Starts this object. + + This method must be called before attempting to exchange tickets with this + object. + """ + with self._lock: + self._completion_queue = _intermediary_low.CompletionQueue() + self._pool = logging_pool.pool(1) + self._pool.submit(self._spin, self._completion_queue) + + def stop(self): + """Stops this object. + + This method must be called for proper termination of this object, and no + attempts to exchange tickets with this object may be made after this method + has been called. + """ + with self._lock: + if not self._rpc_states: + self._completion_queue.stop() + self._completion_queue = None + pool = self._pool + pool.shutdown(wait=True) + + +class InvocationLink(links.Link, activated.Activated): + """A links.Link for use on the invocation-side of a gRPC connection. + + Implementations of this interface are only valid for use when activated. + """ + __metaclass__ = abc.ABCMeta + + +class _InvocationLink(InvocationLink): + + def __init__( + self, channel, host, metadata_transformer, request_serializers, + response_deserializers): + self._relay = relay.relay(None) + self._kernel = _Kernel( + channel, host, + _IDENTITY if metadata_transformer is None else metadata_transformer, + {} if request_serializers is None else request_serializers, + {} if response_deserializers is None else response_deserializers, + self._relay) + + def _start(self): + self._relay.start() + self._kernel.start() + return self + + def _stop(self): + self._kernel.stop() + self._relay.stop() + + def accept_ticket(self, ticket): + """See links.Link.accept_ticket for specification.""" + self._kernel.add_ticket(ticket) + + def join_link(self, link): + """See links.Link.join_link for specification.""" + self._relay.set_behavior(link.accept_ticket) + + def __enter__(self): + """See activated.Activated.__enter__ for specification.""" + return self._start() + + def __exit__(self, exc_type, exc_val, exc_tb): + """See activated.Activated.__exit__ for specification.""" + self._stop() + return False + + def start(self): + """See activated.Activated.start for specification.""" + return self._start() + + def stop(self): + """See activated.Activated.stop for specification.""" + self._stop() + + +def invocation_link( + channel, host, metadata_transformer, request_serializers, + response_deserializers): + """Creates an InvocationLink. + + Args: + channel: An _intermediary_low.Channel for use by the link. + host: The host to specify when invoking RPCs. + metadata_transformer: A callable that takes an invocation-side initial + metadata value and returns another metadata value to send in its place. + May be None. + request_serializers: A dict from group-method pair to request object + serialization behavior. + response_deserializers: A dict from group-method pair to response object + deserialization behavior. + + Returns: + An InvocationLink. + """ + return _InvocationLink( + channel, host, metadata_transformer, request_serializers, + response_deserializers) diff --git a/src/python/grpcio/grpc/_links/service.py b/src/python/grpcio/grpc/_links/service.py new file mode 100644 index 00000000..f56df840 --- /dev/null +++ b/src/python/grpcio/grpc/_links/service.py @@ -0,0 +1,505 @@ +# Copyright 2015, 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. + +"""The RPC-service-side bridge between RPC Framework and GRPC-on-the-wire.""" + +import abc +import enum +import logging +import threading +import time + +from grpc._adapter import _intermediary_low +from grpc._links import _constants +from grpc.beta import interfaces as beta_interfaces +from grpc.framework.foundation import logging_pool +from grpc.framework.foundation import relay +from grpc.framework.interfaces.links import links + +_IDENTITY = lambda x: x + +_TERMINATION_KIND_TO_CODE = { + links.Ticket.Termination.COMPLETION: _intermediary_low.Code.OK, + links.Ticket.Termination.CANCELLATION: _intermediary_low.Code.CANCELLED, + links.Ticket.Termination.EXPIRATION: + _intermediary_low.Code.DEADLINE_EXCEEDED, + links.Ticket.Termination.SHUTDOWN: _intermediary_low.Code.UNAVAILABLE, + links.Ticket.Termination.RECEPTION_FAILURE: _intermediary_low.Code.INTERNAL, + links.Ticket.Termination.TRANSMISSION_FAILURE: + _intermediary_low.Code.INTERNAL, + links.Ticket.Termination.LOCAL_FAILURE: _intermediary_low.Code.UNKNOWN, + links.Ticket.Termination.REMOTE_FAILURE: _intermediary_low.Code.UNKNOWN, +} + +_STOP = _intermediary_low.Event.Kind.STOP +_WRITE = _intermediary_low.Event.Kind.WRITE_ACCEPTED +_COMPLETE = _intermediary_low.Event.Kind.COMPLETE_ACCEPTED +_SERVICE = _intermediary_low.Event.Kind.SERVICE_ACCEPTED +_READ = _intermediary_low.Event.Kind.READ_ACCEPTED +_FINISH = _intermediary_low.Event.Kind.FINISH + + +@enum.unique +class _Read(enum.Enum): + READING = 'reading' + # TODO(issue 2916): This state will again be necessary after eliminating the + # "early_read" field of _RPCState and going back to only reading when granted + # allowance to read. + # AWAITING_ALLOWANCE = 'awaiting allowance' + CLOSED = 'closed' + + +@enum.unique +class _HighWrite(enum.Enum): + OPEN = 'open' + CLOSED = 'closed' + + +@enum.unique +class _LowWrite(enum.Enum): + """The possible categories of low-level write state.""" + + OPEN = 'OPEN' + ACTIVE = 'ACTIVE' + CLOSED = 'CLOSED' + + +class _Context(beta_interfaces.GRPCServicerContext): + + def __init__(self, call): + self._lock = threading.Lock() + self._call = call + self._disable_next_compression = False + + def peer(self): + with self._lock: + return self._call.peer() + + def disable_next_response_compression(self): + with self._lock: + self._disable_next_compression = True + + def next_compression_disabled(self): + with self._lock: + disabled = self._disable_next_compression + self._disable_next_compression = False + return disabled + + +class _RPCState(object): + + def __init__( + self, request_deserializer, response_serializer, sequence_number, read, + early_read, allowance, high_write, low_write, premetadataed, + terminal_metadata, code, message, due, context): + self.request_deserializer = request_deserializer + self.response_serializer = response_serializer + self.sequence_number = sequence_number + self.read = read + # TODO(issue 2916): Eliminate this by eliminating the necessity of calling + # call.read just to advance the RPC. + self.early_read = early_read # A raw (not deserialized) read. + self.allowance = allowance + self.high_write = high_write + self.low_write = low_write + self.premetadataed = premetadataed + self.terminal_metadata = terminal_metadata + self.code = code + self.message = message + self.due = due + self.context = context + + +def _no_longer_due(kind, rpc_state, key, rpc_states): + rpc_state.due.remove(kind) + if not rpc_state.due: + del rpc_states[key] + + +def _metadatafy(call, metadata): + for metadata_key, metadata_value in metadata: + call.add_metadata(metadata_key, metadata_value) + + +def _status(termination_kind, high_code, details): + low_details = b'' if details is None else details + if high_code is None: + low_code = _TERMINATION_KIND_TO_CODE[termination_kind] + else: + low_code = _constants.HIGH_STATUS_CODE_TO_LOW_STATUS_CODE[high_code] + return _intermediary_low.Status(low_code, low_details) + + +class _Kernel(object): + + def __init__(self, request_deserializers, response_serializers, ticket_relay): + self._lock = threading.Lock() + self._request_deserializers = request_deserializers + self._response_serializers = response_serializers + self._relay = ticket_relay + + self._completion_queue = None + self._due = set() + self._server = None + self._rpc_states = {} + self._pool = None + + def _on_service_acceptance_event(self, event, server): + server.service(None) + + service_acceptance = event.service_acceptance + call = service_acceptance.call + call.accept(self._completion_queue, call) + try: + group, method = service_acceptance.method.split('/')[1:3] + except ValueError: + logging.info('Illegal path "%s"!', service_acceptance.method) + return + request_deserializer = self._request_deserializers.get( + (group, method), _IDENTITY) + response_serializer = self._response_serializers.get( + (group, method), _IDENTITY) + + call.read(call) + context = _Context(call) + self._rpc_states[call] = _RPCState( + request_deserializer, response_serializer, 1, _Read.READING, None, 1, + _HighWrite.OPEN, _LowWrite.OPEN, False, None, None, None, + set((_READ, _FINISH,)), context) + protocol = links.Protocol(links.Protocol.Kind.SERVICER_CONTEXT, context) + ticket = links.Ticket( + call, 0, group, method, links.Ticket.Subscription.FULL, + service_acceptance.deadline - time.time(), None, event.metadata, None, + None, None, None, None, protocol) + self._relay.add_value(ticket) + + def _on_read_event(self, event): + call = event.tag + rpc_state = self._rpc_states[call] + + if event.bytes is None: + rpc_state.read = _Read.CLOSED + payload = None + termination = links.Ticket.Termination.COMPLETION + _no_longer_due(_READ, rpc_state, call, self._rpc_states) + else: + if 0 < rpc_state.allowance: + payload = rpc_state.request_deserializer(event.bytes) + termination = None + rpc_state.allowance -= 1 + call.read(call) + else: + rpc_state.early_read = event.bytes + _no_longer_due(_READ, rpc_state, call, self._rpc_states) + return + # TODO(issue 2916): Instead of returning: + # rpc_state.read = _Read.AWAITING_ALLOWANCE + ticket = links.Ticket( + call, rpc_state.sequence_number, None, None, None, None, None, None, + payload, None, None, None, termination, None) + rpc_state.sequence_number += 1 + self._relay.add_value(ticket) + + def _on_write_event(self, event): + call = event.tag + rpc_state = self._rpc_states[call] + + if rpc_state.high_write is _HighWrite.CLOSED: + if rpc_state.terminal_metadata is not None: + _metadatafy(call, rpc_state.terminal_metadata) + status = _status( + links.Ticket.Termination.COMPLETION, rpc_state.code, + rpc_state.message) + call.status(status, call) + rpc_state.low_write = _LowWrite.CLOSED + rpc_state.due.add(_COMPLETE) + rpc_state.due.remove(_WRITE) + else: + ticket = links.Ticket( + call, rpc_state.sequence_number, None, None, None, None, 1, None, + None, None, None, None, None, None) + rpc_state.sequence_number += 1 + self._relay.add_value(ticket) + rpc_state.low_write = _LowWrite.OPEN + _no_longer_due(_WRITE, rpc_state, call, self._rpc_states) + + def _on_finish_event(self, event): + call = event.tag + rpc_state = self._rpc_states[call] + _no_longer_due(_FINISH, rpc_state, call, self._rpc_states) + code = event.status.code + if code is _intermediary_low.Code.OK: + return + + if code is _intermediary_low.Code.CANCELLED: + termination = links.Ticket.Termination.CANCELLATION + elif code is _intermediary_low.Code.DEADLINE_EXCEEDED: + termination = links.Ticket.Termination.EXPIRATION + else: + termination = links.Ticket.Termination.TRANSMISSION_FAILURE + ticket = links.Ticket( + call, rpc_state.sequence_number, None, None, None, None, None, None, + None, None, None, None, termination, None) + rpc_state.sequence_number += 1 + self._relay.add_value(ticket) + + def _spin(self, completion_queue, server): + while True: + event = completion_queue.get(None) + with self._lock: + if event.kind is _STOP: + self._due.remove(_STOP) + elif event.kind is _READ: + self._on_read_event(event) + elif event.kind is _WRITE: + self._on_write_event(event) + elif event.kind is _COMPLETE: + _no_longer_due( + _COMPLETE, self._rpc_states.get(event.tag), event.tag, + self._rpc_states) + elif event.kind is _intermediary_low.Event.Kind.FINISH: + self._on_finish_event(event) + elif event.kind is _SERVICE: + if self._server is None: + self._due.remove(_SERVICE) + else: + self._on_service_acceptance_event(event, server) + else: + logging.error('Illegal event! %s', (event,)) + + if not self._due and not self._rpc_states: + completion_queue.stop() + return + + def add_ticket(self, ticket): + with self._lock: + call = ticket.operation_id + rpc_state = self._rpc_states.get(call) + if rpc_state is None: + return + + if ticket.initial_metadata is not None: + _metadatafy(call, ticket.initial_metadata) + call.premetadata() + rpc_state.premetadataed = True + elif not rpc_state.premetadataed: + if (ticket.terminal_metadata is not None or + ticket.payload is not None or + ticket.termination is not None or + ticket.code is not None or + ticket.message is not None): + call.premetadata() + rpc_state.premetadataed = True + + if ticket.allowance is not None: + if rpc_state.early_read is None: + rpc_state.allowance += ticket.allowance + else: + payload = rpc_state.request_deserializer(rpc_state.early_read) + rpc_state.allowance += ticket.allowance - 1 + rpc_state.early_read = None + if rpc_state.read is _Read.READING: + call.read(call) + rpc_state.due.add(_READ) + termination = None + else: + termination = links.Ticket.Termination.COMPLETION + early_read_ticket = links.Ticket( + call, rpc_state.sequence_number, None, None, None, None, None, + None, payload, None, None, None, termination, None) + rpc_state.sequence_number += 1 + self._relay.add_value(early_read_ticket) + + if ticket.payload is not None: + disable_compression = rpc_state.context.next_compression_disabled() + if disable_compression: + flags = _intermediary_low.WriteFlags.WRITE_NO_COMPRESS + else: + flags = 0 + call.write(rpc_state.response_serializer(ticket.payload), call, flags) + rpc_state.due.add(_WRITE) + rpc_state.low_write = _LowWrite.ACTIVE + + if ticket.terminal_metadata is not None: + rpc_state.terminal_metadata = ticket.terminal_metadata + if ticket.code is not None: + rpc_state.code = ticket.code + if ticket.message is not None: + rpc_state.message = ticket.message + + if ticket.termination is links.Ticket.Termination.COMPLETION: + rpc_state.high_write = _HighWrite.CLOSED + if rpc_state.low_write is _LowWrite.OPEN: + if rpc_state.terminal_metadata is not None: + _metadatafy(call, rpc_state.terminal_metadata) + status = _status( + links.Ticket.Termination.COMPLETION, rpc_state.code, + rpc_state.message) + call.status(status, call) + rpc_state.due.add(_COMPLETE) + rpc_state.low_write = _LowWrite.CLOSED + elif ticket.termination is not None: + if rpc_state.terminal_metadata is not None: + _metadatafy(call, rpc_state.terminal_metadata) + status = _status( + ticket.termination, rpc_state.code, rpc_state.message) + call.status(status, call) + rpc_state.due.add(_COMPLETE) + + def add_port(self, address, server_credentials): + with self._lock: + if self._server is None: + self._completion_queue = _intermediary_low.CompletionQueue() + self._server = _intermediary_low.Server(self._completion_queue) + if server_credentials is None: + return self._server.add_http2_addr(address) + else: + return self._server.add_secure_http2_addr(address, server_credentials) + + def start(self): + with self._lock: + if self._server is None: + self._completion_queue = _intermediary_low.CompletionQueue() + self._server = _intermediary_low.Server(self._completion_queue) + self._pool = logging_pool.pool(1) + self._pool.submit(self._spin, self._completion_queue, self._server) + self._server.start() + self._server.service(None) + self._due.add(_SERVICE) + + def begin_stop(self): + with self._lock: + self._server.stop() + self._due.add(_STOP) + self._server = None + + def end_stop(self): + with self._lock: + pool = self._pool + pool.shutdown(wait=True) + + +class ServiceLink(links.Link): + """A links.Link for use on the service-side of a gRPC connection. + + Implementations of this interface are only valid for use between calls to + their start method and one of their stop methods. + """ + + @abc.abstractmethod + def add_port(self, address, server_credentials): + """Adds a port on which to service RPCs after this link has been started. + + Args: + address: The address on which to service RPCs with a port number of zero + requesting that a port number be automatically selected and used. + server_credentials: An _intermediary_low.ServerCredentials object, or + None for insecure service. + + Returns: + An integer port on which RPCs will be serviced after this link has been + started. This is typically the same number as the port number contained + in the passed address, but will likely be different if the port number + contained in the passed address was zero. + """ + raise NotImplementedError() + + @abc.abstractmethod + def start(self): + """Starts this object. + + This method must be called before attempting to use this Link in ticket + exchange. + """ + raise NotImplementedError() + + @abc.abstractmethod + def begin_stop(self): + """Indicate imminent link stop and immediate rejection of new RPCs. + + New RPCs will be rejected as soon as this method is called, but ongoing RPCs + will be allowed to continue until they terminate. This method does not + block. + """ + raise NotImplementedError() + + @abc.abstractmethod + def end_stop(self): + """Finishes stopping this link. + + begin_stop must have been called exactly once before calling this method. + + All in-progress RPCs will be terminated immediately. + """ + raise NotImplementedError() + + +class _ServiceLink(ServiceLink): + + def __init__(self, request_deserializers, response_serializers): + self._relay = relay.relay(None) + self._kernel = _Kernel( + {} if request_deserializers is None else request_deserializers, + {} if response_serializers is None else response_serializers, + self._relay) + + def accept_ticket(self, ticket): + self._kernel.add_ticket(ticket) + + def join_link(self, link): + self._relay.set_behavior(link.accept_ticket) + + def add_port(self, address, server_credentials): + return self._kernel.add_port(address, server_credentials) + + def start(self): + self._relay.start() + return self._kernel.start() + + def begin_stop(self): + self._kernel.begin_stop() + + def end_stop(self): + self._kernel.end_stop() + self._relay.stop() + + +def service_link(request_deserializers, response_serializers): + """Creates a ServiceLink. + + Args: + request_deserializers: A dict from group-method pair to request object + deserialization behavior. + response_serializers: A dict from group-method pair to response ojbect + serialization behavior. + + Returns: + A ServiceLink. + """ + return _ServiceLink(request_deserializers, response_serializers) diff --git a/src/python/grpcio/grpc/beta/__init__.py b/src/python/grpcio/grpc/beta/__init__.py new file mode 100644 index 00000000..b8939880 --- /dev/null +++ b/src/python/grpcio/grpc/beta/__init__.py @@ -0,0 +1,28 @@ +# Copyright 2015, 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. diff --git a/src/python/grpcio/grpc/beta/_connectivity_channel.py b/src/python/grpcio/grpc/beta/_connectivity_channel.py new file mode 100644 index 00000000..61674a70 --- /dev/null +++ b/src/python/grpcio/grpc/beta/_connectivity_channel.py @@ -0,0 +1,156 @@ +# Copyright 2015, 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. + +"""Affords a connectivity-state-listenable channel.""" + +import threading +import time + +from grpc._adapter import _low +from grpc._adapter import _types +from grpc.beta import interfaces +from grpc.framework.foundation import callable_util + +_CHANNEL_SUBSCRIPTION_CALLBACK_ERROR_LOG_MESSAGE = ( + 'Exception calling channel subscription callback!') + +_LOW_CONNECTIVITY_STATE_TO_CHANNEL_CONNECTIVITY = { + state: connectivity for state, connectivity in zip( + _types.ConnectivityState, interfaces.ChannelConnectivity) +} + + +class ConnectivityChannel(object): + + def __init__(self, low_channel): + self._lock = threading.Lock() + self._low_channel = low_channel + + self._polling = False + self._connectivity = None + self._try_to_connect = False + self._callbacks_and_connectivities = [] + self._delivering = False + + def _deliveries(self, connectivity): + callbacks_needing_update = [] + for callback_and_connectivity in self._callbacks_and_connectivities: + callback, callback_connectivity = callback_and_connectivity + if callback_connectivity is not connectivity: + callbacks_needing_update.append(callback) + callback_and_connectivity[1] = connectivity + return callbacks_needing_update + + def _deliver(self, initial_connectivity, initial_callbacks): + connectivity = initial_connectivity + callbacks = initial_callbacks + while True: + for callback in callbacks: + callable_util.call_logging_exceptions( + callback, _CHANNEL_SUBSCRIPTION_CALLBACK_ERROR_LOG_MESSAGE, + connectivity) + with self._lock: + callbacks = self._deliveries(self._connectivity) + if callbacks: + connectivity = self._connectivity + else: + self._delivering = False + return + + def _spawn_delivery(self, connectivity, callbacks): + delivering_thread = threading.Thread( + target=self._deliver, args=(connectivity, callbacks,)) + delivering_thread.start() + self._delivering = True + + # TODO(issue 3064): Don't poll. + def _poll_connectivity(self, low_channel, initial_try_to_connect): + try_to_connect = initial_try_to_connect + low_connectivity = low_channel.check_connectivity_state(try_to_connect) + with self._lock: + self._connectivity = _LOW_CONNECTIVITY_STATE_TO_CHANNEL_CONNECTIVITY[ + low_connectivity] + callbacks = tuple( + callback for callback, unused_but_known_to_be_none_connectivity + in self._callbacks_and_connectivities) + for callback_and_connectivity in self._callbacks_and_connectivities: + callback_and_connectivity[1] = self._connectivity + if callbacks: + self._spawn_delivery(self._connectivity, callbacks) + completion_queue = _low.CompletionQueue() + while True: + low_channel.watch_connectivity_state( + low_connectivity, time.time() + 0.2, completion_queue, None) + event = completion_queue.next() + with self._lock: + if not self._callbacks_and_connectivities and not self._try_to_connect: + self._polling = False + self._connectivity = None + completion_queue.shutdown() + break + try_to_connect = self._try_to_connect + self._try_to_connect = False + if event.success or try_to_connect: + low_connectivity = low_channel.check_connectivity_state(try_to_connect) + with self._lock: + self._connectivity = _LOW_CONNECTIVITY_STATE_TO_CHANNEL_CONNECTIVITY[ + low_connectivity] + if not self._delivering: + callbacks = self._deliveries(self._connectivity) + if callbacks: + self._spawn_delivery(self._connectivity, callbacks) + + def subscribe(self, callback, try_to_connect): + with self._lock: + if not self._callbacks_and_connectivities and not self._polling: + polling_thread = threading.Thread( + target=self._poll_connectivity, + args=(self._low_channel, bool(try_to_connect))) + polling_thread.start() + self._polling = True + self._callbacks_and_connectivities.append([callback, None]) + elif not self._delivering and self._connectivity is not None: + self._spawn_delivery(self._connectivity, (callback,)) + self._try_to_connect |= bool(try_to_connect) + self._callbacks_and_connectivities.append( + [callback, self._connectivity]) + else: + self._try_to_connect |= bool(try_to_connect) + self._callbacks_and_connectivities.append([callback, None]) + + def unsubscribe(self, callback): + with self._lock: + for index, (subscribed_callback, unused_connectivity) in enumerate( + self._callbacks_and_connectivities): + if callback == subscribed_callback: + self._callbacks_and_connectivities.pop(index) + break + + def low_channel(self): + return self._low_channel diff --git a/src/python/grpcio/grpc/beta/_server.py b/src/python/grpcio/grpc/beta/_server.py new file mode 100644 index 00000000..05b954d1 --- /dev/null +++ b/src/python/grpcio/grpc/beta/_server.py @@ -0,0 +1,146 @@ +# Copyright 2015, 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. + +"""Beta API server implementation.""" + +import threading + +from grpc._links import service +from grpc.beta import interfaces +from grpc.framework.core import implementations as _core_implementations +from grpc.framework.crust import implementations as _crust_implementations +from grpc.framework.foundation import logging_pool +from grpc.framework.interfaces.base import base +from grpc.framework.interfaces.links import utilities + +_DEFAULT_POOL_SIZE = 8 +_DEFAULT_TIMEOUT = 300 +_MAXIMUM_TIMEOUT = 24 * 60 * 60 + + +class _GRPCServicer(base.Servicer): + + def __init__(self, delegate): + self._delegate = delegate + + def service(self, group, method, context, output_operator): + try: + return self._delegate.service(group, method, context, output_operator) + except base.NoSuchMethodError as e: + if e.code is None and e.details is None: + raise base.NoSuchMethodError( + interfaces.StatusCode.UNIMPLEMENTED, + b'Method "%s" of service "%s" not implemented!' % (method, group)) + else: + raise + + +def _disassemble(grpc_link, end_link, pool, event, grace): + grpc_link.begin_stop() + end_link.stop(grace).wait() + grpc_link.end_stop() + grpc_link.join_link(utilities.NULL_LINK) + end_link.join_link(utilities.NULL_LINK) + if pool is not None: + pool.shutdown(wait=True) + event.set() + + +class Server(interfaces.Server): + + def __init__(self, grpc_link, end_link, pool): + self._grpc_link = grpc_link + self._end_link = end_link + self._pool = pool + + def add_insecure_port(self, address): + return self._grpc_link.add_port(address, None) + + def add_secure_port(self, address, server_credentials): + return self._grpc_link.add_port( + address, server_credentials._intermediary_low_credentials) # pylint: disable=protected-access + + def _start(self): + self._grpc_link.join_link(self._end_link) + self._end_link.join_link(self._grpc_link) + self._grpc_link.start() + self._end_link.start() + + def _stop(self, grace): + stop_event = threading.Event() + if 0 < grace: + disassembly_thread = threading.Thread( + target=_disassemble, + args=( + self._grpc_link, self._end_link, self._pool, stop_event, grace,)) + disassembly_thread.start() + return stop_event + else: + _disassemble(self._grpc_link, self._end_link, self._pool, stop_event, 0) + return stop_event + + def start(self): + self._start() + + def stop(self, grace): + return self._stop(grace) + + def __enter__(self): + self._start() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self._stop(0).wait() + return False + + +def server( + implementations, multi_implementation, request_deserializers, + response_serializers, thread_pool, thread_pool_size, default_timeout, + maximum_timeout): + if thread_pool is None: + service_thread_pool = logging_pool.pool( + _DEFAULT_POOL_SIZE if thread_pool_size is None else thread_pool_size) + assembly_thread_pool = service_thread_pool + else: + service_thread_pool = thread_pool + assembly_thread_pool = None + + servicer = _GRPCServicer( + _crust_implementations.servicer( + implementations, multi_implementation, service_thread_pool)) + + grpc_link = service.service_link(request_deserializers, response_serializers) + + end_link = _core_implementations.service_end_link( + servicer, + _DEFAULT_TIMEOUT if default_timeout is None else default_timeout, + _MAXIMUM_TIMEOUT if maximum_timeout is None else maximum_timeout) + + return Server(grpc_link, end_link, assembly_thread_pool) diff --git a/src/python/grpcio/grpc/beta/_stub.py b/src/python/grpcio/grpc/beta/_stub.py new file mode 100644 index 00000000..11dab889 --- /dev/null +++ b/src/python/grpcio/grpc/beta/_stub.py @@ -0,0 +1,117 @@ +# Copyright 2015, 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. + +"""Beta API stub implementation.""" + +import threading + +from grpc._links import invocation +from grpc.framework.core import implementations as _core_implementations +from grpc.framework.crust import implementations as _crust_implementations +from grpc.framework.foundation import logging_pool +from grpc.framework.interfaces.links import utilities + +_DEFAULT_POOL_SIZE = 6 + + +class _AutoIntermediary(object): + + def __init__(self, delegate, on_deletion): + self._delegate = delegate + self._on_deletion = on_deletion + + def __getattr__(self, attr): + return getattr(self._delegate, attr) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + return False + + def __del__(self): + self._on_deletion() + + +def _assemble( + channel, host, metadata_transformer, request_serializers, + response_deserializers, thread_pool, thread_pool_size): + end_link = _core_implementations.invocation_end_link() + grpc_link = invocation.invocation_link( + channel, host, metadata_transformer, request_serializers, + response_deserializers) + if thread_pool is None: + invocation_pool = logging_pool.pool( + _DEFAULT_POOL_SIZE if thread_pool_size is None else thread_pool_size) + assembly_pool = invocation_pool + else: + invocation_pool = thread_pool + assembly_pool = None + end_link.join_link(grpc_link) + grpc_link.join_link(end_link) + end_link.start() + grpc_link.start() + return end_link, grpc_link, invocation_pool, assembly_pool + + +def _disassemble(end_link, grpc_link, pool): + end_link.stop(24 * 60 * 60).wait() + grpc_link.stop() + end_link.join_link(utilities.NULL_LINK) + grpc_link.join_link(utilities.NULL_LINK) + if pool is not None: + pool.shutdown(wait=True) + + +def _wrap_assembly(stub, end_link, grpc_link, assembly_pool): + disassembly_thread = threading.Thread( + target=_disassemble, args=(end_link, grpc_link, assembly_pool)) + return _AutoIntermediary(stub, disassembly_thread.start) + + +def generic_stub( + channel, host, metadata_transformer, request_serializers, + response_deserializers, thread_pool, thread_pool_size): + end_link, grpc_link, invocation_pool, assembly_pool = _assemble( + channel, host, metadata_transformer, request_serializers, + response_deserializers, thread_pool, thread_pool_size) + stub = _crust_implementations.generic_stub(end_link, invocation_pool) + return _wrap_assembly(stub, end_link, grpc_link, assembly_pool) + + +def dynamic_stub( + channel, host, service, cardinalities, metadata_transformer, + request_serializers, response_deserializers, thread_pool, + thread_pool_size): + end_link, grpc_link, invocation_pool, assembly_pool = _assemble( + channel, host, metadata_transformer, request_serializers, + response_deserializers, thread_pool, thread_pool_size) + stub = _crust_implementations.dynamic_stub( + end_link, service, cardinalities, invocation_pool) + return _wrap_assembly(stub, end_link, grpc_link, assembly_pool) diff --git a/src/python/grpcio/grpc/beta/implementations.py b/src/python/grpcio/grpc/beta/implementations.py new file mode 100644 index 00000000..c9d64ad3 --- /dev/null +++ b/src/python/grpcio/grpc/beta/implementations.py @@ -0,0 +1,367 @@ +# Copyright 2015, 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. + +"""Entry points into the Beta API of gRPC Python.""" + +# threading is referenced from specification in this module. +import abc +import enum +import threading # pylint: disable=unused-import + +# cardinality and face are referenced from specification in this module. +from grpc._adapter import _intermediary_low +from grpc._adapter import _types +from grpc.beta import _connectivity_channel +from grpc.beta import _server +from grpc.beta import _stub +from grpc.beta import interfaces +from grpc.framework.common import cardinality # pylint: disable=unused-import +from grpc.framework.interfaces.face import face # pylint: disable=unused-import + +_CHANNEL_SUBSCRIPTION_CALLBACK_ERROR_LOG_MESSAGE = ( + 'Exception calling channel subscription callback!') + + +class ClientCredentials(object): + """A value encapsulating the data required to create a secure Channel. + + This class and its instances have no supported interface - it exists to define + the type of its instances and its instances exist to be passed to other + functions. + """ + + def __init__(self, low_credentials, intermediary_low_credentials): + self._low_credentials = low_credentials + self._intermediary_low_credentials = intermediary_low_credentials + + +def ssl_client_credentials(root_certificates, private_key, certificate_chain): + """Creates a ClientCredentials for use with an SSL-enabled Channel. + + Args: + root_certificates: The PEM-encoded root certificates or None to ask for + them to be retrieved from a default location. + private_key: The PEM-encoded private key to use or None if no private key + should be used. + certificate_chain: The PEM-encoded certificate chain to use or None if no + certificate chain should be used. + + Returns: + A ClientCredentials for use with an SSL-enabled Channel. + """ + intermediary_low_credentials = _intermediary_low.ClientCredentials( + root_certificates, private_key, certificate_chain) + return ClientCredentials( + intermediary_low_credentials._internal, intermediary_low_credentials) # pylint: disable=protected-access + + +class Channel(object): + """A channel to a remote host through which RPCs may be conducted. + + Only the "subscribe" and "unsubscribe" methods are supported for application + use. This class' instance constructor and all other attributes are + unsupported. + """ + + def __init__(self, low_channel, intermediary_low_channel): + self._low_channel = low_channel + self._intermediary_low_channel = intermediary_low_channel + self._connectivity_channel = _connectivity_channel.ConnectivityChannel( + low_channel) + + def subscribe(self, callback, try_to_connect=None): + """Subscribes to this Channel's connectivity. + + Args: + callback: A callable to be invoked and passed an + interfaces.ChannelConnectivity identifying this Channel's connectivity. + The callable will be invoked immediately upon subscription and again for + every change to this Channel's connectivity thereafter until it is + unsubscribed. + try_to_connect: A boolean indicating whether or not this Channel should + attempt to connect if it is not already connected and ready to conduct + RPCs. + """ + self._connectivity_channel.subscribe(callback, try_to_connect) + + def unsubscribe(self, callback): + """Unsubscribes a callback from this Channel's connectivity. + + Args: + callback: A callable previously registered with this Channel from having + been passed to its "subscribe" method. + """ + self._connectivity_channel.unsubscribe(callback) + + +def insecure_channel(host, port): + """Creates an insecure Channel to a remote host. + + Args: + host: The name of the remote host to which to connect. + port: The port of the remote host to which to connect. + + Returns: + A Channel to the remote host through which RPCs may be conducted. + """ + intermediary_low_channel = _intermediary_low.Channel( + '%s:%d' % (host, port), None) + return Channel(intermediary_low_channel._internal, intermediary_low_channel) # pylint: disable=protected-access + + +def secure_channel(host, port, client_credentials): + """Creates a secure Channel to a remote host. + + Args: + host: The name of the remote host to which to connect. + port: The port of the remote host to which to connect. + client_credentials: A ClientCredentials. + + Returns: + A secure Channel to the remote host through which RPCs may be conducted. + """ + intermediary_low_channel = _intermediary_low.Channel( + '%s:%d' % (host, port), client_credentials._intermediary_low_credentials) + return Channel(intermediary_low_channel._internal, intermediary_low_channel) # pylint: disable=protected-access + + +class StubOptions(object): + """A value encapsulating the various options for creation of a Stub. + + This class and its instances have no supported interface - it exists to define + the type of its instances and its instances exist to be passed to other + functions. + """ + + def __init__( + self, host, request_serializers, response_deserializers, + metadata_transformer, thread_pool, thread_pool_size): + self.host = host + self.request_serializers = request_serializers + self.response_deserializers = response_deserializers + self.metadata_transformer = metadata_transformer + self.thread_pool = thread_pool + self.thread_pool_size = thread_pool_size + +_EMPTY_STUB_OPTIONS = StubOptions( + None, None, None, None, None, None) + + +def stub_options( + host=None, request_serializers=None, response_deserializers=None, + metadata_transformer=None, thread_pool=None, thread_pool_size=None): + """Creates a StubOptions value to be passed at stub creation. + + All parameters are optional and should always be passed by keyword. + + Args: + host: A host string to set on RPC calls. + request_serializers: A dictionary from service name-method name pair to + request serialization behavior. + response_deserializers: A dictionary from service name-method name pair to + response deserialization behavior. + metadata_transformer: A callable that given a metadata object produces + another metadata object to be used in the underlying communication on the + wire. + thread_pool: A thread pool to use in stubs. + thread_pool_size: The size of thread pool to create for use in stubs; + ignored if thread_pool has been passed. + + Returns: + A StubOptions value created from the passed parameters. + """ + return StubOptions( + host, request_serializers, response_deserializers, + metadata_transformer, thread_pool, thread_pool_size) + + +def generic_stub(channel, options=None): + """Creates a face.GenericStub on which RPCs can be made. + + Args: + channel: A Channel for use by the created stub. + options: A StubOptions customizing the created stub. + + Returns: + A face.GenericStub on which RPCs can be made. + """ + effective_options = _EMPTY_STUB_OPTIONS if options is None else options + return _stub.generic_stub( + channel._intermediary_low_channel, effective_options.host, # pylint: disable=protected-access + effective_options.metadata_transformer, + effective_options.request_serializers, + effective_options.response_deserializers, effective_options.thread_pool, + effective_options.thread_pool_size) + + +def dynamic_stub(channel, service, cardinalities, options=None): + """Creates a face.DynamicStub with which RPCs can be invoked. + + Args: + channel: A Channel for the returned face.DynamicStub to use. + service: The package-qualified full name of the service. + cardinalities: A dictionary from RPC method name to cardinality.Cardinality + value identifying the cardinality of the RPC method. + options: An optional StubOptions value further customizing the functionality + of the returned face.DynamicStub. + + Returns: + A face.DynamicStub with which RPCs can be invoked. + """ + effective_options = StubOptions() if options is None else options + return _stub.dynamic_stub( + channel._intermediary_low_channel, effective_options.host, service, # pylint: disable=protected-access + cardinalities, effective_options.metadata_transformer, + effective_options.request_serializers, + effective_options.response_deserializers, effective_options.thread_pool, + effective_options.thread_pool_size) + + +class ServerCredentials(object): + """A value encapsulating the data required to open a secure port on a Server. + + This class and its instances have no supported interface - it exists to define + the type of its instances and its instances exist to be passed to other + functions. + """ + + def __init__(self, low_credentials, intermediary_low_credentials): + self._low_credentials = low_credentials + self._intermediary_low_credentials = intermediary_low_credentials + + +def ssl_server_credentials( + private_key_certificate_chain_pairs, root_certificates=None, + require_client_auth=False): + """Creates a ServerCredentials for use with an SSL-enabled Server. + + Args: + private_key_certificate_chain_pairs: A nonempty sequence each element of + which is a pair the first element of which is a PEM-encoded private key + and the second element of which is the corresponding PEM-encoded + certificate chain. + root_certificates: PEM-encoded client root certificates to be used for + verifying authenticated clients. If omitted, require_client_auth must also + be omitted or be False. + require_client_auth: A boolean indicating whether or not to require clients + to be authenticated. May only be True if root_certificates is not None. + + Returns: + A ServerCredentials for use with an SSL-enabled Server. + """ + if len(private_key_certificate_chain_pairs) == 0: + raise ValueError( + 'At least one private key-certificate chain pairis required!') + elif require_client_auth and root_certificates is None: + raise ValueError( + 'Illegal to require client auth without providing root certificates!') + else: + intermediary_low_credentials = _intermediary_low.ServerCredentials( + root_certificates, private_key_certificate_chain_pairs, + require_client_auth) + return ServerCredentials( + intermediary_low_credentials._internal, intermediary_low_credentials) # pylint: disable=protected-access + + +class ServerOptions(object): + """A value encapsulating the various options for creation of a Server. + + This class and its instances have no supported interface - it exists to define + the type of its instances and its instances exist to be passed to other + functions. + """ + + def __init__( + self, multi_method_implementation, request_deserializers, + response_serializers, thread_pool, thread_pool_size, default_timeout, + maximum_timeout): + self.multi_method_implementation = multi_method_implementation + self.request_deserializers = request_deserializers + self.response_serializers = response_serializers + self.thread_pool = thread_pool + self.thread_pool_size = thread_pool_size + self.default_timeout = default_timeout + self.maximum_timeout = maximum_timeout + +_EMPTY_SERVER_OPTIONS = ServerOptions( + None, None, None, None, None, None, None) + + +def server_options( + multi_method_implementation=None, request_deserializers=None, + response_serializers=None, thread_pool=None, thread_pool_size=None, + default_timeout=None, maximum_timeout=None): + """Creates a ServerOptions value to be passed at server creation. + + All parameters are optional and should always be passed by keyword. + + Args: + multi_method_implementation: A face.MultiMethodImplementation to be called + to service an RPC if the server has no specific method implementation for + the name of the RPC for which service was requested. + request_deserializers: A dictionary from service name-method name pair to + request deserialization behavior. + response_serializers: A dictionary from service name-method name pair to + response serialization behavior. + thread_pool: A thread pool to use in stubs. + thread_pool_size: The size of thread pool to create for use in stubs; + ignored if thread_pool has been passed. + default_timeout: A duration in seconds to allow for RPC service when + servicing RPCs that did not include a timeout value when invoked. + maximum_timeout: A duration in seconds to allow for RPC service when + servicing RPCs no matter what timeout value was passed when the RPC was + invoked. + + Returns: + A StubOptions value created from the passed parameters. + """ + return ServerOptions( + multi_method_implementation, request_deserializers, response_serializers, + thread_pool, thread_pool_size, default_timeout, maximum_timeout) + + +def server(service_implementations, options=None): + """Creates an interfaces.Server with which RPCs can be serviced. + + Args: + service_implementations: A dictionary from service name-method name pair to + face.MethodImplementation. + options: An optional ServerOptions value further customizing the + functionality of the returned Server. + + Returns: + An interfaces.Server with which RPCs can be serviced. + """ + effective_options = _EMPTY_SERVER_OPTIONS if options is None else options + return _server.server( + service_implementations, effective_options.multi_method_implementation, + effective_options.request_deserializers, + effective_options.response_serializers, effective_options.thread_pool, + effective_options.thread_pool_size, effective_options.default_timeout, + effective_options.maximum_timeout) diff --git a/src/python/grpcio/grpc/beta/interfaces.py b/src/python/grpcio/grpc/beta/interfaces.py new file mode 100644 index 00000000..07c8618f --- /dev/null +++ b/src/python/grpcio/grpc/beta/interfaces.py @@ -0,0 +1,214 @@ +# Copyright 2015, 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. + +"""Constants and interfaces of the Beta API of gRPC Python.""" + +import abc +import enum + +from grpc._adapter import _types + + +@enum.unique +class ChannelConnectivity(enum.Enum): + """Mirrors grpc_connectivity_state in the gRPC Core. + + Attributes: + IDLE: The channel is idle. + CONNECTING: The channel is connecting. + READY: The channel is ready to conduct RPCs. + TRANSIENT_FAILURE: The channel has seen a failure from which it expects to + recover. + FATAL_FAILURE: The channel has seen a failure from which it cannot recover. + """ + IDLE = (_types.ConnectivityState.IDLE, 'idle',) + CONNECTING = (_types.ConnectivityState.CONNECTING, 'connecting',) + READY = (_types.ConnectivityState.READY, 'ready',) + TRANSIENT_FAILURE = ( + _types.ConnectivityState.TRANSIENT_FAILURE, 'transient failure',) + FATAL_FAILURE = (_types.ConnectivityState.FATAL_FAILURE, 'fatal failure',) + + +@enum.unique +class StatusCode(enum.Enum): + """Mirrors grpc_status_code in the C core.""" + OK = 0 + CANCELLED = 1 + UNKNOWN = 2 + INVALID_ARGUMENT = 3 + DEADLINE_EXCEEDED = 4 + NOT_FOUND = 5 + ALREADY_EXISTS = 6 + PERMISSION_DENIED = 7 + RESOURCE_EXHAUSTED = 8 + FAILED_PRECONDITION = 9 + ABORTED = 10 + OUT_OF_RANGE = 11 + UNIMPLEMENTED = 12 + INTERNAL = 13 + UNAVAILABLE = 14 + DATA_LOSS = 15 + UNAUTHENTICATED = 16 + + +class GRPCCallOptions(object): + """A value encapsulating gRPC-specific options passed on RPC invocation. + + This class and its instances have no supported interface - it exists to + define the type of its instances and its instances exist to be passed to + other functions. + """ + + def __init__(self, disable_compression, subcall_of, credentials): + self.disable_compression = disable_compression + self.subcall_of = subcall_of + self.credentials = credentials + + +def grpc_call_options(disable_compression=False, credentials=None): + """Creates a GRPCCallOptions value to be passed at RPC invocation. + + All parameters are optional and should always be passed by keyword. + + Args: + disable_compression: A boolean indicating whether or not compression should + be disabled for the request object of the RPC. Only valid for + request-unary RPCs. + credentials: A ClientCredentials object to use for the invoked RPC. + """ + return GRPCCallOptions(disable_compression, None, credentials) + + +class GRPCServicerContext(object): + """Exposes gRPC-specific options and behaviors to code servicing RPCs.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def peer(self): + """Identifies the peer that invoked the RPC being serviced. + + Returns: + A string identifying the peer that invoked the RPC being serviced. + """ + raise NotImplementedError() + + @abc.abstractmethod + def disable_next_response_compression(self): + """Disables compression of the next response passed by the application.""" + raise NotImplementedError() + + +class GRPCInvocationContext(object): + """Exposes gRPC-specific options and behaviors to code invoking RPCs.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def disable_next_request_compression(self): + """Disables compression of the next request passed by the application.""" + raise NotImplementedError() + + +class Server(object): + """Services RPCs.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def add_insecure_port(self, address): + """Reserves a port for insecure RPC service once this Server becomes active. + + This method may only be called before calling this Server's start method is + called. + + Args: + address: The address for which to open a port. + + Returns: + An integer port on which RPCs will be serviced after this link has been + started. This is typically the same number as the port number contained + in the passed address, but will likely be different if the port number + contained in the passed address was zero. + """ + raise NotImplementedError() + + @abc.abstractmethod + def add_secure_port(self, address, server_credentials): + """Reserves a port for secure RPC service after this Server becomes active. + + This method may only be called before calling this Server's start method is + called. + + Args: + address: The address for which to open a port. + server_credentials: A ServerCredentials. + + Returns: + An integer port on which RPCs will be serviced after this link has been + started. This is typically the same number as the port number contained + in the passed address, but will likely be different if the port number + contained in the passed address was zero. + """ + raise NotImplementedError() + + @abc.abstractmethod + def start(self): + """Starts this Server's service of RPCs. + + This method may only be called while the server is not serving RPCs (i.e. it + is not idempotent). + """ + raise NotImplementedError() + + @abc.abstractmethod + def stop(self, grace): + """Stops this Server's service of RPCs. + + All calls to this method immediately stop service of new RPCs. When existing + RPCs are aborted is controlled by the grace period parameter passed to this + method. + + This method may be called at any time and is idempotent. Passing a smaller + grace value than has been passed in a previous call will have the effect of + stopping the Server sooner. Passing a larger grace value than has been + passed in a previous call will not have the effect of stopping the sooner + later. + + Args: + grace: A duration of time in seconds to allow existing RPCs to complete + before being aborted by this Server's stopping. May be zero for + immediate abortion of all in-progress RPCs. + + Returns: + A threading.Event that will be set when this Server has completely + stopped. The returned event may not be set until after the full grace + period (if some ongoing RPC continues for the full length of the period) + of it may be set much sooner (such as if this Server had no RPCs underway + at the time it was stopped or if all RPCs that it had underway completed + very early in the grace period). + """ + raise NotImplementedError() diff --git a/src/python/grpcio/grpc/beta/utilities.py b/src/python/grpcio/grpc/beta/utilities.py new file mode 100644 index 00000000..fb07a765 --- /dev/null +++ b/src/python/grpcio/grpc/beta/utilities.py @@ -0,0 +1,164 @@ +# Copyright 2015, 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. + +"""Utilities for the gRPC Python Beta API.""" + +import threading +import time + +# implementations is referenced from specification in this module. +from grpc.beta import implementations # pylint: disable=unused-import +from grpc.beta import interfaces +from grpc.framework.foundation import callable_util +from grpc.framework.foundation import future + +_DONE_CALLBACK_EXCEPTION_LOG_MESSAGE = ( + 'Exception calling connectivity future "done" callback!') + + +class _ChannelReadyFuture(future.Future): + + def __init__(self, channel): + self._condition = threading.Condition() + self._channel = channel + + self._matured = False + self._cancelled = False + self._done_callbacks = [] + + def _block(self, timeout): + until = None if timeout is None else time.time() + timeout + with self._condition: + while True: + if self._cancelled: + raise future.CancelledError() + elif self._matured: + return + else: + if until is None: + self._condition.wait() + else: + remaining = until - time.time() + if remaining < 0: + raise future.TimeoutError() + else: + self._condition.wait(timeout=remaining) + + def _update(self, connectivity): + with self._condition: + if (not self._cancelled and + connectivity is interfaces.ChannelConnectivity.READY): + self._matured = True + self._channel.unsubscribe(self._update) + self._condition.notify_all() + done_callbacks = tuple(self._done_callbacks) + self._done_callbacks = None + else: + return + + for done_callback in done_callbacks: + callable_util.call_logging_exceptions( + done_callback, _DONE_CALLBACK_EXCEPTION_LOG_MESSAGE, self) + + def cancel(self): + with self._condition: + if not self._matured: + self._cancelled = True + self._channel.unsubscribe(self._update) + self._condition.notify_all() + done_callbacks = tuple(self._done_callbacks) + self._done_callbacks = None + else: + return False + + for done_callback in done_callbacks: + callable_util.call_logging_exceptions( + done_callback, _DONE_CALLBACK_EXCEPTION_LOG_MESSAGE, self) + + def cancelled(self): + with self._condition: + return self._cancelled + + def running(self): + with self._condition: + return not self._cancelled and not self._matured + + def done(self): + with self._condition: + return self._cancelled or self._matured + + def result(self, timeout=None): + self._block(timeout) + return None + + def exception(self, timeout=None): + self._block(timeout) + return None + + def traceback(self, timeout=None): + self._block(timeout) + return None + + def add_done_callback(self, fn): + with self._condition: + if not self._cancelled and not self._matured: + self._done_callbacks.append(fn) + return + + fn(self) + + def start(self): + with self._condition: + self._channel.subscribe(self._update, try_to_connect=True) + + def __del__(self): + with self._condition: + if not self._cancelled and not self._matured: + self._channel.unsubscribe(self._update) + + +def channel_ready_future(channel): + """Creates a future.Future tracking when an implementations.Channel is ready. + + Cancelling the returned future.Future does not tell the given + implementations.Channel to abandon attempts it may have been making to + connect; cancelling merely deactivates the return future.Future's + subscription to the given implementations.Channel's connectivity. + + Args: + channel: An implementations.Channel. + + Returns: + A future.Future that matures when the given Channel has connectivity + interfaces.ChannelConnectivity.READY. + """ + ready_future = _ChannelReadyFuture(channel) + ready_future.start() + return ready_future + diff --git a/src/python/grpcio/grpc/early_adopter/__init__.py b/src/python/grpcio/grpc/early_adopter/__init__.py new file mode 100644 index 00000000..70865191 --- /dev/null +++ b/src/python/grpcio/grpc/early_adopter/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, 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. + + diff --git a/src/python/grpcio/grpc/early_adopter/implementations.py b/src/python/grpcio/grpc/early_adopter/implementations.py new file mode 100644 index 00000000..9c396aa7 --- /dev/null +++ b/src/python/grpcio/grpc/early_adopter/implementations.py @@ -0,0 +1,262 @@ +# Copyright 2015, 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. + +"""Entry points into GRPC.""" + +import threading + +from grpc._adapter import fore as _fore +from grpc._adapter import rear as _rear +from grpc.framework.alpha import _face_utilities +from grpc.framework.alpha import _reexport +from grpc.framework.alpha import interfaces +from grpc.framework.base import implementations as _base_implementations +from grpc.framework.base import util as _base_utilities +from grpc.framework.face import implementations as _face_implementations +from grpc.framework.foundation import logging_pool + +_DEFAULT_THREAD_POOL_SIZE = 8 +_ONE_DAY_IN_SECONDS = 24 * 60 * 60 + + +class _Server(interfaces.Server): + + def __init__( + self, breakdown, port, private_key, certificate_chain, + thread_pool_size=_DEFAULT_THREAD_POOL_SIZE): + self._lock = threading.Lock() + self._breakdown = breakdown + self._port = port + if private_key is None or certificate_chain is None: + self._key_chain_pairs = () + else: + self._key_chain_pairs = ((private_key, certificate_chain),) + + self._pool_size = thread_pool_size + self._pool = None + self._back = None + self._fore_link = None + + def _start(self): + with self._lock: + if self._pool is None: + self._pool = logging_pool.pool(self._pool_size) + servicer = _face_implementations.servicer( + self._pool, self._breakdown.implementations, None) + self._back = _base_implementations.back_link( + servicer, self._pool, self._pool, self._pool, _ONE_DAY_IN_SECONDS, + _ONE_DAY_IN_SECONDS) + self._fore_link = _fore.ForeLink( + self._pool, self._breakdown.request_deserializers, + self._breakdown.response_serializers, None, self._key_chain_pairs, + port=self._port) + self._back.join_fore_link(self._fore_link) + self._fore_link.join_rear_link(self._back) + self._fore_link.start() + else: + raise ValueError('Server currently running!') + + def _stop(self): + with self._lock: + if self._pool is None: + raise ValueError('Server not running!') + else: + self._fore_link.stop() + _base_utilities.wait_for_idle(self._back) + self._pool.shutdown(wait=True) + self._fore_link = None + self._back = None + self._pool = None + + def __enter__(self): + self._start() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self._stop() + return False + + def start(self): + self._start() + + def stop(self): + self._stop() + + def port(self): + with self._lock: + return self._fore_link.port() + + +class _Stub(interfaces.Stub): + + def __init__( + self, breakdown, host, port, secure, root_certificates, private_key, + certificate_chain, metadata_transformer=None, server_host_override=None, + thread_pool_size=_DEFAULT_THREAD_POOL_SIZE): + self._lock = threading.Lock() + self._breakdown = breakdown + self._host = host + self._port = port + self._secure = secure + self._root_certificates = root_certificates + self._private_key = private_key + self._certificate_chain = certificate_chain + self._metadata_transformer = metadata_transformer + self._server_host_override = server_host_override + + self._pool_size = thread_pool_size + self._pool = None + self._front = None + self._rear_link = None + self._understub = None + + def __enter__(self): + with self._lock: + if self._pool is None: + self._pool = logging_pool.pool(self._pool_size) + self._front = _base_implementations.front_link( + self._pool, self._pool, self._pool) + self._rear_link = _rear.RearLink( + self._host, self._port, self._pool, + self._breakdown.request_serializers, + self._breakdown.response_deserializers, self._secure, + self._root_certificates, self._private_key, self._certificate_chain, + metadata_transformer=self._metadata_transformer, + server_host_override=self._server_host_override) + self._front.join_rear_link(self._rear_link) + self._rear_link.join_fore_link(self._front) + self._rear_link.start() + self._understub = _face_implementations.dynamic_stub( + self._breakdown.face_cardinalities, self._front, self._pool, '') + else: + raise ValueError('Tried to __enter__ already-__enter__ed Stub!') + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + with self._lock: + if self._pool is None: + raise ValueError('Tried to __exit__ non-__enter__ed Stub!') + else: + self._rear_link.stop() + _base_utilities.wait_for_idle(self._front) + self._pool.shutdown(wait=True) + self._rear_link = None + self._front = None + self._pool = None + self._understub = None + return False + + def __getattr__(self, attr): + with self._lock: + if self._pool is None: + raise ValueError('Tried to __getattr__ non-__enter__ed Stub!') + else: + method_cardinality = self._breakdown.cardinalities.get(attr) + underlying_attr = getattr( + self._understub, self._breakdown.qualified_names.get(attr), None) + if method_cardinality is interfaces.Cardinality.UNARY_UNARY: + return _reexport.unary_unary_sync_async(underlying_attr) + elif method_cardinality is interfaces.Cardinality.UNARY_STREAM: + return lambda request, timeout: _reexport.cancellable_iterator( + underlying_attr(request, timeout)) + elif method_cardinality is interfaces.Cardinality.STREAM_UNARY: + return _reexport.stream_unary_sync_async(underlying_attr) + elif method_cardinality is interfaces.Cardinality.STREAM_STREAM: + return lambda request_iterator, timeout: ( + _reexport.cancellable_iterator(underlying_attr( + request_iterator, timeout))) + else: + raise AttributeError(attr) + + +def stub( + service_name, methods, host, port, metadata_transformer=None, secure=False, + root_certificates=None, private_key=None, certificate_chain=None, + server_host_override=None, thread_pool_size=_DEFAULT_THREAD_POOL_SIZE): + """Constructs an interfaces.Stub. + + Args: + service_name: The package-qualified full name of the service. + methods: A dictionary from RPC method name to + interfaces.RpcMethodInvocationDescription describing the RPCs to be + supported by the created stub. The RPC method names in the dictionary are + not qualified by the service name or decorated in any other way. + host: The host to which to connect for RPC service. + port: The port to which to connect for RPC service. + metadata_transformer: A callable that given a metadata object produces + another metadata object to be used in the underlying communication on the + wire. + secure: Whether or not to construct the stub with a secure connection. + root_certificates: The PEM-encoded root certificates or None to ask for + them to be retrieved from a default location. + private_key: The PEM-encoded private key to use or None if no private key + should be used. + certificate_chain: The PEM-encoded certificate chain to use or None if no + certificate chain should be used. + server_host_override: (For testing only) the target name used for SSL + host name checking. + thread_pool_size: The maximum number of threads to allow in the backing + thread pool. + + Returns: + An interfaces.Stub affording RPC invocation. + """ + breakdown = _face_utilities.break_down_invocation(service_name, methods) + return _Stub( + breakdown, host, port, secure, root_certificates, private_key, + certificate_chain, server_host_override=server_host_override, + metadata_transformer=metadata_transformer, + thread_pool_size=thread_pool_size) + + +def server( + service_name, methods, port, private_key=None, certificate_chain=None, + thread_pool_size=_DEFAULT_THREAD_POOL_SIZE): + """Constructs an interfaces.Server. + + Args: + service_name: The package-qualified full name of the service. + methods: A dictionary from RPC method name to + interfaces.RpcMethodServiceDescription describing the RPCs to + be serviced by the created server. The RPC method names in the dictionary + are not qualified by the service name or decorated in any other way. + port: The port on which to serve or zero to ask for a port to be + automatically selected. + private_key: A pem-encoded private key, or None for an insecure server. + certificate_chain: A pem-encoded certificate chain, or None for an insecure + server. + thread_pool_size: The maximum number of threads to allow in the backing + thread pool. + + Returns: + An interfaces.Server that will serve secure traffic. + """ + breakdown = _face_utilities.break_down_service(service_name, methods) + return _Server(breakdown, port, private_key, certificate_chain, + thread_pool_size=thread_pool_size) diff --git a/src/python/grpcio/grpc/framework/__init__.py b/src/python/grpcio/grpc/framework/__init__.py new file mode 100644 index 00000000..70865191 --- /dev/null +++ b/src/python/grpcio/grpc/framework/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, 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. + + diff --git a/src/python/grpcio/grpc/framework/alpha/__init__.py b/src/python/grpcio/grpc/framework/alpha/__init__.py new file mode 100644 index 00000000..b8939880 --- /dev/null +++ b/src/python/grpcio/grpc/framework/alpha/__init__.py @@ -0,0 +1,28 @@ +# Copyright 2015, 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. diff --git a/src/python/grpcio/grpc/framework/alpha/_face_utilities.py b/src/python/grpcio/grpc/framework/alpha/_face_utilities.py new file mode 100644 index 00000000..fb0cfe42 --- /dev/null +++ b/src/python/grpcio/grpc/framework/alpha/_face_utilities.py @@ -0,0 +1,183 @@ +# Copyright 2015, 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. + +import abc +import collections + +# face_interfaces is referenced from specification in this module. +from grpc.framework.common import cardinality +from grpc.framework.face import interfaces as face_interfaces # pylint: disable=unused-import +from grpc.framework.face import utilities as face_utilities +from grpc.framework.alpha import _reexport +from grpc.framework.alpha import interfaces + + +def _qualified_name(service_name, method_name): + return '/%s/%s' % (service_name, method_name) + + +# TODO(nathaniel): This structure is getting bloated; it could be shrunk if +# implementations._Stub used a generic rather than a dynamic underlying +# face-layer stub. +class InvocationBreakdown(object): + """An intermediate representation of invocation-side views of RPC methods. + + Attributes: + cardinalities: A dictionary from RPC method name to interfaces.Cardinality + value. + qualified_names: A dictionary from unqualified RPC method name to + service-qualified RPC method name. + face_cardinalities: A dictionary from service-qualified RPC method name to + to cardinality.Cardinality value. + request_serializers: A dictionary from service-qualified RPC method name to + callable behavior to be used serializing request values for the RPC. + response_deserializers: A dictionary from service-qualified RPC method name + to callable behavior to be used deserializing response values for the + RPC. + """ + __metaclass__ = abc.ABCMeta + + +class _EasyInvocationBreakdown( + InvocationBreakdown, + collections.namedtuple( + '_EasyInvocationBreakdown', + ('cardinalities', 'qualified_names', 'face_cardinalities', + 'request_serializers', 'response_deserializers'))): + pass + + +class ServiceBreakdown(object): + """An intermediate representation of service-side views of RPC methods. + + Attributes: + implementations: A dictionary from service-qualified RPC method name to + face_interfaces.MethodImplementation implementing the RPC method. + request_deserializers: A dictionary from service-qualified RPC method name + to callable behavior to be used deserializing request values for the RPC. + response_serializers: A dictionary from service-qualified RPC method name + to callable behavior to be used serializing response values for the RPC. + """ + __metaclass__ = abc.ABCMeta + + +class _EasyServiceBreakdown( + ServiceBreakdown, + collections.namedtuple( + '_EasyServiceBreakdown', + ('implementations', 'request_deserializers', 'response_serializers'))): + pass + + +def break_down_invocation(service_name, method_descriptions): + """Derives an InvocationBreakdown from several RPC method descriptions. + + Args: + service_name: The package-qualified full name of the service. + method_descriptions: A dictionary from RPC method name to + interfaces.RpcMethodInvocationDescription describing the RPCs. + + Returns: + An InvocationBreakdown corresponding to the given method descriptions. + """ + cardinalities = {} + qualified_names = {} + face_cardinalities = {} + request_serializers = {} + response_deserializers = {} + for name, method_description in method_descriptions.iteritems(): + qualified_name = _qualified_name(service_name, name) + method_cardinality = method_description.cardinality() + cardinalities[name] = method_description.cardinality() + qualified_names[name] = qualified_name + face_cardinalities[qualified_name] = _reexport.common_cardinality( + method_cardinality) + request_serializers[qualified_name] = method_description.serialize_request + response_deserializers[qualified_name] = ( + method_description.deserialize_response) + return _EasyInvocationBreakdown( + cardinalities, qualified_names, face_cardinalities, request_serializers, + response_deserializers) + + +def break_down_service(service_name, method_descriptions): + """Derives a ServiceBreakdown from several RPC method descriptions. + + Args: + method_descriptions: A dictionary from RPC method name to + interfaces.RpcMethodServiceDescription describing the RPCs. + + Returns: + A ServiceBreakdown corresponding to the given method descriptions. + """ + implementations = {} + request_deserializers = {} + response_serializers = {} + for name, method_description in method_descriptions.iteritems(): + qualified_name = _qualified_name(service_name, name) + method_cardinality = method_description.cardinality() + if method_cardinality is interfaces.Cardinality.UNARY_UNARY: + def service( + request, face_rpc_context, + service_behavior=method_description.service_unary_unary): + return service_behavior( + request, _reexport.rpc_context(face_rpc_context)) + implementations[qualified_name] = face_utilities.unary_unary_inline( + service) + elif method_cardinality is interfaces.Cardinality.UNARY_STREAM: + def service( + request, face_rpc_context, + service_behavior=method_description.service_unary_stream): + return service_behavior( + request, _reexport.rpc_context(face_rpc_context)) + implementations[qualified_name] = face_utilities.unary_stream_inline( + service) + elif method_cardinality is interfaces.Cardinality.STREAM_UNARY: + def service( + request_iterator, face_rpc_context, + service_behavior=method_description.service_stream_unary): + return service_behavior( + request_iterator, _reexport.rpc_context(face_rpc_context)) + implementations[qualified_name] = face_utilities.stream_unary_inline( + service) + elif method_cardinality is interfaces.Cardinality.STREAM_STREAM: + def service( + request_iterator, face_rpc_context, + service_behavior=method_description.service_stream_stream): + return service_behavior( + request_iterator, _reexport.rpc_context(face_rpc_context)) + implementations[qualified_name] = face_utilities.stream_stream_inline( + service) + request_deserializers[qualified_name] = ( + method_description.deserialize_request) + response_serializers[qualified_name] = ( + method_description.serialize_response) + + return _EasyServiceBreakdown( + implementations, request_deserializers, response_serializers) diff --git a/src/python/grpcio/grpc/framework/alpha/_reexport.py b/src/python/grpcio/grpc/framework/alpha/_reexport.py new file mode 100644 index 00000000..198cb95a --- /dev/null +++ b/src/python/grpcio/grpc/framework/alpha/_reexport.py @@ -0,0 +1,203 @@ +# Copyright 2015, 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. + +from grpc.framework.common import cardinality +from grpc.framework.face import exceptions as face_exceptions +from grpc.framework.face import interfaces as face_interfaces +from grpc.framework.foundation import future +from grpc.framework.alpha import exceptions +from grpc.framework.alpha import interfaces + +_EARLY_ADOPTER_CARDINALITY_TO_COMMON_CARDINALITY = { + interfaces.Cardinality.UNARY_UNARY: cardinality.Cardinality.UNARY_UNARY, + interfaces.Cardinality.UNARY_STREAM: cardinality.Cardinality.UNARY_STREAM, + interfaces.Cardinality.STREAM_UNARY: cardinality.Cardinality.STREAM_UNARY, + interfaces.Cardinality.STREAM_STREAM: cardinality.Cardinality.STREAM_STREAM, +} + +_ABORTION_REEXPORT = { + face_interfaces.Abortion.CANCELLED: interfaces.Abortion.CANCELLED, + face_interfaces.Abortion.EXPIRED: interfaces.Abortion.EXPIRED, + face_interfaces.Abortion.NETWORK_FAILURE: + interfaces.Abortion.NETWORK_FAILURE, + face_interfaces.Abortion.SERVICED_FAILURE: + interfaces.Abortion.SERVICED_FAILURE, + face_interfaces.Abortion.SERVICER_FAILURE: + interfaces.Abortion.SERVICER_FAILURE, +} + + +class _RpcError(exceptions.RpcError): + pass + + +def _reexport_error(face_rpc_error): + if isinstance(face_rpc_error, face_exceptions.CancellationError): + return exceptions.CancellationError() + elif isinstance(face_rpc_error, face_exceptions.ExpirationError): + return exceptions.ExpirationError() + else: + return _RpcError() + + +def _as_face_abortion_callback(abortion_callback): + def face_abortion_callback(face_abortion): + abortion_callback(_ABORTION_REEXPORT[face_abortion]) + return face_abortion_callback + + +class _ReexportedFuture(future.Future): + + def __init__(self, face_future): + self._face_future = face_future + + def cancel(self): + return self._face_future.cancel() + + def cancelled(self): + return self._face_future.cancelled() + + def running(self): + return self._face_future.running() + + def done(self): + return self._face_future.done() + + def result(self, timeout=None): + try: + return self._face_future.result(timeout=timeout) + except face_exceptions.RpcError as e: + raise _reexport_error(e) + + def exception(self, timeout=None): + face_error = self._face_future.exception(timeout=timeout) + return None if face_error is None else _reexport_error(face_error) + + def traceback(self, timeout=None): + return self._face_future.traceback(timeout=timeout) + + def add_done_callback(self, fn): + self._face_future.add_done_callback(lambda unused_face_future: fn(self)) + + +def _call_reexporting_errors(behavior, *args, **kwargs): + try: + return behavior(*args, **kwargs) + except face_exceptions.RpcError as e: + raise _reexport_error(e) + + +def _reexported_future(face_future): + return _ReexportedFuture(face_future) + + +class _CancellableIterator(interfaces.CancellableIterator): + + def __init__(self, face_cancellable_iterator): + self._face_cancellable_iterator = face_cancellable_iterator + + def __iter__(self): + return self + + def next(self): + return _call_reexporting_errors(self._face_cancellable_iterator.next) + + def cancel(self): + self._face_cancellable_iterator.cancel() + + +class _RpcContext(interfaces.RpcContext): + + def __init__(self, face_rpc_context): + self._face_rpc_context = face_rpc_context + + def is_active(self): + return self._face_rpc_context.is_active() + + def time_remaining(self): + return self._face_rpc_context.time_remaining() + + def add_abortion_callback(self, abortion_callback): + self._face_rpc_context.add_abortion_callback( + _as_face_abortion_callback(abortion_callback)) + + +class _UnaryUnarySyncAsync(interfaces.UnaryUnarySyncAsync): + + def __init__(self, face_unary_unary_multi_callable): + self._underlying = face_unary_unary_multi_callable + + def __call__(self, request, timeout): + return _call_reexporting_errors( + self._underlying, request, timeout) + + def async(self, request, timeout): + return _ReexportedFuture(self._underlying.future(request, timeout)) + + +class _StreamUnarySyncAsync(interfaces.StreamUnarySyncAsync): + + def __init__(self, face_stream_unary_multi_callable): + self._underlying = face_stream_unary_multi_callable + + def __call__(self, request_iterator, timeout): + return _call_reexporting_errors( + self._underlying, request_iterator, timeout) + + def async(self, request_iterator, timeout): + return _ReexportedFuture(self._underlying.future(request_iterator, timeout)) + + +def common_cardinality(early_adopter_cardinality): + return _EARLY_ADOPTER_CARDINALITY_TO_COMMON_CARDINALITY[ + early_adopter_cardinality] + + +def common_cardinalities(early_adopter_cardinalities): + common_cardinalities = {} + for name, early_adopter_cardinality in early_adopter_cardinalities.iteritems(): + common_cardinalities[name] = _EARLY_ADOPTER_CARDINALITY_TO_COMMON_CARDINALITY[ + early_adopter_cardinality] + return common_cardinalities + + +def rpc_context(face_rpc_context): + return _RpcContext(face_rpc_context) + + +def cancellable_iterator(face_cancellable_iterator): + return _CancellableIterator(face_cancellable_iterator) + + +def unary_unary_sync_async(face_unary_unary_multi_callable): + return _UnaryUnarySyncAsync(face_unary_unary_multi_callable) + + +def stream_unary_sync_async(face_stream_unary_multi_callable): + return _StreamUnarySyncAsync(face_stream_unary_multi_callable) diff --git a/src/python/grpcio/grpc/framework/alpha/exceptions.py b/src/python/grpcio/grpc/framework/alpha/exceptions.py new file mode 100644 index 00000000..5234d3b9 --- /dev/null +++ b/src/python/grpcio/grpc/framework/alpha/exceptions.py @@ -0,0 +1,48 @@ +# Copyright 2015, 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. + +"""Exceptions raised by GRPC. + +Only GRPC should instantiate and raise these exceptions. +""" + +import abc + + +class RpcError(Exception): + """Common super type for all exceptions raised by GRPC.""" + __metaclass__ = abc.ABCMeta + + +class CancellationError(RpcError): + """Indicates that an RPC has been cancelled.""" + + +class ExpirationError(RpcError): + """Indicates that an RPC has expired ("timed out").""" diff --git a/src/python/grpcio/grpc/framework/alpha/interfaces.py b/src/python/grpcio/grpc/framework/alpha/interfaces.py new file mode 100644 index 00000000..8380567c --- /dev/null +++ b/src/python/grpcio/grpc/framework/alpha/interfaces.py @@ -0,0 +1,388 @@ +# Copyright 2015, 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. + +"""Interfaces of GRPC.""" + +import abc +import enum + +# exceptions is referenced from specification in this module. +from grpc.framework.alpha import exceptions # pylint: disable=unused-import +from grpc.framework.foundation import activated +from grpc.framework.foundation import future + + +@enum.unique +class Cardinality(enum.Enum): + """Constants for the four cardinalities of RPC.""" + + UNARY_UNARY = 'request-unary/response-unary' + UNARY_STREAM = 'request-unary/response-streaming' + STREAM_UNARY = 'request-streaming/response-unary' + STREAM_STREAM = 'request-streaming/response-streaming' + + +@enum.unique +class Abortion(enum.Enum): + """Categories of RPC abortion.""" + + CANCELLED = 'cancelled' + EXPIRED = 'expired' + NETWORK_FAILURE = 'network failure' + SERVICED_FAILURE = 'serviced failure' + SERVICER_FAILURE = 'servicer failure' + + +class CancellableIterator(object): + """Implements the Iterator protocol and affords a cancel method.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def __iter__(self): + """Returns the self object in accordance with the Iterator protocol.""" + raise NotImplementedError() + + @abc.abstractmethod + def next(self): + """Returns a value or raises StopIteration per the Iterator protocol.""" + raise NotImplementedError() + + @abc.abstractmethod + def cancel(self): + """Requests cancellation of whatever computation underlies this iterator.""" + raise NotImplementedError() + + +class RpcContext(object): + """Provides RPC-related information and action.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def is_active(self): + """Describes whether the RPC is active or has terminated.""" + raise NotImplementedError() + + @abc.abstractmethod + def time_remaining(self): + """Describes the length of allowed time remaining for the RPC. + Returns: + A nonnegative float indicating the length of allowed time in seconds + remaining for the RPC to complete before it is considered to have timed + out. + """ + raise NotImplementedError() + + @abc.abstractmethod + def add_abortion_callback(self, abortion_callback): + """Registers a callback to be called if the RPC is aborted. + Args: + abortion_callback: A callable to be called and passed an Abortion value + in the event of RPC abortion. + """ + raise NotImplementedError() + + +class UnaryUnarySyncAsync(object): + """Affords invoking a unary-unary RPC synchronously or asynchronously. + Values implementing this interface are directly callable and present an + "async" method. Both calls take a request value and a numeric timeout. + Direct invocation of a value of this type invokes its associated RPC and + blocks until the RPC's response is available. Calling the "async" method + of a value of this type invokes its associated RPC and immediately returns a + future.Future bound to the asynchronous execution of the RPC. + """ + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def __call__(self, request, timeout): + """Synchronously invokes the underlying RPC. + Args: + request: The request value for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + Returns: + The response value for the RPC. + Raises: + exceptions.RpcError: Indicating that the RPC was aborted. + """ + raise NotImplementedError() + + @abc.abstractmethod + def async(self, request, timeout): + """Asynchronously invokes the underlying RPC. + Args: + request: The request value for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + Returns: + A future.Future representing the RPC. In the event of RPC completion, the + returned Future's result value will be the response value of the RPC. + In the event of RPC abortion, the returned Future's exception value + will be an exceptions.RpcError. + """ + raise NotImplementedError() + + +class StreamUnarySyncAsync(object): + """Affords invoking a stream-unary RPC synchronously or asynchronously. + Values implementing this interface are directly callable and present an + "async" method. Both calls take an iterator of request values and a numeric + timeout. Direct invocation of a value of this type invokes its associated RPC + and blocks until the RPC's response is available. Calling the "async" method + of a value of this type invokes its associated RPC and immediately returns a + future.Future bound to the asynchronous execution of the RPC. + """ + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def __call__(self, request_iterator, timeout): + """Synchronously invokes the underlying RPC. + + Args: + request_iterator: An iterator that yields request values for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + The response value for the RPC. + + Raises: + exceptions.RpcError: Indicating that the RPC was aborted. + """ + raise NotImplementedError() + + @abc.abstractmethod + def async(self, request_iterator, timeout): + """Asynchronously invokes the underlying RPC. + + Args: + request_iterator: An iterator that yields request values for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A future.Future representing the RPC. In the event of RPC completion, the + returned Future's result value will be the response value of the RPC. + In the event of RPC abortion, the returned Future's exception value + will be an exceptions.RpcError. + """ + raise NotImplementedError() + + +class RpcMethodDescription(object): + """A type for the common aspects of RPC method descriptions.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def cardinality(self): + """Identifies the cardinality of this RpcMethodDescription. + + Returns: + A Cardinality value identifying whether or not this + RpcMethodDescription is request-unary or request-streaming and + whether or not it is response-unary or response-streaming. + """ + raise NotImplementedError() + + +class RpcMethodInvocationDescription(RpcMethodDescription): + """Invocation-side description of an RPC method.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def serialize_request(self, request): + """Serializes a request value. + + Args: + request: A request value appropriate for the RPC method described by this + RpcMethodInvocationDescription. + + Returns: + The serialization of the given request value as a + bytestring. + """ + raise NotImplementedError() + + @abc.abstractmethod + def deserialize_response(self, serialized_response): + """Deserializes a response value. + + Args: + serialized_response: A bytestring that is the serialization of a response + value appropriate for the RPC method described by this + RpcMethodInvocationDescription. + + Returns: + A response value corresponding to the given bytestring. + """ + raise NotImplementedError() + + +class RpcMethodServiceDescription(RpcMethodDescription): + """Service-side description of an RPC method.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def deserialize_request(self, serialized_request): + """Deserializes a request value. + + Args: + serialized_request: A bytestring that is the serialization of a request + value appropriate for the RPC method described by this + RpcMethodServiceDescription. + + Returns: + A request value corresponding to the given bytestring. + """ + raise NotImplementedError() + + @abc.abstractmethod + def serialize_response(self, response): + """Serializes a response value. + + Args: + response: A response value appropriate for the RPC method described by + this RpcMethodServiceDescription. + + Returns: + The serialization of the given response value as a + bytestring. + """ + raise NotImplementedError() + + @abc.abstractmethod + def service_unary_unary(self, request, context): + """Carries out this RPC. + + This method may only be called if the cardinality of this + RpcMethodServiceDescription is Cardinality.UNARY_UNARY. + + Args: + request: A request value appropriate for the RPC method described by this + RpcMethodServiceDescription. + context: An RpcContext object for the RPC. + + Returns: + A response value appropriate for the RPC method described by this + RpcMethodServiceDescription. + """ + raise NotImplementedError() + + @abc.abstractmethod + def service_unary_stream(self, request, context): + """Carries out this RPC. + + This method may only be called if the cardinality of this + RpcMethodServiceDescription is Cardinality.UNARY_STREAM. + + Args: + request: A request value appropriate for the RPC method described by this + RpcMethodServiceDescription. + context: An RpcContext object for the RPC. + + Yields: + Zero or more response values appropriate for the RPC method described by + this RpcMethodServiceDescription. + """ + raise NotImplementedError() + + @abc.abstractmethod + def service_stream_unary(self, request_iterator, context): + """Carries out this RPC. + + This method may only be called if the cardinality of this + RpcMethodServiceDescription is Cardinality.STREAM_UNARY. + + Args: + request_iterator: An iterator of request values appropriate for the RPC + method described by this RpcMethodServiceDescription. + context: An RpcContext object for the RPC. + + Returns: + A response value appropriate for the RPC method described by this + RpcMethodServiceDescription. + """ + raise NotImplementedError() + + @abc.abstractmethod + def service_stream_stream(self, request_iterator, context): + """Carries out this RPC. + + This method may only be called if the cardinality of this + RpcMethodServiceDescription is Cardinality.STREAM_STREAM. + + Args: + request_iterator: An iterator of request values appropriate for the RPC + method described by this RpcMethodServiceDescription. + context: An RpcContext object for the RPC. + + Yields: + Zero or more response values appropriate for the RPC method described by + this RpcMethodServiceDescription. + """ + raise NotImplementedError() + + +class Stub(object): + """A stub with callable RPC method names for attributes. + + Instances of this type are context managers and only afford RPC invocation + when used in context. + + Instances of this type, when used in context, respond to attribute access + as follows: if the requested attribute is the name of a unary-unary RPC + method, the value of the attribute will be a UnaryUnarySyncAsync with which + to invoke the RPC method. If the requested attribute is the name of a + unary-stream RPC method, the value of the attribute will be a callable taking + a request object and a timeout parameter and returning a CancellableIterator + that yields the response values of the RPC. If the requested attribute is the + name of a stream-unary RPC method, the value of the attribute will be a + StreamUnarySyncAsync with which to invoke the RPC method. If the requested + attribute is the name of a stream-stream RPC method, the value of the + attribute will be a callable taking an iterator of request objects and a + timeout and returning a CancellableIterator that yields the response values + of the RPC. + + In all cases indication of abortion is indicated by raising of + exceptions.RpcError, exceptions.CancellationError, + and exceptions.ExpirationError. + """ + __metaclass__ = abc.ABCMeta + + +class Server(activated.Activated): + """A GRPC Server.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def port(self): + """Reports the port on which the server is serving. + + This method may only be called while the server is activated. + + Returns: + The port on which the server is serving. + """ + raise NotImplementedError() diff --git a/src/python/grpcio/grpc/framework/alpha/utilities.py b/src/python/grpcio/grpc/framework/alpha/utilities.py new file mode 100644 index 00000000..7d7f78f5 --- /dev/null +++ b/src/python/grpcio/grpc/framework/alpha/utilities.py @@ -0,0 +1,269 @@ +# Copyright 2015, 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. + +"""Utilities for use with GRPC.""" + +from grpc.framework.alpha import interfaces + + +class _RpcMethodDescription( + interfaces.RpcMethodInvocationDescription, + interfaces.RpcMethodServiceDescription): + + def __init__( + self, cardinality, unary_unary, unary_stream, stream_unary, + stream_stream, request_serializer, request_deserializer, + response_serializer, response_deserializer): + self._cardinality = cardinality + self._unary_unary = unary_unary + self._unary_stream = unary_stream + self._stream_unary = stream_unary + self._stream_stream = stream_stream + self._request_serializer = request_serializer + self._request_deserializer = request_deserializer + self._response_serializer = response_serializer + self._response_deserializer = response_deserializer + + def cardinality(self): + """See interfaces.RpcMethodDescription.cardinality for specification.""" + return self._cardinality + + def serialize_request(self, request): + """See interfaces.RpcMethodInvocationDescription.serialize_request.""" + return self._request_serializer(request) + + def deserialize_request(self, serialized_request): + """See interfaces.RpcMethodServiceDescription.deserialize_request.""" + return self._request_deserializer(serialized_request) + + def serialize_response(self, response): + """See interfaces.RpcMethodServiceDescription.serialize_response.""" + return self._response_serializer(response) + + def deserialize_response(self, serialized_response): + """See interfaces.RpcMethodInvocationDescription.deserialize_response.""" + return self._response_deserializer(serialized_response) + + def service_unary_unary(self, request, context): + """See interfaces.RpcMethodServiceDescription.service_unary_unary.""" + return self._unary_unary(request, context) + + def service_unary_stream(self, request, context): + """See interfaces.RpcMethodServiceDescription.service_unary_stream.""" + return self._unary_stream(request, context) + + def service_stream_unary(self, request_iterator, context): + """See interfaces.RpcMethodServiceDescription.service_stream_unary.""" + return self._stream_unary(request_iterator, context) + + def service_stream_stream(self, request_iterator, context): + """See interfaces.RpcMethodServiceDescription.service_stream_stream.""" + return self._stream_stream(request_iterator, context) + + +def unary_unary_invocation_description( + request_serializer, response_deserializer): + """Creates an interfaces.RpcMethodInvocationDescription for an RPC method. + + Args: + request_serializer: A callable that when called on a request + value returns a bytestring corresponding to that value. + response_deserializer: A callable that when called on a + bytestring returns the response value corresponding to + that bytestring. + + Returns: + An interfaces.RpcMethodInvocationDescription constructed from the given + arguments representing a unary-request/unary-response RPC method. + """ + return _RpcMethodDescription( + interfaces.Cardinality.UNARY_UNARY, None, None, None, None, + request_serializer, None, None, response_deserializer) + + +def unary_stream_invocation_description( + request_serializer, response_deserializer): + """Creates an interfaces.RpcMethodInvocationDescription for an RPC method. + + Args: + request_serializer: A callable that when called on a request + value returns a bytestring corresponding to that value. + response_deserializer: A callable that when called on a + bytestring returns the response value corresponding to + that bytestring. + + Returns: + An interfaces.RpcMethodInvocationDescription constructed from the given + arguments representing a unary-request/streaming-response RPC method. + """ + return _RpcMethodDescription( + interfaces.Cardinality.UNARY_STREAM, None, None, None, None, + request_serializer, None, None, response_deserializer) + + +def stream_unary_invocation_description( + request_serializer, response_deserializer): + """Creates an interfaces.RpcMethodInvocationDescription for an RPC method. + + Args: + request_serializer: A callable that when called on a request + value returns a bytestring corresponding to that value. + response_deserializer: A callable that when called on a + bytestring returns the response value corresponding to + that bytestring. + + Returns: + An interfaces.RpcMethodInvocationDescription constructed from the given + arguments representing a streaming-request/unary-response RPC method. + """ + return _RpcMethodDescription( + interfaces.Cardinality.STREAM_UNARY, None, None, None, None, + request_serializer, None, None, response_deserializer) + + +def stream_stream_invocation_description( + request_serializer, response_deserializer): + """Creates an interfaces.RpcMethodInvocationDescription for an RPC method. + + Args: + request_serializer: A callable that when called on a request + value returns a bytestring corresponding to that value. + response_deserializer: A callable that when called on a + bytestring returns the response value corresponding to + that bytestring. + + Returns: + An interfaces.RpcMethodInvocationDescription constructed from the given + arguments representing a streaming-request/streaming-response RPC + method. + """ + return _RpcMethodDescription( + interfaces.Cardinality.STREAM_STREAM, None, None, None, None, + request_serializer, None, None, response_deserializer) + + +def unary_unary_service_description( + behavior, request_deserializer, response_serializer): + """Creates an interfaces.RpcMethodServiceDescription for the given behavior. + + Args: + behavior: A callable that implements a unary-unary RPC + method that accepts a single request and an interfaces.RpcContext and + returns a single response. + request_deserializer: A callable that when called on a + bytestring returns the request value corresponding to that + bytestring. + response_serializer: A callable that when called on a + response value returns the bytestring corresponding to + that value. + + Returns: + An interfaces.RpcMethodServiceDescription constructed from the given + arguments representing a unary-request/unary-response RPC + method. + """ + return _RpcMethodDescription( + interfaces.Cardinality.UNARY_UNARY, behavior, None, None, None, + None, request_deserializer, response_serializer, None) + + +def unary_stream_service_description( + behavior, request_deserializer, response_serializer): + """Creates an interfaces.RpcMethodServiceDescription for the given behavior. + + Args: + behavior: A callable that implements a unary-stream RPC + method that accepts a single request and an interfaces.RpcContext + and returns an iterator of zero or more responses. + request_deserializer: A callable that when called on a + bytestring returns the request value corresponding to that + bytestring. + response_serializer: A callable that when called on a + response value returns the bytestring corresponding to + that value. + + Returns: + An interfaces.RpcMethodServiceDescription constructed from the given + arguments representing a unary-request/streaming-response + RPC method. + """ + return _RpcMethodDescription( + interfaces.Cardinality.UNARY_STREAM, None, behavior, None, None, + None, request_deserializer, response_serializer, None) + + +def stream_unary_service_description( + behavior, request_deserializer, response_serializer): + """Creates an interfaces.RpcMethodServiceDescription for the given behavior. + + Args: + behavior: A callable that implements a stream-unary RPC + method that accepts an iterator of zero or more requests + and an interfaces.RpcContext and returns a single response. + request_deserializer: A callable that when called on a + bytestring returns the request value corresponding to that + bytestring. + response_serializer: A callable that when called on a + response value returns the bytestring corresponding to + that value. + + Returns: + An interfaces.RpcMethodServiceDescription constructed from the given + arguments representing a streaming-request/unary-response + RPC method. + """ + return _RpcMethodDescription( + interfaces.Cardinality.STREAM_UNARY, None, None, behavior, None, + None, request_deserializer, response_serializer, None) + + +def stream_stream_service_description( + behavior, request_deserializer, response_serializer): + """Creates an interfaces.RpcMethodServiceDescription for the given behavior. + + Args: + behavior: A callable that implements a stream-stream RPC + method that accepts an iterator of zero or more requests + and an interfaces.RpcContext and returns an iterator of + zero or more responses. + request_deserializer: A callable that when called on a + bytestring returns the request value corresponding to that + bytestring. + response_serializer: A callable that when called on a + response value returns the bytestring corresponding to + that value. + + Returns: + An interfaces.RpcMethodServiceDescription constructed from the given + arguments representing a + streaming-request/streaming-response RPC method. + """ + return _RpcMethodDescription( + interfaces.Cardinality.STREAM_STREAM, None, None, None, behavior, + None, request_deserializer, response_serializer, None) diff --git a/src/python/grpcio/grpc/framework/base/__init__.py b/src/python/grpcio/grpc/framework/base/__init__.py new file mode 100644 index 00000000..70865191 --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, 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. + + diff --git a/src/python/grpcio/grpc/framework/base/_cancellation.py b/src/python/grpcio/grpc/framework/base/_cancellation.py new file mode 100644 index 00000000..ffbc9066 --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/_cancellation.py @@ -0,0 +1,64 @@ +# Copyright 2015, 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. + +"""State and behavior for operation cancellation.""" + +from grpc.framework.base import _interfaces +from grpc.framework.base import interfaces + + +class CancellationManager(_interfaces.CancellationManager): + """An implementation of _interfaces.CancellationManager.""" + + def __init__( + self, lock, termination_manager, transmission_manager, ingestion_manager, + expiration_manager): + """Constructor. + + Args: + lock: The operation-wide lock. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + ingestion_manager: The _interfaces.IngestionManager for the operation. + expiration_manager: The _interfaces.ExpirationManager for the operation. + """ + self._lock = lock + self._termination_manager = termination_manager + self._transmission_manager = transmission_manager + self._ingestion_manager = ingestion_manager + self._expiration_manager = expiration_manager + + def cancel(self): + """See _interfaces.CancellationManager.cancel for specification.""" + with self._lock: + self._termination_manager.abort(interfaces.Outcome.CANCELLED) + self._transmission_manager.abort(interfaces.Outcome.CANCELLED) + self._ingestion_manager.abort() + self._expiration_manager.abort() diff --git a/src/python/grpcio/grpc/framework/base/_constants.py b/src/python/grpcio/grpc/framework/base/_constants.py new file mode 100644 index 00000000..8fbdc827 --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/_constants.py @@ -0,0 +1,32 @@ +# Copyright 2015, 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. + +"""Private constants for the package.""" + +INTERNAL_ERROR_LOG_MESSAGE = ':-( RPC Framework (Base) internal error! :-(' diff --git a/src/python/grpcio/grpc/framework/base/_context.py b/src/python/grpcio/grpc/framework/base/_context.py new file mode 100644 index 00000000..d84871d6 --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/_context.py @@ -0,0 +1,99 @@ +# Copyright 2015, 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. + +"""State and behavior for operation context.""" + +import time + +# _interfaces is referenced from specification in this module. +from grpc.framework.base import interfaces +from grpc.framework.base import _interfaces # pylint: disable=unused-import + + +class OperationContext(interfaces.OperationContext): + """An implementation of interfaces.OperationContext.""" + + def __init__( + self, lock, operation_id, local_failure, termination_manager, + transmission_manager): + """Constructor. + + Args: + lock: The operation-wide lock. + operation_id: An object identifying the operation. + local_failure: Whichever one of interfaces.Outcome.SERVICED_FAILURE or + interfaces.Outcome.SERVICER_FAILURE describes local failure of + customer code. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + """ + self._lock = lock + self._local_failure = local_failure + self._termination_manager = termination_manager + self._transmission_manager = transmission_manager + self._ingestion_manager = None + self._expiration_manager = None + + self.operation_id = operation_id + + def set_ingestion_and_expiration_managers( + self, ingestion_manager, expiration_manager): + """Sets managers with which this OperationContext cooperates. + + Args: + ingestion_manager: The _interfaces.IngestionManager for the operation. + expiration_manager: The _interfaces.ExpirationManager for the operation. + """ + self._ingestion_manager = ingestion_manager + self._expiration_manager = expiration_manager + + def is_active(self): + """See interfaces.OperationContext.is_active for specification.""" + with self._lock: + return self._termination_manager.is_active() + + def add_termination_callback(self, callback): + """See interfaces.OperationContext.add_termination_callback.""" + with self._lock: + self._termination_manager.add_callback(callback) + + def time_remaining(self): + """See interfaces.OperationContext.time_remaining for specification.""" + with self._lock: + deadline = self._expiration_manager.deadline() + return max(0.0, deadline - time.time()) + + def fail(self, exception): + """See interfaces.OperationContext.fail for specification.""" + with self._lock: + self._termination_manager.abort(self._local_failure) + self._transmission_manager.abort(self._local_failure) + self._ingestion_manager.abort() + self._expiration_manager.abort() diff --git a/src/python/grpcio/grpc/framework/base/_emission.py b/src/python/grpcio/grpc/framework/base/_emission.py new file mode 100644 index 00000000..1829669a --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/_emission.py @@ -0,0 +1,125 @@ +# Copyright 2015, 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. + +"""State and behavior for handling emitted values.""" + +from grpc.framework.base import interfaces +from grpc.framework.base import _interfaces + + +class _EmissionManager(_interfaces.EmissionManager): + """An implementation of _interfaces.EmissionManager.""" + + def __init__( + self, lock, failure_outcome, termination_manager, transmission_manager): + """Constructor. + + Args: + lock: The operation-wide lock. + failure_outcome: Whichever one of interfaces.Outcome.SERVICED_FAILURE or + interfaces.Outcome.SERVICER_FAILURE describes this object's methods + being called inappropriately by customer code. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + """ + self._lock = lock + self._failure_outcome = failure_outcome + self._termination_manager = termination_manager + self._transmission_manager = transmission_manager + self._ingestion_manager = None + self._expiration_manager = None + + self._emission_complete = False + + def set_ingestion_manager_and_expiration_manager( + self, ingestion_manager, expiration_manager): + self._ingestion_manager = ingestion_manager + self._expiration_manager = expiration_manager + + def _abort(self): + self._termination_manager.abort(self._failure_outcome) + self._transmission_manager.abort(self._failure_outcome) + self._ingestion_manager.abort() + self._expiration_manager.abort() + + def consume(self, value): + with self._lock: + if self._emission_complete: + self._abort() + else: + self._transmission_manager.inmit(value, False) + + def terminate(self): + with self._lock: + if not self._emission_complete: + self._termination_manager.emission_complete() + self._transmission_manager.inmit(None, True) + self._emission_complete = True + + def consume_and_terminate(self, value): + with self._lock: + if self._emission_complete: + self._abort() + else: + self._termination_manager.emission_complete() + self._transmission_manager.inmit(value, True) + self._emission_complete = True + + +def front_emission_manager(lock, termination_manager, transmission_manager): + """Creates an _interfaces.EmissionManager appropriate for front-side use. + + Args: + lock: The operation-wide lock. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the operation. + + Returns: + An _interfaces.EmissionManager appropriate for front-side use. + """ + return _EmissionManager( + lock, interfaces.Outcome.SERVICED_FAILURE, termination_manager, + transmission_manager) + + +def back_emission_manager(lock, termination_manager, transmission_manager): + """Creates an _interfaces.EmissionManager appropriate for back-side use. + + Args: + lock: The operation-wide lock. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the operation. + + Returns: + An _interfaces.EmissionManager appropriate for back-side use. + """ + return _EmissionManager( + lock, interfaces.Outcome.SERVICER_FAILURE, termination_manager, + transmission_manager) diff --git a/src/python/grpcio/grpc/framework/base/_ends.py b/src/python/grpcio/grpc/framework/base/_ends.py new file mode 100644 index 00000000..176f3ac0 --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/_ends.py @@ -0,0 +1,399 @@ +# Copyright 2015, 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. + +"""Implementations of FrontLinks and BackLinks.""" + +import collections +import threading +import uuid + +# _interfaces is referenced from specification in this module. +from grpc.framework.base import _cancellation +from grpc.framework.base import _context +from grpc.framework.base import _emission +from grpc.framework.base import _expiration +from grpc.framework.base import _ingestion +from grpc.framework.base import _interfaces # pylint: disable=unused-import +from grpc.framework.base import _reception +from grpc.framework.base import _termination +from grpc.framework.base import _transmission +from grpc.framework.base import interfaces +from grpc.framework.foundation import callable_util + +_IDLE_ACTION_EXCEPTION_LOG_MESSAGE = 'Exception calling idle action!' + + +class _EasyOperation(interfaces.Operation): + """A trivial implementation of interfaces.Operation.""" + + def __init__(self, emission_manager, context, cancellation_manager): + """Constructor. + + Args: + emission_manager: The _interfaces.EmissionManager for the operation that + will accept values emitted by customer code. + context: The interfaces.OperationContext for use by the customer + during the operation. + cancellation_manager: The _interfaces.CancellationManager for the + operation. + """ + self.consumer = emission_manager + self.context = context + self._cancellation_manager = cancellation_manager + + def cancel(self): + self._cancellation_manager.cancel() + + +class _Endlette(object): + """Utility for stateful behavior common to Fronts and Backs.""" + + def __init__(self, pool): + """Constructor. + + Args: + pool: A thread pool to use when calling registered idle actions. + """ + self._lock = threading.Lock() + self._pool = pool + # Dictionary from operation IDs to ReceptionManager-or-None. A None value + # indicates an in-progress fire-and-forget operation for which the customer + # has chosen to ignore results. + self._operations = {} + self._stats = {outcome: 0 for outcome in interfaces.Outcome} + self._idle_actions = [] + + def terminal_action(self, operation_id): + """Constructs the termination action for a single operation. + + Args: + operation_id: An operation ID. + + Returns: + A callable that takes an operation outcome for an argument to be used as + the termination action for the operation associated with the given + operation ID. + """ + def termination_action(outcome): + with self._lock: + self._stats[outcome] += 1 + self._operations.pop(operation_id, None) + if not self._operations: + for action in self._idle_actions: + self._pool.submit(callable_util.with_exceptions_logged( + action, _IDLE_ACTION_EXCEPTION_LOG_MESSAGE)) + self._idle_actions = [] + return termination_action + + def __enter__(self): + self._lock.acquire() + + def __exit__(self, exc_type, exc_val, exc_tb): + self._lock.release() + + def get_operation(self, operation_id): + return self._operations.get(operation_id, None) + + def add_operation(self, operation_id, operation_reception_manager): + self._operations[operation_id] = operation_reception_manager + + def operation_stats(self): + with self._lock: + return dict(self._stats) + + def add_idle_action(self, action): + with self._lock: + if self._operations: + self._idle_actions.append(action) + else: + self._pool.submit(callable_util.with_exceptions_logged( + action, _IDLE_ACTION_EXCEPTION_LOG_MESSAGE)) + + +class _FrontManagement( + collections.namedtuple( + '_FrontManagement', + ('reception', 'emission', 'operation', 'cancellation'))): + """Just a trivial helper class to bundle four fellow-traveling objects.""" + + +def _front_operate( + callback, work_pool, transmission_pool, utility_pool, + termination_action, operation_id, name, payload, complete, timeout, + subscription, trace_id): + """Constructs objects necessary for front-side operation management. + + Args: + callback: A callable that accepts interfaces.FrontToBackTickets and + delivers them to the other side of the operation. Execution of this + callable may take any arbitrary length of time. + work_pool: A thread pool in which to execute customer code. + transmission_pool: A thread pool to use for transmitting to the other side + of the operation. + utility_pool: A thread pool for utility tasks. + termination_action: A no-arg behavior to be called upon operation + completion. + operation_id: An object identifying the operation. + name: The name of the method being called during the operation. + payload: The first customer-significant value to be transmitted to the other + side. May be None if there is no such value or if the customer chose not + to pass it at operation invocation. + complete: A boolean indicating whether or not additional payloads will be + supplied by the customer. + timeout: A length of time in seconds to allow for the operation. + subscription: A interfaces.ServicedSubscription describing the + customer's interest in the results of the operation. + trace_id: A uuid.UUID identifying a set of related operations to which this + operation belongs. May be None. + + Returns: + A _FrontManagement object bundling together the + _interfaces.ReceptionManager, _interfaces.EmissionManager, + _context.OperationContext, and _interfaces.CancellationManager for the + operation. + """ + lock = threading.Lock() + with lock: + termination_manager = _termination.front_termination_manager( + work_pool, utility_pool, termination_action, subscription.kind) + transmission_manager = _transmission.front_transmission_manager( + lock, transmission_pool, callback, operation_id, name, + subscription.kind, trace_id, timeout, termination_manager) + operation_context = _context.OperationContext( + lock, operation_id, interfaces.Outcome.SERVICED_FAILURE, + termination_manager, transmission_manager) + emission_manager = _emission.front_emission_manager( + lock, termination_manager, transmission_manager) + ingestion_manager = _ingestion.front_ingestion_manager( + lock, work_pool, subscription, termination_manager, + transmission_manager, operation_context) + expiration_manager = _expiration.front_expiration_manager( + lock, termination_manager, transmission_manager, ingestion_manager, + timeout) + reception_manager = _reception.front_reception_manager( + lock, termination_manager, transmission_manager, ingestion_manager, + expiration_manager) + cancellation_manager = _cancellation.CancellationManager( + lock, termination_manager, transmission_manager, ingestion_manager, + expiration_manager) + + termination_manager.set_expiration_manager(expiration_manager) + transmission_manager.set_ingestion_and_expiration_managers( + ingestion_manager, expiration_manager) + operation_context.set_ingestion_and_expiration_managers( + ingestion_manager, expiration_manager) + emission_manager.set_ingestion_manager_and_expiration_manager( + ingestion_manager, expiration_manager) + ingestion_manager.set_expiration_manager(expiration_manager) + + transmission_manager.inmit(payload, complete) + + if subscription.kind is interfaces.ServicedSubscription.Kind.NONE: + returned_reception_manager = None + else: + returned_reception_manager = reception_manager + + return _FrontManagement( + returned_reception_manager, emission_manager, operation_context, + cancellation_manager) + + +class FrontLink(interfaces.FrontLink): + """An implementation of interfaces.FrontLink.""" + + def __init__(self, work_pool, transmission_pool, utility_pool): + """Constructor. + + Args: + work_pool: A thread pool to be used for executing customer code. + transmission_pool: A thread pool to be used for transmitting values to + the other side of the operation. + utility_pool: A thread pool to be used for utility tasks. + """ + self._endlette = _Endlette(utility_pool) + self._work_pool = work_pool + self._transmission_pool = transmission_pool + self._utility_pool = utility_pool + self._callback = None + + self._operations = {} + + def join_rear_link(self, rear_link): + """See interfaces.ForeLink.join_rear_link for specification.""" + with self._endlette: + self._callback = rear_link.accept_front_to_back_ticket + + def operation_stats(self): + """See interfaces.End.operation_stats for specification.""" + return self._endlette.operation_stats() + + def add_idle_action(self, action): + """See interfaces.End.add_idle_action for specification.""" + self._endlette.add_idle_action(action) + + def operate( + self, name, payload, complete, timeout, subscription, trace_id): + """See interfaces.Front.operate for specification.""" + operation_id = uuid.uuid4() + with self._endlette: + management = _front_operate( + self._callback, self._work_pool, self._transmission_pool, + self._utility_pool, self._endlette.terminal_action(operation_id), + operation_id, name, payload, complete, timeout, subscription, + trace_id) + self._endlette.add_operation(operation_id, management.reception) + return _EasyOperation( + management.emission, management.operation, management.cancellation) + + def accept_back_to_front_ticket(self, ticket): + """See interfaces.End.act for specification.""" + with self._endlette: + reception_manager = self._endlette.get_operation(ticket.operation_id) + if reception_manager: + reception_manager.receive_ticket(ticket) + + +def _back_operate( + servicer, callback, work_pool, transmission_pool, utility_pool, + termination_action, ticket, default_timeout, maximum_timeout): + """Constructs objects necessary for back-side operation management. + + Also begins back-side operation by feeding the first received ticket into the + constructed _interfaces.ReceptionManager. + + Args: + servicer: An interfaces.Servicer for servicing operations. + callback: A callable that accepts interfaces.BackToFrontTickets and + delivers them to the other side of the operation. Execution of this + callable may take any arbitrary length of time. + work_pool: A thread pool in which to execute customer code. + transmission_pool: A thread pool to use for transmitting to the other side + of the operation. + utility_pool: A thread pool for utility tasks. + termination_action: A no-arg behavior to be called upon operation + completion. + ticket: The first interfaces.FrontToBackTicket received for the operation. + default_timeout: A length of time in seconds to be used as the default + time alloted for a single operation. + maximum_timeout: A length of time in seconds to be used as the maximum + time alloted for a single operation. + + Returns: + The _interfaces.ReceptionManager to be used for the operation. + """ + lock = threading.Lock() + with lock: + termination_manager = _termination.back_termination_manager( + work_pool, utility_pool, termination_action, ticket.subscription) + transmission_manager = _transmission.back_transmission_manager( + lock, transmission_pool, callback, ticket.operation_id, + termination_manager, ticket.subscription) + operation_context = _context.OperationContext( + lock, ticket.operation_id, interfaces.Outcome.SERVICER_FAILURE, + termination_manager, transmission_manager) + emission_manager = _emission.back_emission_manager( + lock, termination_manager, transmission_manager) + ingestion_manager = _ingestion.back_ingestion_manager( + lock, work_pool, servicer, termination_manager, + transmission_manager, operation_context, emission_manager) + expiration_manager = _expiration.back_expiration_manager( + lock, termination_manager, transmission_manager, ingestion_manager, + ticket.timeout, default_timeout, maximum_timeout) + reception_manager = _reception.back_reception_manager( + lock, termination_manager, transmission_manager, ingestion_manager, + expiration_manager) + + termination_manager.set_expiration_manager(expiration_manager) + transmission_manager.set_ingestion_and_expiration_managers( + ingestion_manager, expiration_manager) + operation_context.set_ingestion_and_expiration_managers( + ingestion_manager, expiration_manager) + emission_manager.set_ingestion_manager_and_expiration_manager( + ingestion_manager, expiration_manager) + ingestion_manager.set_expiration_manager(expiration_manager) + + reception_manager.receive_ticket(ticket) + + return reception_manager + + +class BackLink(interfaces.BackLink): + """An implementation of interfaces.BackLink.""" + + def __init__( + self, servicer, work_pool, transmission_pool, utility_pool, + default_timeout, maximum_timeout): + """Constructor. + + Args: + servicer: An interfaces.Servicer for servicing operations. + work_pool: A thread pool in which to execute customer code. + transmission_pool: A thread pool to use for transmitting to the other side + of the operation. + utility_pool: A thread pool for utility tasks. + default_timeout: A length of time in seconds to be used as the default + time alloted for a single operation. + maximum_timeout: A length of time in seconds to be used as the maximum + time alloted for a single operation. + """ + self._endlette = _Endlette(utility_pool) + self._servicer = servicer + self._work_pool = work_pool + self._transmission_pool = transmission_pool + self._utility_pool = utility_pool + self._default_timeout = default_timeout + self._maximum_timeout = maximum_timeout + self._callback = None + + def join_fore_link(self, fore_link): + """See interfaces.RearLink.join_fore_link for specification.""" + with self._endlette: + self._callback = fore_link.accept_back_to_front_ticket + + def accept_front_to_back_ticket(self, ticket): + """See interfaces.RearLink.accept_front_to_back_ticket for specification.""" + with self._endlette: + reception_manager = self._endlette.get_operation(ticket.operation_id) + if reception_manager is None: + reception_manager = _back_operate( + self._servicer, self._callback, self._work_pool, + self._transmission_pool, self._utility_pool, + self._endlette.terminal_action(ticket.operation_id), ticket, + self._default_timeout, self._maximum_timeout) + self._endlette.add_operation(ticket.operation_id, reception_manager) + else: + reception_manager.receive_ticket(ticket) + + def operation_stats(self): + """See interfaces.End.operation_stats for specification.""" + return self._endlette.operation_stats() + + def add_idle_action(self, action): + """See interfaces.End.add_idle_action for specification.""" + self._endlette.add_idle_action(action) diff --git a/src/python/grpcio/grpc/framework/base/_expiration.py b/src/python/grpcio/grpc/framework/base/_expiration.py new file mode 100644 index 00000000..17acbef4 --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/_expiration.py @@ -0,0 +1,158 @@ +# Copyright 2015, 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. + +"""State and behavior for operation expiration.""" + +import time + +from grpc.framework.base import _interfaces +from grpc.framework.base import interfaces +from grpc.framework.foundation import later + + +class _ExpirationManager(_interfaces.ExpirationManager): + """An implementation of _interfaces.ExpirationManager.""" + + def __init__( + self, lock, termination_manager, transmission_manager, ingestion_manager, + commencement, timeout, maximum_timeout): + """Constructor. + + Args: + lock: The operation-wide lock. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + ingestion_manager: The _interfaces.IngestionManager for the operation. + commencement: The time in seconds since the epoch at which the operation + began. + timeout: A length of time in seconds to allow for the operation to run. + maximum_timeout: The maximum length of time in seconds to allow for the + operation to run despite what is requested via this object's + change_timout method. + """ + self._lock = lock + self._termination_manager = termination_manager + self._transmission_manager = transmission_manager + self._ingestion_manager = ingestion_manager + self._commencement = commencement + self._maximum_timeout = maximum_timeout + + self._timeout = timeout + self._deadline = commencement + timeout + self._index = None + self._future = None + + def _expire(self, index): + with self._lock: + if self._future is not None and index == self._index: + self._future = None + self._termination_manager.abort(interfaces.Outcome.EXPIRED) + self._transmission_manager.abort(interfaces.Outcome.EXPIRED) + self._ingestion_manager.abort() + + def start(self): + self._index = 0 + self._future = later.later(self._timeout, lambda: self._expire(0)) + + def change_timeout(self, timeout): + if self._future is not None and timeout != self._timeout: + self._future.cancel() + new_timeout = min(timeout, self._maximum_timeout) + new_index = self._index + 1 + self._timeout = new_timeout + self._deadline = self._commencement + new_timeout + self._index = new_index + delay = self._deadline - time.time() + self._future = later.later( + delay, lambda: self._expire(new_index)) + + def deadline(self): + return self._deadline + + def abort(self): + if self._future: + self._future.cancel() + self._future = None + self._deadline_index = None + + +def front_expiration_manager( + lock, termination_manager, transmission_manager, ingestion_manager, + timeout): + """Creates an _interfaces.ExpirationManager appropriate for front-side use. + + Args: + lock: The operation-wide lock. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + ingestion_manager: The _interfaces.IngestionManager for the operation. + timeout: A length of time in seconds to allow for the operation to run. + + Returns: + An _interfaces.ExpirationManager appropriate for front-side use. + """ + commencement = time.time() + expiration_manager = _ExpirationManager( + lock, termination_manager, transmission_manager, ingestion_manager, + commencement, timeout, timeout) + expiration_manager.start() + return expiration_manager + + +def back_expiration_manager( + lock, termination_manager, transmission_manager, ingestion_manager, + timeout, default_timeout, maximum_timeout): + """Creates an _interfaces.ExpirationManager appropriate for back-side use. + + Args: + lock: The operation-wide lock. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + ingestion_manager: The _interfaces.IngestionManager for the operation. + timeout: A length of time in seconds to allow for the operation to run. May + be None in which case default_timeout will be used. + default_timeout: The default length of time in seconds to allow for the + operation to run if the front-side customer has not specified such a value + (or if the value they specified is not yet known). + maximum_timeout: The maximum length of time in seconds to allow for the + operation to run. + + Returns: + An _interfaces.ExpirationManager appropriate for back-side use. + """ + commencement = time.time() + expiration_manager = _ExpirationManager( + lock, termination_manager, transmission_manager, ingestion_manager, + commencement, default_timeout if timeout is None else timeout, + maximum_timeout) + expiration_manager.start() + return expiration_manager diff --git a/src/python/grpcio/grpc/framework/base/_ingestion.py b/src/python/grpcio/grpc/framework/base/_ingestion.py new file mode 100644 index 00000000..06d5b92f --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/_ingestion.py @@ -0,0 +1,442 @@ +# Copyright 2015, 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. + +"""State and behavior for ingestion during an operation.""" + +import abc +import collections + +from grpc.framework.base import _constants +from grpc.framework.base import _interfaces +from grpc.framework.base import exceptions +from grpc.framework.base import interfaces +from grpc.framework.foundation import abandonment +from grpc.framework.foundation import callable_util +from grpc.framework.foundation import stream + +_CREATE_CONSUMER_EXCEPTION_LOG_MESSAGE = 'Exception initializing ingestion!' +_CONSUME_EXCEPTION_LOG_MESSAGE = 'Exception during ingestion!' + + +class _ConsumerCreation(collections.namedtuple( + '_ConsumerCreation', ('consumer', 'remote_error', 'abandoned'))): + """A sum type for the outcome of ingestion initialization. + + Either consumer will be non-None, remote_error will be True, or abandoned will + be True. + + Attributes: + consumer: A stream.Consumer for ingesting payloads. + remote_error: A boolean indicating that the consumer could not be created + due to an error on the remote side of the operation. + abandoned: A boolean indicating that the consumer creation was abandoned. + """ + + +class _EmptyConsumer(stream.Consumer): + """A no-operative stream.Consumer that ignores all inputs and calls.""" + + def consume(self, value): + """See stream.Consumer.consume for specification.""" + + def terminate(self): + """See stream.Consumer.terminate for specification.""" + + def consume_and_terminate(self, value): + """See stream.Consumer.consume_and_terminate for specification.""" + + +class _ConsumerCreator(object): + """Common specification of different consumer-creating behavior.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def create_consumer(self, requirement): + """Creates the stream.Consumer to which customer payloads will be delivered. + + Any exceptions raised by this method should be attributed to and treated as + defects in the serviced or servicer code called by this method. + + Args: + requirement: A value required by this _ConsumerCreator for consumer + creation. + + Returns: + A _ConsumerCreation describing the result of consumer creation. + """ + raise NotImplementedError() + + +class _FrontConsumerCreator(_ConsumerCreator): + """A _ConsumerCreator appropriate for front-side use.""" + + def __init__(self, subscription, operation_context): + """Constructor. + + Args: + subscription: The serviced's interfaces.ServicedSubscription for the + operation. + operation_context: The interfaces.OperationContext object for the + operation. + """ + self._subscription = subscription + self._operation_context = operation_context + + def create_consumer(self, requirement): + """See _ConsumerCreator.create_consumer for specification.""" + if self._subscription.kind is interfaces.ServicedSubscription.Kind.FULL: + try: + return _ConsumerCreation( + self._subscription.ingestor.consumer(self._operation_context), + False, False) + except abandonment.Abandoned: + return _ConsumerCreation(None, False, True) + else: + return _ConsumerCreation(_EmptyConsumer(), False, False) + + +class _BackConsumerCreator(_ConsumerCreator): + """A _ConsumerCreator appropriate for back-side use.""" + + def __init__(self, servicer, operation_context, emission_consumer): + """Constructor. + + Args: + servicer: The interfaces.Servicer that will service the operation. + operation_context: The interfaces.OperationContext object for the + operation. + emission_consumer: The stream.Consumer object to which payloads emitted + from the operation will be passed. + """ + self._servicer = servicer + self._operation_context = operation_context + self._emission_consumer = emission_consumer + + def create_consumer(self, requirement): + """See _ConsumerCreator.create_consumer for full specification. + + Args: + requirement: The name of the Servicer method to be called during this + operation. + + Returns: + A _ConsumerCreation describing the result of consumer creation. + """ + try: + return _ConsumerCreation( + self._servicer.service( + requirement, self._operation_context, self._emission_consumer), + False, False) + except exceptions.NoSuchMethodError: + return _ConsumerCreation(None, True, False) + except abandonment.Abandoned: + return _ConsumerCreation(None, False, True) + + +class _WrappedConsumer(object): + """Wraps a consumer to catch the exceptions that it is allowed to throw.""" + + def __init__(self, consumer): + """Constructor. + + Args: + consumer: A stream.Consumer that may raise abandonment.Abandoned from any + of its methods. + """ + self._consumer = consumer + + def moar(self, payload, complete): + """Makes progress with the wrapped consumer. + + This method catches all exceptions allowed to be thrown by the wrapped + consumer. Any exceptions raised by this method should be blamed on the + customer-supplied consumer. + + Args: + payload: A customer-significant payload object. May be None only if + complete is True. + complete: Whether or not the end of the payload sequence has been reached. + Must be True if payload is None. + + Returns: + True if the wrapped consumer made progress or False if the wrapped + consumer raised abandonment.Abandoned to indicate its abandonment of + progress. + """ + try: + if payload is None: + self._consumer.terminate() + elif complete: + self._consumer.consume_and_terminate(payload) + else: + self._consumer.consume(payload) + return True + except abandonment.Abandoned: + return False + + +class _IngestionManager(_interfaces.IngestionManager): + """An implementation of _interfaces.IngestionManager.""" + + def __init__( + self, lock, pool, consumer_creator, failure_outcome, termination_manager, + transmission_manager): + """Constructor. + + Args: + lock: The operation-wide lock. + pool: A thread pool in which to execute customer code. + consumer_creator: A _ConsumerCreator wrapping the portion of customer code + that when called returns the stream.Consumer with which the customer + code will ingest payload values. + failure_outcome: Whichever one of + interfaces.Outcome.SERVICED_FAILURE or + interfaces.Outcome.SERVICER_FAILURE describes local failure of + customer code. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + """ + self._lock = lock + self._pool = pool + self._consumer_creator = consumer_creator + self._failure_outcome = failure_outcome + self._termination_manager = termination_manager + self._transmission_manager = transmission_manager + self._expiration_manager = None + + self._wrapped_ingestion_consumer = None + self._pending_ingestion = [] + self._ingestion_complete = False + self._processing = False + + def set_expiration_manager(self, expiration_manager): + self._expiration_manager = expiration_manager + + def _abort_internal_only(self): + self._wrapped_ingestion_consumer = None + self._pending_ingestion = None + + def _abort_and_notify(self, outcome): + self._abort_internal_only() + self._termination_manager.abort(outcome) + self._transmission_manager.abort(outcome) + self._expiration_manager.abort() + + def _next(self): + """Computes the next step for ingestion. + + Returns: + A payload, complete, continue triplet indicating what payload (if any) is + available to feed into customer code, whether or not the sequence of + payloads has terminated, and whether or not there is anything + immediately actionable to call customer code to do. + """ + if self._pending_ingestion is None: + return None, False, False + elif self._pending_ingestion: + payload = self._pending_ingestion.pop(0) + complete = self._ingestion_complete and not self._pending_ingestion + return payload, complete, True + elif self._ingestion_complete: + return None, True, True + else: + return None, False, False + + def _process(self, wrapped_ingestion_consumer, payload, complete): + """A method to call to execute customer code. + + This object's lock must *not* be held when calling this method. + + Args: + wrapped_ingestion_consumer: The _WrappedConsumer with which to pass + payloads to customer code. + payload: A customer payload. May be None only if complete is True. + complete: Whether or not the sequence of payloads to pass to the customer + has concluded. + """ + while True: + consumption_outcome = callable_util.call_logging_exceptions( + wrapped_ingestion_consumer.moar, _CONSUME_EXCEPTION_LOG_MESSAGE, + payload, complete) + if consumption_outcome.exception is None: + if consumption_outcome.return_value: + with self._lock: + if complete: + self._pending_ingestion = None + self._termination_manager.ingestion_complete() + return + else: + payload, complete, moar = self._next() + if not moar: + self._processing = False + return + else: + with self._lock: + if self._pending_ingestion is not None: + self._abort_and_notify(self._failure_outcome) + self._processing = False + return + else: + with self._lock: + self._abort_and_notify(self._failure_outcome) + self._processing = False + return + + def start(self, requirement): + if self._pending_ingestion is not None: + def initialize(): + consumer_creation_outcome = callable_util.call_logging_exceptions( + self._consumer_creator.create_consumer, + _CREATE_CONSUMER_EXCEPTION_LOG_MESSAGE, requirement) + if consumer_creation_outcome.return_value is None: + with self._lock: + self._abort_and_notify(self._failure_outcome) + self._processing = False + elif consumer_creation_outcome.return_value.remote_error: + with self._lock: + self._abort_and_notify(interfaces.Outcome.RECEPTION_FAILURE) + self._processing = False + elif consumer_creation_outcome.return_value.abandoned: + with self._lock: + if self._pending_ingestion is not None: + self._abort_and_notify(self._failure_outcome) + self._processing = False + else: + wrapped_ingestion_consumer = _WrappedConsumer( + consumer_creation_outcome.return_value.consumer) + with self._lock: + self._wrapped_ingestion_consumer = wrapped_ingestion_consumer + payload, complete, moar = self._next() + if not moar: + self._processing = False + return + + self._process(wrapped_ingestion_consumer, payload, complete) + + self._pool.submit( + callable_util.with_exceptions_logged( + initialize, _constants.INTERNAL_ERROR_LOG_MESSAGE)) + self._processing = True + + def consume(self, payload): + if self._ingestion_complete: + self._abort_and_notify(self._failure_outcome) + elif self._pending_ingestion is not None: + if self._processing: + self._pending_ingestion.append(payload) + else: + self._pool.submit( + callable_util.with_exceptions_logged( + self._process, _constants.INTERNAL_ERROR_LOG_MESSAGE), + self._wrapped_ingestion_consumer, payload, False) + self._processing = True + + def terminate(self): + if self._ingestion_complete: + self._abort_and_notify(self._failure_outcome) + else: + self._ingestion_complete = True + if self._pending_ingestion is not None and not self._processing: + self._pool.submit( + callable_util.with_exceptions_logged( + self._process, _constants.INTERNAL_ERROR_LOG_MESSAGE), + self._wrapped_ingestion_consumer, None, True) + self._processing = True + + def consume_and_terminate(self, payload): + if self._ingestion_complete: + self._abort_and_notify(self._failure_outcome) + else: + self._ingestion_complete = True + if self._pending_ingestion is not None: + if self._processing: + self._pending_ingestion.append(payload) + else: + self._pool.submit( + callable_util.with_exceptions_logged( + self._process, _constants.INTERNAL_ERROR_LOG_MESSAGE), + self._wrapped_ingestion_consumer, payload, True) + self._processing = True + + def abort(self): + """See _interfaces.IngestionManager.abort for specification.""" + self._abort_internal_only() + + +def front_ingestion_manager( + lock, pool, subscription, termination_manager, transmission_manager, + operation_context): + """Creates an IngestionManager appropriate for front-side use. + + Args: + lock: The operation-wide lock. + pool: A thread pool in which to execute customer code. + subscription: A interfaces.ServicedSubscription indicating the + customer's interest in the results of the operation. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + operation_context: A interfaces.OperationContext for the operation. + + Returns: + An IngestionManager appropriate for front-side use. + """ + ingestion_manager = _IngestionManager( + lock, pool, _FrontConsumerCreator(subscription, operation_context), + interfaces.Outcome.SERVICED_FAILURE, termination_manager, + transmission_manager) + ingestion_manager.start(None) + return ingestion_manager + + +def back_ingestion_manager( + lock, pool, servicer, termination_manager, transmission_manager, + operation_context, emission_consumer): + """Creates an IngestionManager appropriate for back-side use. + + Args: + lock: The operation-wide lock. + pool: A thread pool in which to execute customer code. + servicer: A interfaces.Servicer for servicing the operation. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + operation_context: A interfaces.OperationContext for the operation. + emission_consumer: The _interfaces.EmissionConsumer for the operation. + + Returns: + An IngestionManager appropriate for back-side use. + """ + ingestion_manager = _IngestionManager( + lock, pool, _BackConsumerCreator( + servicer, operation_context, emission_consumer), + interfaces.Outcome.SERVICER_FAILURE, termination_manager, + transmission_manager) + return ingestion_manager diff --git a/src/python/grpcio/grpc/framework/base/_interfaces.py b/src/python/grpcio/grpc/framework/base/_interfaces.py new file mode 100644 index 00000000..d88cf765 --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/_interfaces.py @@ -0,0 +1,271 @@ +# Copyright 2015, 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. + +"""Package-internal interfaces.""" + +import abc + +# interfaces is referenced from specification in this module. +from grpc.framework.base import interfaces # pylint: disable=unused-import +from grpc.framework.foundation import stream + + +class TerminationManager(object): + """An object responsible for handling the termination of an operation.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def set_expiration_manager(self, expiration_manager): + """Sets the ExpirationManager with which this object will cooperate.""" + raise NotImplementedError() + + @abc.abstractmethod + def is_active(self): + """Reports whether or not the operation is active. + + Returns: + True if the operation is active or False if the operation has terminated. + """ + raise NotImplementedError() + + @abc.abstractmethod + def add_callback(self, callback): + """Registers a callback to be called on operation termination. + + If the operation has already terminated, the callback will be called + immediately. + + Args: + callback: A callable that will be passed an interfaces.Outcome value. + """ + raise NotImplementedError() + + @abc.abstractmethod + def emission_complete(self): + """Indicates that emissions from customer code have completed.""" + raise NotImplementedError() + + @abc.abstractmethod + def transmission_complete(self): + """Indicates that transmissions to the remote end are complete.""" + raise NotImplementedError() + + @abc.abstractmethod + def ingestion_complete(self): + """Indicates that customer code ingestion of received values is complete.""" + raise NotImplementedError() + + @abc.abstractmethod + def abort(self, outcome): + """Indicates that the operation must abort for the indicated reason. + + Args: + outcome: An interfaces.Outcome indicating operation abortion. + """ + raise NotImplementedError() + + +class TransmissionManager(object): + """A manager responsible for transmitting to the other end of an operation.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def inmit(self, emission, complete): + """Accepts a value for transmission to the other end of the operation. + + Args: + emission: A value of some significance to the customer to be transmitted + to the other end of the operation. May be None only if complete is True. + complete: A boolean that if True indicates that customer code has emitted + all values it intends to emit. + """ + raise NotImplementedError() + + @abc.abstractmethod + def abort(self, outcome): + """Indicates that the operation has aborted for the indicated reason. + + Args: + outcome: An interfaces.Outcome indicating operation abortion. + """ + raise NotImplementedError() + + +class EmissionManager(stream.Consumer): + """A manager of values emitted by customer code.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def set_ingestion_manager_and_expiration_manager( + self, ingestion_manager, expiration_manager): + """Sets two other objects with which this EmissionManager will cooperate. + + Args: + ingestion_manager: The IngestionManager for the operation. + expiration_manager: The ExpirationManager for the operation. + """ + raise NotImplementedError() + + @abc.abstractmethod + def consume(self, value): + """Accepts a value emitted by customer code. + + This method should only be called by customer code. + + Args: + value: Any value of significance to the customer. + """ + raise NotImplementedError() + + @abc.abstractmethod + def terminate(self): + """Indicates that no more values will be emitted by customer code. + + This method should only be called by customer code. + + Implementations of this method may be idempotent and forgive customer code + calling this method more than once. + """ + raise NotImplementedError() + + @abc.abstractmethod + def consume_and_terminate(self, value): + """Accepts the last value emitted by customer code. + + This method should only be called by customer code. + + Args: + value: Any value of significance to the customer. + """ + raise NotImplementedError() + + +class IngestionManager(stream.Consumer): + """A manager responsible for executing customer code.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def set_expiration_manager(self, expiration_manager): + """Sets the ExpirationManager with which this object will cooperate.""" + raise NotImplementedError() + + @abc.abstractmethod + def start(self, requirement): + """Commences execution of customer code. + + Args: + requirement: Some value unavailable at the time of this object's + construction that is required to begin executing customer code. + """ + raise NotImplementedError() + + @abc.abstractmethod + def consume(self, payload): + """Accepts a customer-significant value to be supplied to customer code. + + Args: + payload: Some customer-significant value. + """ + raise NotImplementedError() + + @abc.abstractmethod + def terminate(self): + """Indicates the end of values to be supplied to customer code.""" + raise NotImplementedError() + + @abc.abstractmethod + def consume_and_terminate(self, payload): + """Accepts the last value to be supplied to customer code. + + Args: + payload: Some customer-significant value (and the last such value). + """ + raise NotImplementedError() + + @abc.abstractmethod + def abort(self): + """Indicates to this manager that the operation has aborted.""" + raise NotImplementedError() + + +class ExpirationManager(object): + """A manager responsible for aborting the operation if it runs out of time.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def change_timeout(self, timeout): + """Changes the timeout allotted for the operation. + + Operation duration is always measure from the beginning of the operation; + calling this method changes the operation's allotted time to timeout total + seconds, not timeout seconds from the time of this method call. + + Args: + timeout: A length of time in seconds to allow for the operation. + """ + raise NotImplementedError() + + @abc.abstractmethod + def deadline(self): + """Returns the time until which the operation is allowed to run. + + Returns: + The time (seconds since the epoch) at which the operation will expire. + """ + raise NotImplementedError() + + @abc.abstractmethod + def abort(self): + """Indicates to this manager that the operation has aborted.""" + raise NotImplementedError() + + +class ReceptionManager(object): + """A manager responsible for receiving tickets from the other end.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def receive_ticket(self, ticket): + """Handle a ticket from the other side of the operation. + + Args: + ticket: An interfaces.BackToFrontTicket or interfaces.FrontToBackTicket + appropriate to this end of the operation and this object. + """ + raise NotImplementedError() + + +class CancellationManager(object): + """A manager of operation cancellation.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def cancel(self): + """Cancels the operation.""" + raise NotImplementedError() diff --git a/src/python/grpcio/grpc/framework/base/_reception.py b/src/python/grpcio/grpc/framework/base/_reception.py new file mode 100644 index 00000000..dd428964 --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/_reception.py @@ -0,0 +1,399 @@ +# Copyright 2015, 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. + +"""State and behavior for ticket reception.""" + +import abc + +from grpc.framework.base import interfaces +from grpc.framework.base import _interfaces + +_INITIAL_FRONT_TO_BACK_TICKET_KINDS = ( + interfaces.FrontToBackTicket.Kind.COMMENCEMENT, + interfaces.FrontToBackTicket.Kind.ENTIRE, +) + + +class _Receiver(object): + """Common specification of different ticket-handling behavior.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def abort_if_abortive(self, ticket): + """Aborts the operation if the ticket is abortive. + + Args: + ticket: A just-arrived ticket. + + Returns: + A boolean indicating whether or not this Receiver aborted the operation + based on the ticket. + """ + raise NotImplementedError() + + @abc.abstractmethod + def receive(self, ticket): + """Handles a just-arrived ticket. + + Args: + ticket: A just-arrived ticket. + + Returns: + A boolean indicating whether or not the ticket was terminal (i.e. whether + or not non-abortive tickets are legal after this one). + """ + raise NotImplementedError() + + @abc.abstractmethod + def reception_failure(self): + """Aborts the operation with an indication of reception failure.""" + raise NotImplementedError() + + +def _abort( + outcome, termination_manager, transmission_manager, ingestion_manager, + expiration_manager): + """Indicates abortion with the given outcome to the given managers.""" + termination_manager.abort(outcome) + transmission_manager.abort(outcome) + ingestion_manager.abort() + expiration_manager.abort() + + +def _abort_if_abortive( + ticket, abortive, termination_manager, transmission_manager, + ingestion_manager, expiration_manager): + """Determines a ticket's being abortive and if so aborts the operation. + + Args: + ticket: A just-arrived ticket. + abortive: A callable that takes a ticket and returns an interfaces.Outcome + indicating that the operation should be aborted or None indicating that + the operation should not be aborted. + termination_manager: The operation's _interfaces.TerminationManager. + transmission_manager: The operation's _interfaces.TransmissionManager. + ingestion_manager: The operation's _interfaces.IngestionManager. + expiration_manager: The operation's _interfaces.ExpirationManager. + + Returns: + True if the operation was aborted; False otherwise. + """ + abortion_outcome = abortive(ticket) + if abortion_outcome is None: + return False + else: + _abort( + abortion_outcome, termination_manager, transmission_manager, + ingestion_manager, expiration_manager) + return True + + +def _reception_failure( + termination_manager, transmission_manager, ingestion_manager, + expiration_manager): + """Aborts the operation with an indication of reception failure.""" + _abort( + interfaces.Outcome.RECEPTION_FAILURE, termination_manager, + transmission_manager, ingestion_manager, expiration_manager) + + +class _BackReceiver(_Receiver): + """Ticket-handling specific to the back side of an operation.""" + + def __init__( + self, termination_manager, transmission_manager, ingestion_manager, + expiration_manager): + """Constructor. + + Args: + termination_manager: The operation's _interfaces.TerminationManager. + transmission_manager: The operation's _interfaces.TransmissionManager. + ingestion_manager: The operation's _interfaces.IngestionManager. + expiration_manager: The operation's _interfaces.ExpirationManager. + """ + self._termination_manager = termination_manager + self._transmission_manager = transmission_manager + self._ingestion_manager = ingestion_manager + self._expiration_manager = expiration_manager + + self._first_ticket_seen = False + self._last_ticket_seen = False + + def _abortive(self, ticket): + """Determines whether or not (and if so, how) a ticket is abortive. + + Args: + ticket: A just-arrived ticket. + + Returns: + An interfaces.Outcome value describing operation abortion if the + ticket is abortive or None if the ticket is not abortive. + """ + if ticket.kind is interfaces.FrontToBackTicket.Kind.CANCELLATION: + return interfaces.Outcome.CANCELLED + elif ticket.kind is interfaces.FrontToBackTicket.Kind.EXPIRATION: + return interfaces.Outcome.EXPIRED + elif ticket.kind is interfaces.FrontToBackTicket.Kind.SERVICED_FAILURE: + return interfaces.Outcome.SERVICED_FAILURE + elif ticket.kind is interfaces.FrontToBackTicket.Kind.RECEPTION_FAILURE: + return interfaces.Outcome.SERVICED_FAILURE + elif (ticket.kind in _INITIAL_FRONT_TO_BACK_TICKET_KINDS and + self._first_ticket_seen): + return interfaces.Outcome.RECEPTION_FAILURE + elif self._last_ticket_seen: + return interfaces.Outcome.RECEPTION_FAILURE + else: + return None + + def abort_if_abortive(self, ticket): + """See _Receiver.abort_if_abortive for specification.""" + return _abort_if_abortive( + ticket, self._abortive, self._termination_manager, + self._transmission_manager, self._ingestion_manager, + self._expiration_manager) + + def receive(self, ticket): + """See _Receiver.receive for specification.""" + if ticket.timeout is not None: + self._expiration_manager.change_timeout(ticket.timeout) + + if ticket.kind is interfaces.FrontToBackTicket.Kind.COMMENCEMENT: + self._first_ticket_seen = True + self._ingestion_manager.start(ticket.name) + if ticket.payload is not None: + self._ingestion_manager.consume(ticket.payload) + elif ticket.kind is interfaces.FrontToBackTicket.Kind.CONTINUATION: + self._ingestion_manager.consume(ticket.payload) + elif ticket.kind is interfaces.FrontToBackTicket.Kind.COMPLETION: + self._last_ticket_seen = True + if ticket.payload is None: + self._ingestion_manager.terminate() + else: + self._ingestion_manager.consume_and_terminate(ticket.payload) + else: + self._first_ticket_seen = True + self._last_ticket_seen = True + self._ingestion_manager.start(ticket.name) + if ticket.payload is None: + self._ingestion_manager.terminate() + else: + self._ingestion_manager.consume_and_terminate(ticket.payload) + + def reception_failure(self): + """See _Receiver.reception_failure for specification.""" + _reception_failure( + self._termination_manager, self._transmission_manager, + self._ingestion_manager, self._expiration_manager) + + +class _FrontReceiver(_Receiver): + """Ticket-handling specific to the front side of an operation.""" + + def __init__( + self, termination_manager, transmission_manager, ingestion_manager, + expiration_manager): + """Constructor. + + Args: + termination_manager: The operation's _interfaces.TerminationManager. + transmission_manager: The operation's _interfaces.TransmissionManager. + ingestion_manager: The operation's _interfaces.IngestionManager. + expiration_manager: The operation's _interfaces.ExpirationManager. + """ + self._termination_manager = termination_manager + self._transmission_manager = transmission_manager + self._ingestion_manager = ingestion_manager + self._expiration_manager = expiration_manager + + self._last_ticket_seen = False + + def _abortive(self, ticket): + """Determines whether or not (and if so, how) a ticket is abortive. + + Args: + ticket: A just-arrived ticket. + + Returns: + An interfaces.Outcome value describing operation abortion if the ticket + is abortive or None if the ticket is not abortive. + """ + if ticket.kind is interfaces.BackToFrontTicket.Kind.CANCELLATION: + return interfaces.Outcome.CANCELLED + elif ticket.kind is interfaces.BackToFrontTicket.Kind.EXPIRATION: + return interfaces.Outcome.EXPIRED + elif ticket.kind is interfaces.BackToFrontTicket.Kind.SERVICER_FAILURE: + return interfaces.Outcome.SERVICER_FAILURE + elif ticket.kind is interfaces.BackToFrontTicket.Kind.RECEPTION_FAILURE: + return interfaces.Outcome.SERVICER_FAILURE + elif self._last_ticket_seen: + return interfaces.Outcome.RECEPTION_FAILURE + else: + return None + + def abort_if_abortive(self, ticket): + """See _Receiver.abort_if_abortive for specification.""" + return _abort_if_abortive( + ticket, self._abortive, self._termination_manager, + self._transmission_manager, self._ingestion_manager, + self._expiration_manager) + + def receive(self, ticket): + """See _Receiver.receive for specification.""" + if ticket.kind is interfaces.BackToFrontTicket.Kind.CONTINUATION: + self._ingestion_manager.consume(ticket.payload) + elif ticket.kind is interfaces.BackToFrontTicket.Kind.COMPLETION: + self._last_ticket_seen = True + if ticket.payload is None: + self._ingestion_manager.terminate() + else: + self._ingestion_manager.consume_and_terminate(ticket.payload) + + def reception_failure(self): + """See _Receiver.reception_failure for specification.""" + _reception_failure( + self._termination_manager, self._transmission_manager, + self._ingestion_manager, self._expiration_manager) + + +class _ReceptionManager(_interfaces.ReceptionManager): + """A ReceptionManager based around a _Receiver passed to it.""" + + def __init__(self, lock, receiver): + """Constructor. + + Args: + lock: The operation-servicing-wide lock object. + receiver: A _Receiver responsible for handling received tickets. + """ + self._lock = lock + self._receiver = receiver + + self._lowest_unseen_sequence_number = 0 + self._out_of_sequence_tickets = {} + self._completed_sequence_number = None + self._aborted = False + + def _sequence_failure(self, ticket): + """Determines a just-arrived ticket's sequential legitimacy. + + Args: + ticket: A just-arrived ticket. + + Returns: + True if the ticket is sequentially legitimate; False otherwise. + """ + if ticket.sequence_number < self._lowest_unseen_sequence_number: + return True + elif ticket.sequence_number in self._out_of_sequence_tickets: + return True + elif (self._completed_sequence_number is not None and + self._completed_sequence_number <= ticket.sequence_number): + return True + else: + return False + + def _process(self, ticket): + """Process those tickets ready to be processed. + + Args: + ticket: A just-arrived ticket the sequence number of which matches this + _ReceptionManager's _lowest_unseen_sequence_number field. + """ + while True: + completed = self._receiver.receive(ticket) + if completed: + self._out_of_sequence_tickets.clear() + self._completed_sequence_number = ticket.sequence_number + self._lowest_unseen_sequence_number = ticket.sequence_number + 1 + return + else: + next_ticket = self._out_of_sequence_tickets.pop( + ticket.sequence_number + 1, None) + if next_ticket is None: + self._lowest_unseen_sequence_number = ticket.sequence_number + 1 + return + else: + ticket = next_ticket + + def receive_ticket(self, ticket): + """See _interfaces.ReceptionManager.receive_ticket for specification.""" + with self._lock: + if self._aborted: + return + elif self._sequence_failure(ticket): + self._receiver.reception_failure() + self._aborted = True + elif self._receiver.abort_if_abortive(ticket): + self._aborted = True + elif ticket.sequence_number == self._lowest_unseen_sequence_number: + self._process(ticket) + else: + self._out_of_sequence_tickets[ticket.sequence_number] = ticket + + +def front_reception_manager( + lock, termination_manager, transmission_manager, ingestion_manager, + expiration_manager): + """Creates a _interfaces.ReceptionManager for front-side use. + + Args: + lock: The operation-servicing-wide lock object. + termination_manager: The operation's _interfaces.TerminationManager. + transmission_manager: The operation's _interfaces.TransmissionManager. + ingestion_manager: The operation's _interfaces.IngestionManager. + expiration_manager: The operation's _interfaces.ExpirationManager. + + Returns: + A _interfaces.ReceptionManager appropriate for front-side use. + """ + return _ReceptionManager( + lock, _FrontReceiver( + termination_manager, transmission_manager, ingestion_manager, + expiration_manager)) + + +def back_reception_manager( + lock, termination_manager, transmission_manager, ingestion_manager, + expiration_manager): + """Creates a _interfaces.ReceptionManager for back-side use. + + Args: + lock: The operation-servicing-wide lock object. + termination_manager: The operation's _interfaces.TerminationManager. + transmission_manager: The operation's _interfaces.TransmissionManager. + ingestion_manager: The operation's _interfaces.IngestionManager. + expiration_manager: The operation's _interfaces.ExpirationManager. + + Returns: + A _interfaces.ReceptionManager appropriate for back-side use. + """ + return _ReceptionManager( + lock, _BackReceiver( + termination_manager, transmission_manager, ingestion_manager, + expiration_manager)) diff --git a/src/python/grpcio/grpc/framework/base/_termination.py b/src/python/grpcio/grpc/framework/base/_termination.py new file mode 100644 index 00000000..ddcbc602 --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/_termination.py @@ -0,0 +1,204 @@ +# Copyright 2015, 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. + +"""State and behavior for operation termination.""" + +import enum + +from grpc.framework.base import _constants +from grpc.framework.base import _interfaces +from grpc.framework.base import interfaces +from grpc.framework.foundation import callable_util + +_CALLBACK_EXCEPTION_LOG_MESSAGE = 'Exception calling termination callback!' + + +@enum.unique +class _Requirement(enum.Enum): + """Symbols indicating events required for termination.""" + + EMISSION = 'emission' + TRANSMISSION = 'transmission' + INGESTION = 'ingestion' + +_FRONT_NOT_LISTENING_REQUIREMENTS = (_Requirement.TRANSMISSION,) +_BACK_NOT_LISTENING_REQUIREMENTS = ( + _Requirement.EMISSION, _Requirement.INGESTION,) +_LISTENING_REQUIREMENTS = ( + _Requirement.TRANSMISSION, _Requirement.INGESTION,) + + +class _TerminationManager(_interfaces.TerminationManager): + """An implementation of _interfaces.TerminationManager.""" + + def __init__( + self, work_pool, utility_pool, action, requirements, local_failure): + """Constructor. + + Args: + work_pool: A thread pool in which customer work will be done. + utility_pool: A thread pool in which work utility work will be done. + action: An action to call on operation termination. + requirements: A combination of _Requirement values identifying what + must finish for the operation to be considered completed. + local_failure: An interfaces.Outcome specifying what constitutes local + failure of customer work. + """ + self._work_pool = work_pool + self._utility_pool = utility_pool + self._action = action + self._local_failure = local_failure + self._has_locally_failed = False + self._expiration_manager = None + + self._outstanding_requirements = set(requirements) + self._outcome = None + self._callbacks = [] + + def set_expiration_manager(self, expiration_manager): + self._expiration_manager = expiration_manager + + def _terminate(self, outcome): + """Terminates the operation. + + Args: + outcome: An interfaces.Outcome describing the outcome of the operation. + """ + self._expiration_manager.abort() + self._outstanding_requirements = None + callbacks = list(self._callbacks) + self._callbacks = None + self._outcome = outcome + + act = callable_util.with_exceptions_logged( + self._action, _constants.INTERNAL_ERROR_LOG_MESSAGE) + + if self._has_locally_failed: + self._utility_pool.submit(act, outcome) + else: + def call_callbacks_and_act(callbacks, outcome): + for callback in callbacks: + callback_outcome = callable_util.call_logging_exceptions( + callback, _CALLBACK_EXCEPTION_LOG_MESSAGE, outcome) + if callback_outcome.exception is not None: + outcome = self._local_failure + break + self._utility_pool.submit(act, outcome) + + self._work_pool.submit(callable_util.with_exceptions_logged( + call_callbacks_and_act, + _constants.INTERNAL_ERROR_LOG_MESSAGE), + callbacks, outcome) + + def is_active(self): + """See _interfaces.TerminationManager.is_active for specification.""" + return self._outstanding_requirements is not None + + def add_callback(self, callback): + """See _interfaces.TerminationManager.add_callback for specification.""" + if not self._has_locally_failed: + if self._outstanding_requirements is None: + self._work_pool.submit( + callable_util.with_exceptions_logged( + callback, _CALLBACK_EXCEPTION_LOG_MESSAGE), self._outcome) + else: + self._callbacks.append(callback) + + def emission_complete(self): + """See superclass method for specification.""" + if self._outstanding_requirements is not None: + self._outstanding_requirements.discard(_Requirement.EMISSION) + if not self._outstanding_requirements: + self._terminate(interfaces.Outcome.COMPLETED) + + def transmission_complete(self): + """See superclass method for specification.""" + if self._outstanding_requirements is not None: + self._outstanding_requirements.discard(_Requirement.TRANSMISSION) + if not self._outstanding_requirements: + self._terminate(interfaces.Outcome.COMPLETED) + + def ingestion_complete(self): + """See superclass method for specification.""" + if self._outstanding_requirements is not None: + self._outstanding_requirements.discard(_Requirement.INGESTION) + if not self._outstanding_requirements: + self._terminate(interfaces.Outcome.COMPLETED) + + def abort(self, outcome): + """See _interfaces.TerminationManager.abort for specification.""" + if outcome is self._local_failure: + self._has_failed_locally = True + if self._outstanding_requirements is not None: + self._terminate(outcome) + + +def front_termination_manager( + work_pool, utility_pool, action, subscription_kind): + """Creates a TerminationManager appropriate for front-side use. + + Args: + work_pool: A thread pool in which customer work will be done. + utility_pool: A thread pool in which work utility work will be done. + action: An action to call on operation termination. + subscription_kind: An interfaces.ServicedSubscription.Kind value. + + Returns: + A TerminationManager appropriate for front-side use. + """ + if subscription_kind is interfaces.ServicedSubscription.Kind.NONE: + requirements = _FRONT_NOT_LISTENING_REQUIREMENTS + else: + requirements = _LISTENING_REQUIREMENTS + + return _TerminationManager( + work_pool, utility_pool, action, requirements, + interfaces.Outcome.SERVICED_FAILURE) + + +def back_termination_manager(work_pool, utility_pool, action, subscription_kind): + """Creates a TerminationManager appropriate for back-side use. + + Args: + work_pool: A thread pool in which customer work will be done. + utility_pool: A thread pool in which work utility work will be done. + action: An action to call on operation termination. + subscription_kind: An interfaces.ServicedSubscription.Kind value. + + Returns: + A TerminationManager appropriate for back-side use. + """ + if subscription_kind is interfaces.ServicedSubscription.Kind.NONE: + requirements = _BACK_NOT_LISTENING_REQUIREMENTS + else: + requirements = _LISTENING_REQUIREMENTS + + return _TerminationManager( + work_pool, utility_pool, action, requirements, + interfaces.Outcome.SERVICER_FAILURE) diff --git a/src/python/grpcio/grpc/framework/base/_transmission.py b/src/python/grpcio/grpc/framework/base/_transmission.py new file mode 100644 index 00000000..68451292 --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/_transmission.py @@ -0,0 +1,429 @@ +# Copyright 2015, 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. + +"""State and behavior for ticket transmission during an operation.""" + +import abc + +from grpc.framework.base import _constants +from grpc.framework.base import _interfaces +from grpc.framework.base import interfaces +from grpc.framework.foundation import callable_util + +_TRANSMISSION_EXCEPTION_LOG_MESSAGE = 'Exception during transmission!' + +_FRONT_TO_BACK_NO_TRANSMISSION_OUTCOMES = ( + interfaces.Outcome.SERVICER_FAILURE, + ) +_BACK_TO_FRONT_NO_TRANSMISSION_OUTCOMES = ( + interfaces.Outcome.CANCELLED, + interfaces.Outcome.SERVICED_FAILURE, + ) + +_ABORTION_OUTCOME_TO_FRONT_TO_BACK_TICKET_KIND = { + interfaces.Outcome.CANCELLED: + interfaces.FrontToBackTicket.Kind.CANCELLATION, + interfaces.Outcome.EXPIRED: + interfaces.FrontToBackTicket.Kind.EXPIRATION, + interfaces.Outcome.RECEPTION_FAILURE: + interfaces.FrontToBackTicket.Kind.RECEPTION_FAILURE, + interfaces.Outcome.TRANSMISSION_FAILURE: + interfaces.FrontToBackTicket.Kind.TRANSMISSION_FAILURE, + interfaces.Outcome.SERVICED_FAILURE: + interfaces.FrontToBackTicket.Kind.SERVICED_FAILURE, + interfaces.Outcome.SERVICER_FAILURE: + interfaces.FrontToBackTicket.Kind.SERVICER_FAILURE, +} + +_ABORTION_OUTCOME_TO_BACK_TO_FRONT_TICKET_KIND = { + interfaces.Outcome.CANCELLED: + interfaces.BackToFrontTicket.Kind.CANCELLATION, + interfaces.Outcome.EXPIRED: + interfaces.BackToFrontTicket.Kind.EXPIRATION, + interfaces.Outcome.RECEPTION_FAILURE: + interfaces.BackToFrontTicket.Kind.RECEPTION_FAILURE, + interfaces.Outcome.TRANSMISSION_FAILURE: + interfaces.BackToFrontTicket.Kind.TRANSMISSION_FAILURE, + interfaces.Outcome.SERVICED_FAILURE: + interfaces.BackToFrontTicket.Kind.SERVICED_FAILURE, + interfaces.Outcome.SERVICER_FAILURE: + interfaces.BackToFrontTicket.Kind.SERVICER_FAILURE, +} + + +class _Ticketizer(object): + """Common specification of different ticket-creating behavior.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def ticketize(self, operation_id, sequence_number, payload, complete): + """Creates a ticket indicating ordinary operation progress. + + Args: + operation_id: The operation ID for the current operation. + sequence_number: A sequence number for the ticket. + payload: A customer payload object. May be None if sequence_number is + zero or complete is true. + complete: A boolean indicating whether or not the ticket should describe + itself as (but for a later indication of operation abortion) the last + ticket to be sent. + + Returns: + An object of an appropriate type suitable for transmission to the other + side of the operation. + """ + raise NotImplementedError() + + @abc.abstractmethod + def ticketize_abortion(self, operation_id, sequence_number, outcome): + """Creates a ticket indicating that the operation is aborted. + + Args: + operation_id: The operation ID for the current operation. + sequence_number: A sequence number for the ticket. + outcome: An interfaces.Outcome value describing the operation abortion. + + Returns: + An object of an appropriate type suitable for transmission to the other + side of the operation, or None if transmission is not appropriate for + the given outcome. + """ + raise NotImplementedError() + + +class _FrontTicketizer(_Ticketizer): + """Front-side ticket-creating behavior.""" + + def __init__(self, name, subscription_kind, trace_id, timeout): + """Constructor. + + Args: + name: The name of the operation. + subscription_kind: An interfaces.ServicedSubscription.Kind value + describing the interest the front has in tickets sent from the back. + trace_id: A uuid.UUID identifying a set of related operations to which + this operation belongs. + timeout: A length of time in seconds to allow for the entire operation. + """ + self._name = name + self._subscription_kind = subscription_kind + self._trace_id = trace_id + self._timeout = timeout + + def ticketize(self, operation_id, sequence_number, payload, complete): + """See _Ticketizer.ticketize for specification.""" + if sequence_number: + if complete: + kind = interfaces.FrontToBackTicket.Kind.COMPLETION + else: + kind = interfaces.FrontToBackTicket.Kind.CONTINUATION + return interfaces.FrontToBackTicket( + operation_id, sequence_number, kind, self._name, + self._subscription_kind, self._trace_id, payload, self._timeout) + else: + if complete: + kind = interfaces.FrontToBackTicket.Kind.ENTIRE + else: + kind = interfaces.FrontToBackTicket.Kind.COMMENCEMENT + return interfaces.FrontToBackTicket( + operation_id, 0, kind, self._name, self._subscription_kind, + self._trace_id, payload, self._timeout) + + def ticketize_abortion(self, operation_id, sequence_number, outcome): + """See _Ticketizer.ticketize_abortion for specification.""" + if outcome in _FRONT_TO_BACK_NO_TRANSMISSION_OUTCOMES: + return None + else: + kind = _ABORTION_OUTCOME_TO_FRONT_TO_BACK_TICKET_KIND[outcome] + return interfaces.FrontToBackTicket( + operation_id, sequence_number, kind, None, None, None, None, None) + + +class _BackTicketizer(_Ticketizer): + """Back-side ticket-creating behavior.""" + + def ticketize(self, operation_id, sequence_number, payload, complete): + """See _Ticketizer.ticketize for specification.""" + if complete: + kind = interfaces.BackToFrontTicket.Kind.COMPLETION + else: + kind = interfaces.BackToFrontTicket.Kind.CONTINUATION + return interfaces.BackToFrontTicket( + operation_id, sequence_number, kind, payload) + + def ticketize_abortion(self, operation_id, sequence_number, outcome): + """See _Ticketizer.ticketize_abortion for specification.""" + if outcome in _BACK_TO_FRONT_NO_TRANSMISSION_OUTCOMES: + return None + else: + kind = _ABORTION_OUTCOME_TO_BACK_TO_FRONT_TICKET_KIND[outcome] + return interfaces.BackToFrontTicket( + operation_id, sequence_number, kind, None) + + +class TransmissionManager(_interfaces.TransmissionManager): + """A _interfaces.TransmissionManager on which other managers may be set.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def set_ingestion_and_expiration_managers( + self, ingestion_manager, expiration_manager): + """Sets two of the other managers with which this manager may interact. + + Args: + ingestion_manager: The _interfaces.IngestionManager associated with the + current operation. + expiration_manager: The _interfaces.ExpirationManager associated with the + current operation. + """ + raise NotImplementedError() + + +class _EmptyTransmissionManager(TransmissionManager): + """A completely no-operative _interfaces.TransmissionManager.""" + + def set_ingestion_and_expiration_managers( + self, ingestion_manager, expiration_manager): + """See overriden method for specification.""" + + def inmit(self, emission, complete): + """See _interfaces.TransmissionManager.inmit for specification.""" + + def abort(self, outcome): + """See _interfaces.TransmissionManager.abort for specification.""" + + +class _TransmittingTransmissionManager(TransmissionManager): + """A TransmissionManager implementation that sends tickets.""" + + def __init__( + self, lock, pool, callback, operation_id, ticketizer, + termination_manager): + """Constructor. + + Args: + lock: The operation-servicing-wide lock object. + pool: A thread pool in which the work of transmitting tickets will be + performed. + callback: A callable that accepts tickets and sends them to the other side + of the operation. + operation_id: The operation's ID. + ticketizer: A _Ticketizer for ticket creation. + termination_manager: The _interfaces.TerminationManager associated with + this operation. + """ + self._lock = lock + self._pool = pool + self._callback = callback + self._operation_id = operation_id + self._ticketizer = ticketizer + self._termination_manager = termination_manager + self._ingestion_manager = None + self._expiration_manager = None + + self._emissions = [] + self._emission_complete = False + self._outcome = None + self._lowest_unused_sequence_number = 0 + self._transmitting = False + + def set_ingestion_and_expiration_managers( + self, ingestion_manager, expiration_manager): + """See overridden method for specification.""" + self._ingestion_manager = ingestion_manager + self._expiration_manager = expiration_manager + + def _lead_ticket(self, emission, complete): + """Creates a ticket suitable for leading off the transmission loop. + + Args: + emission: A customer payload object to be sent to the other side of the + operation. + complete: Whether or not the sequence of customer payloads ends with + the passed object. + + Returns: + A ticket with which to lead off the transmission loop. + """ + sequence_number = self._lowest_unused_sequence_number + self._lowest_unused_sequence_number += 1 + return self._ticketizer.ticketize( + self._operation_id, sequence_number, emission, complete) + + def _abortive_response_ticket(self, outcome): + """Creates a ticket indicating operation abortion. + + Args: + outcome: An interfaces.Outcome value describing operation abortion. + + Returns: + A ticket indicating operation abortion. + """ + ticket = self._ticketizer.ticketize_abortion( + self._operation_id, self._lowest_unused_sequence_number, outcome) + if ticket is None: + return None + else: + self._lowest_unused_sequence_number += 1 + return ticket + + def _next_ticket(self): + """Creates the next ticket to be sent to the other side of the operation. + + Returns: + A (completed, ticket) tuple comprised of a boolean indicating whether or + not the sequence of tickets has completed normally and a ticket to send + to the other side if the sequence of tickets hasn't completed. The tuple + will never have both a True first element and a non-None second element. + """ + if self._emissions is None: + return False, None + elif self._outcome is None: + if self._emissions: + payload = self._emissions.pop(0) + complete = self._emission_complete and not self._emissions + sequence_number = self._lowest_unused_sequence_number + self._lowest_unused_sequence_number += 1 + return complete, self._ticketizer.ticketize( + self._operation_id, sequence_number, payload, complete) + else: + return self._emission_complete, None + else: + ticket = self._abortive_response_ticket(self._outcome) + self._emissions = None + return False, None if ticket is None else ticket + + def _transmit(self, ticket): + """Commences the transmission loop sending tickets. + + Args: + ticket: A ticket to be sent to the other side of the operation. + """ + def transmit(ticket): + while True: + transmission_outcome = callable_util.call_logging_exceptions( + self._callback, _TRANSMISSION_EXCEPTION_LOG_MESSAGE, ticket) + if transmission_outcome.exception is None: + with self._lock: + complete, ticket = self._next_ticket() + if ticket is None: + if complete: + self._termination_manager.transmission_complete() + self._transmitting = False + return + else: + with self._lock: + self._emissions = None + self._termination_manager.abort( + interfaces.Outcome.TRANSMISSION_FAILURE) + self._ingestion_manager.abort() + self._expiration_manager.abort() + self._transmitting = False + return + + self._pool.submit(callable_util.with_exceptions_logged( + transmit, _constants.INTERNAL_ERROR_LOG_MESSAGE), ticket) + self._transmitting = True + + def inmit(self, emission, complete): + """See _interfaces.TransmissionManager.inmit for specification.""" + if self._emissions is not None and self._outcome is None: + self._emission_complete = complete + if self._transmitting: + self._emissions.append(emission) + else: + self._transmit(self._lead_ticket(emission, complete)) + + def abort(self, outcome): + """See _interfaces.TransmissionManager.abort for specification.""" + if self._emissions is not None and self._outcome is None: + self._outcome = outcome + if not self._transmitting: + ticket = self._abortive_response_ticket(outcome) + self._emissions = None + if ticket is not None: + self._transmit(ticket) + + +def front_transmission_manager( + lock, pool, callback, operation_id, name, subscription_kind, trace_id, + timeout, termination_manager): + """Creates a TransmissionManager appropriate for front-side use. + + Args: + lock: The operation-servicing-wide lock object. + pool: A thread pool in which the work of transmitting tickets will be + performed. + callback: A callable that accepts tickets and sends them to the other side + of the operation. + operation_id: The operation's ID. + name: The name of the operation. + subscription_kind: An interfaces.ServicedSubscription.Kind value + describing the interest the front has in tickets sent from the back. + trace_id: A uuid.UUID identifying a set of related operations to which + this operation belongs. + timeout: A length of time in seconds to allow for the entire operation. + termination_manager: The _interfaces.TerminationManager associated with + this operation. + + Returns: + A TransmissionManager appropriate for front-side use. + """ + return _TransmittingTransmissionManager( + lock, pool, callback, operation_id, _FrontTicketizer( + name, subscription_kind, trace_id, timeout), + termination_manager) + + +def back_transmission_manager( + lock, pool, callback, operation_id, termination_manager, + subscription_kind): + """Creates a TransmissionManager appropriate for back-side use. + + Args: + lock: The operation-servicing-wide lock object. + pool: A thread pool in which the work of transmitting tickets will be + performed. + callback: A callable that accepts tickets and sends them to the other side + of the operation. + operation_id: The operation's ID. + termination_manager: The _interfaces.TerminationManager associated with + this operation. + subscription_kind: An interfaces.ServicedSubscription.Kind value + describing the interest the front has in tickets sent from the back. + + Returns: + A TransmissionManager appropriate for back-side use. + """ + if subscription_kind is interfaces.ServicedSubscription.Kind.NONE: + return _EmptyTransmissionManager() + else: + return _TransmittingTransmissionManager( + lock, pool, callback, operation_id, _BackTicketizer(), + termination_manager) diff --git a/src/python/grpcio/grpc/framework/base/exceptions.py b/src/python/grpcio/grpc/framework/base/exceptions.py new file mode 100644 index 00000000..b8f47521 --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/exceptions.py @@ -0,0 +1,34 @@ +# Copyright 2015, 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. + +"""Exceptions defined and used by the base layer of RPC Framework.""" + + +class NoSuchMethodError(Exception): + """Indicates that an operation with an unrecognized name has been called.""" diff --git a/src/python/grpcio/grpc/framework/base/implementations.py b/src/python/grpcio/grpc/framework/base/implementations.py new file mode 100644 index 00000000..5656f9f9 --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/implementations.py @@ -0,0 +1,77 @@ +# Copyright 2015, 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. + +"""Entry points into the ticket-exchange-based base layer implementation.""" + +# interfaces is referenced from specification in this module. +from grpc.framework.base import _ends +from grpc.framework.base import interfaces # pylint: disable=unused-import + + +def front_link(work_pool, transmission_pool, utility_pool): + """Factory function for creating interfaces.FrontLinks. + + Args: + work_pool: A thread pool to be used for doing work within the created + FrontLink object. + transmission_pool: A thread pool to be used within the created FrontLink + object for transmitting values to a joined RearLink object. + utility_pool: A thread pool to be used within the created FrontLink object + for utility tasks. + + Returns: + An interfaces.FrontLink. + """ + return _ends.FrontLink(work_pool, transmission_pool, utility_pool) + + +def back_link( + servicer, work_pool, transmission_pool, utility_pool, default_timeout, + maximum_timeout): + """Factory function for creating interfaces.BackLinks. + + Args: + servicer: An interfaces.Servicer for servicing operations. + work_pool: A thread pool to be used for doing work within the created + BackLink object. + transmission_pool: A thread pool to be used within the created BackLink + object for transmitting values to a joined ForeLink object. + utility_pool: A thread pool to be used within the created BackLink object + for utility tasks. + default_timeout: A length of time in seconds to be used as the default + time alloted for a single operation. + maximum_timeout: A length of time in seconds to be used as the maximum + time alloted for a single operation. + + Returns: + An interfaces.BackLink. + """ + return _ends.BackLink( + servicer, work_pool, transmission_pool, utility_pool, default_timeout, + maximum_timeout) diff --git a/src/python/grpcio/grpc/framework/base/in_memory.py b/src/python/grpcio/grpc/framework/base/in_memory.py new file mode 100644 index 00000000..c92d0bc6 --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/in_memory.py @@ -0,0 +1,108 @@ +# Copyright 2015, 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. + +"""In-memory implementations of base layer interfaces.""" + +import threading + +from grpc.framework.base import _constants +from grpc.framework.base import interfaces +from grpc.framework.foundation import callable_util + + +class _Serializer(object): + """A utility for serializing values that may arrive concurrently.""" + + def __init__(self, pool): + self._lock = threading.Lock() + self._pool = pool + self._sink = None + self._spinning = False + self._values = [] + + def _spin(self, sink, value): + while True: + sink(value) + with self._lock: + if self._sink is None or not self._values: + self._spinning = False + return + else: + sink, value = self._sink, self._values.pop(0) + + def set_sink(self, sink): + with self._lock: + self._sink = sink + if sink is not None and self._values and not self._spinning: + self._spinning = True + self._pool.submit( + callable_util.with_exceptions_logged( + self._spin, _constants.INTERNAL_ERROR_LOG_MESSAGE), + sink, self._values.pop(0)) + + def add_value(self, value): + with self._lock: + if self._sink and not self._spinning: + self._spinning = True + self._pool.submit( + callable_util.with_exceptions_logged( + self._spin, _constants.INTERNAL_ERROR_LOG_MESSAGE), + self._sink, value) + else: + self._values.append(value) + + +class Link(interfaces.ForeLink, interfaces.RearLink): + """A trivial implementation of interfaces.ForeLink and interfaces.RearLink.""" + + def __init__(self, pool): + """Constructor. + + Args: + pool: A thread pool to be used for serializing ticket exchange in each + direction. + """ + self._front_to_back = _Serializer(pool) + self._back_to_front = _Serializer(pool) + + def join_fore_link(self, fore_link): + """See interfaces.RearLink.join_fore_link for specification.""" + self._back_to_front.set_sink(fore_link.accept_back_to_front_ticket) + + def join_rear_link(self, rear_link): + """See interfaces.ForeLink.join_rear_link for specification.""" + self._front_to_back.set_sink(rear_link.accept_front_to_back_ticket) + + def accept_front_to_back_ticket(self, ticket): + """See interfaces.ForeLink.accept_front_to_back_ticket for specification.""" + self._front_to_back.add_value(ticket) + + def accept_back_to_front_ticket(self, ticket): + """See interfaces.RearLink.accept_back_to_front_ticket for specification.""" + self._back_to_front.add_value(ticket) diff --git a/src/python/grpcio/grpc/framework/base/interfaces.py b/src/python/grpcio/grpc/framework/base/interfaces.py new file mode 100644 index 00000000..e22c10d9 --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/interfaces.py @@ -0,0 +1,363 @@ +# Copyright 2015, 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. + +"""Interfaces defined and used by the base layer of RPC Framework.""" + +import abc +import collections +import enum + +# stream is referenced from specification in this module. +from grpc.framework.foundation import stream # pylint: disable=unused-import + + +@enum.unique +class Outcome(enum.Enum): + """Operation outcomes.""" + + COMPLETED = 'completed' + CANCELLED = 'cancelled' + EXPIRED = 'expired' + RECEPTION_FAILURE = 'reception failure' + TRANSMISSION_FAILURE = 'transmission failure' + SERVICER_FAILURE = 'servicer failure' + SERVICED_FAILURE = 'serviced failure' + + +class OperationContext(object): + """Provides operation-related information and action. + + Attributes: + trace_id: A uuid.UUID identifying a particular set of related operations. + """ + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def is_active(self): + """Describes whether the operation is active or has terminated.""" + raise NotImplementedError() + + @abc.abstractmethod + def add_termination_callback(self, callback): + """Adds a function to be called upon operation termination. + + Args: + callback: A callable that will be passed an Outcome value. + """ + raise NotImplementedError() + + @abc.abstractmethod + def time_remaining(self): + """Describes the length of allowed time remaining for the operation. + + Returns: + A nonnegative float indicating the length of allowed time in seconds + remaining for the operation to complete before it is considered to have + timed out. + """ + raise NotImplementedError() + + @abc.abstractmethod + def fail(self, exception): + """Indicates that the operation has failed. + + Args: + exception: An exception germane to the operation failure. May be None. + """ + raise NotImplementedError() + + +class Servicer(object): + """Interface for service implementations.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def service(self, name, context, output_consumer): + """Services an operation. + + Args: + name: The name of the operation. + context: A ServicerContext object affording contextual information and + actions. + output_consumer: A stream.Consumer that will accept output values of + the operation. + + Returns: + A stream.Consumer that will accept input values for the operation. + + Raises: + exceptions.NoSuchMethodError: If this Servicer affords no method with the + given name. + abandonment.Abandoned: If the operation has been aborted and there no + longer is any reason to service the operation. + """ + raise NotImplementedError() + + +class Operation(object): + """Representation of an in-progress operation. + + Attributes: + consumer: A stream.Consumer into which payloads constituting the operation's + input may be passed. + context: An OperationContext affording information and action about the + operation. + """ + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def cancel(self): + """Cancels this operation.""" + raise NotImplementedError() + + +class ServicedIngestor(object): + """Responsible for accepting the result of an operation.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def consumer(self, operation_context): + """Affords a consumer to which operation results will be passed. + + Args: + operation_context: An OperationContext object for the current operation. + + Returns: + A stream.Consumer to which the results of the current operation will be + passed. + + Raises: + abandonment.Abandoned: If the operation has been aborted and there no + longer is any reason to service the operation. + """ + raise NotImplementedError() + + +class ServicedSubscription(object): + """A sum type representing a serviced's interest in an operation. + + Attributes: + kind: A Kind value. + ingestor: A ServicedIngestor. Must be present if kind is Kind.FULL. Must + be None if kind is Kind.TERMINATION_ONLY or Kind.NONE. + """ + __metaclass__ = abc.ABCMeta + + @enum.unique + class Kind(enum.Enum): + """Kinds of subscription.""" + + FULL = 'full' + TERMINATION_ONLY = 'termination only' + NONE = 'none' + + +class End(object): + """Common type for entry-point objects on both sides of an operation.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def operation_stats(self): + """Reports the number of terminated operations broken down by outcome. + + Returns: + A dictionary from Outcome value to an integer identifying the number + of operations that terminated with that outcome. + """ + raise NotImplementedError() + + @abc.abstractmethod + def add_idle_action(self, action): + """Adds an action to be called when this End has no ongoing operations. + + Args: + action: A callable that accepts no arguments. + """ + raise NotImplementedError() + + +class Front(End): + """Clientish objects that afford the invocation of operations.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def operate( + self, name, payload, complete, timeout, subscription, trace_id): + """Commences an operation. + + Args: + name: The name of the method invoked for the operation. + payload: An initial payload for the operation. May be None. + complete: A boolean indicating whether or not additional payloads to be + sent to the servicer may be supplied after this call. + timeout: A length of time in seconds to allow for the operation. + subscription: A ServicedSubscription for the operation. + trace_id: A uuid.UUID identifying a set of related operations to which + this operation belongs. + + Returns: + An Operation object affording information and action about the operation + in progress. + """ + raise NotImplementedError() + + +class Back(End): + """Serverish objects that perform the work of operations.""" + __metaclass__ = abc.ABCMeta + + +class FrontToBackTicket( + collections.namedtuple( + 'FrontToBackTicket', + ['operation_id', 'sequence_number', 'kind', 'name', 'subscription', + 'trace_id', 'payload', 'timeout'])): + """A sum type for all values sent from a front to a back. + + Attributes: + operation_id: A unique-with-respect-to-equality hashable object identifying + a particular operation. + sequence_number: A zero-indexed integer sequence number identifying the + ticket's place among all the tickets sent from front to back for this + particular operation. Must be zero if kind is Kind.COMMENCEMENT or + Kind.ENTIRE. Must be positive for any other kind. + kind: A Kind value describing the overall kind of ticket. + name: The name of an operation. Must be present if kind is Kind.COMMENCEMENT + or Kind.ENTIRE. Must be None for any other kind. + subscription: An ServicedSubscription.Kind value describing the interest + the front has in tickets sent from the back. Must be present if + kind is Kind.COMMENCEMENT or Kind.ENTIRE. Must be None for any other kind. + trace_id: A uuid.UUID identifying a set of related operations to which this + operation belongs. May be None. + payload: A customer payload object. Must be present if kind is + Kind.CONTINUATION. Must be None if kind is Kind.CANCELLATION. May be None + for any other kind. + timeout: An optional length of time (measured from the beginning of the + operation) to allow for the entire operation. If None, a default value on + the back will be used. If present and excessively large, the back may + limit the operation to a smaller duration of its choice. May be present + for any ticket kind; setting a value on a later ticket allows fronts + to request time extensions (or even time reductions!) on in-progress + operations. + """ + + @enum.unique + class Kind(enum.Enum): + """Identifies the overall kind of a FrontToBackTicket.""" + + COMMENCEMENT = 'commencement' + CONTINUATION = 'continuation' + COMPLETION = 'completion' + ENTIRE = 'entire' + CANCELLATION = 'cancellation' + EXPIRATION = 'expiration' + SERVICER_FAILURE = 'servicer failure' + SERVICED_FAILURE = 'serviced failure' + RECEPTION_FAILURE = 'reception failure' + TRANSMISSION_FAILURE = 'transmission failure' + + +class BackToFrontTicket( + collections.namedtuple( + 'BackToFrontTicket', + ['operation_id', 'sequence_number', 'kind', 'payload'])): + """A sum type for all values sent from a back to a front. + + Attributes: + operation_id: A unique-with-respect-to-equality hashable object identifying + a particular operation. + sequence_number: A zero-indexed integer sequence number identifying the + ticket's place among all the tickets sent from back to front for this + particular operation. + kind: A Kind value describing the overall kind of ticket. + payload: A customer payload object. Must be present if kind is + Kind.CONTINUATION. May be None if kind is Kind.COMPLETION. Must be None + otherwise. + """ + + @enum.unique + class Kind(enum.Enum): + """Identifies the overall kind of a BackToFrontTicket.""" + + CONTINUATION = 'continuation' + COMPLETION = 'completion' + CANCELLATION = 'cancellation' + EXPIRATION = 'expiration' + SERVICER_FAILURE = 'servicer failure' + SERVICED_FAILURE = 'serviced failure' + RECEPTION_FAILURE = 'reception failure' + TRANSMISSION_FAILURE = 'transmission failure' + + +class ForeLink(object): + """Accepts back-to-front tickets and emits front-to-back tickets.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def accept_back_to_front_ticket(self, ticket): + """Accept a BackToFrontTicket. + + Args: + ticket: Any BackToFrontTicket. + """ + raise NotImplementedError() + + @abc.abstractmethod + def join_rear_link(self, rear_link): + """Mates this object with a peer with which it will exchange tickets.""" + raise NotImplementedError() + + +class RearLink(object): + """Accepts front-to-back tickets and emits back-to-front tickets.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def accept_front_to_back_ticket(self, ticket): + """Accepts a FrontToBackTicket. + + Args: + ticket: Any FrontToBackTicket. + """ + raise NotImplementedError() + + @abc.abstractmethod + def join_fore_link(self, fore_link): + """Mates this object with a peer with which it will exchange tickets.""" + raise NotImplementedError() + + +class FrontLink(Front, ForeLink): + """Clientish objects that operate by sending and receiving tickets.""" + __metaclass__ = abc.ABCMeta + + +class BackLink(Back, RearLink): + """Serverish objects that operate by sending and receiving tickets.""" + __metaclass__ = abc.ABCMeta diff --git a/src/python/grpcio/grpc/framework/base/null.py b/src/python/grpcio/grpc/framework/base/null.py new file mode 100644 index 00000000..1e30d455 --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/null.py @@ -0,0 +1,56 @@ +# Copyright 2015, 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. + +"""Null links that ignore tickets passed to them.""" + +from grpc.framework.base import interfaces + + +class _NullForeLink(interfaces.ForeLink): + """A do-nothing ForeLink.""" + + def accept_back_to_front_ticket(self, ticket): + pass + + def join_rear_link(self, rear_link): + raise NotImplementedError() + + +class _NullRearLink(interfaces.RearLink): + """A do-nothing RearLink.""" + + def accept_front_to_back_ticket(self, ticket): + pass + + def join_fore_link(self, fore_link): + raise NotImplementedError() + + +NULL_FORE_LINK = _NullForeLink() +NULL_REAR_LINK = _NullRearLink() diff --git a/src/python/grpcio/grpc/framework/base/util.py b/src/python/grpcio/grpc/framework/base/util.py new file mode 100644 index 00000000..c832c826 --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/util.py @@ -0,0 +1,94 @@ +# Copyright 2015, 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. + +"""Utilities helpful for working with the base layer of RPC Framework.""" + +import collections +import threading + +from grpc.framework.base import interfaces + + +class _ServicedSubscription( + collections.namedtuple('_ServicedSubscription', ['kind', 'ingestor']), + interfaces.ServicedSubscription): + """See interfaces.ServicedSubscription for specification.""" + +_NONE_SUBSCRIPTION = _ServicedSubscription( + interfaces.ServicedSubscription.Kind.NONE, None) +_TERMINATION_ONLY_SUBSCRIPTION = _ServicedSubscription( + interfaces.ServicedSubscription.Kind.TERMINATION_ONLY, None) + + +def none_serviced_subscription(): + """Creates a "none" interfaces.ServicedSubscription object. + + Returns: + An interfaces.ServicedSubscription indicating no subscription to an + operation's results (such as would be the case for a fire-and-forget + operation invocation). + """ + return _NONE_SUBSCRIPTION + + +def termination_only_serviced_subscription(): + """Creates a "termination only" interfaces.ServicedSubscription object. + + Returns: + An interfaces.ServicedSubscription indicating that the front-side customer + is interested only in the overall termination outcome of the operation + (such as completion or expiration) and would ignore the actual results of + the operation. + """ + return _TERMINATION_ONLY_SUBSCRIPTION + + +def full_serviced_subscription(ingestor): + """Creates a "full" interfaces.ServicedSubscription object. + + Args: + ingestor: An interfaces.ServicedIngestor. + + Returns: + An interfaces.ServicedSubscription object indicating a full + subscription. + """ + return _ServicedSubscription( + interfaces.ServicedSubscription.Kind.FULL, ingestor) + + +def wait_for_idle(end): + """Waits for an interfaces.End to complete all operations. + + Args: + end: Any interfaces.End. + """ + event = threading.Event() + end.add_idle_action(event.set) + event.wait() diff --git a/src/python/grpcio/grpc/framework/common/__init__.py b/src/python/grpcio/grpc/framework/common/__init__.py new file mode 100644 index 00000000..70865191 --- /dev/null +++ b/src/python/grpcio/grpc/framework/common/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, 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. + + diff --git a/src/python/grpcio/grpc/framework/common/cardinality.py b/src/python/grpcio/grpc/framework/common/cardinality.py new file mode 100644 index 00000000..610425e8 --- /dev/null +++ b/src/python/grpcio/grpc/framework/common/cardinality.py @@ -0,0 +1,42 @@ +# Copyright 2015, 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. + +"""Defines an enum for classifying RPC methods by streaming semantics.""" + +import enum + + +@enum.unique +class Cardinality(enum.Enum): + """Describes the streaming semantics of an RPC method.""" + + UNARY_UNARY = 'request-unary/response-unary' + UNARY_STREAM = 'request-unary/response-streaming' + STREAM_UNARY = 'request-streaming/response-unary' + STREAM_STREAM = 'request-streaming/response-streaming' diff --git a/src/python/grpcio/grpc/framework/common/style.py b/src/python/grpcio/grpc/framework/common/style.py new file mode 100644 index 00000000..6ae694bd --- /dev/null +++ b/src/python/grpcio/grpc/framework/common/style.py @@ -0,0 +1,40 @@ +# Copyright 2015, 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. + +"""Defines an enum for classifying RPC methods by control flow semantics.""" + +import enum + + +@enum.unique +class Service(enum.Enum): + """Describes the control flow style of RPC method implementation.""" + + INLINE = 'inline' + EVENT = 'event' diff --git a/src/python/grpcio/grpc/framework/core/__init__.py b/src/python/grpcio/grpc/framework/core/__init__.py new file mode 100644 index 00000000..70865191 --- /dev/null +++ b/src/python/grpcio/grpc/framework/core/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, 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. + + diff --git a/src/python/grpcio/grpc/framework/core/_constants.py b/src/python/grpcio/grpc/framework/core/_constants.py new file mode 100644 index 00000000..0f47cb48 --- /dev/null +++ b/src/python/grpcio/grpc/framework/core/_constants.py @@ -0,0 +1,60 @@ +# Copyright 2015, 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. + +"""Private constants for the package.""" + +from grpc.framework.interfaces.base import base +from grpc.framework.interfaces.links import links + +TICKET_SUBSCRIPTION_FOR_BASE_SUBSCRIPTION_KIND = { + base.Subscription.Kind.NONE: links.Ticket.Subscription.NONE, + base.Subscription.Kind.TERMINATION_ONLY: + links.Ticket.Subscription.TERMINATION, + base.Subscription.Kind.FULL: links.Ticket.Subscription.FULL, + } + +# Mapping from abortive operation outcome to ticket termination to be +# sent to the other side of the operation, or None to indicate that no +# ticket should be sent to the other side in the event of such an +# outcome. +ABORTION_OUTCOME_TO_TICKET_TERMINATION = { + base.Outcome.Kind.CANCELLED: links.Ticket.Termination.CANCELLATION, + base.Outcome.Kind.EXPIRED: links.Ticket.Termination.EXPIRATION, + base.Outcome.Kind.LOCAL_SHUTDOWN: links.Ticket.Termination.SHUTDOWN, + base.Outcome.Kind.REMOTE_SHUTDOWN: None, + base.Outcome.Kind.RECEPTION_FAILURE: + links.Ticket.Termination.RECEPTION_FAILURE, + base.Outcome.Kind.TRANSMISSION_FAILURE: None, + base.Outcome.Kind.LOCAL_FAILURE: links.Ticket.Termination.LOCAL_FAILURE, + base.Outcome.Kind.REMOTE_FAILURE: links.Ticket.Termination.REMOTE_FAILURE, +} + +INTERNAL_ERROR_LOG_MESSAGE = ':-( RPC Framework (Core) internal error! )-:' +TERMINATION_CALLBACK_EXCEPTION_LOG_MESSAGE = ( + 'Exception calling termination callback!') diff --git a/src/python/grpcio/grpc/framework/core/_context.py b/src/python/grpcio/grpc/framework/core/_context.py new file mode 100644 index 00000000..a346e9d4 --- /dev/null +++ b/src/python/grpcio/grpc/framework/core/_context.py @@ -0,0 +1,94 @@ +# Copyright 2015, 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. + +"""State and behavior for operation context.""" + +import time + +# _interfaces is referenced from specification in this module. +from grpc.framework.core import _interfaces # pylint: disable=unused-import +from grpc.framework.core import _utilities +from grpc.framework.interfaces.base import base + + +class OperationContext(base.OperationContext): + """An implementation of interfaces.OperationContext.""" + + def __init__( + self, lock, termination_manager, transmission_manager, + expiration_manager): + """Constructor. + + Args: + lock: The operation-wide lock. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + expiration_manager: The _interfaces.ExpirationManager for the operation. + """ + self._lock = lock + self._termination_manager = termination_manager + self._transmission_manager = transmission_manager + self._expiration_manager = expiration_manager + + def _abort(self, outcome_kind): + with self._lock: + if self._termination_manager.outcome is None: + outcome = _utilities.Outcome(outcome_kind, None, None) + self._termination_manager.abort(outcome) + self._transmission_manager.abort(outcome) + self._expiration_manager.terminate() + + def outcome(self): + """See base.OperationContext.outcome for specification.""" + with self._lock: + return self._termination_manager.outcome + + def add_termination_callback(self, callback): + """See base.OperationContext.add_termination_callback.""" + with self._lock: + if self._termination_manager.outcome is None: + self._termination_manager.add_callback(callback) + return None + else: + return self._termination_manager.outcome + + def time_remaining(self): + """See base.OperationContext.time_remaining for specification.""" + with self._lock: + deadline = self._expiration_manager.deadline() + return max(0.0, deadline - time.time()) + + def cancel(self): + """See base.OperationContext.cancel for specification.""" + self._abort(base.Outcome.Kind.CANCELLED) + + def fail(self, exception): + """See base.OperationContext.fail for specification.""" + self._abort(base.Outcome.Kind.LOCAL_FAILURE) diff --git a/src/python/grpcio/grpc/framework/core/_emission.py b/src/python/grpcio/grpc/framework/core/_emission.py new file mode 100644 index 00000000..8ab59dc3 --- /dev/null +++ b/src/python/grpcio/grpc/framework/core/_emission.py @@ -0,0 +1,100 @@ +# Copyright 2015, 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. + +"""State and behavior for handling emitted values.""" + +from grpc.framework.core import _interfaces +from grpc.framework.core import _utilities +from grpc.framework.interfaces.base import base + + +class EmissionManager(_interfaces.EmissionManager): + """An EmissionManager implementation.""" + + def __init__( + self, lock, termination_manager, transmission_manager, + expiration_manager): + """Constructor. + + Args: + lock: The operation-wide lock. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + expiration_manager: The _interfaces.ExpirationManager for the operation. + """ + self._lock = lock + self._termination_manager = termination_manager + self._transmission_manager = transmission_manager + self._expiration_manager = expiration_manager + self._ingestion_manager = None + + self._initial_metadata_seen = False + self._payload_seen = False + self._completion_seen = False + + def set_ingestion_manager(self, ingestion_manager): + """Sets the ingestion manager with which this manager will cooperate. + + Args: + ingestion_manager: The _interfaces.IngestionManager for the operation. + """ + self._ingestion_manager = ingestion_manager + + def advance( + self, initial_metadata=None, payload=None, completion=None, + allowance=None): + initial_metadata_present = initial_metadata is not None + payload_present = payload is not None + completion_present = completion is not None + allowance_present = allowance is not None + with self._lock: + if self._termination_manager.outcome is None: + if (initial_metadata_present and ( + self._initial_metadata_seen or self._payload_seen or + self._completion_seen) or + payload_present and self._completion_seen or + completion_present and self._completion_seen or + allowance_present and allowance <= 0): + outcome = _utilities.Outcome( + base.Outcome.Kind.LOCAL_FAILURE, None, None) + self._termination_manager.abort(outcome) + self._transmission_manager.abort(outcome) + self._expiration_manager.terminate() + else: + self._initial_metadata_seen |= initial_metadata_present + self._payload_seen |= payload_present + self._completion_seen |= completion_present + if completion_present: + self._termination_manager.emission_complete() + self._ingestion_manager.local_emissions_done() + self._transmission_manager.advance( + initial_metadata, payload, completion, allowance) + if allowance_present: + self._ingestion_manager.add_local_allowance(allowance) diff --git a/src/python/grpcio/grpc/framework/core/_end.py b/src/python/grpcio/grpc/framework/core/_end.py new file mode 100644 index 00000000..8e07d906 --- /dev/null +++ b/src/python/grpcio/grpc/framework/core/_end.py @@ -0,0 +1,249 @@ +# Copyright 2015, 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. + +"""Implementation of base.End.""" + +import abc +import threading +import uuid + +from grpc.framework.core import _operation +from grpc.framework.core import _utilities +from grpc.framework.foundation import callable_util +from grpc.framework.foundation import later +from grpc.framework.foundation import logging_pool +from grpc.framework.interfaces.base import base +from grpc.framework.interfaces.links import links +from grpc.framework.interfaces.links import utilities + +_IDLE_ACTION_EXCEPTION_LOG_MESSAGE = 'Exception calling idle action!' + + +class End(base.End, links.Link): + """A bridge between base.End and links.Link. + + Implementations of this interface translate arriving tickets into + calls on application objects implementing base interfaces and + translate calls from application objects implementing base interfaces + into tickets sent to a joined link. + """ + __metaclass__ = abc.ABCMeta + + +class _Cycle(object): + """State for a single start-stop End lifecycle.""" + + def __init__(self, pool): + self.pool = pool + self.grace = False + self.futures = [] + self.operations = {} + self.idle_actions = [] + + +def _abort(operations): + for operation in operations: + operation.abort(base.Outcome.Kind.LOCAL_SHUTDOWN) + + +def _cancel_futures(futures): + for future in futures: + future.cancel() + + +def _future_shutdown(lock, cycle, event): + def in_future(): + with lock: + _abort(cycle.operations.values()) + _cancel_futures(cycle.futures) + return in_future + + +def _termination_action(lock, stats, operation_id, cycle): + """Constructs the termination action for a single operation. + + Args: + lock: A lock to hold during the termination action. + stats: A mapping from base.Outcome.Kind values to integers to increment + with the outcome kind given to the termination action. + operation_id: The operation ID for the termination action. + cycle: A _Cycle value to be updated during the termination action. + + Returns: + A callable that takes an operation outcome kind as its sole parameter and + that should be used as the termination action for the operation + associated with the given operation ID. + """ + def termination_action(outcome_kind): + with lock: + stats[outcome_kind] += 1 + cycle.operations.pop(operation_id, None) + if not cycle.operations: + for action in cycle.idle_actions: + cycle.pool.submit(action) + cycle.idle_actions = [] + if cycle.grace: + _cancel_futures(cycle.futures) + cycle.pool.shutdown(wait=False) + return termination_action + + +class _End(End): + """An End implementation.""" + + def __init__(self, servicer_package): + """Constructor. + + Args: + servicer_package: A _ServicerPackage for servicing operations or None if + this end will not be used to service operations. + """ + self._lock = threading.Condition() + self._servicer_package = servicer_package + + self._stats = {outcome_kind: 0 for outcome_kind in base.Outcome.Kind} + + self._mate = None + + self._cycle = None + + def start(self): + """See base.End.start for specification.""" + with self._lock: + if self._cycle is not None: + raise ValueError('Tried to start a not-stopped End!') + else: + self._cycle = _Cycle(logging_pool.pool(1)) + + def stop(self, grace): + """See base.End.stop for specification.""" + with self._lock: + if self._cycle is None: + event = threading.Event() + event.set() + return event + elif not self._cycle.operations: + event = threading.Event() + self._cycle.pool.submit(event.set) + self._cycle.pool.shutdown(wait=False) + self._cycle = None + return event + else: + self._cycle.grace = True + event = threading.Event() + self._cycle.idle_actions.append(event.set) + if 0 < grace: + future = later.later( + grace, _future_shutdown(self._lock, self._cycle, event)) + self._cycle.futures.append(future) + else: + _abort(self._cycle.operations.values()) + return event + + def operate( + self, group, method, subscription, timeout, initial_metadata=None, + payload=None, completion=None, protocol_options=None): + """See base.End.operate for specification.""" + operation_id = uuid.uuid4() + with self._lock: + if self._cycle is None or self._cycle.grace: + raise ValueError('Can\'t operate on stopped or stopping End!') + termination_action = _termination_action( + self._lock, self._stats, operation_id, self._cycle) + operation = _operation.invocation_operate( + operation_id, group, method, subscription, timeout, protocol_options, + initial_metadata, payload, completion, self._mate.accept_ticket, + termination_action, self._cycle.pool) + self._cycle.operations[operation_id] = operation + return operation.context, operation.operator + + def operation_stats(self): + """See base.End.operation_stats for specification.""" + with self._lock: + return dict(self._stats) + + def add_idle_action(self, action): + """See base.End.add_idle_action for specification.""" + with self._lock: + if self._cycle is None: + raise ValueError('Can\'t add idle action to stopped End!') + action_with_exceptions_logged = callable_util.with_exceptions_logged( + action, _IDLE_ACTION_EXCEPTION_LOG_MESSAGE) + if self._cycle.operations: + self._cycle.idle_actions.append(action_with_exceptions_logged) + else: + self._cycle.pool.submit(action_with_exceptions_logged) + + def accept_ticket(self, ticket): + """See links.Link.accept_ticket for specification.""" + with self._lock: + if self._cycle is not None: + operation = self._cycle.operations.get(ticket.operation_id) + if operation is not None: + operation.handle_ticket(ticket) + elif self._servicer_package is not None and not self._cycle.grace: + termination_action = _termination_action( + self._lock, self._stats, ticket.operation_id, self._cycle) + operation = _operation.service_operate( + self._servicer_package, ticket, self._mate.accept_ticket, + termination_action, self._cycle.pool) + if operation is not None: + self._cycle.operations[ticket.operation_id] = operation + + def join_link(self, link): + """See links.Link.join_link for specification.""" + with self._lock: + self._mate = utilities.NULL_LINK if link is None else link + + +def serviceless_end_link(): + """Constructs an End usable only for invoking operations. + + Returns: + An End usable for translating operations into ticket exchange. + """ + return _End(None) + + +def serviceful_end_link(servicer, default_timeout, maximum_timeout): + """Constructs an End capable of servicing operations. + + Args: + servicer: An interfaces.Servicer for servicing operations. + default_timeout: A length of time in seconds to be used as the default + time alloted for a single operation. + maximum_timeout: A length of time in seconds to be used as the maximum + time alloted for a single operation. + + Returns: + An End capable of servicing the operations requested of it through ticket + exchange. + """ + return _End( + _utilities.ServicerPackage(servicer, default_timeout, maximum_timeout)) diff --git a/src/python/grpcio/grpc/framework/core/_expiration.py b/src/python/grpcio/grpc/framework/core/_expiration.py new file mode 100644 index 00000000..ded0ab6b --- /dev/null +++ b/src/python/grpcio/grpc/framework/core/_expiration.py @@ -0,0 +1,154 @@ +# Copyright 2015, 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. + +"""State and behavior for operation expiration.""" + +import time + +from grpc.framework.core import _interfaces +from grpc.framework.core import _utilities +from grpc.framework.foundation import later +from grpc.framework.interfaces.base import base + + +class _ExpirationManager(_interfaces.ExpirationManager): + """An implementation of _interfaces.ExpirationManager.""" + + def __init__( + self, commencement, timeout, maximum_timeout, lock, termination_manager, + transmission_manager): + """Constructor. + + Args: + commencement: The time in seconds since the epoch at which the operation + began. + timeout: A length of time in seconds to allow for the operation to run. + maximum_timeout: The maximum length of time in seconds to allow for the + operation to run despite what is requested via this object's + change_timout method. + lock: The operation-wide lock. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + """ + self._lock = lock + self._termination_manager = termination_manager + self._transmission_manager = transmission_manager + self._commencement = commencement + self._maximum_timeout = maximum_timeout + + self._timeout = timeout + self._deadline = commencement + timeout + self._index = None + self._future = None + + def _expire(self, index): + def expire(): + with self._lock: + if self._future is not None and index == self._index: + self._future = None + self._termination_manager.expire() + self._transmission_manager.abort( + _utilities.Outcome(base.Outcome.Kind.EXPIRED, None, None)) + return expire + + def start(self): + self._index = 0 + self._future = later.later(self._timeout, self._expire(0)) + + def change_timeout(self, timeout): + if self._future is not None and timeout != self._timeout: + self._future.cancel() + new_timeout = min(timeout, self._maximum_timeout) + new_index = self._index + 1 + self._timeout = new_timeout + self._deadline = self._commencement + new_timeout + self._index = new_index + delay = self._deadline - time.time() + self._future = later.later(delay, self._expire(new_index)) + if new_timeout != timeout: + self._transmission_manager.timeout(new_timeout) + + def deadline(self): + return self._deadline + + def terminate(self): + if self._future: + self._future.cancel() + self._future = None + self._deadline_index = None + + +def invocation_expiration_manager( + timeout, lock, termination_manager, transmission_manager): + """Creates an _interfaces.ExpirationManager appropriate for front-side use. + + Args: + timeout: A length of time in seconds to allow for the operation to run. + lock: The operation-wide lock. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + + Returns: + An _interfaces.ExpirationManager appropriate for invocation-side use. + """ + expiration_manager = _ExpirationManager( + time.time(), timeout, timeout, lock, termination_manager, + transmission_manager) + expiration_manager.start() + return expiration_manager + + +def service_expiration_manager( + timeout, default_timeout, maximum_timeout, lock, termination_manager, + transmission_manager): + """Creates an _interfaces.ExpirationManager appropriate for back-side use. + + Args: + timeout: A length of time in seconds to allow for the operation to run. May + be None in which case default_timeout will be used. + default_timeout: The default length of time in seconds to allow for the + operation to run if the front-side customer has not specified such a value + (or if the value they specified is not yet known). + maximum_timeout: The maximum length of time in seconds to allow for the + operation to run. + lock: The operation-wide lock. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + + Returns: + An _interfaces.ExpirationManager appropriate for service-side use. + """ + expiration_manager = _ExpirationManager( + time.time(), default_timeout if timeout is None else timeout, + maximum_timeout, lock, termination_manager, transmission_manager) + expiration_manager.start() + return expiration_manager diff --git a/src/python/grpcio/grpc/framework/core/_ingestion.py b/src/python/grpcio/grpc/framework/core/_ingestion.py new file mode 100644 index 00000000..4129a8ce --- /dev/null +++ b/src/python/grpcio/grpc/framework/core/_ingestion.py @@ -0,0 +1,438 @@ +# Copyright 2015, 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. + +"""State and behavior for ingestion during an operation.""" + +import abc +import collections +import enum + +from grpc.framework.core import _constants +from grpc.framework.core import _interfaces +from grpc.framework.core import _utilities +from grpc.framework.foundation import abandonment +from grpc.framework.foundation import callable_util +from grpc.framework.interfaces.base import base + +_CREATE_SUBSCRIPTION_EXCEPTION_LOG_MESSAGE = 'Exception initializing ingestion!' +_INGESTION_EXCEPTION_LOG_MESSAGE = 'Exception during ingestion!' + + +class _SubscriptionCreation( + collections.namedtuple( + '_SubscriptionCreation', + ('kind', 'subscription', 'code', 'details',))): + """A sum type for the outcome of ingestion initialization. + + Attributes: + kind: A Kind value coarsely indicating how subscription creation completed. + subscription: The created subscription. Only present if kind is + Kind.SUBSCRIPTION. + code: A code value to be sent to the other side of the operation along with + an indication that the operation is being aborted due to an error on the + remote side of the operation. Only present if kind is Kind.REMOTE_ERROR. + details: A details value to be sent to the other side of the operation + along with an indication that the operation is being aborted due to an + error on the remote side of the operation. Only present if kind is + Kind.REMOTE_ERROR. + """ + + @enum.unique + class Kind(enum.Enum): + SUBSCRIPTION = 'subscription' + REMOTE_ERROR = 'remote error' + ABANDONED = 'abandoned' + + +class _SubscriptionCreator(object): + """Common specification of subscription-creating behavior.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def create(self, group, method): + """Creates the base.Subscription of the local customer. + + Any exceptions raised by this method should be attributed to and treated as + defects in the customer code called by this method. + + Args: + group: The group identifier of the operation. + method: The method identifier of the operation. + + Returns: + A _SubscriptionCreation describing the result of subscription creation. + """ + raise NotImplementedError() + + +class _ServiceSubscriptionCreator(_SubscriptionCreator): + """A _SubscriptionCreator appropriate for service-side use.""" + + def __init__(self, servicer, operation_context, output_operator): + """Constructor. + + Args: + servicer: The base.Servicer that will service the operation. + operation_context: A base.OperationContext for the operation to be passed + to the customer. + output_operator: A base.Operator for the operation to be passed to the + customer and to be called by the customer to accept operation data + emitted by the customer. + """ + self._servicer = servicer + self._operation_context = operation_context + self._output_operator = output_operator + + def create(self, group, method): + try: + subscription = self._servicer.service( + group, method, self._operation_context, self._output_operator) + except base.NoSuchMethodError as e: + return _SubscriptionCreation( + _SubscriptionCreation.Kind.REMOTE_ERROR, None, e.code, e.details) + except abandonment.Abandoned: + return _SubscriptionCreation( + _SubscriptionCreation.Kind.ABANDONED, None, None, None) + else: + return _SubscriptionCreation( + _SubscriptionCreation.Kind.SUBSCRIPTION, subscription, None, None) + + +def _wrap(behavior): + def wrapped(*args, **kwargs): + try: + behavior(*args, **kwargs) + except abandonment.Abandoned: + return False + else: + return True + return wrapped + + +class _IngestionManager(_interfaces.IngestionManager): + """An implementation of _interfaces.IngestionManager.""" + + def __init__( + self, lock, pool, subscription, subscription_creator, termination_manager, + transmission_manager, expiration_manager, protocol_manager): + """Constructor. + + Args: + lock: The operation-wide lock. + pool: A thread pool in which to execute customer code. + subscription: A base.Subscription describing the customer's interest in + operation values from the other side. May be None if + subscription_creator is not None. + subscription_creator: A _SubscriptionCreator wrapping the portion of + customer code that when called returns the base.Subscription describing + the customer's interest in operation values from the other side. May be + None if subscription is not None. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + expiration_manager: The _interfaces.ExpirationManager for the operation. + protocol_manager: The _interfaces.ProtocolManager for the operation. + """ + self._lock = lock + self._pool = pool + self._termination_manager = termination_manager + self._transmission_manager = transmission_manager + self._expiration_manager = expiration_manager + self._protocol_manager = protocol_manager + + if subscription is None: + self._subscription_creator = subscription_creator + self._wrapped_operator = None + elif subscription.kind is base.Subscription.Kind.FULL: + self._subscription_creator = None + self._wrapped_operator = _wrap(subscription.operator.advance) + else: + # TODO(nathaniel): Support other subscriptions. + raise ValueError('Unsupported subscription "%s"!' % subscription.kind) + self._pending_initial_metadata = None + self._pending_payloads = [] + self._pending_completion = None + self._local_allowance = 1 + # A nonnegative integer or None, with None indicating that the local + # customer is done emitting anyway so there's no need to bother it by + # informing it that the remote customer has granted it further permission to + # emit. + self._remote_allowance = 0 + self._processing = False + + def _abort_internal_only(self): + self._subscription_creator = None + self._wrapped_operator = None + self._pending_initial_metadata = None + self._pending_payloads = None + self._pending_completion = None + + def _abort_and_notify(self, outcome_kind, code, details): + self._abort_internal_only() + if self._termination_manager.outcome is None: + outcome = _utilities.Outcome(outcome_kind, code, details) + self._termination_manager.abort(outcome) + self._transmission_manager.abort(outcome) + self._expiration_manager.terminate() + + def _operator_next(self): + """Computes the next step for full-subscription ingestion. + + Returns: + An initial_metadata, payload, completion, allowance, continue quintet + indicating what operation values (if any) are available to pass into + customer code and whether or not there is anything immediately + actionable to call customer code to do. + """ + if self._wrapped_operator is None: + return None, None, None, None, False + else: + initial_metadata, payload, completion, allowance, action = [None] * 5 + if self._pending_initial_metadata is not None: + initial_metadata = self._pending_initial_metadata + self._pending_initial_metadata = None + action = True + if self._pending_payloads and 0 < self._local_allowance: + payload = self._pending_payloads.pop(0) + self._local_allowance -= 1 + action = True + if not self._pending_payloads and self._pending_completion is not None: + completion = self._pending_completion + self._pending_completion = None + action = True + if self._remote_allowance is not None and 0 < self._remote_allowance: + allowance = self._remote_allowance + self._remote_allowance = 0 + action = True + return initial_metadata, payload, completion, allowance, bool(action) + + def _operator_process( + self, wrapped_operator, initial_metadata, payload, + completion, allowance): + while True: + advance_outcome = callable_util.call_logging_exceptions( + wrapped_operator, _INGESTION_EXCEPTION_LOG_MESSAGE, + initial_metadata=initial_metadata, payload=payload, + completion=completion, allowance=allowance) + if advance_outcome.exception is None: + if advance_outcome.return_value: + with self._lock: + if self._termination_manager.outcome is not None: + return + if completion is not None: + self._termination_manager.ingestion_complete() + initial_metadata, payload, completion, allowance, moar = ( + self._operator_next()) + if not moar: + self._processing = False + return + else: + with self._lock: + if self._termination_manager.outcome is None: + self._abort_and_notify( + base.Outcome.Kind.LOCAL_FAILURE, None, None) + return + else: + with self._lock: + if self._termination_manager.outcome is None: + self._abort_and_notify(base.Outcome.Kind.LOCAL_FAILURE, None, None) + return + + def _operator_post_create(self, subscription): + wrapped_operator = _wrap(subscription.operator.advance) + with self._lock: + if self._termination_manager.outcome is not None: + return + self._wrapped_operator = wrapped_operator + self._subscription_creator = None + metadata, payload, completion, allowance, moar = self._operator_next() + if not moar: + self._processing = False + return + self._operator_process( + wrapped_operator, metadata, payload, completion, allowance) + + def _create(self, subscription_creator, group, name): + outcome = callable_util.call_logging_exceptions( + subscription_creator.create, + _CREATE_SUBSCRIPTION_EXCEPTION_LOG_MESSAGE, group, name) + if outcome.return_value is None: + with self._lock: + if self._termination_manager.outcome is None: + self._abort_and_notify(base.Outcome.Kind.LOCAL_FAILURE, None, None) + elif outcome.return_value.kind is _SubscriptionCreation.Kind.ABANDONED: + with self._lock: + if self._termination_manager.outcome is None: + self._abort_and_notify(base.Outcome.Kind.LOCAL_FAILURE, None, None) + elif outcome.return_value.kind is _SubscriptionCreation.Kind.REMOTE_ERROR: + code = outcome.return_value.code + details = outcome.return_value.details + with self._lock: + if self._termination_manager.outcome is None: + self._abort_and_notify( + base.Outcome.Kind.REMOTE_FAILURE, code, details) + elif outcome.return_value.subscription.kind is base.Subscription.Kind.FULL: + self._protocol_manager.set_protocol_receiver( + outcome.return_value.subscription.protocol_receiver) + self._operator_post_create(outcome.return_value.subscription) + else: + # TODO(nathaniel): Support other subscriptions. + raise ValueError( + 'Unsupported "%s"!' % outcome.return_value.subscription.kind) + + def _store_advance(self, initial_metadata, payload, completion, allowance): + if initial_metadata is not None: + self._pending_initial_metadata = initial_metadata + if payload is not None: + self._pending_payloads.append(payload) + if completion is not None: + self._pending_completion = completion + if allowance is not None and self._remote_allowance is not None: + self._remote_allowance += allowance + + def _operator_advance(self, initial_metadata, payload, completion, allowance): + if self._processing: + self._store_advance(initial_metadata, payload, completion, allowance) + else: + action = False + if initial_metadata is not None: + action = True + if payload is not None: + if 0 < self._local_allowance: + self._local_allowance -= 1 + action = True + else: + self._pending_payloads.append(payload) + payload = False + if completion is not None: + if self._pending_payloads: + self._pending_completion = completion + else: + action = True + if allowance is not None and self._remote_allowance is not None: + allowance += self._remote_allowance + self._remote_allowance = 0 + action = True + if action: + self._pool.submit( + callable_util.with_exceptions_logged( + self._operator_process, _constants.INTERNAL_ERROR_LOG_MESSAGE), + self._wrapped_operator, initial_metadata, payload, completion, + allowance) + + def set_group_and_method(self, group, method): + """See _interfaces.IngestionManager.set_group_and_method for spec.""" + if self._subscription_creator is not None and not self._processing: + self._pool.submit( + callable_util.with_exceptions_logged( + self._create, _constants.INTERNAL_ERROR_LOG_MESSAGE), + self._subscription_creator, group, method) + self._processing = True + + def add_local_allowance(self, allowance): + """See _interfaces.IngestionManager.add_local_allowance for spec.""" + if any((self._subscription_creator, self._wrapped_operator,)): + self._local_allowance += allowance + if not self._processing: + initial_metadata, payload, completion, allowance, moar = ( + self._operator_next()) + if moar: + self._pool.submit( + callable_util.with_exceptions_logged( + self._operator_process, + _constants.INTERNAL_ERROR_LOG_MESSAGE), + initial_metadata, payload, completion, allowance) + + def local_emissions_done(self): + self._remote_allowance = None + + def advance(self, initial_metadata, payload, completion, allowance): + """See _interfaces.IngestionManager.advance for specification.""" + if self._subscription_creator is not None: + self._store_advance(initial_metadata, payload, completion, allowance) + elif self._wrapped_operator is not None: + self._operator_advance(initial_metadata, payload, completion, allowance) + + +def invocation_ingestion_manager( + subscription, lock, pool, termination_manager, transmission_manager, + expiration_manager, protocol_manager): + """Creates an IngestionManager appropriate for invocation-side use. + + Args: + subscription: A base.Subscription indicating the customer's interest in the + data and results from the service-side of the operation. + lock: The operation-wide lock. + pool: A thread pool in which to execute customer code. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + expiration_manager: The _interfaces.ExpirationManager for the operation. + protocol_manager: The _interfaces.ProtocolManager for the operation. + + Returns: + An IngestionManager appropriate for invocation-side use. + """ + return _IngestionManager( + lock, pool, subscription, None, termination_manager, transmission_manager, + expiration_manager, protocol_manager) + + +def service_ingestion_manager( + servicer, operation_context, output_operator, lock, pool, + termination_manager, transmission_manager, expiration_manager, + protocol_manager): + """Creates an IngestionManager appropriate for service-side use. + + The returned IngestionManager will require its set_group_and_name method to be + called before its advance method may be called. + + Args: + servicer: A base.Servicer for servicing the operation. + operation_context: A base.OperationContext for the operation to be passed to + the customer. + output_operator: A base.Operator for the operation to be passed to the + customer and to be called by the customer to accept operation data output + by the customer. + lock: The operation-wide lock. + pool: A thread pool in which to execute customer code. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + expiration_manager: The _interfaces.ExpirationManager for the operation. + protocol_manager: The _interfaces.ProtocolManager for the operation. + + Returns: + An IngestionManager appropriate for service-side use. + """ + subscription_creator = _ServiceSubscriptionCreator( + servicer, operation_context, output_operator) + return _IngestionManager( + lock, pool, None, subscription_creator, termination_manager, + transmission_manager, expiration_manager, protocol_manager) diff --git a/src/python/grpcio/grpc/framework/core/_interfaces.py b/src/python/grpcio/grpc/framework/core/_interfaces.py new file mode 100644 index 00000000..ffa686b2 --- /dev/null +++ b/src/python/grpcio/grpc/framework/core/_interfaces.py @@ -0,0 +1,337 @@ +# Copyright 2015, 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. + +"""Package-internal interfaces.""" + +import abc + +from grpc.framework.interfaces.base import base + + +class TerminationManager(object): + """An object responsible for handling the termination of an operation. + + Attributes: + outcome: None if the operation is active or a base.Outcome value if it has + terminated. + """ + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def add_callback(self, callback): + """Registers a callback to be called on operation termination. + + If the operation has already terminated the callback will not be called. + + Args: + callback: A callable that will be passed a base.Outcome value. + + Returns: + None if the operation has not yet terminated and the passed callback will + be called when it does, or a base.Outcome value describing the + operation termination if the operation has terminated and the callback + will not be called as a result of this method call. + """ + raise NotImplementedError() + + @abc.abstractmethod + def emission_complete(self): + """Indicates that emissions from customer code have completed.""" + raise NotImplementedError() + + @abc.abstractmethod + def transmission_complete(self): + """Indicates that transmissions to the remote end are complete. + + Returns: + True if the operation has terminated or False if the operation remains + ongoing. + """ + raise NotImplementedError() + + @abc.abstractmethod + def reception_complete(self, code, details): + """Indicates that reception from the other side is complete. + + Args: + code: An application-specific code value. + details: An application-specific details value. + """ + raise NotImplementedError() + + @abc.abstractmethod + def ingestion_complete(self): + """Indicates that customer code ingestion of received values is complete.""" + raise NotImplementedError() + + @abc.abstractmethod + def expire(self): + """Indicates that the operation must abort because it has taken too long.""" + raise NotImplementedError() + + @abc.abstractmethod + def abort(self, outcome): + """Indicates that the operation must abort for the indicated reason. + + Args: + outcome: A base.Outcome indicating operation abortion. + """ + raise NotImplementedError() + + +class TransmissionManager(object): + """A manager responsible for transmitting to the other end of an operation.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def kick_off( + self, group, method, timeout, protocol_options, initial_metadata, + payload, completion, allowance): + """Transmits the values associated with operation invocation.""" + raise NotImplementedError() + + @abc.abstractmethod + def advance(self, initial_metadata, payload, completion, allowance): + """Accepts values for transmission to the other end of the operation. + + Args: + initial_metadata: An initial metadata value to be transmitted to the other + side of the operation. May only ever be non-None once. + payload: A payload value. + completion: A base.Completion value. May only ever be non-None in the last + transmission to be made to the other side. + allowance: A positive integer communicating the number of additional + payloads allowed to be transmitted from the other side to this side of + the operation, or None if no additional allowance is being granted in + this call. + """ + raise NotImplementedError() + + @abc.abstractmethod + def timeout(self, timeout): + """Accepts for transmission to the other side a new timeout value. + + Args: + timeout: A positive float used as the new timeout value for the operation + to be transmitted to the other side. + """ + raise NotImplementedError() + + @abc.abstractmethod + def allowance(self, allowance): + """Indicates to this manager that the remote customer is allowing payloads. + + Args: + allowance: A positive integer indicating the number of additional payloads + the remote customer is allowing to be transmitted from this side of the + operation. + """ + raise NotImplementedError() + + @abc.abstractmethod + def remote_complete(self): + """Indicates to this manager that data from the remote side is complete.""" + raise NotImplementedError() + + @abc.abstractmethod + def abort(self, outcome): + """Indicates that the operation has aborted. + + Args: + outcome: A base.Outcome for the operation. If None, indicates that the + operation abortion should not be communicated to the other side of the + operation. + """ + raise NotImplementedError() + + +class ExpirationManager(object): + """A manager responsible for aborting the operation if it runs out of time.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def change_timeout(self, timeout): + """Changes the timeout allotted for the operation. + + Operation duration is always measure from the beginning of the operation; + calling this method changes the operation's allotted time to timeout total + seconds, not timeout seconds from the time of this method call. + + Args: + timeout: A length of time in seconds to allow for the operation. + """ + raise NotImplementedError() + + @abc.abstractmethod + def deadline(self): + """Returns the time until which the operation is allowed to run. + + Returns: + The time (seconds since the epoch) at which the operation will expire. + """ + raise NotImplementedError() + + @abc.abstractmethod + def terminate(self): + """Indicates to this manager that the operation has terminated.""" + raise NotImplementedError() + + +class ProtocolManager(object): + """A manager of protocol-specific values passing through an operation.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def set_protocol_receiver(self, protocol_receiver): + """Registers the customer object that will receive protocol objects. + + Args: + protocol_receiver: A base.ProtocolReceiver to which protocol objects for + the operation should be passed. + """ + raise NotImplementedError() + + @abc.abstractmethod + def accept_protocol_context(self, protocol_context): + """Accepts the protocol context object for the operation. + + Args: + protocol_context: An object designated for use as the protocol context + of the operation, with further semantics implementation-determined. + """ + raise NotImplementedError() + + +class EmissionManager(base.Operator): + """A manager of values emitted by customer code.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def advance( + self, initial_metadata=None, payload=None, completion=None, + allowance=None): + """Accepts a value emitted by customer code. + + This method should only be called by customer code. + + Args: + initial_metadata: An initial metadata value emitted by the local customer + to be sent to the other side of the operation. + payload: A payload value emitted by the local customer to be sent to the + other side of the operation. + completion: A Completion value emitted by the local customer to be sent to + the other side of the operation. + allowance: A positive integer indicating an additional number of payloads + that the local customer is willing to accept from the other side of the + operation. + """ + raise NotImplementedError() + + +class IngestionManager(object): + """A manager responsible for executing customer code. + + This name of this manager comes from its responsibility to pass successive + values from the other side of the operation into the code of the local + customer. + """ + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def set_group_and_method(self, group, method): + """Communicates to this IngestionManager the operation group and method. + + Args: + group: The group identifier of the operation. + method: The method identifier of the operation. + """ + raise NotImplementedError() + + @abc.abstractmethod + def add_local_allowance(self, allowance): + """Communicates to this IngestionManager that more payloads may be ingested. + + Args: + allowance: A positive integer indicating an additional number of payloads + that the local customer is willing to ingest. + """ + raise NotImplementedError() + + @abc.abstractmethod + def local_emissions_done(self): + """Indicates to this manager that local emissions are done.""" + raise NotImplementedError() + + @abc.abstractmethod + def advance(self, initial_metadata, payload, completion, allowance): + """Advances the operation by passing values to the local customer.""" + raise NotImplementedError() + + +class ReceptionManager(object): + """A manager responsible for receiving tickets from the other end.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def receive_ticket(self, ticket): + """Handle a ticket from the other side of the operation. + + Args: + ticket: A links.Ticket for the operation. + """ + raise NotImplementedError() + + +class Operation(object): + """An ongoing operation. + + Attributes: + context: A base.OperationContext object for the operation. + operator: A base.Operator object for the operation for use by the customer + of the operation. + """ + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def handle_ticket(self, ticket): + """Handle a ticket from the other side of the operation. + + Args: + ticket: A links.Ticket from the other side of the operation. + """ + raise NotImplementedError() + + @abc.abstractmethod + def abort(self, outcome_kind): + """Aborts the operation. + + Args: + outcome_kind: A base.Outcome.Kind value indicating operation abortion. + """ + raise NotImplementedError() diff --git a/src/python/grpcio/grpc/framework/core/_operation.py b/src/python/grpcio/grpc/framework/core/_operation.py new file mode 100644 index 00000000..020c0c9e --- /dev/null +++ b/src/python/grpcio/grpc/framework/core/_operation.py @@ -0,0 +1,204 @@ +# Copyright 2015, 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. + +"""Implementation of operations.""" + +import threading + +from grpc.framework.core import _context +from grpc.framework.core import _emission +from grpc.framework.core import _expiration +from grpc.framework.core import _ingestion +from grpc.framework.core import _interfaces +from grpc.framework.core import _protocol +from grpc.framework.core import _reception +from grpc.framework.core import _termination +from grpc.framework.core import _transmission +from grpc.framework.core import _utilities + + +class _EasyOperation(_interfaces.Operation): + """A trivial implementation of interfaces.Operation.""" + + def __init__( + self, lock, termination_manager, transmission_manager, expiration_manager, + context, operator, reception_manager): + """Constructor. + + Args: + lock: The operation-wide lock. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + expiration_manager: The _interfaces.ExpirationManager for the operation. + context: A base.OperationContext for use by the customer during the + operation. + operator: A base.Operator for use by the customer during the operation. + reception_manager: The _interfaces.ReceptionManager for the operation. + """ + self._lock = lock + self._termination_manager = termination_manager + self._transmission_manager = transmission_manager + self._expiration_manager = expiration_manager + self._reception_manager = reception_manager + + self.context = context + self.operator = operator + + def handle_ticket(self, ticket): + with self._lock: + self._reception_manager.receive_ticket(ticket) + + def abort(self, outcome_kind): + with self._lock: + if self._termination_manager.outcome is None: + outcome = _utilities.Outcome(outcome_kind, None, None) + self._termination_manager.abort(outcome) + self._transmission_manager.abort(outcome) + self._expiration_manager.terminate() + + +def invocation_operate( + operation_id, group, method, subscription, timeout, protocol_options, + initial_metadata, payload, completion, ticket_sink, termination_action, + pool): + """Constructs objects necessary for front-side operation management. + + Args: + operation_id: An object identifying the operation. + group: The group identifier of the operation. + method: The method identifier of the operation. + subscription: A base.Subscription describing the customer's interest in the + results of the operation. + timeout: A length of time in seconds to allow for the operation. + protocol_options: A transport-specific, application-specific, and/or + protocol-specific value relating to the invocation. May be None. + initial_metadata: An initial metadata value to be sent to the other side of + the operation. May be None if the initial metadata will be passed later or + if there will be no initial metadata passed at all. + payload: The first payload value to be transmitted to the other side. May be + None if there is no such value or if the customer chose not to pass it at + operation invocation. + completion: A base.Completion value indicating the end of values passed to + the other side of the operation. + ticket_sink: A callable that accepts links.Tickets and delivers them to the + other side of the operation. + termination_action: A callable that accepts the outcome of the operation as + a base.Outcome value to be called on operation completion. + pool: A thread pool with which to do the work of the operation. + + Returns: + An _interfaces.Operation for the operation. + """ + lock = threading.Lock() + with lock: + termination_manager = _termination.invocation_termination_manager( + termination_action, pool) + transmission_manager = _transmission.TransmissionManager( + operation_id, ticket_sink, lock, pool, termination_manager) + expiration_manager = _expiration.invocation_expiration_manager( + timeout, lock, termination_manager, transmission_manager) + protocol_manager = _protocol.invocation_protocol_manager( + subscription, lock, pool, termination_manager, transmission_manager, + expiration_manager) + operation_context = _context.OperationContext( + lock, termination_manager, transmission_manager, expiration_manager) + emission_manager = _emission.EmissionManager( + lock, termination_manager, transmission_manager, expiration_manager) + ingestion_manager = _ingestion.invocation_ingestion_manager( + subscription, lock, pool, termination_manager, transmission_manager, + expiration_manager, protocol_manager) + reception_manager = _reception.ReceptionManager( + termination_manager, transmission_manager, expiration_manager, + protocol_manager, ingestion_manager) + + termination_manager.set_expiration_manager(expiration_manager) + transmission_manager.set_expiration_manager(expiration_manager) + emission_manager.set_ingestion_manager(ingestion_manager) + + transmission_manager.kick_off( + group, method, timeout, protocol_options, initial_metadata, payload, + completion, None) + + return _EasyOperation( + lock, termination_manager, transmission_manager, expiration_manager, + operation_context, emission_manager, reception_manager) + + +def service_operate( + servicer_package, ticket, ticket_sink, termination_action, pool): + """Constructs an Operation for service of an operation. + + Args: + servicer_package: A _utilities.ServicerPackage to be used servicing the + operation. + ticket: The first links.Ticket received for the operation. + ticket_sink: A callable that accepts links.Tickets and delivers them to the + other side of the operation. + termination_action: A callable that accepts the outcome of the operation as + a base.Outcome value to be called on operation completion. + pool: A thread pool with which to do the work of the operation. + + Returns: + An _interfaces.Operation for the operation. + """ + lock = threading.Lock() + with lock: + termination_manager = _termination.service_termination_manager( + termination_action, pool) + transmission_manager = _transmission.TransmissionManager( + ticket.operation_id, ticket_sink, lock, pool, termination_manager) + expiration_manager = _expiration.service_expiration_manager( + ticket.timeout, servicer_package.default_timeout, + servicer_package.maximum_timeout, lock, termination_manager, + transmission_manager) + protocol_manager = _protocol.service_protocol_manager( + lock, pool, termination_manager, transmission_manager, + expiration_manager) + operation_context = _context.OperationContext( + lock, termination_manager, transmission_manager, expiration_manager) + emission_manager = _emission.EmissionManager( + lock, termination_manager, transmission_manager, expiration_manager) + ingestion_manager = _ingestion.service_ingestion_manager( + servicer_package.servicer, operation_context, emission_manager, lock, + pool, termination_manager, transmission_manager, expiration_manager, + protocol_manager) + reception_manager = _reception.ReceptionManager( + termination_manager, transmission_manager, expiration_manager, + protocol_manager, ingestion_manager) + + termination_manager.set_expiration_manager(expiration_manager) + transmission_manager.set_expiration_manager(expiration_manager) + emission_manager.set_ingestion_manager(ingestion_manager) + + reception_manager.receive_ticket(ticket) + + return _EasyOperation( + lock, termination_manager, transmission_manager, expiration_manager, + operation_context, emission_manager, reception_manager) diff --git a/src/python/grpcio/grpc/framework/core/_protocol.py b/src/python/grpcio/grpc/framework/core/_protocol.py new file mode 100644 index 00000000..3177b5e3 --- /dev/null +++ b/src/python/grpcio/grpc/framework/core/_protocol.py @@ -0,0 +1,176 @@ +# Copyright 2015, 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. + +"""State and behavior for passing protocol objects in an operation.""" + +import collections +import enum + +from grpc.framework.core import _constants +from grpc.framework.core import _interfaces +from grpc.framework.core import _utilities +from grpc.framework.foundation import callable_util +from grpc.framework.interfaces.base import base + +_EXCEPTION_LOG_MESSAGE = 'Exception delivering protocol object!' + +_LOCAL_FAILURE_OUTCOME = _utilities.Outcome( + base.Outcome.Kind.LOCAL_FAILURE, None, None) + + +class _Awaited( + collections.namedtuple('_Awaited', ('kind', 'value',))): + + @enum.unique + class Kind(enum.Enum): + NOT_YET_ARRIVED = 'not yet arrived' + ARRIVED = 'arrived' + +_NOT_YET_ARRIVED = _Awaited(_Awaited.Kind.NOT_YET_ARRIVED, None) +_ARRIVED_AND_NONE = _Awaited(_Awaited.Kind.ARRIVED, None) + + +class _Transitory( + collections.namedtuple('_Transitory', ('kind', 'value',))): + + @enum.unique + class Kind(enum.Enum): + NOT_YET_SEEN = 'not yet seen' + PRESENT = 'present' + GONE = 'gone' + +_NOT_YET_SEEN = _Transitory(_Transitory.Kind.NOT_YET_SEEN, None) +_GONE = _Transitory(_Transitory.Kind.GONE, None) + + +class _ProtocolManager(_interfaces.ProtocolManager): + """An implementation of _interfaces.ExpirationManager.""" + + def __init__( + self, protocol_receiver, lock, pool, termination_manager, + transmission_manager, expiration_manager): + """Constructor. + + Args: + protocol_receiver: An _Awaited wrapping of the base.ProtocolReceiver to + which protocol objects should be passed during the operation. May be + of kind _Awaited.Kind.NOT_YET_ARRIVED if the customer's subscription is + not yet known and may be of kind _Awaited.Kind.ARRIVED but with a value + of None if the customer's subscription did not include a + ProtocolReceiver. + lock: The operation-wide lock. + pool: A thread pool. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + expiration_manager: The _interfaces.ExpirationManager for the operation. + """ + self._lock = lock + self._pool = pool + self._termination_manager = termination_manager + self._transmission_manager = transmission_manager + self._expiration_manager = expiration_manager + + self._protocol_receiver = protocol_receiver + self._context = _NOT_YET_SEEN + + def _abort_and_notify(self, outcome): + if self._termination_manager.outcome is None: + self._termination_manager.abort(outcome) + self._transmission_manager.abort(outcome) + self._expiration_manager.terminate() + + def _deliver(self, behavior, value): + def deliver(): + delivery_outcome = callable_util.call_logging_exceptions( + behavior, _EXCEPTION_LOG_MESSAGE, value) + if delivery_outcome.kind is callable_util.Outcome.Kind.RAISED: + with self._lock: + self._abort_and_notify(_LOCAL_FAILURE_OUTCOME) + self._pool.submit( + callable_util.with_exceptions_logged( + deliver, _constants.INTERNAL_ERROR_LOG_MESSAGE)) + + def set_protocol_receiver(self, protocol_receiver): + """See _interfaces.ProtocolManager.set_protocol_receiver for spec.""" + self._protocol_receiver = _Awaited(_Awaited.Kind.ARRIVED, protocol_receiver) + if (self._context.kind is _Transitory.Kind.PRESENT and + protocol_receiver is not None): + self._deliver(protocol_receiver.context, self._context.value) + self._context = _GONE + + def accept_protocol_context(self, protocol_context): + """See _interfaces.ProtocolManager.accept_protocol_context for spec.""" + if self._protocol_receiver.kind is _Awaited.Kind.ARRIVED: + if self._protocol_receiver.value is not None: + self._deliver(self._protocol_receiver.value.context, protocol_context) + self._context = _GONE + else: + self._context = _Transitory(_Transitory.Kind.PRESENT, protocol_context) + + +def invocation_protocol_manager( + subscription, lock, pool, termination_manager, transmission_manager, + expiration_manager): + """Creates an _interfaces.ProtocolManager for invocation-side use. + + Args: + subscription: The local customer's subscription to the operation. + lock: The operation-wide lock. + pool: A thread pool. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + expiration_manager: The _interfaces.ExpirationManager for the operation. + """ + if subscription.kind is base.Subscription.Kind.FULL: + awaited_protocol_receiver = _Awaited( + _Awaited.Kind.ARRIVED, subscription.protocol_receiver) + else: + awaited_protocol_receiver = _ARRIVED_AND_NONE + return _ProtocolManager( + awaited_protocol_receiver, lock, pool, termination_manager, + transmission_manager, expiration_manager) + + +def service_protocol_manager( + lock, pool, termination_manager, transmission_manager, expiration_manager): + """Creates an _interfaces.ProtocolManager for service-side use. + + Args: + lock: The operation-wide lock. + pool: A thread pool. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + expiration_manager: The _interfaces.ExpirationManager for the operation. + """ + return _ProtocolManager( + _NOT_YET_ARRIVED, lock, pool, termination_manager, transmission_manager, + expiration_manager) diff --git a/src/python/grpcio/grpc/framework/core/_reception.py b/src/python/grpcio/grpc/framework/core/_reception.py new file mode 100644 index 00000000..ff81450d --- /dev/null +++ b/src/python/grpcio/grpc/framework/core/_reception.py @@ -0,0 +1,159 @@ +# Copyright 2015, 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. + +"""State and behavior for ticket reception.""" + +from grpc.framework.core import _interfaces +from grpc.framework.core import _utilities +from grpc.framework.interfaces.base import base +from grpc.framework.interfaces.base import utilities +from grpc.framework.interfaces.links import links + +_REMOTE_TICKET_TERMINATION_TO_LOCAL_OUTCOME_KIND = { + links.Ticket.Termination.CANCELLATION: base.Outcome.Kind.CANCELLED, + links.Ticket.Termination.EXPIRATION: base.Outcome.Kind.EXPIRED, + links.Ticket.Termination.SHUTDOWN: base.Outcome.Kind.REMOTE_SHUTDOWN, + links.Ticket.Termination.RECEPTION_FAILURE: + base.Outcome.Kind.RECEPTION_FAILURE, + links.Ticket.Termination.TRANSMISSION_FAILURE: + base.Outcome.Kind.TRANSMISSION_FAILURE, + links.Ticket.Termination.LOCAL_FAILURE: base.Outcome.Kind.REMOTE_FAILURE, + links.Ticket.Termination.REMOTE_FAILURE: base.Outcome.Kind.LOCAL_FAILURE, +} + +_RECEPTION_FAILURE_OUTCOME = _utilities.Outcome( + base.Outcome.Kind.RECEPTION_FAILURE, None, None) + + +def _carrying_protocol_context(ticket): + return ticket.protocol is not None and ticket.protocol.kind in ( + links.Protocol.Kind.INVOCATION_CONTEXT, + links.Protocol.Kind.SERVICER_CONTEXT,) + + +class ReceptionManager(_interfaces.ReceptionManager): + """A ReceptionManager based around a _Receiver passed to it.""" + + def __init__( + self, termination_manager, transmission_manager, expiration_manager, + protocol_manager, ingestion_manager): + """Constructor. + + Args: + termination_manager: The operation's _interfaces.TerminationManager. + transmission_manager: The operation's _interfaces.TransmissionManager. + expiration_manager: The operation's _interfaces.ExpirationManager. + protocol_manager: The operation's _interfaces.ProtocolManager. + ingestion_manager: The operation's _interfaces.IngestionManager. + """ + self._termination_manager = termination_manager + self._transmission_manager = transmission_manager + self._expiration_manager = expiration_manager + self._protocol_manager = protocol_manager + self._ingestion_manager = ingestion_manager + + self._lowest_unseen_sequence_number = 0 + self._out_of_sequence_tickets = {} + self._aborted = False + + def _abort(self, outcome): + self._aborted = True + if self._termination_manager.outcome is None: + self._termination_manager.abort(outcome) + self._transmission_manager.abort(None) + self._expiration_manager.terminate() + + def _sequence_failure(self, ticket): + """Determines a just-arrived ticket's sequential legitimacy. + + Args: + ticket: A just-arrived ticket. + + Returns: + True if the ticket is sequentially legitimate; False otherwise. + """ + if ticket.sequence_number < self._lowest_unseen_sequence_number: + return True + elif ticket.sequence_number in self._out_of_sequence_tickets: + return True + else: + return False + + def _process_one(self, ticket): + if ticket.sequence_number == 0: + self._ingestion_manager.set_group_and_method(ticket.group, ticket.method) + if _carrying_protocol_context(ticket): + self._protocol_manager.accept_protocol_context(ticket.protocol.value) + else: + self._protocol_manager.accept_protocol_context(None) + if ticket.timeout is not None: + self._expiration_manager.change_timeout(ticket.timeout) + if ticket.termination is None: + completion = None + else: + completion = utilities.completion( + ticket.terminal_metadata, ticket.code, ticket.message) + self._termination_manager.reception_complete(ticket.code, ticket.message) + self._ingestion_manager.advance( + ticket.initial_metadata, ticket.payload, completion, ticket.allowance) + if ticket.allowance is not None: + self._transmission_manager.allowance(ticket.allowance) + + def _process(self, ticket): + """Process those tickets ready to be processed. + + Args: + ticket: A just-arrived ticket the sequence number of which matches this + _ReceptionManager's _lowest_unseen_sequence_number field. + """ + while True: + self._process_one(ticket) + next_ticket = self._out_of_sequence_tickets.pop( + ticket.sequence_number + 1, None) + if next_ticket is None: + self._lowest_unseen_sequence_number = ticket.sequence_number + 1 + return + else: + ticket = next_ticket + + def receive_ticket(self, ticket): + """See _interfaces.ReceptionManager.receive_ticket for specification.""" + if self._aborted: + return + elif self._sequence_failure(ticket): + self._abort(_RECEPTION_FAILURE_OUTCOME) + elif ticket.termination not in (None, links.Ticket.Termination.COMPLETION): + outcome_kind = _REMOTE_TICKET_TERMINATION_TO_LOCAL_OUTCOME_KIND[ + ticket.termination] + self._abort( + _utilities.Outcome(outcome_kind, ticket.code, ticket.message)) + elif ticket.sequence_number == self._lowest_unseen_sequence_number: + self._process(ticket) + else: + self._out_of_sequence_tickets[ticket.sequence_number] = ticket diff --git a/src/python/grpcio/grpc/framework/core/_termination.py b/src/python/grpcio/grpc/framework/core/_termination.py new file mode 100644 index 00000000..bdb9147e --- /dev/null +++ b/src/python/grpcio/grpc/framework/core/_termination.py @@ -0,0 +1,228 @@ +# Copyright 2015, 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. + +"""State and behavior for operation termination.""" + +import abc + +from grpc.framework.core import _constants +from grpc.framework.core import _interfaces +from grpc.framework.core import _utilities +from grpc.framework.foundation import callable_util +from grpc.framework.interfaces.base import base + + +def _invocation_completion_predicate( + unused_emission_complete, unused_transmission_complete, + unused_reception_complete, ingestion_complete): + return ingestion_complete + + +def _service_completion_predicate( + unused_emission_complete, transmission_complete, unused_reception_complete, + unused_ingestion_complete): + return transmission_complete + + +class TerminationManager(_interfaces.TerminationManager): + """A _interfaces.TransmissionManager on which another manager may be set.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def set_expiration_manager(self, expiration_manager): + """Sets the expiration manager with which this manager will interact. + + Args: + expiration_manager: The _interfaces.ExpirationManager associated with the + current operation. + """ + raise NotImplementedError() + + +class _TerminationManager(TerminationManager): + """An implementation of TerminationManager.""" + + def __init__(self, predicate, action, pool): + """Constructor. + + Args: + predicate: One of _invocation_completion_predicate or + _service_completion_predicate to be used to determine when the operation + has completed. + action: A behavior to pass the operation outcome's kind on operation + termination. + pool: A thread pool. + """ + self._predicate = predicate + self._action = action + self._pool = pool + self._expiration_manager = None + + self._callbacks = [] + + self._code = None + self._details = None + self._emission_complete = False + self._transmission_complete = False + self._reception_complete = False + self._ingestion_complete = False + + # The None-ness of outcome is the operation-wide record of whether and how + # the operation has terminated. + self.outcome = None + + def set_expiration_manager(self, expiration_manager): + self._expiration_manager = expiration_manager + + def _terminate_internal_only(self, outcome): + """Terminates the operation. + + Args: + outcome: A base.Outcome describing the outcome of the operation. + """ + self.outcome = outcome + callbacks = list(self._callbacks) + self._callbacks = None + + act = callable_util.with_exceptions_logged( + self._action, _constants.INTERNAL_ERROR_LOG_MESSAGE) + + # TODO(issue 3202): Don't call the local application's callbacks if it has + # previously shown a programming defect. + if False and outcome.kind is base.Outcome.Kind.LOCAL_FAILURE: + self._pool.submit(act, base.Outcome.Kind.LOCAL_FAILURE) + else: + def call_callbacks_and_act(callbacks, outcome): + for callback in callbacks: + callback_outcome = callable_util.call_logging_exceptions( + callback, _constants.TERMINATION_CALLBACK_EXCEPTION_LOG_MESSAGE, + outcome) + if callback_outcome.exception is not None: + act_outcome_kind = base.Outcome.Kind.LOCAL_FAILURE + break + else: + act_outcome_kind = outcome.kind + act(act_outcome_kind) + + self._pool.submit( + callable_util.with_exceptions_logged( + call_callbacks_and_act, _constants.INTERNAL_ERROR_LOG_MESSAGE), + callbacks, outcome) + + def _terminate_and_notify(self, outcome): + self._terminate_internal_only(outcome) + self._expiration_manager.terminate() + + def _perhaps_complete(self): + if self._predicate( + self._emission_complete, self._transmission_complete, + self._reception_complete, self._ingestion_complete): + self._terminate_and_notify( + _utilities.Outcome( + base.Outcome.Kind.COMPLETED, self._code, self._details)) + return True + else: + return False + + def is_active(self): + """See _interfaces.TerminationManager.is_active for specification.""" + return self.outcome is None + + def add_callback(self, callback): + """See _interfaces.TerminationManager.add_callback for specification.""" + if self.outcome is None: + self._callbacks.append(callback) + return None + else: + return self.outcome + + def emission_complete(self): + """See superclass method for specification.""" + if self.outcome is None: + self._emission_complete = True + self._perhaps_complete() + + def transmission_complete(self): + """See superclass method for specification.""" + if self.outcome is None: + self._transmission_complete = True + return self._perhaps_complete() + else: + return False + + def reception_complete(self, code, details): + """See superclass method for specification.""" + if self.outcome is None: + self._reception_complete = True + self._code = code + self._details = details + self._perhaps_complete() + + def ingestion_complete(self): + """See superclass method for specification.""" + if self.outcome is None: + self._ingestion_complete = True + self._perhaps_complete() + + def expire(self): + """See _interfaces.TerminationManager.expire for specification.""" + self._terminate_internal_only( + _utilities.Outcome(base.Outcome.Kind.EXPIRED, None, None)) + + def abort(self, outcome): + """See _interfaces.TerminationManager.abort for specification.""" + self._terminate_and_notify(outcome) + + +def invocation_termination_manager(action, pool): + """Creates a TerminationManager appropriate for invocation-side use. + + Args: + action: An action to call on operation termination. + pool: A thread pool in which to execute the passed action and any + termination callbacks that are registered during the operation. + + Returns: + A TerminationManager appropriate for invocation-side use. + """ + return _TerminationManager(_invocation_completion_predicate, action, pool) + + +def service_termination_manager(action, pool): + """Creates a TerminationManager appropriate for service-side use. + + Args: + action: An action to call on operation termination. + pool: A thread pool in which to execute the passed action and any + termination callbacks that are registered during the operation. + + Returns: + A TerminationManager appropriate for service-side use. + """ + return _TerminationManager(_service_completion_predicate, action, pool) diff --git a/src/python/grpcio/grpc/framework/core/_transmission.py b/src/python/grpcio/grpc/framework/core/_transmission.py new file mode 100644 index 00000000..65b12c41 --- /dev/null +++ b/src/python/grpcio/grpc/framework/core/_transmission.py @@ -0,0 +1,335 @@ +# Copyright 2015, 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. + +"""State and behavior for ticket transmission during an operation.""" + +import collections +import enum + +from grpc.framework.core import _constants +from grpc.framework.core import _interfaces +from grpc.framework.core import _utilities +from grpc.framework.foundation import callable_util +from grpc.framework.interfaces.base import base +from grpc.framework.interfaces.links import links + +_TRANSMISSION_EXCEPTION_LOG_MESSAGE = 'Exception during transmission!' + +_TRANSMISSION_FAILURE_OUTCOME = _utilities.Outcome( + base.Outcome.Kind.TRANSMISSION_FAILURE, None, None) + + +def _explode_completion(completion): + if completion is None: + return None, None, None, None + else: + return ( + completion.terminal_metadata, completion.code, completion.message, + links.Ticket.Termination.COMPLETION) + + +class _Abort( + collections.namedtuple( + '_Abort', ('kind', 'termination', 'code', 'details',))): + """Tracks whether the operation aborted and what is to be done about it. + + Attributes: + kind: A Kind value describing the overall kind of the _Abort. + termination: A links.Ticket.Termination value to be sent to the other side + of the operation. Only valid if kind is Kind.ABORTED_NOTIFY_NEEDED. + code: A code value to be sent to the other side of the operation. Only + valid if kind is Kind.ABORTED_NOTIFY_NEEDED. + details: A details value to be sent to the other side of the operation. + Only valid if kind is Kind.ABORTED_NOTIFY_NEEDED. + """ + + @enum.unique + class Kind(enum.Enum): + NOT_ABORTED = 'not aborted' + ABORTED_NOTIFY_NEEDED = 'aborted notify needed' + ABORTED_NO_NOTIFY = 'aborted no notify' + +_NOT_ABORTED = _Abort(_Abort.Kind.NOT_ABORTED, None, None, None) +_ABORTED_NO_NOTIFY = _Abort(_Abort.Kind.ABORTED_NO_NOTIFY, None, None, None) + + +class TransmissionManager(_interfaces.TransmissionManager): + """An _interfaces.TransmissionManager that sends links.Tickets.""" + + def __init__( + self, operation_id, ticket_sink, lock, pool, termination_manager): + """Constructor. + + Args: + operation_id: The operation's ID. + ticket_sink: A callable that accepts tickets and sends them to the other + side of the operation. + lock: The operation-servicing-wide lock object. + pool: A thread pool in which the work of transmitting tickets will be + performed. + termination_manager: The _interfaces.TerminationManager associated with + this operation. + """ + self._lock = lock + self._pool = pool + self._ticket_sink = ticket_sink + self._operation_id = operation_id + self._termination_manager = termination_manager + self._expiration_manager = None + + self._lowest_unused_sequence_number = 0 + self._remote_allowance = 1 + self._remote_complete = False + self._timeout = None + self._local_allowance = 0 + self._initial_metadata = None + self._payloads = [] + self._completion = None + self._abort = _NOT_ABORTED + self._transmitting = False + + def set_expiration_manager(self, expiration_manager): + """Sets the ExpirationManager with which this manager will cooperate.""" + self._expiration_manager = expiration_manager + + def _next_ticket(self): + """Creates the next ticket to be transmitted. + + Returns: + A links.Ticket to be sent to the other side of the operation or None if + there is nothing to be sent at this time. + """ + if self._abort.kind is _Abort.Kind.ABORTED_NO_NOTIFY: + return None + elif self._abort.kind is _Abort.Kind.ABORTED_NOTIFY_NEEDED: + termination = self._abort.termination + code, details = self._abort.code, self._abort.details + self._abort = _ABORTED_NO_NOTIFY + return links.Ticket( + self._operation_id, self._lowest_unused_sequence_number, None, None, + None, None, None, None, None, None, code, details, termination, None) + + action = False + # TODO(nathaniel): Support other subscriptions. + local_subscription = links.Ticket.Subscription.FULL + timeout = self._timeout + if timeout is not None: + self._timeout = None + action = True + if self._local_allowance <= 0: + allowance = None + else: + allowance = self._local_allowance + self._local_allowance = 0 + action = True + initial_metadata = self._initial_metadata + if initial_metadata is not None: + self._initial_metadata = None + action = True + if not self._payloads or self._remote_allowance <= 0: + payload = None + else: + payload = self._payloads.pop(0) + self._remote_allowance -= 1 + action = True + if self._completion is None or self._payloads: + terminal_metadata, code, message, termination = None, None, None, None + else: + terminal_metadata, code, message, termination = _explode_completion( + self._completion) + self._completion = None + action = True + + if action: + ticket = links.Ticket( + self._operation_id, self._lowest_unused_sequence_number, None, None, + local_subscription, timeout, allowance, initial_metadata, payload, + terminal_metadata, code, message, termination, None) + self._lowest_unused_sequence_number += 1 + return ticket + else: + return None + + def _transmit(self, ticket): + """Commences the transmission loop sending tickets. + + Args: + ticket: A links.Ticket to be sent to the other side of the operation. + """ + def transmit(ticket): + while True: + transmission_outcome = callable_util.call_logging_exceptions( + self._ticket_sink, _TRANSMISSION_EXCEPTION_LOG_MESSAGE, ticket) + if transmission_outcome.exception is None: + with self._lock: + if ticket.termination is links.Ticket.Termination.COMPLETION: + self._termination_manager.transmission_complete() + ticket = self._next_ticket() + if ticket is None: + self._transmitting = False + return + else: + with self._lock: + self._abort = _ABORTED_NO_NOTIFY + if self._termination_manager.outcome is None: + self._termination_manager.abort(_TRANSMISSION_FAILURE_OUTCOME) + self._expiration_manager.terminate() + return + + self._pool.submit(callable_util.with_exceptions_logged( + transmit, _constants.INTERNAL_ERROR_LOG_MESSAGE), ticket) + self._transmitting = True + + def kick_off( + self, group, method, timeout, protocol_options, initial_metadata, + payload, completion, allowance): + """See _interfaces.TransmissionManager.kickoff for specification.""" + # TODO(nathaniel): Support other subscriptions. + subscription = links.Ticket.Subscription.FULL + terminal_metadata, code, message, termination = _explode_completion( + completion) + self._remote_allowance = 1 if payload is None else 0 + protocol = links.Protocol(links.Protocol.Kind.CALL_OPTION, protocol_options) + ticket = links.Ticket( + self._operation_id, 0, group, method, subscription, timeout, allowance, + initial_metadata, payload, terminal_metadata, code, message, + termination, protocol) + self._lowest_unused_sequence_number = 1 + self._transmit(ticket) + + def advance(self, initial_metadata, payload, completion, allowance): + """See _interfaces.TransmissionManager.advance for specification.""" + if self._abort.kind is not _Abort.Kind.NOT_ABORTED: + return + + effective_initial_metadata = initial_metadata + effective_payload = payload + effective_completion = completion + if allowance is not None and not self._remote_complete: + effective_allowance = allowance + else: + effective_allowance = None + if self._transmitting: + if effective_initial_metadata is not None: + self._initial_metadata = effective_initial_metadata + if effective_payload is not None: + self._payloads.append(effective_payload) + if effective_completion is not None: + self._completion = effective_completion + if effective_allowance is not None: + self._local_allowance += effective_allowance + else: + if effective_payload is not None: + if 0 < self._remote_allowance: + ticket_payload = effective_payload + self._remote_allowance -= 1 + else: + self._payloads.append(effective_payload) + ticket_payload = None + else: + ticket_payload = None + if effective_completion is not None and not self._payloads: + ticket_completion = effective_completion + else: + self._completion = effective_completion + ticket_completion = None + if any( + (effective_initial_metadata, ticket_payload, ticket_completion, + effective_allowance)): + terminal_metadata, code, message, termination = _explode_completion( + completion) + ticket = links.Ticket( + self._operation_id, self._lowest_unused_sequence_number, None, None, + None, None, allowance, effective_initial_metadata, ticket_payload, + terminal_metadata, code, message, termination, None) + self._lowest_unused_sequence_number += 1 + self._transmit(ticket) + + def timeout(self, timeout): + """See _interfaces.TransmissionManager.timeout for specification.""" + if self._abort.kind is not _Abort.Kind.NOT_ABORTED: + return + elif self._transmitting: + self._timeout = timeout + else: + ticket = links.Ticket( + self._operation_id, self._lowest_unused_sequence_number, None, None, + None, timeout, None, None, None, None, None, None, None, None) + self._lowest_unused_sequence_number += 1 + self._transmit(ticket) + + def allowance(self, allowance): + """See _interfaces.TransmissionManager.allowance for specification.""" + if self._abort.kind is not _Abort.Kind.NOT_ABORTED: + return + elif self._transmitting or not self._payloads: + self._remote_allowance += allowance + else: + self._remote_allowance += allowance - 1 + payload = self._payloads.pop(0) + if self._payloads: + completion = None + else: + completion = self._completion + self._completion = None + terminal_metadata, code, message, termination = _explode_completion( + completion) + ticket = links.Ticket( + self._operation_id, self._lowest_unused_sequence_number, None, None, + None, None, None, None, payload, terminal_metadata, code, message, + termination, None) + self._lowest_unused_sequence_number += 1 + self._transmit(ticket) + + def remote_complete(self): + """See _interfaces.TransmissionManager.remote_complete for specification.""" + self._remote_complete = True + self._local_allowance = 0 + + def abort(self, outcome): + """See _interfaces.TransmissionManager.abort for specification.""" + if self._abort.kind is _Abort.Kind.NOT_ABORTED: + if outcome is None: + self._abort = _ABORTED_NO_NOTIFY + else: + termination = _constants.ABORTION_OUTCOME_TO_TICKET_TERMINATION.get( + outcome.kind) + if termination is None: + self._abort = _ABORTED_NO_NOTIFY + elif self._transmitting: + self._abort = _Abort( + _Abort.Kind.ABORTED_NOTIFY_NEEDED, termination, outcome.code, + outcome.details) + else: + ticket = links.Ticket( + self._operation_id, self._lowest_unused_sequence_number, None, + None, None, None, None, None, None, None, outcome.code, + outcome.details, termination, None) + self._transmit(ticket) + self._abort = _ABORTED_NO_NOTIFY diff --git a/src/python/grpcio/grpc/framework/core/_utilities.py b/src/python/grpcio/grpc/framework/core/_utilities.py new file mode 100644 index 00000000..abedc727 --- /dev/null +++ b/src/python/grpcio/grpc/framework/core/_utilities.py @@ -0,0 +1,54 @@ +# Copyright 2015, 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. + +"""Package-internal utilities.""" + +import collections + +from grpc.framework.interfaces.base import base + + +class ServicerPackage( + collections.namedtuple( + 'ServicerPackage', ('servicer', 'default_timeout', 'maximum_timeout'))): + """A trivial bundle class. + + Attributes: + servicer: A base.Servicer. + default_timeout: A float indicating the length of time in seconds to allow + for an operation invoked without a timeout. + maximum_timeout: A float indicating the maximum length of time in seconds to + allow for an operation. + """ + + +class Outcome( + base.Outcome, + collections.namedtuple('Outcome', ('kind', 'code', 'details',))): + """A trivial implementation of base.Outcome.""" diff --git a/src/python/grpcio/grpc/framework/core/implementations.py b/src/python/grpcio/grpc/framework/core/implementations.py new file mode 100644 index 00000000..364a7fae --- /dev/null +++ b/src/python/grpcio/grpc/framework/core/implementations.py @@ -0,0 +1,62 @@ +# Copyright 2015, 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. + +"""Entry points into the ticket-exchange-based base layer implementation.""" + +# base and links are referenced from specification in this module. +from grpc.framework.core import _end +from grpc.framework.interfaces.base import base # pylint: disable=unused-import +from grpc.framework.interfaces.links import links # pylint: disable=unused-import + + +def invocation_end_link(): + """Creates a base.End-links.Link suitable for operation invocation. + + Returns: + An object that is both a base.End and a links.Link, that supports operation + invocation, and that translates operation invocation into ticket exchange. + """ + return _end.serviceless_end_link() + + +def service_end_link(servicer, default_timeout, maximum_timeout): + """Creates a base.End-links.Link suitable for operation service. + + Args: + servicer: A base.Servicer for servicing operations. + default_timeout: A length of time in seconds to be used as the default + time alloted for a single operation. + maximum_timeout: A length of time in seconds to be used as the maximum + time alloted for a single operation. + + Returns: + An object that is both a base.End and a links.Link and that services + operations that arrive at it through ticket exchange. + """ + return _end.serviceful_end_link(servicer, default_timeout, maximum_timeout) diff --git a/src/python/grpcio/grpc/framework/crust/__init__.py b/src/python/grpcio/grpc/framework/crust/__init__.py new file mode 100644 index 00000000..70865191 --- /dev/null +++ b/src/python/grpcio/grpc/framework/crust/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, 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. + + diff --git a/src/python/grpcio/grpc/framework/crust/_calls.py b/src/python/grpcio/grpc/framework/crust/_calls.py new file mode 100644 index 00000000..bff940d7 --- /dev/null +++ b/src/python/grpcio/grpc/framework/crust/_calls.py @@ -0,0 +1,223 @@ +# Copyright 2015, 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. + +"""Utility functions for invoking RPCs.""" + +from grpc.framework.crust import _control +from grpc.framework.interfaces.base import utilities +from grpc.framework.interfaces.face import face + +_ITERATOR_EXCEPTION_LOG_MESSAGE = 'Exception iterating over requests!' + +_EMPTY_COMPLETION = utilities.completion(None, None, None) + + +def _invoke( + end, group, method, timeout, protocol_options, initial_metadata, payload, + complete): + rendezvous = _control.Rendezvous(None, None) + subscription = utilities.full_subscription( + rendezvous, _control.protocol_receiver(rendezvous)) + operation_context, operator = end.operate( + group, method, subscription, timeout, protocol_options=protocol_options, + initial_metadata=initial_metadata, payload=payload, + completion=_EMPTY_COMPLETION if complete else None) + rendezvous.set_operator_and_context(operator, operation_context) + outcome = operation_context.add_termination_callback(rendezvous.set_outcome) + if outcome is not None: + rendezvous.set_outcome(outcome) + return rendezvous, operation_context, outcome + + +def _event_return_unary( + receiver, abortion_callback, rendezvous, operation_context, outcome, pool): + if outcome is None: + def in_pool(): + abortion = rendezvous.add_abortion_callback(abortion_callback) + if abortion is None: + try: + receiver.initial_metadata(rendezvous.initial_metadata()) + receiver.response(next(rendezvous)) + receiver.complete( + rendezvous.terminal_metadata(), rendezvous.code(), + rendezvous.details()) + except face.AbortionError: + pass + else: + abortion_callback(abortion) + pool.submit(_control.pool_wrap(in_pool, operation_context)) + return rendezvous + + +def _event_return_stream( + receiver, abortion_callback, rendezvous, operation_context, outcome, pool): + if outcome is None: + def in_pool(): + abortion = rendezvous.add_abortion_callback(abortion_callback) + if abortion is None: + try: + receiver.initial_metadata(rendezvous.initial_metadata()) + for response in rendezvous: + receiver.response(response) + receiver.complete( + rendezvous.terminal_metadata(), rendezvous.code(), + rendezvous.details()) + except face.AbortionError: + pass + else: + abortion_callback(abortion) + pool.submit(_control.pool_wrap(in_pool, operation_context)) + return rendezvous + + +def blocking_unary_unary( + end, group, method, timeout, with_call, protocol_options, initial_metadata, + payload): + """Services in a blocking fashion a unary-unary servicer method.""" + rendezvous, unused_operation_context, unused_outcome = _invoke( + end, group, method, timeout, protocol_options, initial_metadata, payload, + True) + if with_call: + return next(rendezvous), rendezvous + else: + return next(rendezvous) + + +def future_unary_unary( + end, group, method, timeout, protocol_options, initial_metadata, payload): + """Services a value-in value-out servicer method by returning a Future.""" + rendezvous, unused_operation_context, unused_outcome = _invoke( + end, group, method, timeout, protocol_options, initial_metadata, payload, + True) + return rendezvous + + +def inline_unary_stream( + end, group, method, timeout, protocol_options, initial_metadata, payload): + """Services a value-in stream-out servicer method.""" + rendezvous, unused_operation_context, unused_outcome = _invoke( + end, group, method, timeout, protocol_options, initial_metadata, payload, + True) + return rendezvous + + +def blocking_stream_unary( + end, group, method, timeout, with_call, protocol_options, initial_metadata, + payload_iterator, pool): + """Services in a blocking fashion a stream-in value-out servicer method.""" + rendezvous, operation_context, outcome = _invoke( + end, group, method, timeout, protocol_options, initial_metadata, None, + False) + if outcome is None: + def in_pool(): + for payload in payload_iterator: + rendezvous.consume(payload) + rendezvous.terminate() + pool.submit(_control.pool_wrap(in_pool, operation_context)) + if with_call: + return next(rendezvous), rendezvous + else: + return next(rendezvous) + else: + if with_call: + return next(rendezvous), rendezvous + else: + return next(rendezvous) + + +def future_stream_unary( + end, group, method, timeout, protocol_options, initial_metadata, + payload_iterator, pool): + """Services a stream-in value-out servicer method by returning a Future.""" + rendezvous, operation_context, outcome = _invoke( + end, group, method, timeout, protocol_options, initial_metadata, None, + False) + if outcome is None: + def in_pool(): + for payload in payload_iterator: + rendezvous.consume(payload) + rendezvous.terminate() + pool.submit(_control.pool_wrap(in_pool, operation_context)) + return rendezvous + + +def inline_stream_stream( + end, group, method, timeout, protocol_options, initial_metadata, + payload_iterator, pool): + """Services a stream-in stream-out servicer method.""" + rendezvous, operation_context, outcome = _invoke( + end, group, method, timeout, protocol_options, initial_metadata, None, + False) + if outcome is None: + def in_pool(): + for payload in payload_iterator: + rendezvous.consume(payload) + rendezvous.terminate() + pool.submit(_control.pool_wrap(in_pool, operation_context)) + return rendezvous + + +def event_unary_unary( + end, group, method, timeout, protocol_options, initial_metadata, payload, + receiver, abortion_callback, pool): + rendezvous, operation_context, outcome = _invoke( + end, group, method, timeout, protocol_options, initial_metadata, payload, + True) + return _event_return_unary( + receiver, abortion_callback, rendezvous, operation_context, outcome, pool) + + +def event_unary_stream( + end, group, method, timeout, protocol_options, initial_metadata, payload, + receiver, abortion_callback, pool): + rendezvous, operation_context, outcome = _invoke( + end, group, method, timeout, protocol_options, initial_metadata, payload, + True) + return _event_return_stream( + receiver, abortion_callback, rendezvous, operation_context, outcome, pool) + + +def event_stream_unary( + end, group, method, timeout, protocol_options, initial_metadata, receiver, + abortion_callback, pool): + rendezvous, operation_context, outcome = _invoke( + end, group, method, timeout, protocol_options, initial_metadata, None, + False) + return _event_return_unary( + receiver, abortion_callback, rendezvous, operation_context, outcome, pool) + + +def event_stream_stream( + end, group, method, timeout, protocol_options, initial_metadata, receiver, + abortion_callback, pool): + rendezvous, operation_context, outcome = _invoke( + end, group, method, timeout, protocol_options, initial_metadata, None, + False) + return _event_return_stream( + receiver, abortion_callback, rendezvous, operation_context, outcome, pool) diff --git a/src/python/grpcio/grpc/framework/crust/_control.py b/src/python/grpcio/grpc/framework/crust/_control.py new file mode 100644 index 00000000..5e9efdf7 --- /dev/null +++ b/src/python/grpcio/grpc/framework/crust/_control.py @@ -0,0 +1,581 @@ +# Copyright 2015, 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. + +"""State and behavior for translating between sync and async control flow.""" + +import collections +import enum +import sys +import threading +import time + +from grpc.framework.foundation import abandonment +from grpc.framework.foundation import callable_util +from grpc.framework.foundation import future +from grpc.framework.foundation import stream +from grpc.framework.interfaces.base import base +from grpc.framework.interfaces.base import utilities +from grpc.framework.interfaces.face import face + +_DONE_CALLBACK_LOG_MESSAGE = 'Exception calling Future "done" callback!' +_INTERNAL_ERROR_LOG_MESSAGE = ':-( RPC Framework (Crust) Internal Error! )-:' + +_CANNOT_SET_INITIAL_METADATA = ( + 'Could not set initial metadata - has it already been set, or has a ' + + 'payload already been sent?') +_CANNOT_SET_TERMINAL_METADATA = ( + 'Could not set terminal metadata - has it already been set, or has RPC ' + + 'completion already been indicated?') +_CANNOT_SET_CODE = ( + 'Could not set code - has it already been set, or has RPC completion ' + + 'already been indicated?') +_CANNOT_SET_DETAILS = ( + 'Could not set details - has it already been set, or has RPC completion ' + + 'already been indicated?') + + +class _DummyOperator(base.Operator): + + def advance( + self, initial_metadata=None, payload=None, completion=None, + allowance=None): + pass + +_DUMMY_OPERATOR = _DummyOperator() + + +class _Awaited( + collections.namedtuple('_Awaited', ('kind', 'value',))): + + @enum.unique + class Kind(enum.Enum): + NOT_YET_ARRIVED = 'not yet arrived' + ARRIVED = 'arrived' + +_NOT_YET_ARRIVED = _Awaited(_Awaited.Kind.NOT_YET_ARRIVED, None) +_ARRIVED_AND_NONE = _Awaited(_Awaited.Kind.ARRIVED, None) + + +class _Transitory( + collections.namedtuple('_Transitory', ('kind', 'value',))): + + @enum.unique + class Kind(enum.Enum): + NOT_YET_SEEN = 'not yet seen' + PRESENT = 'present' + GONE = 'gone' + +_NOT_YET_SEEN = _Transitory(_Transitory.Kind.NOT_YET_SEEN, None) +_GONE = _Transitory(_Transitory.Kind.GONE, None) + + +class _Termination( + collections.namedtuple( + '_Termination', ('terminated', 'abortion', 'abortion_error',))): + """Values indicating whether and how an RPC has terminated. + + Attributes: + terminated: A boolean indicating whether or not the RPC has terminated. + abortion: A face.Abortion value describing the RPC's abortion or None if the + RPC did not abort. + abortion_error: A face.AbortionError describing the RPC's abortion or None + if the RPC did not abort. + """ + +_NOT_TERMINATED = _Termination(False, None, None) + +_OPERATION_OUTCOME_KIND_TO_TERMINATION_CONSTRUCTOR = { + base.Outcome.Kind.COMPLETED: lambda *unused_args: _Termination( + True, None, None), + base.Outcome.Kind.CANCELLED: lambda *args: _Termination( + True, face.Abortion(face.Abortion.Kind.CANCELLED, *args), + face.CancellationError(*args)), + base.Outcome.Kind.EXPIRED: lambda *args: _Termination( + True, face.Abortion(face.Abortion.Kind.EXPIRED, *args), + face.ExpirationError(*args)), + base.Outcome.Kind.LOCAL_SHUTDOWN: lambda *args: _Termination( + True, face.Abortion(face.Abortion.Kind.LOCAL_SHUTDOWN, *args), + face.LocalShutdownError(*args)), + base.Outcome.Kind.REMOTE_SHUTDOWN: lambda *args: _Termination( + True, face.Abortion(face.Abortion.Kind.REMOTE_SHUTDOWN, *args), + face.RemoteShutdownError(*args)), + base.Outcome.Kind.RECEPTION_FAILURE: lambda *args: _Termination( + True, face.Abortion(face.Abortion.Kind.NETWORK_FAILURE, *args), + face.NetworkError(*args)), + base.Outcome.Kind.TRANSMISSION_FAILURE: lambda *args: _Termination( + True, face.Abortion(face.Abortion.Kind.NETWORK_FAILURE, *args), + face.NetworkError(*args)), + base.Outcome.Kind.LOCAL_FAILURE: lambda *args: _Termination( + True, face.Abortion(face.Abortion.Kind.LOCAL_FAILURE, *args), + face.LocalError(*args)), + base.Outcome.Kind.REMOTE_FAILURE: lambda *args: _Termination( + True, face.Abortion(face.Abortion.Kind.REMOTE_FAILURE, *args), + face.RemoteError(*args)), +} + + +def _wait_once_until(condition, until): + if until is None: + condition.wait() + else: + remaining = until - time.time() + if remaining < 0: + raise future.TimeoutError() + else: + condition.wait(timeout=remaining) + + +def _done_callback_as_operation_termination_callback( + done_callback, rendezvous): + def operation_termination_callback(operation_outcome): + rendezvous.set_outcome(operation_outcome) + done_callback(rendezvous) + return operation_termination_callback + + +def _abortion_callback_as_operation_termination_callback( + rpc_abortion_callback, rendezvous_set_outcome): + def operation_termination_callback(operation_outcome): + termination = rendezvous_set_outcome(operation_outcome) + if termination.abortion is not None: + rpc_abortion_callback(termination.abortion) + return operation_termination_callback + + +class Rendezvous(base.Operator, future.Future, stream.Consumer, face.Call): + """A rendez-vous for the threads of an operation. + + Instances of this object present iterator and stream.Consumer interfaces for + interacting with application code and present a base.Operator interface and + maintain a base.Operator internally for interacting with base interface code. + """ + + def __init__(self, operator, operation_context): + self._condition = threading.Condition() + + self._operator = operator + self._operation_context = operation_context + + self._protocol_context = _NOT_YET_ARRIVED + + self._up_initial_metadata = _NOT_YET_ARRIVED + self._up_payload = None + self._up_allowance = 1 + self._up_completion = _NOT_YET_ARRIVED + self._down_initial_metadata = _NOT_YET_SEEN + self._down_payload = None + self._down_allowance = 1 + self._down_terminal_metadata = _NOT_YET_SEEN + self._down_code = _NOT_YET_SEEN + self._down_details = _NOT_YET_SEEN + + self._termination = _NOT_TERMINATED + + # The semantics of future.Future.cancel and future.Future.cancelled are + # slightly wonky, so they have to be tracked separately from the rest of the + # result of the RPC. This field tracks whether cancellation was requested + # prior to termination of the RPC + self._cancelled = False + + def set_operator_and_context(self, operator, operation_context): + with self._condition: + self._operator = operator + self._operation_context = operation_context + + def _down_completion(self): + if self._down_terminal_metadata.kind is _Transitory.Kind.NOT_YET_SEEN: + terminal_metadata = None + self._down_terminal_metadata = _GONE + elif self._down_terminal_metadata.kind is _Transitory.Kind.PRESENT: + terminal_metadata = self._down_terminal_metadata.value + self._down_terminal_metadata = _GONE + else: + terminal_metadata = None + if self._down_code.kind is _Transitory.Kind.NOT_YET_SEEN: + code = None + self._down_code = _GONE + elif self._down_code.kind is _Transitory.Kind.PRESENT: + code = self._down_code.value + self._down_code = _GONE + else: + code = None + if self._down_details.kind is _Transitory.Kind.NOT_YET_SEEN: + details = None + self._down_details = _GONE + elif self._down_details.kind is _Transitory.Kind.PRESENT: + details = self._down_details.value + self._down_details = _GONE + else: + details = None + return utilities.completion(terminal_metadata, code, details) + + def _set_outcome(self, outcome): + if not self._termination.terminated: + self._operator = _DUMMY_OPERATOR + self._operation_context = None + self._down_initial_metadata = _GONE + self._down_payload = None + self._down_terminal_metadata = _GONE + self._down_code = _GONE + self._down_details = _GONE + + if self._up_initial_metadata.kind is _Awaited.Kind.NOT_YET_ARRIVED: + initial_metadata = None + else: + initial_metadata = self._up_initial_metadata.value + if self._up_completion.kind is _Awaited.Kind.NOT_YET_ARRIVED: + terminal_metadata = None + else: + terminal_metadata = self._up_completion.value.terminal_metadata + if outcome.kind is base.Outcome.Kind.COMPLETED: + code = self._up_completion.value.code + details = self._up_completion.value.message + else: + code = outcome.code + details = outcome.details + self._termination = _OPERATION_OUTCOME_KIND_TO_TERMINATION_CONSTRUCTOR[ + outcome.kind](initial_metadata, terminal_metadata, code, details) + + self._condition.notify_all() + + return self._termination + + def advance( + self, initial_metadata=None, payload=None, completion=None, + allowance=None): + with self._condition: + if initial_metadata is not None: + self._up_initial_metadata = _Awaited( + _Awaited.Kind.ARRIVED, initial_metadata) + if payload is not None: + if self._up_initial_metadata.kind is _Awaited.Kind.NOT_YET_ARRIVED: + self._up_initial_metadata = _ARRIVED_AND_NONE + self._up_payload = payload + self._up_allowance -= 1 + if completion is not None: + if self._up_initial_metadata.kind is _Awaited.Kind.NOT_YET_ARRIVED: + self._up_initial_metadata = _ARRIVED_AND_NONE + self._up_completion = _Awaited( + _Awaited.Kind.ARRIVED, completion) + if allowance is not None: + if self._down_payload is not None: + self._operator.advance(payload=self._down_payload) + self._down_payload = None + self._down_allowance += allowance - 1 + else: + self._down_allowance += allowance + self._condition.notify_all() + + def cancel(self): + with self._condition: + if self._operation_context is not None: + self._operation_context.cancel() + self._cancelled = True + return False + + def cancelled(self): + with self._condition: + return self._cancelled + + def running(self): + with self._condition: + return not self._termination.terminated + + def done(self): + with self._condition: + return self._termination.terminated + + def result(self, timeout=None): + until = None if timeout is None else time.time() + timeout + with self._condition: + while True: + if self._termination.terminated: + if self._termination.abortion is None: + return self._up_payload + elif self._termination.abortion.kind is face.Abortion.Kind.CANCELLED: + raise future.CancelledError() + else: + raise self._termination.abortion_error # pylint: disable=raising-bad-type + else: + _wait_once_until(self._condition, until) + + def exception(self, timeout=None): + until = None if timeout is None else time.time() + timeout + with self._condition: + while True: + if self._termination.terminated: + if self._termination.abortion is None: + return None + else: + return self._termination.abortion_error + else: + _wait_once_until(self._condition, until) + + def traceback(self, timeout=None): + until = None if timeout is None else time.time() + timeout + with self._condition: + while True: + if self._termination.terminated: + if self._termination.abortion_error is None: + return None + else: + abortion_error = self._termination.abortion_error + break + else: + _wait_once_until(self._condition, until) + + try: + raise abortion_error + except face.AbortionError: + return sys.exc_info()[2] + + def add_done_callback(self, fn): + with self._condition: + if self._operation_context is not None: + outcome = self._operation_context.add_termination_callback( + _done_callback_as_operation_termination_callback(fn, self)) + if outcome is None: + return + else: + self._set_outcome(outcome) + + fn(self) + + def consume(self, value): + with self._condition: + while True: + if self._termination.terminated: + return + elif 0 < self._down_allowance: + self._operator.advance(payload=value) + self._down_allowance -= 1 + return + else: + self._condition.wait() + + def terminate(self): + with self._condition: + if self._termination.terminated: + return + elif self._down_code.kind is _Transitory.Kind.GONE: + # Conform to specified idempotence of terminate by ignoring extra calls. + return + else: + completion = self._down_completion() + self._operator.advance(completion=completion) + + def consume_and_terminate(self, value): + with self._condition: + while True: + if self._termination.terminated: + return + elif 0 < self._down_allowance: + completion = self._down_completion() + self._operator.advance(payload=value, completion=completion) + return + else: + self._condition.wait() + + def __iter__(self): + return self + + def next(self): + with self._condition: + while True: + if self._termination.abortion_error is not None: + raise self._termination.abortion_error + elif self._up_payload is not None: + payload = self._up_payload + self._up_payload = None + if self._up_completion.kind is _Awaited.Kind.NOT_YET_ARRIVED: + self._operator.advance(allowance=1) + return payload + elif self._up_completion.kind is _Awaited.Kind.ARRIVED: + raise StopIteration() + else: + self._condition.wait() + + def is_active(self): + with self._condition: + return not self._termination.terminated + + def time_remaining(self): + if self._operation_context is None: + return 0 + else: + return self._operation_context.time_remaining() + + def add_abortion_callback(self, abortion_callback): + with self._condition: + if self._operation_context is None: + return self._termination.abortion + else: + outcome = self._operation_context.add_termination_callback( + _abortion_callback_as_operation_termination_callback( + abortion_callback, self.set_outcome)) + if outcome is not None: + return self._set_outcome(outcome).abortion + else: + return self._termination.abortion + + def protocol_context(self): + with self._condition: + while True: + if self._protocol_context.kind is _Awaited.Kind.ARRIVED: + return self._protocol_context.value + elif self._termination.abortion_error is not None: + raise self._termination.abortion_error + else: + self._condition.wait() + + def initial_metadata(self): + with self._condition: + while True: + if self._up_initial_metadata.kind is _Awaited.Kind.ARRIVED: + return self._up_initial_metadata.value + elif self._termination.terminated: + return None + else: + self._condition.wait() + + def terminal_metadata(self): + with self._condition: + while True: + if self._up_completion.kind is _Awaited.Kind.ARRIVED: + return self._up_completion.value.terminal_metadata + elif self._termination.terminated: + return None + else: + self._condition.wait() + + def code(self): + with self._condition: + while True: + if self._up_completion.kind is _Awaited.Kind.ARRIVED: + return self._up_completion.value.code + elif self._termination.terminated: + return None + else: + self._condition.wait() + + def details(self): + with self._condition: + while True: + if self._up_completion.kind is _Awaited.Kind.ARRIVED: + return self._up_completion.value.message + elif self._termination.terminated: + return None + else: + self._condition.wait() + + def set_initial_metadata(self, initial_metadata): + with self._condition: + if (self._down_initial_metadata.kind is not + _Transitory.Kind.NOT_YET_SEEN): + raise ValueError(_CANNOT_SET_INITIAL_METADATA) + else: + self._down_initial_metadata = _GONE + self._operator.advance(initial_metadata=initial_metadata) + + def set_terminal_metadata(self, terminal_metadata): + with self._condition: + if (self._down_terminal_metadata.kind is not + _Transitory.Kind.NOT_YET_SEEN): + raise ValueError(_CANNOT_SET_TERMINAL_METADATA) + else: + self._down_terminal_metadata = _Transitory( + _Transitory.Kind.PRESENT, terminal_metadata) + + def set_code(self, code): + with self._condition: + if self._down_code.kind is not _Transitory.Kind.NOT_YET_SEEN: + raise ValueError(_CANNOT_SET_CODE) + else: + self._down_code = _Transitory(_Transitory.Kind.PRESENT, code) + + def set_details(self, details): + with self._condition: + if self._down_details.kind is not _Transitory.Kind.NOT_YET_SEEN: + raise ValueError(_CANNOT_SET_DETAILS) + else: + self._down_details = _Transitory(_Transitory.Kind.PRESENT, details) + + def set_protocol_context(self, protocol_context): + with self._condition: + self._protocol_context = _Awaited( + _Awaited.Kind.ARRIVED, protocol_context) + self._condition.notify_all() + + def set_outcome(self, outcome): + with self._condition: + return self._set_outcome(outcome) + + +class _ProtocolReceiver(base.ProtocolReceiver): + + def __init__(self, rendezvous): + self._rendezvous = rendezvous + + def context(self, protocol_context): + self._rendezvous.set_protocol_context(protocol_context) + + +def protocol_receiver(rendezvous): + return _ProtocolReceiver(rendezvous) + + +def pool_wrap(behavior, operation_context): + """Wraps an operation-related behavior so that it may be called in a pool. + + Args: + behavior: A callable related to carrying out an operation. + operation_context: A base_interfaces.OperationContext for the operation. + + Returns: + A callable that when called carries out the behavior of the given callable + and handles whatever exceptions it raises appropriately. + """ + def translation(*args): + try: + behavior(*args) + except ( + abandonment.Abandoned, + face.CancellationError, + face.ExpirationError, + face.LocalShutdownError, + face.RemoteShutdownError, + face.NetworkError, + face.RemoteError, + ) as e: + if operation_context.outcome() is None: + operation_context.fail(e) + except Exception as e: + operation_context.fail(e) + return callable_util.with_exceptions_logged( + translation, _INTERNAL_ERROR_LOG_MESSAGE) diff --git a/src/python/grpcio/grpc/framework/crust/_service.py b/src/python/grpcio/grpc/framework/crust/_service.py new file mode 100644 index 00000000..9903415c --- /dev/null +++ b/src/python/grpcio/grpc/framework/crust/_service.py @@ -0,0 +1,173 @@ +# Copyright 2015, 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. + +"""Behaviors for servicing RPCs.""" + +from grpc.framework.crust import _control +from grpc.framework.foundation import abandonment +from grpc.framework.interfaces.base import utilities +from grpc.framework.interfaces.face import face + + +class _ServicerContext(face.ServicerContext): + + def __init__(self, rendezvous): + self._rendezvous = rendezvous + + def is_active(self): + return self._rendezvous.is_active() + + def time_remaining(self): + return self._rendezvous.time_remaining() + + def add_abortion_callback(self, abortion_callback): + return self._rendezvous.add_abortion_callback(abortion_callback) + + def cancel(self): + self._rendezvous.cancel() + + def protocol_context(self): + return self._rendezvous.protocol_context() + + def invocation_metadata(self): + return self._rendezvous.initial_metadata() + + def initial_metadata(self, initial_metadata): + self._rendezvous.set_initial_metadata(initial_metadata) + + def terminal_metadata(self, terminal_metadata): + self._rendezvous.set_terminal_metadata(terminal_metadata) + + def code(self, code): + self._rendezvous.set_code(code) + + def details(self, details): + self._rendezvous.set_details(details) + + +def _adaptation(pool, in_pool): + def adaptation(operator, operation_context): + rendezvous = _control.Rendezvous(operator, operation_context) + subscription = utilities.full_subscription( + rendezvous, _control.protocol_receiver(rendezvous)) + outcome = operation_context.add_termination_callback(rendezvous.set_outcome) + if outcome is None: + pool.submit(_control.pool_wrap(in_pool, operation_context), rendezvous) + return subscription + else: + raise abandonment.Abandoned() + return adaptation + + +def adapt_inline_unary_unary(method, pool): + def in_pool(rendezvous): + request = next(rendezvous) + response = method(request, _ServicerContext(rendezvous)) + rendezvous.consume_and_terminate(response) + return _adaptation(pool, in_pool) + + +def adapt_inline_unary_stream(method, pool): + def in_pool(rendezvous): + request = next(rendezvous) + response_iterator = method(request, _ServicerContext(rendezvous)) + for response in response_iterator: + rendezvous.consume(response) + rendezvous.terminate() + return _adaptation(pool, in_pool) + + +def adapt_inline_stream_unary(method, pool): + def in_pool(rendezvous): + response = method(rendezvous, _ServicerContext(rendezvous)) + rendezvous.consume_and_terminate(response) + return _adaptation(pool, in_pool) + + +def adapt_inline_stream_stream(method, pool): + def in_pool(rendezvous): + response_iterator = method(rendezvous, _ServicerContext(rendezvous)) + for response in response_iterator: + rendezvous.consume(response) + rendezvous.terminate() + return _adaptation(pool, in_pool) + + +def adapt_event_unary_unary(method, pool): + def in_pool(rendezvous): + request = next(rendezvous) + method( + request, rendezvous.consume_and_terminate, _ServicerContext(rendezvous)) + return _adaptation(pool, in_pool) + + +def adapt_event_unary_stream(method, pool): + def in_pool(rendezvous): + request = next(rendezvous) + method(request, rendezvous, _ServicerContext(rendezvous)) + return _adaptation(pool, in_pool) + + +def adapt_event_stream_unary(method, pool): + def in_pool(rendezvous): + request_consumer = method( + rendezvous.consume_and_terminate, _ServicerContext(rendezvous)) + for request in rendezvous: + request_consumer.consume(request) + request_consumer.terminate() + return _adaptation(pool, in_pool) + + +def adapt_event_stream_stream(method, pool): + def in_pool(rendezvous): + request_consumer = method(rendezvous, _ServicerContext(rendezvous)) + for request in rendezvous: + request_consumer.consume(request) + request_consumer.terminate() + return _adaptation(pool, in_pool) + + +def adapt_multi_method(multi_method, pool): + def adaptation(group, method, operator, operation_context): + rendezvous = _control.Rendezvous(operator, operation_context) + subscription = utilities.full_subscription( + rendezvous, _control.protocol_receiver(rendezvous)) + outcome = operation_context.add_termination_callback(rendezvous.set_outcome) + if outcome is None: + def in_pool(): + request_consumer = multi_method.service( + group, method, rendezvous, _ServicerContext(rendezvous)) + for request in rendezvous: + request_consumer.consume(request) + request_consumer.terminate() + pool.submit(_control.pool_wrap(in_pool, operation_context), rendezvous) + return subscription + else: + raise abandonment.Abandoned() + return adaptation diff --git a/src/python/grpcio/grpc/framework/crust/implementations.py b/src/python/grpcio/grpc/framework/crust/implementations.py new file mode 100644 index 00000000..4ebc4e9a --- /dev/null +++ b/src/python/grpcio/grpc/framework/crust/implementations.py @@ -0,0 +1,364 @@ +# Copyright 2015, 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. + +"""Entry points into the Crust layer of RPC Framework.""" + +from grpc.framework.common import cardinality +from grpc.framework.common import style +from grpc.framework.crust import _calls +from grpc.framework.crust import _service +from grpc.framework.interfaces.base import base +from grpc.framework.interfaces.face import face + + +class _BaseServicer(base.Servicer): + + def __init__(self, adapted_methods, adapted_multi_method): + self._adapted_methods = adapted_methods + self._adapted_multi_method = adapted_multi_method + + def service(self, group, method, context, output_operator): + adapted_method = self._adapted_methods.get((group, method), None) + if adapted_method is not None: + return adapted_method(output_operator, context) + elif self._adapted_multi_method is not None: + try: + return self._adapted_multi_method( + group, method, output_operator, context) + except face.NoSuchMethodError: + raise base.NoSuchMethodError(None, None) + else: + raise base.NoSuchMethodError(None, None) + + +class _UnaryUnaryMultiCallable(face.UnaryUnaryMultiCallable): + + def __init__(self, end, group, method, pool): + self._end = end + self._group = group + self._method = method + self._pool = pool + + def __call__( + self, request, timeout, metadata=None, with_call=False, + protocol_options=None): + return _calls.blocking_unary_unary( + self._end, self._group, self._method, timeout, with_call, + protocol_options, metadata, request) + + def future(self, request, timeout, metadata=None, protocol_options=None): + return _calls.future_unary_unary( + self._end, self._group, self._method, timeout, protocol_options, + metadata, request) + + def event( + self, request, receiver, abortion_callback, timeout, + metadata=None, protocol_options=None): + return _calls.event_unary_unary( + self._end, self._group, self._method, timeout, protocol_options, + metadata, request, receiver, abortion_callback, self._pool) + + +class _UnaryStreamMultiCallable(face.UnaryStreamMultiCallable): + + def __init__(self, end, group, method, pool): + self._end = end + self._group = group + self._method = method + self._pool = pool + + def __call__(self, request, timeout, metadata=None, protocol_options=None): + return _calls.inline_unary_stream( + self._end, self._group, self._method, timeout, protocol_options, + metadata, request) + + def event( + self, request, receiver, abortion_callback, timeout, + metadata=None, protocol_options=None): + return _calls.event_unary_stream( + self._end, self._group, self._method, timeout, protocol_options, + metadata, request, receiver, abortion_callback, self._pool) + + +class _StreamUnaryMultiCallable(face.StreamUnaryMultiCallable): + + def __init__(self, end, group, method, pool): + self._end = end + self._group = group + self._method = method + self._pool = pool + + def __call__( + self, request_iterator, timeout, metadata=None, + with_call=False, protocol_options=None): + return _calls.blocking_stream_unary( + self._end, self._group, self._method, timeout, with_call, + protocol_options, metadata, request_iterator, self._pool) + + def future( + self, request_iterator, timeout, metadata=None, protocol_options=None): + return _calls.future_stream_unary( + self._end, self._group, self._method, timeout, protocol_options, + metadata, request_iterator, self._pool) + + def event( + self, receiver, abortion_callback, timeout, metadata=None, + protocol_options=None): + return _calls.event_stream_unary( + self._end, self._group, self._method, timeout, protocol_options, + metadata, receiver, abortion_callback, self._pool) + + +class _StreamStreamMultiCallable(face.StreamStreamMultiCallable): + + def __init__(self, end, group, method, pool): + self._end = end + self._group = group + self._method = method + self._pool = pool + + def __call__( + self, request_iterator, timeout, metadata=None, protocol_options=None): + return _calls.inline_stream_stream( + self._end, self._group, self._method, timeout, protocol_options, + metadata, request_iterator, self._pool) + + def event( + self, receiver, abortion_callback, timeout, metadata=None, + protocol_options=None): + return _calls.event_stream_stream( + self._end, self._group, self._method, timeout, protocol_options, + metadata, receiver, abortion_callback, self._pool) + + +class _GenericStub(face.GenericStub): + """An face.GenericStub implementation.""" + + def __init__(self, end, pool): + self._end = end + self._pool = pool + + def blocking_unary_unary( + self, group, method, request, timeout, metadata=None, + with_call=None, protocol_options=None): + return _calls.blocking_unary_unary( + self._end, group, method, timeout, with_call, protocol_options, + metadata, request) + + def future_unary_unary( + self, group, method, request, timeout, metadata=None, + protocol_options=None): + return _calls.future_unary_unary( + self._end, group, method, timeout, protocol_options, metadata, request) + + def inline_unary_stream( + self, group, method, request, timeout, metadata=None, + protocol_options=None): + return _calls.inline_unary_stream( + self._end, group, method, timeout, protocol_options, metadata, request) + + def blocking_stream_unary( + self, group, method, request_iterator, timeout, metadata=None, + with_call=None, protocol_options=None): + return _calls.blocking_stream_unary( + self._end, group, method, timeout, with_call, protocol_options, + metadata, request_iterator, self._pool) + + def future_stream_unary( + self, group, method, request_iterator, timeout, metadata=None, + protocol_options=None): + return _calls.future_stream_unary( + self._end, group, method, timeout, protocol_options, metadata, + request_iterator, self._pool) + + def inline_stream_stream( + self, group, method, request_iterator, timeout, metadata=None, + protocol_options=None): + return _calls.inline_stream_stream( + self._end, group, method, timeout, protocol_options, metadata, + request_iterator, self._pool) + + def event_unary_unary( + self, group, method, request, receiver, abortion_callback, timeout, + metadata=None, protocol_options=None): + return _calls.event_unary_unary( + self._end, group, method, timeout, protocol_options, metadata, request, + receiver, abortion_callback, self._pool) + + def event_unary_stream( + self, group, method, request, receiver, abortion_callback, timeout, + metadata=None, protocol_options=None): + return _calls.event_unary_stream( + self._end, group, method, timeout, protocol_options, metadata, request, + receiver, abortion_callback, self._pool) + + def event_stream_unary( + self, group, method, receiver, abortion_callback, timeout, + metadata=None, protocol_options=None): + return _calls.event_stream_unary( + self._end, group, method, timeout, protocol_options, metadata, receiver, + abortion_callback, self._pool) + + def event_stream_stream( + self, group, method, receiver, abortion_callback, timeout, + metadata=None, protocol_options=None): + return _calls.event_stream_stream( + self._end, group, method, timeout, protocol_options, metadata, receiver, + abortion_callback, self._pool) + + def unary_unary(self, group, method): + return _UnaryUnaryMultiCallable(self._end, group, method, self._pool) + + def unary_stream(self, group, method): + return _UnaryStreamMultiCallable(self._end, group, method, self._pool) + + def stream_unary(self, group, method): + return _StreamUnaryMultiCallable(self._end, group, method, self._pool) + + def stream_stream(self, group, method): + return _StreamStreamMultiCallable(self._end, group, method, self._pool) + + +class _DynamicStub(face.DynamicStub): + """An face.DynamicStub implementation.""" + + def __init__(self, end, group, cardinalities, pool): + self._end = end + self._group = group + self._cardinalities = cardinalities + self._pool = pool + + def __getattr__(self, attr): + method_cardinality = self._cardinalities.get(attr) + if method_cardinality is cardinality.Cardinality.UNARY_UNARY: + return _UnaryUnaryMultiCallable(self._end, self._group, attr, self._pool) + elif method_cardinality is cardinality.Cardinality.UNARY_STREAM: + return _UnaryStreamMultiCallable(self._end, self._group, attr, self._pool) + elif method_cardinality is cardinality.Cardinality.STREAM_UNARY: + return _StreamUnaryMultiCallable(self._end, self._group, attr, self._pool) + elif method_cardinality is cardinality.Cardinality.STREAM_STREAM: + return _StreamStreamMultiCallable( + self._end, self._group, attr, self._pool) + else: + raise AttributeError('_DynamicStub object has no attribute "%s"!' % attr) + + +def _adapt_method_implementations(method_implementations, pool): + adapted_implementations = {} + for name, method_implementation in method_implementations.iteritems(): + if method_implementation.style is style.Service.INLINE: + if method_implementation.cardinality is cardinality.Cardinality.UNARY_UNARY: + adapted_implementations[name] = _service.adapt_inline_unary_unary( + method_implementation.unary_unary_inline, pool) + elif method_implementation.cardinality is cardinality.Cardinality.UNARY_STREAM: + adapted_implementations[name] = _service.adapt_inline_unary_stream( + method_implementation.unary_stream_inline, pool) + elif method_implementation.cardinality is cardinality.Cardinality.STREAM_UNARY: + adapted_implementations[name] = _service.adapt_inline_stream_unary( + method_implementation.stream_unary_inline, pool) + elif method_implementation.cardinality is cardinality.Cardinality.STREAM_STREAM: + adapted_implementations[name] = _service.adapt_inline_stream_stream( + method_implementation.stream_stream_inline, pool) + elif method_implementation.style is style.Service.EVENT: + if method_implementation.cardinality is cardinality.Cardinality.UNARY_UNARY: + adapted_implementations[name] = _service.adapt_event_unary_unary( + method_implementation.unary_unary_event, pool) + elif method_implementation.cardinality is cardinality.Cardinality.UNARY_STREAM: + adapted_implementations[name] = _service.adapt_event_unary_stream( + method_implementation.unary_stream_event, pool) + elif method_implementation.cardinality is cardinality.Cardinality.STREAM_UNARY: + adapted_implementations[name] = _service.adapt_event_stream_unary( + method_implementation.stream_unary_event, pool) + elif method_implementation.cardinality is cardinality.Cardinality.STREAM_STREAM: + adapted_implementations[name] = _service.adapt_event_stream_stream( + method_implementation.stream_stream_event, pool) + return adapted_implementations + + +def servicer(method_implementations, multi_method_implementation, pool): + """Creates a base.Servicer. + + It is guaranteed that any passed face.MultiMethodImplementation will + only be called to service an RPC if there is no + face.MethodImplementation for the RPC method in the passed + method_implementations dictionary. + + Args: + method_implementations: A dictionary from RPC method name to + face.MethodImplementation object to be used to service the named + RPC method. + multi_method_implementation: An face.MultiMethodImplementation to be + used to service any RPCs not serviced by the + face.MethodImplementations given in the method_implementations + dictionary, or None. + pool: A thread pool. + + Returns: + A base.Servicer that services RPCs via the given implementations. + """ + adapted_implementations = _adapt_method_implementations( + method_implementations, pool) + if multi_method_implementation is None: + adapted_multi_method_implementation = None + else: + adapted_multi_method_implementation = _service.adapt_multi_method( + multi_method_implementation, pool) + return _BaseServicer( + adapted_implementations, adapted_multi_method_implementation) + + +def generic_stub(end, pool): + """Creates an face.GenericStub. + + Args: + end: A base.End. + pool: A futures.ThreadPoolExecutor. + + Returns: + A face.GenericStub that performs RPCs via the given base.End. + """ + return _GenericStub(end, pool) + + +def dynamic_stub(end, group, cardinalities, pool): + """Creates an face.DynamicStub. + + Args: + end: A base.End. + group: The group identifier for all RPCs to be made with the created + face.DynamicStub. + cardinalities: A dict from method identifier to cardinality.Cardinality + value identifying the cardinality of every RPC method to be supported by + the created face.DynamicStub. + pool: A futures.ThreadPoolExecutor. + + Returns: + A face.DynamicStub that performs RPCs via the given base.End. + """ + return _DynamicStub(end, group, cardinalities, pool) diff --git a/src/python/grpcio/grpc/framework/face/__init__.py b/src/python/grpcio/grpc/framework/face/__init__.py new file mode 100644 index 00000000..70865191 --- /dev/null +++ b/src/python/grpcio/grpc/framework/face/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, 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. + + diff --git a/src/python/grpcio/grpc/framework/face/_calls.py b/src/python/grpcio/grpc/framework/face/_calls.py new file mode 100644 index 00000000..87edeb0f --- /dev/null +++ b/src/python/grpcio/grpc/framework/face/_calls.py @@ -0,0 +1,422 @@ +# Copyright 2015, 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. + +"""Utility functions for invoking RPCs.""" + +import sys +import threading + +from grpc.framework.base import interfaces as base_interfaces +from grpc.framework.base import util as base_util +from grpc.framework.face import _control +from grpc.framework.face import interfaces +from grpc.framework.foundation import callable_util +from grpc.framework.foundation import future + +_ITERATOR_EXCEPTION_LOG_MESSAGE = 'Exception iterating over requests!' +_DONE_CALLBACK_LOG_MESSAGE = 'Exception calling Future "done" callback!' + + +class _RendezvousServicedIngestor(base_interfaces.ServicedIngestor): + + def __init__(self, rendezvous): + self._rendezvous = rendezvous + + def consumer(self, operation_context): + return self._rendezvous + + +class _EventServicedIngestor(base_interfaces.ServicedIngestor): + + def __init__(self, result_consumer, abortion_callback): + self._result_consumer = result_consumer + self._abortion_callback = abortion_callback + + def consumer(self, operation_context): + operation_context.add_termination_callback( + _control.as_operation_termination_callback(self._abortion_callback)) + return self._result_consumer + + +def _rendezvous_subscription(rendezvous): + return base_util.full_serviced_subscription( + _RendezvousServicedIngestor(rendezvous)) + + +def _unary_event_subscription(completion_callback, abortion_callback): + return base_util.full_serviced_subscription( + _EventServicedIngestor( + _control.UnaryConsumer(completion_callback), abortion_callback)) + + +def _stream_event_subscription(result_consumer, abortion_callback): + return base_util.full_serviced_subscription( + _EventServicedIngestor(result_consumer, abortion_callback)) + + +# NOTE(nathaniel): This class has some extremely special semantics around +# cancellation that allow it to be used by both "blocking" APIs and "futures" +# APIs. +# +# Since futures.Future defines its own exception for cancellation, we want these +# objects, when returned by methods of a returning-Futures-from-other-methods +# object, to raise the same exception for cancellation. But that's weird in a +# blocking API - why should this object, also returned by methods of blocking +# APIs, raise exceptions from the "future" module? Should we do something like +# have this class be parameterized by the type of exception that it raises in +# cancellation circumstances? +# +# We don't have to take such a dramatic step: since blocking APIs define no +# cancellation semantics whatsoever, there is no supported way for +# blocking-API-users of these objects to cancel RPCs, and thus no supported way +# for them to see an exception the type of which would be weird to them. +# +# Bonus: in both blocking and futures APIs, this object still properly raises +# exceptions.CancellationError for any *server-side cancellation* of an RPC. +class _OperationCancellableIterator(interfaces.CancellableIterator): + """An interfaces.CancellableIterator for response-streaming operations.""" + + def __init__(self, rendezvous, operation): + self._lock = threading.Lock() + self._rendezvous = rendezvous + self._operation = operation + self._cancelled = False + + def __iter__(self): + return self + + def next(self): + with self._lock: + if self._cancelled: + raise future.CancelledError() + return next(self._rendezvous) + + def cancel(self): + with self._lock: + self._cancelled = True + self._operation.cancel() + self._rendezvous.set_outcome(base_interfaces.Outcome.CANCELLED) + + +class _OperationFuture(future.Future): + """A future.Future interface to an operation.""" + + def __init__(self, rendezvous, operation): + self._condition = threading.Condition() + self._rendezvous = rendezvous + self._operation = operation + + self._cancelled = False + self._computed = False + self._payload = None + self._exception = None + self._traceback = None + self._callbacks = [] + + def cancel(self): + """See future.Future.cancel for specification.""" + with self._condition: + if not self._cancelled and not self._computed: + self._operation.cancel() + self._cancelled = True + self._condition.notify_all() + return False + + def cancelled(self): + """See future.Future.cancelled for specification.""" + with self._condition: + return self._cancelled + + def running(self): + """See future.Future.running for specification.""" + with self._condition: + return not self._cancelled and not self._computed + + def done(self): + """See future.Future.done for specification.""" + with self._condition: + return self._cancelled or self._computed + + def result(self, timeout=None): + """See future.Future.result for specification.""" + with self._condition: + if self._cancelled: + raise future.CancelledError() + if self._computed: + if self._payload is None: + raise self._exception # pylint: disable=raising-bad-type + else: + return self._payload + + condition = threading.Condition() + def notify_condition(unused_future): + with condition: + condition.notify() + self._callbacks.append(notify_condition) + + with condition: + condition.wait(timeout=timeout) + + with self._condition: + if self._cancelled: + raise future.CancelledError() + elif self._computed: + if self._payload is None: + raise self._exception # pylint: disable=raising-bad-type + else: + return self._payload + else: + raise future.TimeoutError() + + def exception(self, timeout=None): + """See future.Future.exception for specification.""" + with self._condition: + if self._cancelled: + raise future.CancelledError() + if self._computed: + return self._exception + + condition = threading.Condition() + def notify_condition(unused_future): + with condition: + condition.notify() + self._callbacks.append(notify_condition) + + with condition: + condition.wait(timeout=timeout) + + with self._condition: + if self._cancelled: + raise future.CancelledError() + elif self._computed: + return self._exception + else: + raise future.TimeoutError() + + def traceback(self, timeout=None): + """See future.Future.traceback for specification.""" + with self._condition: + if self._cancelled: + raise future.CancelledError() + if self._computed: + return self._traceback + + condition = threading.Condition() + def notify_condition(unused_future): + with condition: + condition.notify() + self._callbacks.append(notify_condition) + + with condition: + condition.wait(timeout=timeout) + + with self._condition: + if self._cancelled: + raise future.CancelledError() + elif self._computed: + return self._traceback + else: + raise future.TimeoutError() + + def add_done_callback(self, fn): + """See future.Future.add_done_callback for specification.""" + with self._condition: + if self._callbacks is not None: + self._callbacks.append(fn) + return + + callable_util.call_logging_exceptions(fn, _DONE_CALLBACK_LOG_MESSAGE, self) + + def on_operation_termination(self, operation_outcome): + """Indicates to this object that the operation has terminated. + + Args: + operation_outcome: A base_interfaces.Outcome value indicating the + outcome of the operation. + """ + with self._condition: + cancelled = self._cancelled + if cancelled: + callbacks = list(self._callbacks) + self._callbacks = None + else: + rendezvous = self._rendezvous + + if not cancelled: + payload = None + exception = None + traceback = None + if operation_outcome == base_interfaces.Outcome.COMPLETED: + try: + payload = next(rendezvous) + except Exception as e: # pylint: disable=broad-except + exception = e + traceback = sys.exc_info()[2] + else: + try: + # We raise and then immediately catch in order to create a traceback. + raise _control.abortion_outcome_to_exception(operation_outcome) + except Exception as e: # pylint: disable=broad-except + exception = e + traceback = sys.exc_info()[2] + with self._condition: + if not self._cancelled: + self._computed = True + self._payload = payload + self._exception = exception + self._traceback = traceback + callbacks = list(self._callbacks) + self._callbacks = None + + for callback in callbacks: + callable_util.call_logging_exceptions( + callback, _DONE_CALLBACK_LOG_MESSAGE, self) + + +class _Call(interfaces.Call): + + def __init__(self, operation): + self._operation = operation + self.context = _control.RpcContext(operation.context) + + def cancel(self): + self._operation.cancel() + + +def blocking_value_in_value_out(front, name, payload, timeout, trace_id): + """Services in a blocking fashion a value-in value-out servicer method.""" + rendezvous = _control.Rendezvous() + subscription = _rendezvous_subscription(rendezvous) + operation = front.operate( + name, payload, True, timeout, subscription, trace_id) + operation.context.add_termination_callback(rendezvous.set_outcome) + return next(rendezvous) + + +def future_value_in_value_out(front, name, payload, timeout, trace_id): + """Services a value-in value-out servicer method by returning a Future.""" + rendezvous = _control.Rendezvous() + subscription = _rendezvous_subscription(rendezvous) + operation = front.operate( + name, payload, True, timeout, subscription, trace_id) + operation.context.add_termination_callback(rendezvous.set_outcome) + operation_future = _OperationFuture(rendezvous, operation) + operation.context.add_termination_callback( + operation_future.on_operation_termination) + return operation_future + + +def inline_value_in_stream_out(front, name, payload, timeout, trace_id): + """Services a value-in stream-out servicer method.""" + rendezvous = _control.Rendezvous() + subscription = _rendezvous_subscription(rendezvous) + operation = front.operate( + name, payload, True, timeout, subscription, trace_id) + operation.context.add_termination_callback(rendezvous.set_outcome) + return _OperationCancellableIterator(rendezvous, operation) + + +def blocking_stream_in_value_out( + front, name, payload_iterator, timeout, trace_id): + """Services in a blocking fashion a stream-in value-out servicer method.""" + rendezvous = _control.Rendezvous() + subscription = _rendezvous_subscription(rendezvous) + operation = front.operate(name, None, False, timeout, subscription, trace_id) + operation.context.add_termination_callback(rendezvous.set_outcome) + for payload in payload_iterator: + operation.consumer.consume(payload) + operation.consumer.terminate() + return next(rendezvous) + + +def future_stream_in_value_out( + front, name, payload_iterator, timeout, trace_id, pool): + """Services a stream-in value-out servicer method by returning a Future.""" + rendezvous = _control.Rendezvous() + subscription = _rendezvous_subscription(rendezvous) + operation = front.operate(name, None, False, timeout, subscription, trace_id) + operation.context.add_termination_callback(rendezvous.set_outcome) + pool.submit( + callable_util.with_exceptions_logged( + _control.pipe_iterator_to_consumer, _ITERATOR_EXCEPTION_LOG_MESSAGE), + payload_iterator, operation.consumer, lambda: True, True) + operation_future = _OperationFuture(rendezvous, operation) + operation.context.add_termination_callback( + operation_future.on_operation_termination) + return operation_future + + +def inline_stream_in_stream_out( + front, name, payload_iterator, timeout, trace_id, pool): + """Services a stream-in stream-out servicer method.""" + rendezvous = _control.Rendezvous() + subscription = _rendezvous_subscription(rendezvous) + operation = front.operate(name, None, False, timeout, subscription, trace_id) + operation.context.add_termination_callback(rendezvous.set_outcome) + pool.submit( + callable_util.with_exceptions_logged( + _control.pipe_iterator_to_consumer, _ITERATOR_EXCEPTION_LOG_MESSAGE), + payload_iterator, operation.consumer, lambda: True, True) + return _OperationCancellableIterator(rendezvous, operation) + + +def event_value_in_value_out( + front, name, payload, completion_callback, abortion_callback, timeout, + trace_id): + subscription = _unary_event_subscription( + completion_callback, abortion_callback) + operation = front.operate( + name, payload, True, timeout, subscription, trace_id) + return _Call(operation) + + +def event_value_in_stream_out( + front, name, payload, result_payload_consumer, abortion_callback, timeout, + trace_id): + subscription = _stream_event_subscription( + result_payload_consumer, abortion_callback) + operation = front.operate( + name, payload, True, timeout, subscription, trace_id) + return _Call(operation) + + +def event_stream_in_value_out( + front, name, completion_callback, abortion_callback, timeout, trace_id): + subscription = _unary_event_subscription( + completion_callback, abortion_callback) + operation = front.operate(name, None, False, timeout, subscription, trace_id) + return _Call(operation), operation.consumer + + +def event_stream_in_stream_out( + front, name, result_payload_consumer, abortion_callback, timeout, trace_id): + subscription = _stream_event_subscription( + result_payload_consumer, abortion_callback) + operation = front.operate(name, None, False, timeout, subscription, trace_id) + return _Call(operation), operation.consumer diff --git a/src/python/grpcio/grpc/framework/face/_control.py b/src/python/grpcio/grpc/framework/face/_control.py new file mode 100644 index 00000000..e918907b --- /dev/null +++ b/src/python/grpcio/grpc/framework/face/_control.py @@ -0,0 +1,198 @@ +# Copyright 2015, 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. + +"""State and behavior for translating between sync and async control flow.""" + +import threading + +from grpc.framework.base import interfaces as base_interfaces +from grpc.framework.face import exceptions +from grpc.framework.face import interfaces +from grpc.framework.foundation import abandonment +from grpc.framework.foundation import stream + +INTERNAL_ERROR_LOG_MESSAGE = ':-( RPC Framework (Face) Internal Error! :-(' + +_OPERATION_OUTCOME_TO_RPC_ABORTION = { + base_interfaces.Outcome.CANCELLED: interfaces.Abortion.CANCELLED, + base_interfaces.Outcome.EXPIRED: interfaces.Abortion.EXPIRED, + base_interfaces.Outcome.RECEPTION_FAILURE: + interfaces.Abortion.NETWORK_FAILURE, + base_interfaces.Outcome.TRANSMISSION_FAILURE: + interfaces.Abortion.NETWORK_FAILURE, + base_interfaces.Outcome.SERVICED_FAILURE: + interfaces.Abortion.SERVICED_FAILURE, + base_interfaces.Outcome.SERVICER_FAILURE: + interfaces.Abortion.SERVICER_FAILURE, +} + + +def _as_operation_termination_callback(rpc_abortion_callback): + def operation_termination_callback(operation_outcome): + rpc_abortion = _OPERATION_OUTCOME_TO_RPC_ABORTION.get( + operation_outcome, None) + if rpc_abortion is not None: + rpc_abortion_callback(rpc_abortion) + return operation_termination_callback + + +def _abortion_outcome_to_exception(abortion_outcome): + if abortion_outcome == base_interfaces.Outcome.CANCELLED: + return exceptions.CancellationError() + elif abortion_outcome == base_interfaces.Outcome.EXPIRED: + return exceptions.ExpirationError() + elif abortion_outcome == base_interfaces.Outcome.SERVICER_FAILURE: + return exceptions.ServicerError() + elif abortion_outcome == base_interfaces.Outcome.SERVICED_FAILURE: + return exceptions.ServicedError() + else: + return exceptions.NetworkError() + + +class UnaryConsumer(stream.Consumer): + """A stream.Consumer that should only ever be passed one value.""" + + def __init__(self, on_termination): + self._on_termination = on_termination + self._value = None + + def consume(self, value): + self._value = value + + def terminate(self): + self._on_termination(self._value) + + def consume_and_terminate(self, value): + self._on_termination(value) + + +class Rendezvous(stream.Consumer): + """A rendez-vous with stream.Consumer and iterator interfaces.""" + + def __init__(self): + self._condition = threading.Condition() + self._values = [] + self._values_completed = False + self._abortion = None + + def consume(self, value): + with self._condition: + self._values.append(value) + self._condition.notify() + + def terminate(self): + with self._condition: + self._values_completed = True + self._condition.notify() + + def consume_and_terminate(self, value): + with self._condition: + self._values.append(value) + self._values_completed = True + self._condition.notify() + + def __iter__(self): + return self + + def next(self): + with self._condition: + while ((self._abortion is None) and + (not self._values) and + (not self._values_completed)): + self._condition.wait() + if self._abortion is not None: + raise _abortion_outcome_to_exception(self._abortion) + elif self._values: + return self._values.pop(0) + elif self._values_completed: + raise StopIteration() + else: + raise AssertionError('Unreachable code reached!') + + def set_outcome(self, outcome): + with self._condition: + if outcome is not base_interfaces.Outcome.COMPLETED: + self._abortion = outcome + self._condition.notify() + + +class RpcContext(interfaces.RpcContext): + """A wrapped base_interfaces.OperationContext.""" + + def __init__(self, operation_context): + self._operation_context = operation_context + + def is_active(self): + return self._operation_context.is_active() + + def time_remaining(self): + return self._operation_context.time_remaining() + + def add_abortion_callback(self, abortion_callback): + self._operation_context.add_termination_callback( + _as_operation_termination_callback(abortion_callback)) + + +def pipe_iterator_to_consumer(iterator, consumer, active, terminate): + """Pipes values emitted from an iterator to a stream.Consumer. + + Args: + iterator: An iterator from which values will be emitted. + consumer: A stream.Consumer to which values will be passed. + active: A no-argument callable that returns True if the work being done by + this function is still valid and should not be abandoned and False if the + work being done by this function should be abandoned. + terminate: A boolean indicating whether or not this function should + terminate the given consumer after passing to it all values emitted by the + given iterator. + + Raises: + abandonment.Abandoned: If this function quits early after seeing False + returned by the active function passed to it. + Exception: This function raises whatever exceptions are raised by iterating + over the given iterator. + """ + for element in iterator: + if not active(): + raise abandonment.Abandoned() + + consumer.consume(element) + + if not active(): + raise abandonment.Abandoned() + if terminate: + consumer.terminate() + + +def abortion_outcome_to_exception(abortion_outcome): + return _abortion_outcome_to_exception(abortion_outcome) + + +def as_operation_termination_callback(rpc_abortion_callback): + return _as_operation_termination_callback(rpc_abortion_callback) diff --git a/src/python/grpcio/grpc/framework/face/_service.py b/src/python/grpcio/grpc/framework/face/_service.py new file mode 100644 index 00000000..cdf41335 --- /dev/null +++ b/src/python/grpcio/grpc/framework/face/_service.py @@ -0,0 +1,187 @@ +# Copyright 2015, 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. + +"""Behaviors for servicing RPCs.""" + +# base_interfaces and interfaces are referenced from specification in this +# module. +from grpc.framework.base import interfaces as base_interfaces # pylint: disable=unused-import +from grpc.framework.face import _control +from grpc.framework.face import exceptions +from grpc.framework.face import interfaces # pylint: disable=unused-import +from grpc.framework.foundation import abandonment +from grpc.framework.foundation import callable_util +from grpc.framework.foundation import stream +from grpc.framework.foundation import stream_util + + +class _ValueInStreamOutConsumer(stream.Consumer): + """A stream.Consumer that maps inputs one-to-many onto outputs.""" + + def __init__(self, behavior, context, downstream): + """Constructor. + + Args: + behavior: A callable that takes a single value and an + interfaces.RpcContext and returns a generator of arbitrarily many + values. + context: An interfaces.RpcContext. + downstream: A stream.Consumer to which to pass the values generated by the + given behavior. + """ + self._behavior = behavior + self._context = context + self._downstream = downstream + + def consume(self, value): + _control.pipe_iterator_to_consumer( + self._behavior(value, self._context), self._downstream, + self._context.is_active, False) + + def terminate(self): + self._downstream.terminate() + + def consume_and_terminate(self, value): + _control.pipe_iterator_to_consumer( + self._behavior(value, self._context), self._downstream, + self._context.is_active, True) + + +def _pool_wrap(behavior, operation_context): + """Wraps an operation-related behavior so that it may be called in a pool. + + Args: + behavior: A callable related to carrying out an operation. + operation_context: A base_interfaces.OperationContext for the operation. + + Returns: + A callable that when called carries out the behavior of the given callable + and handles whatever exceptions it raises appropriately. + """ + def translation(*args): + try: + behavior(*args) + except ( + abandonment.Abandoned, + exceptions.ExpirationError, + exceptions.CancellationError, + exceptions.ServicedError, + exceptions.NetworkError) as e: + if operation_context.is_active(): + operation_context.fail(e) + except Exception as e: + operation_context.fail(e) + return callable_util.with_exceptions_logged( + translation, _control.INTERNAL_ERROR_LOG_MESSAGE) + + +def adapt_inline_value_in_value_out(method): + def adaptation(response_consumer, operation_context): + rpc_context = _control.RpcContext(operation_context) + return stream_util.TransformingConsumer( + lambda request: method(request, rpc_context), response_consumer) + return adaptation + + +def adapt_inline_value_in_stream_out(method): + def adaptation(response_consumer, operation_context): + rpc_context = _control.RpcContext(operation_context) + return _ValueInStreamOutConsumer(method, rpc_context, response_consumer) + return adaptation + + +def adapt_inline_stream_in_value_out(method, pool): + def adaptation(response_consumer, operation_context): + rendezvous = _control.Rendezvous() + operation_context.add_termination_callback(rendezvous.set_outcome) + def in_pool_thread(): + response_consumer.consume_and_terminate( + method(rendezvous, _control.RpcContext(operation_context))) + pool.submit(_pool_wrap(in_pool_thread, operation_context)) + return rendezvous + return adaptation + + +def adapt_inline_stream_in_stream_out(method, pool): + """Adapts an interfaces.InlineStreamInStreamOutMethod for use with Consumers. + + RPCs may be serviced by calling the return value of this function, passing + request values to the stream.Consumer returned from that call, and receiving + response values from the stream.Consumer passed to that call. + + Args: + method: An interfaces.InlineStreamInStreamOutMethod. + pool: A thread pool. + + Returns: + A callable that takes a stream.Consumer and a + base_interfaces.OperationContext and returns a stream.Consumer. + """ + def adaptation(response_consumer, operation_context): + rendezvous = _control.Rendezvous() + operation_context.add_termination_callback(rendezvous.set_outcome) + def in_pool_thread(): + _control.pipe_iterator_to_consumer( + method(rendezvous, _control.RpcContext(operation_context)), + response_consumer, operation_context.is_active, True) + pool.submit(_pool_wrap(in_pool_thread, operation_context)) + return rendezvous + return adaptation + + +def adapt_event_value_in_value_out(method): + def adaptation(response_consumer, operation_context): + def on_payload(payload): + method( + payload, response_consumer.consume_and_terminate, + _control.RpcContext(operation_context)) + return _control.UnaryConsumer(on_payload) + return adaptation + + +def adapt_event_value_in_stream_out(method): + def adaptation(response_consumer, operation_context): + def on_payload(payload): + method( + payload, response_consumer, _control.RpcContext(operation_context)) + return _control.UnaryConsumer(on_payload) + return adaptation + + +def adapt_event_stream_in_value_out(method): + def adaptation(response_consumer, operation_context): + rpc_context = _control.RpcContext(operation_context) + return method(response_consumer.consume_and_terminate, rpc_context) + return adaptation + + +def adapt_event_stream_in_stream_out(method): + def adaptation(response_consumer, operation_context): + return method(response_consumer, _control.RpcContext(operation_context)) + return adaptation diff --git a/src/python/grpcio/grpc/framework/face/demonstration.py b/src/python/grpcio/grpc/framework/face/demonstration.py new file mode 100644 index 00000000..f6b4b609 --- /dev/null +++ b/src/python/grpcio/grpc/framework/face/demonstration.py @@ -0,0 +1,118 @@ +# Copyright 2015, 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. + +"""Demonstration-suitable implementation of the face layer of RPC Framework.""" + +from grpc.framework.base import util as _base_util +from grpc.framework.base import implementations as _base_implementations +from grpc.framework.face import implementations +from grpc.framework.foundation import logging_pool + +_POOL_SIZE_LIMIT = 5 + +_MAXIMUM_TIMEOUT = 90 + + +class LinkedPair(object): + """A Server and Stub that are linked to one another. + + Attributes: + server: A Server. + stub: A Stub. + """ + + def shut_down(self): + """Shuts down this object and releases its resources.""" + raise NotImplementedError() + + +class _LinkedPair(LinkedPair): + + def __init__(self, server, stub, front, back, pools): + self.server = server + self.stub = stub + self._front = front + self._back = back + self._pools = pools + + def shut_down(self): + _base_util.wait_for_idle(self._front) + _base_util.wait_for_idle(self._back) + + for pool in self._pools: + pool.shutdown(wait=True) + + +def server_and_stub( + default_timeout, + inline_value_in_value_out_methods=None, + inline_value_in_stream_out_methods=None, + inline_stream_in_value_out_methods=None, + inline_stream_in_stream_out_methods=None, + event_value_in_value_out_methods=None, + event_value_in_stream_out_methods=None, + event_stream_in_value_out_methods=None, + event_stream_in_stream_out_methods=None, + multi_method=None): + """Creates a Server and Stub linked together for use.""" + front_work_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + front_transmission_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + front_utility_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + back_work_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + back_transmission_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + back_utility_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + stub_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + pools = ( + front_work_pool, front_transmission_pool, front_utility_pool, + back_work_pool, back_transmission_pool, back_utility_pool, + stub_pool) + + servicer = implementations.servicer( + back_work_pool, + inline_value_in_value_out_methods=inline_value_in_value_out_methods, + inline_value_in_stream_out_methods=inline_value_in_stream_out_methods, + inline_stream_in_value_out_methods=inline_stream_in_value_out_methods, + inline_stream_in_stream_out_methods=inline_stream_in_stream_out_methods, + event_value_in_value_out_methods=event_value_in_value_out_methods, + event_value_in_stream_out_methods=event_value_in_stream_out_methods, + event_stream_in_value_out_methods=event_stream_in_value_out_methods, + event_stream_in_stream_out_methods=event_stream_in_stream_out_methods, + multi_method=multi_method) + + front = _base_implementations.front_link( + front_work_pool, front_transmission_pool, front_utility_pool) + back = _base_implementations.back_link( + servicer, back_work_pool, back_transmission_pool, back_utility_pool, + default_timeout, _MAXIMUM_TIMEOUT) + front.join_rear_link(back) + back.join_fore_link(front) + + stub = implementations.stub(front, stub_pool) + + return _LinkedPair(implementations.server(), stub, front, back, pools) diff --git a/src/python/grpcio/grpc/framework/face/exceptions.py b/src/python/grpcio/grpc/framework/face/exceptions.py new file mode 100644 index 00000000..f112df70 --- /dev/null +++ b/src/python/grpcio/grpc/framework/face/exceptions.py @@ -0,0 +1,77 @@ +# Copyright 2015, 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. + +"""Exceptions used in the Face layer of RPC Framework.""" + +import abc + + +class NoSuchMethodError(Exception): + """Raised by customer code to indicate an unrecognized RPC method name. + + Attributes: + name: The unrecognized name. + """ + + def __init__(self, name): + """Constructor. + + Args: + name: The unrecognized RPC method name. + """ + super(NoSuchMethodError, self).__init__() + self.name = name + + +class RpcError(Exception): + """Common super type for all exceptions raised by the Face layer. + + Only RPC Framework should instantiate and raise these exceptions. + """ + __metaclass__ = abc.ABCMeta + + +class CancellationError(RpcError): + """Indicates that an RPC has been cancelled.""" + + +class ExpirationError(RpcError): + """Indicates that an RPC has expired ("timed out").""" + + +class NetworkError(RpcError): + """Indicates that some error occurred on the network.""" + + +class ServicedError(RpcError): + """Indicates that the Serviced failed in the course of an RPC.""" + + +class ServicerError(RpcError): + """Indicates that the Servicer failed in the course of servicing an RPC.""" diff --git a/src/python/grpcio/grpc/framework/face/implementations.py b/src/python/grpcio/grpc/framework/face/implementations.py new file mode 100644 index 00000000..4a6de529 --- /dev/null +++ b/src/python/grpcio/grpc/framework/face/implementations.py @@ -0,0 +1,318 @@ +# Copyright 2015, 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. + +"""Entry points into the Face layer of RPC Framework.""" + +from grpc.framework.common import cardinality +from grpc.framework.common import style +from grpc.framework.base import exceptions as _base_exceptions +from grpc.framework.base import interfaces as base_interfaces +from grpc.framework.face import _calls +from grpc.framework.face import _service +from grpc.framework.face import exceptions +from grpc.framework.face import interfaces + + +class _BaseServicer(base_interfaces.Servicer): + + def __init__(self, methods, multi_method): + self._methods = methods + self._multi_method = multi_method + + def service(self, name, context, output_consumer): + method = self._methods.get(name, None) + if method is not None: + return method(output_consumer, context) + elif self._multi_method is not None: + try: + return self._multi_method.service(name, output_consumer, context) + except exceptions.NoSuchMethodError: + raise _base_exceptions.NoSuchMethodError() + else: + raise _base_exceptions.NoSuchMethodError() + + +class _UnaryUnaryMultiCallable(interfaces.UnaryUnaryMultiCallable): + + def __init__(self, front, name): + self._front = front + self._name = name + + def __call__(self, request, timeout): + return _calls.blocking_value_in_value_out( + self._front, self._name, request, timeout, 'unused trace ID') + + def future(self, request, timeout): + return _calls.future_value_in_value_out( + self._front, self._name, request, timeout, 'unused trace ID') + + def event(self, request, response_callback, abortion_callback, timeout): + return _calls.event_value_in_value_out( + self._front, self._name, request, response_callback, abortion_callback, + timeout, 'unused trace ID') + + +class _UnaryStreamMultiCallable(interfaces.UnaryStreamMultiCallable): + + def __init__(self, front, name): + self._front = front + self._name = name + + def __call__(self, request, timeout): + return _calls.inline_value_in_stream_out( + self._front, self._name, request, timeout, 'unused trace ID') + + def event(self, request, response_consumer, abortion_callback, timeout): + return _calls.event_value_in_stream_out( + self._front, self._name, request, response_consumer, abortion_callback, + timeout, 'unused trace ID') + + +class _StreamUnaryMultiCallable(interfaces.StreamUnaryMultiCallable): + + def __init__(self, front, name, pool): + self._front = front + self._name = name + self._pool = pool + + def __call__(self, request_iterator, timeout): + return _calls.blocking_stream_in_value_out( + self._front, self._name, request_iterator, timeout, 'unused trace ID') + + def future(self, request_iterator, timeout): + return _calls.future_stream_in_value_out( + self._front, self._name, request_iterator, timeout, 'unused trace ID', + self._pool) + + def event(self, response_callback, abortion_callback, timeout): + return _calls.event_stream_in_value_out( + self._front, self._name, response_callback, abortion_callback, timeout, + 'unused trace ID') + + +class _StreamStreamMultiCallable(interfaces.StreamStreamMultiCallable): + + def __init__(self, front, name, pool): + self._front = front + self._name = name + self._pool = pool + + def __call__(self, request_iterator, timeout): + return _calls.inline_stream_in_stream_out( + self._front, self._name, request_iterator, timeout, 'unused trace ID', + self._pool) + + def event(self, response_consumer, abortion_callback, timeout): + return _calls.event_stream_in_stream_out( + self._front, self._name, response_consumer, abortion_callback, timeout, + 'unused trace ID') + + +class _GenericStub(interfaces.GenericStub): + """An interfaces.GenericStub implementation.""" + + def __init__(self, front, pool): + self._front = front + self._pool = pool + + def blocking_value_in_value_out(self, name, request, timeout): + return _calls.blocking_value_in_value_out( + self._front, name, request, timeout, 'unused trace ID') + + def future_value_in_value_out(self, name, request, timeout): + return _calls.future_value_in_value_out( + self._front, name, request, timeout, 'unused trace ID') + + def inline_value_in_stream_out(self, name, request, timeout): + return _calls.inline_value_in_stream_out( + self._front, name, request, timeout, 'unused trace ID') + + def blocking_stream_in_value_out(self, name, request_iterator, timeout): + return _calls.blocking_stream_in_value_out( + self._front, name, request_iterator, timeout, 'unused trace ID') + + def future_stream_in_value_out(self, name, request_iterator, timeout): + return _calls.future_stream_in_value_out( + self._front, name, request_iterator, timeout, 'unused trace ID', + self._pool) + + def inline_stream_in_stream_out(self, name, request_iterator, timeout): + return _calls.inline_stream_in_stream_out( + self._front, name, request_iterator, timeout, 'unused trace ID', + self._pool) + + def event_value_in_value_out( + self, name, request, response_callback, abortion_callback, timeout): + return _calls.event_value_in_value_out( + self._front, name, request, response_callback, abortion_callback, + timeout, 'unused trace ID') + + def event_value_in_stream_out( + self, name, request, response_consumer, abortion_callback, timeout): + return _calls.event_value_in_stream_out( + self._front, name, request, response_consumer, abortion_callback, + timeout, 'unused trace ID') + + def event_stream_in_value_out( + self, name, response_callback, abortion_callback, timeout): + return _calls.event_stream_in_value_out( + self._front, name, response_callback, abortion_callback, timeout, + 'unused trace ID') + + def event_stream_in_stream_out( + self, name, response_consumer, abortion_callback, timeout): + return _calls.event_stream_in_stream_out( + self._front, name, response_consumer, abortion_callback, timeout, + 'unused trace ID') + + def unary_unary_multi_callable(self, name): + return _UnaryUnaryMultiCallable(self._front, name) + + def unary_stream_multi_callable(self, name): + return _UnaryStreamMultiCallable(self._front, name) + + def stream_unary_multi_callable(self, name): + return _StreamUnaryMultiCallable(self._front, name, self._pool) + + def stream_stream_multi_callable(self, name): + return _StreamStreamMultiCallable(self._front, name, self._pool) + + +class _DynamicStub(interfaces.DynamicStub): + """An interfaces.DynamicStub implementation.""" + + def __init__(self, cardinalities, front, pool): + self._cardinalities = cardinalities + self._front = front + self._pool = pool + + def __getattr__(self, attr): + method_cardinality = self._cardinalities.get(attr) + if method_cardinality is cardinality.Cardinality.UNARY_UNARY: + return _UnaryUnaryMultiCallable(self._front, attr) + elif method_cardinality is cardinality.Cardinality.UNARY_STREAM: + return _UnaryStreamMultiCallable(self._front, attr) + elif method_cardinality is cardinality.Cardinality.STREAM_UNARY: + return _StreamUnaryMultiCallable(self._front, attr, self._pool) + elif method_cardinality is cardinality.Cardinality.STREAM_STREAM: + return _StreamStreamMultiCallable(self._front, attr, self._pool) + else: + raise AttributeError('_DynamicStub object has no attribute "%s"!' % attr) + + +def _adapt_method_implementations(method_implementations, pool): + adapted_implementations = {} + for name, method_implementation in method_implementations.iteritems(): + if method_implementation.style is style.Service.INLINE: + if method_implementation.cardinality is cardinality.Cardinality.UNARY_UNARY: + adapted_implementations[name] = _service.adapt_inline_value_in_value_out( + method_implementation.unary_unary_inline) + elif method_implementation.cardinality is cardinality.Cardinality.UNARY_STREAM: + adapted_implementations[name] = _service.adapt_inline_value_in_stream_out( + method_implementation.unary_stream_inline) + elif method_implementation.cardinality is cardinality.Cardinality.STREAM_UNARY: + adapted_implementations[name] = _service.adapt_inline_stream_in_value_out( + method_implementation.stream_unary_inline, pool) + elif method_implementation.cardinality is cardinality.Cardinality.STREAM_STREAM: + adapted_implementations[name] = _service.adapt_inline_stream_in_stream_out( + method_implementation.stream_stream_inline, pool) + elif method_implementation.style is style.Service.EVENT: + if method_implementation.cardinality is cardinality.Cardinality.UNARY_UNARY: + adapted_implementations[name] = _service.adapt_event_value_in_value_out( + method_implementation.unary_unary_event) + elif method_implementation.cardinality is cardinality.Cardinality.UNARY_STREAM: + adapted_implementations[name] = _service.adapt_event_value_in_stream_out( + method_implementation.unary_stream_event) + elif method_implementation.cardinality is cardinality.Cardinality.STREAM_UNARY: + adapted_implementations[name] = _service.adapt_event_stream_in_value_out( + method_implementation.stream_unary_event) + elif method_implementation.cardinality is cardinality.Cardinality.STREAM_STREAM: + adapted_implementations[name] = _service.adapt_event_stream_in_stream_out( + method_implementation.stream_stream_event) + return adapted_implementations + + +def servicer(pool, method_implementations, multi_method_implementation): + """Creates a base_interfaces.Servicer. + + It is guaranteed that any passed interfaces.MultiMethodImplementation will + only be called to service an RPC if there is no + interfaces.MethodImplementation for the RPC method in the passed + method_implementations dictionary. + + Args: + pool: A thread pool. + method_implementations: A dictionary from RPC method name to + interfaces.MethodImplementation object to be used to service the named + RPC method. + multi_method_implementation: An interfaces.MultiMethodImplementation to be + used to service any RPCs not serviced by the + interfaces.MethodImplementations given in the method_implementations + dictionary, or None. + + Returns: + A base_interfaces.Servicer that services RPCs via the given implementations. + """ + adapted_implementations = _adapt_method_implementations( + method_implementations, pool) + return _BaseServicer(adapted_implementations, multi_method_implementation) + + +def generic_stub(front, pool): + """Creates an interfaces.GenericStub. + + Args: + front: A base_interfaces.Front. + pool: A futures.ThreadPoolExecutor. + + Returns: + An interfaces.GenericStub that performs RPCs via the given + base_interfaces.Front. + """ + return _GenericStub(front, pool) + + +def dynamic_stub(cardinalities, front, pool, prefix): + """Creates an interfaces.DynamicStub. + + Args: + cardinalities: A dict from RPC method name to cardinality.Cardinality + value identifying the cardinality of every RPC method to be supported by + the created interfaces.DynamicStub. + front: A base_interfaces.Front. + pool: A futures.ThreadPoolExecutor. + prefix: A string to prepend when mapping requested attribute name to RPC + method name during attribute access on the created + interfaces.DynamicStub. + + Returns: + An interfaces.DynamicStub that performs RPCs via the given + base_interfaces.Front. + """ + return _DynamicStub(cardinalities, front, pool) diff --git a/src/python/grpcio/grpc/framework/face/interfaces.py b/src/python/grpcio/grpc/framework/face/interfaces.py new file mode 100644 index 00000000..b7cc4c11 --- /dev/null +++ b/src/python/grpcio/grpc/framework/face/interfaces.py @@ -0,0 +1,640 @@ +# Copyright 2015, 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. + +"""Interfaces for the face layer of RPC Framework.""" + +import abc +import enum + +# cardinality, style, exceptions, abandonment, future, and stream are +# referenced from specification in this module. +from grpc.framework.common import cardinality # pylint: disable=unused-import +from grpc.framework.common import style # pylint: disable=unused-import +from grpc.framework.face import exceptions # pylint: disable=unused-import +from grpc.framework.foundation import abandonment # pylint: disable=unused-import +from grpc.framework.foundation import future # pylint: disable=unused-import +from grpc.framework.foundation import stream # pylint: disable=unused-import + + +@enum.unique +class Abortion(enum.Enum): + """Categories of RPC abortion.""" + CANCELLED = 'cancelled' + EXPIRED = 'expired' + NETWORK_FAILURE = 'network failure' + SERVICED_FAILURE = 'serviced failure' + SERVICER_FAILURE = 'servicer failure' + + +class CancellableIterator(object): + """Implements the Iterator protocol and affords a cancel method.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def __iter__(self): + """Returns the self object in accordance with the Iterator protocol.""" + raise NotImplementedError() + + @abc.abstractmethod + def next(self): + """Returns a value or raises StopIteration per the Iterator protocol.""" + raise NotImplementedError() + + @abc.abstractmethod + def cancel(self): + """Requests cancellation of whatever computation underlies this iterator.""" + raise NotImplementedError() + + +class RpcContext(object): + """Provides RPC-related information and action.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def is_active(self): + """Describes whether the RPC is active or has terminated.""" + raise NotImplementedError() + + @abc.abstractmethod + def time_remaining(self): + """Describes the length of allowed time remaining for the RPC. + + Returns: + A nonnegative float indicating the length of allowed time in seconds + remaining for the RPC to complete before it is considered to have timed + out. + """ + raise NotImplementedError() + + @abc.abstractmethod + def add_abortion_callback(self, abortion_callback): + """Registers a callback to be called if the RPC is aborted. + + Args: + abortion_callback: A callable to be called and passed an Abortion value + in the event of RPC abortion. + """ + raise NotImplementedError() + + +class Call(object): + """Invocation-side representation of an RPC. + + Attributes: + context: An RpcContext affording information about the RPC. + """ + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def cancel(self): + """Requests cancellation of the RPC.""" + raise NotImplementedError() + + +class UnaryUnaryMultiCallable(object): + """Affords invoking a unary-unary RPC in any call style.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def __call__(self, request, timeout): + """Synchronously invokes the underlying RPC. + + Args: + request: The request value for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + The response value for the RPC. + + Raises: + exceptions.RpcError: Indicating that the RPC was aborted. + """ + raise NotImplementedError() + + @abc.abstractmethod + def future(self, request, timeout): + """Asynchronously invokes the underlying RPC. + + Args: + request: The request value for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A future.Future representing the RPC. In the event of RPC completion, the + returned Future's result value will be the response value of the RPC. + In the event of RPC abortion, the returned Future's exception value + will be an exceptions.RpcError. + """ + raise NotImplementedError() + + @abc.abstractmethod + def event(self, request, response_callback, abortion_callback, timeout): + """Asynchronously invokes the underlying RPC. + + Args: + request: The request value for the RPC. + response_callback: A callback to be called to accept the restponse value + of the RPC. + abortion_callback: A callback to be called and passed an Abortion value + in the event of RPC abortion. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A Call object for the RPC. + """ + raise NotImplementedError() + + +class UnaryStreamMultiCallable(object): + """Affords invoking a unary-stream RPC in any call style.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def __call__(self, request, timeout): + """Synchronously invokes the underlying RPC. + + Args: + request: The request value for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A CancellableIterator that yields the response values of the RPC and + affords RPC cancellation. Drawing response values from the returned + CancellableIterator may raise exceptions.RpcError indicating abortion + of the RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def event(self, request, response_consumer, abortion_callback, timeout): + """Asynchronously invokes the underlying RPC. + + Args: + request: The request value for the RPC. + response_consumer: A stream.Consumer to be called to accept the restponse + values of the RPC. + abortion_callback: A callback to be called and passed an Abortion value + in the event of RPC abortion. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A Call object for the RPC. + """ + raise NotImplementedError() + + +class StreamUnaryMultiCallable(object): + """Affords invoking a stream-unary RPC in any call style.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def __call__(self, request_iterator, timeout): + """Synchronously invokes the underlying RPC. + + Args: + request_iterator: An iterator that yields request values for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + The response value for the RPC. + + Raises: + exceptions.RpcError: Indicating that the RPC was aborted. + """ + raise NotImplementedError() + + @abc.abstractmethod + def future(self, request_iterator, timeout): + """Asynchronously invokes the underlying RPC. + + Args: + request_iterator: An iterator that yields request values for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A future.Future representing the RPC. In the event of RPC completion, the + returned Future's result value will be the response value of the RPC. + In the event of RPC abortion, the returned Future's exception value + will be an exceptions.RpcError. + """ + raise NotImplementedError() + + @abc.abstractmethod + def event(self, response_callback, abortion_callback, timeout): + """Asynchronously invokes the underlying RPC. + + Args: + request: The request value for the RPC. + response_callback: A callback to be called to accept the restponse value + of the RPC. + abortion_callback: A callback to be called and passed an Abortion value + in the event of RPC abortion. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A pair of a Call object for the RPC and a stream.Consumer to which the + request values of the RPC should be passed. + """ + raise NotImplementedError() + + +class StreamStreamMultiCallable(object): + """Affords invoking a stream-stream RPC in any call style.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def __call__(self, request_iterator, timeout): + """Synchronously invokes the underlying RPC. + + Args: + request_iterator: An iterator that yields request values for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A CancellableIterator that yields the response values of the RPC and + affords RPC cancellation. Drawing response values from the returned + CancellableIterator may raise exceptions.RpcError indicating abortion + of the RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def event(self, response_consumer, abortion_callback, timeout): + """Asynchronously invokes the underlying RPC. + +l Args: + response_consumer: A stream.Consumer to be called to accept the restponse + values of the RPC. + abortion_callback: A callback to be called and passed an Abortion value + in the event of RPC abortion. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A pair of a Call object for the RPC and a stream.Consumer to which the + request values of the RPC should be passed. + """ + raise NotImplementedError() + + +class MethodImplementation(object): + """A sum type that describes an RPC method implementation. + + Attributes: + cardinality: A cardinality.Cardinality value. + style: A style.Service value. + unary_unary_inline: The implementation of the RPC method as a callable + value that takes a request value and an RpcContext object and returns a + response value. Only non-None if cardinality is + cardinality.Cardinality.UNARY_UNARY and style is style.Service.INLINE. + unary_stream_inline: The implementation of the RPC method as a callable + value that takes a request value and an RpcContext object and returns an + iterator of response values. Only non-None if cardinality is + cardinality.Cardinality.UNARY_STREAM and style is style.Service.INLINE. + stream_unary_inline: The implementation of the RPC method as a callable + value that takes an iterator of request values and an RpcContext object + and returns a response value. Only non-None if cardinality is + cardinality.Cardinality.STREAM_UNARY and style is style.Service.INLINE. + stream_stream_inline: The implementation of the RPC method as a callable + value that takes an iterator of request values and an RpcContext object + and returns an iterator of response values. Only non-None if cardinality + is cardinality.Cardinality.STREAM_STREAM and style is + style.Service.INLINE. + unary_unary_event: The implementation of the RPC method as a callable value + that takes a request value, a response callback to which to pass the + response value of the RPC, and an RpcContext. Only non-None if + cardinality is cardinality.Cardinality.UNARY_UNARY and style is + style.Service.EVENT. + unary_stream_event: The implementation of the RPC method as a callable + value that takes a request value, a stream.Consumer to which to pass the + the response values of the RPC, and an RpcContext. Only non-None if + cardinality is cardinality.Cardinality.UNARY_STREAM and style is + style.Service.EVENT. + stream_unary_event: The implementation of the RPC method as a callable + value that takes a response callback to which to pass the response value + of the RPC and an RpcContext and returns a stream.Consumer to which the + request values of the RPC should be passed. Only non-None if cardinality + is cardinality.Cardinality.STREAM_UNARY and style is style.Service.EVENT. + stream_stream_event: The implementation of the RPC method as a callable + value that takes a stream.Consumer to which to pass the response values + of the RPC and an RpcContext and returns a stream.Consumer to which the + request values of the RPC should be passed. Only non-None if cardinality + is cardinality.Cardinality.STREAM_STREAM and style is + style.Service.EVENT. + """ + __metaclass__ = abc.ABCMeta + + +class MultiMethodImplementation(object): + """A general type able to service many RPC methods.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def service(self, name, response_consumer, context): + """Services an RPC. + + Args: + name: The RPC method name. + response_consumer: A stream.Consumer to be called to accept the response + values of the RPC. + context: An RpcContext object. + + Returns: + A stream.Consumer with which to accept the request values of the RPC. The + consumer returned from this method may or may not be invoked to + completion: in the case of RPC abortion, RPC Framework will simply stop + passing values to this object. Implementations must not assume that this + object will be called to completion of the request stream or even called + at all. + + Raises: + abandonment.Abandoned: May or may not be raised when the RPC has been + aborted. + exceptions.NoSuchMethodError: If this MultiMethod does not recognize the + given RPC method name and is not able to service the RPC. + """ + raise NotImplementedError() + + +class GenericStub(object): + """Affords RPC methods to callers.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def blocking_value_in_value_out(self, name, request, timeout): + """Invokes a unary-request-unary-response RPC method. + + This method blocks until either returning the response value of the RPC + (in the event of RPC completion) or raising an exception (in the event of + RPC abortion). + + Args: + name: The RPC method name. + request: The request value for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + The response value for the RPC. + + Raises: + exceptions.RpcError: Indicating that the RPC was aborted. + """ + raise NotImplementedError() + + @abc.abstractmethod + def future_value_in_value_out(self, name, request, timeout): + """Invokes a unary-request-unary-response RPC method. + + Args: + name: The RPC method name. + request: The request value for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A future.Future representing the RPC. In the event of RPC completion, the + returned Future will return an outcome indicating that the RPC returned + the response value of the RPC. In the event of RPC abortion, the + returned Future will return an outcome indicating that the RPC raised + an exceptions.RpcError. + """ + raise NotImplementedError() + + @abc.abstractmethod + def inline_value_in_stream_out(self, name, request, timeout): + """Invokes a unary-request-stream-response RPC method. + + Args: + name: The RPC method name. + request: The request value for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A CancellableIterator that yields the response values of the RPC and + affords RPC cancellation. Drawing response values from the returned + CancellableIterator may raise exceptions.RpcError indicating abortion of + the RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def blocking_stream_in_value_out(self, name, request_iterator, timeout): + """Invokes a stream-request-unary-response RPC method. + + This method blocks until either returning the response value of the RPC + (in the event of RPC completion) or raising an exception (in the event of + RPC abortion). + + Args: + name: The RPC method name. + request_iterator: An iterator that yields the request values of the RPC. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + The response value for the RPC. + + Raises: + exceptions.RpcError: Indicating that the RPC was aborted. + """ + raise NotImplementedError() + + @abc.abstractmethod + def future_stream_in_value_out(self, name, request_iterator, timeout): + """Invokes a stream-request-unary-response RPC method. + + Args: + name: The RPC method name. + request_iterator: An iterator that yields the request values of the RPC. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A future.Future representing the RPC. In the event of RPC completion, the + returned Future will return an outcome indicating that the RPC returned + the response value of the RPC. In the event of RPC abortion, the + returned Future will return an outcome indicating that the RPC raised + an exceptions.RpcError. + """ + raise NotImplementedError() + + @abc.abstractmethod + def inline_stream_in_stream_out(self, name, request_iterator, timeout): + """Invokes a stream-request-stream-response RPC method. + + Args: + name: The RPC method name. + request_iterator: An iterator that yields the request values of the RPC. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A CancellableIterator that yields the response values of the RPC and + affords RPC cancellation. Drawing response values from the returned + CancellableIterator may raise exceptions.RpcError indicating abortion of + the RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def event_value_in_value_out( + self, name, request, response_callback, abortion_callback, timeout): + """Event-driven invocation of a unary-request-unary-response RPC method. + + Args: + name: The RPC method name. + request: The request value for the RPC. + response_callback: A callback to be called to accept the response value + of the RPC. + abortion_callback: A callback to be called and passed an Abortion value + in the event of RPC abortion. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A Call object for the RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def event_value_in_stream_out( + self, name, request, response_consumer, abortion_callback, timeout): + """Event-driven invocation of a unary-request-stream-response RPC method. + + Args: + name: The RPC method name. + request: The request value for the RPC. + response_consumer: A stream.Consumer to be called to accept the response + values of the RPC. + abortion_callback: A callback to be called and passed an Abortion value + in the event of RPC abortion. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A Call object for the RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def event_stream_in_value_out( + self, name, response_callback, abortion_callback, timeout): + """Event-driven invocation of a unary-request-unary-response RPC method. + + Args: + name: The RPC method name. + response_callback: A callback to be called to accept the response value + of the RPC. + abortion_callback: A callback to be called and passed an Abortion value + in the event of RPC abortion. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A pair of a Call object for the RPC and a stream.Consumer to which the + request values of the RPC should be passed. + """ + raise NotImplementedError() + + @abc.abstractmethod + def event_stream_in_stream_out( + self, name, response_consumer, abortion_callback, timeout): + """Event-driven invocation of a unary-request-stream-response RPC method. + + Args: + name: The RPC method name. + response_consumer: A stream.Consumer to be called to accept the response + values of the RPC. + abortion_callback: A callback to be called and passed an Abortion value + in the event of RPC abortion. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A pair of a Call object for the RPC and a stream.Consumer to which the + request values of the RPC should be passed. + """ + raise NotImplementedError() + + @abc.abstractmethod + def unary_unary_multi_callable(self, name): + """Creates a UnaryUnaryMultiCallable for a unary-unary RPC method. + + Args: + name: The RPC method name. + + Returns: + A UnaryUnaryMultiCallable value for the named unary-unary RPC method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def unary_stream_multi_callable(self, name): + """Creates a UnaryStreamMultiCallable for a unary-stream RPC method. + + Args: + name: The RPC method name. + + Returns: + A UnaryStreamMultiCallable value for the name unary-stream RPC method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def stream_unary_multi_callable(self, name): + """Creates a StreamUnaryMultiCallable for a stream-unary RPC method. + + Args: + name: The RPC method name. + + Returns: + A StreamUnaryMultiCallable value for the named stream-unary RPC method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def stream_stream_multi_callable(self, name): + """Creates a StreamStreamMultiCallable for a stream-stream RPC method. + + Args: + name: The RPC method name. + + Returns: + A StreamStreamMultiCallable value for the named stream-stream RPC method. + """ + raise NotImplementedError() + + +class DynamicStub(object): + """A stub with RPC-method-bound multi-callable attributes. + + Instances of this type responsd to attribute access as follows: if the + requested attribute is the name of a unary-unary RPC method, the value of the + attribute will be a UnaryUnaryMultiCallable with which to invoke the RPC + method; if the requested attribute is the name of a unary-stream RPC method, + the value of the attribute will be a UnaryStreamMultiCallable with which to + invoke the RPC method; if the requested attribute is the name of a + stream-unary RPC method, the value of the attribute will be a + StreamUnaryMultiCallable with which to invoke the RPC method; and if the + requested attribute is the name of a stream-stream RPC method, the value of + the attribute will be a StreamStreamMultiCallable with which to invoke the + RPC method. + """ + __metaclass__ = abc.ABCMeta diff --git a/src/python/grpcio/grpc/framework/face/utilities.py b/src/python/grpcio/grpc/framework/face/utilities.py new file mode 100644 index 00000000..a63fe8c6 --- /dev/null +++ b/src/python/grpcio/grpc/framework/face/utilities.py @@ -0,0 +1,177 @@ +# Copyright 2015, 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. + +"""Utilities for RPC framework's face layer.""" + +import collections + +from grpc.framework.common import cardinality +from grpc.framework.common import style +from grpc.framework.face import interfaces +from grpc.framework.foundation import stream + + +class _MethodImplementation( + interfaces.MethodImplementation, + collections.namedtuple( + '_MethodImplementation', + ['cardinality', 'style', 'unary_unary_inline', 'unary_stream_inline', + 'stream_unary_inline', 'stream_stream_inline', 'unary_unary_event', + 'unary_stream_event', 'stream_unary_event', 'stream_stream_event',])): + pass + + +def unary_unary_inline(behavior): + """Creates an interfaces.MethodImplementation for the given behavior. + + Args: + behavior: The implementation of a unary-unary RPC method as a callable value + that takes a request value and an interfaces.RpcContext object and + returns a response value. + + Returns: + An interfaces.MethodImplementation derived from the given behavior. + """ + return _MethodImplementation( + cardinality.Cardinality.UNARY_UNARY, style.Service.INLINE, behavior, + None, None, None, None, None, None, None) + + +def unary_stream_inline(behavior): + """Creates an interfaces.MethodImplementation for the given behavior. + + Args: + behavior: The implementation of a unary-stream RPC method as a callable + value that takes a request value and an interfaces.RpcContext object and + returns an iterator of response values. + + Returns: + An interfaces.MethodImplementation derived from the given behavior. + """ + return _MethodImplementation( + cardinality.Cardinality.UNARY_STREAM, style.Service.INLINE, None, + behavior, None, None, None, None, None, None) + + +def stream_unary_inline(behavior): + """Creates an interfaces.MethodImplementation for the given behavior. + + Args: + behavior: The implementation of a stream-unary RPC method as a callable + value that takes an iterator of request values and an + interfaces.RpcContext object and returns a response value. + + Returns: + An interfaces.MethodImplementation derived from the given behavior. + """ + return _MethodImplementation( + cardinality.Cardinality.STREAM_UNARY, style.Service.INLINE, None, None, + behavior, None, None, None, None, None) + + +def stream_stream_inline(behavior): + """Creates an interfaces.MethodImplementation for the given behavior. + + Args: + behavior: The implementation of a stream-stream RPC method as a callable + value that takes an iterator of request values and an + interfaces.RpcContext object and returns an iterator of response values. + + Returns: + An interfaces.MethodImplementation derived from the given behavior. + """ + return _MethodImplementation( + cardinality.Cardinality.STREAM_STREAM, style.Service.INLINE, None, None, + None, behavior, None, None, None, None) + + +def unary_unary_event(behavior): + """Creates an interfaces.MethodImplementation for the given behavior. + + Args: + behavior: The implementation of a unary-unary RPC method as a callable + value that takes a request value, a response callback to which to pass + the response value of the RPC, and an interfaces.RpcContext. + + Returns: + An interfaces.MethodImplementation derived from the given behavior. + """ + return _MethodImplementation( + cardinality.Cardinality.UNARY_UNARY, style.Service.EVENT, None, None, + None, None, behavior, None, None, None) + + +def unary_stream_event(behavior): + """Creates an interfaces.MethodImplementation for the given behavior. + + Args: + behavior: The implementation of a unary-stream RPC method as a callable + value that takes a request value, a stream.Consumer to which to pass the + the response values of the RPC, and an interfaces.RpcContext. + + Returns: + An interfaces.MethodImplementation derived from the given behavior. + """ + return _MethodImplementation( + cardinality.Cardinality.UNARY_STREAM, style.Service.EVENT, None, None, + None, None, None, behavior, None, None) + + +def stream_unary_event(behavior): + """Creates an interfaces.MethodImplementation for the given behavior. + + Args: + behavior: The implementation of a stream-unary RPC method as a callable + value that takes a response callback to which to pass the response value + of the RPC and an interfaces.RpcContext and returns a stream.Consumer to + which the request values of the RPC should be passed. + + Returns: + An interfaces.MethodImplementation derived from the given behavior. + """ + return _MethodImplementation( + cardinality.Cardinality.STREAM_UNARY, style.Service.EVENT, None, None, + None, None, None, None, behavior, None) + + +def stream_stream_event(behavior): + """Creates an interfaces.MethodImplementation for the given behavior. + + Args: + behavior: The implementation of a stream-stream RPC method as a callable + value that takes a stream.Consumer to which to pass the response values + of the RPC and an interfaces.RpcContext and returns a stream.Consumer to + which the request values of the RPC should be passed. + + Returns: + An interfaces.MethodImplementation derived from the given behavior. + """ + return _MethodImplementation( + cardinality.Cardinality.STREAM_STREAM, style.Service.EVENT, None, None, + None, None, None, None, None, behavior) diff --git a/src/python/grpcio/grpc/framework/foundation/__init__.py b/src/python/grpcio/grpc/framework/foundation/__init__.py new file mode 100644 index 00000000..70865191 --- /dev/null +++ b/src/python/grpcio/grpc/framework/foundation/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, 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. + + diff --git a/src/python/grpcio/grpc/framework/foundation/_timer_future.py b/src/python/grpcio/grpc/framework/foundation/_timer_future.py new file mode 100644 index 00000000..2c9996aa --- /dev/null +++ b/src/python/grpcio/grpc/framework/foundation/_timer_future.py @@ -0,0 +1,228 @@ +# Copyright 2015, 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. + +"""Affords a Future implementation based on Python's threading.Timer.""" + +import sys +import threading +import time + +from grpc.framework.foundation import future + + +class TimerFuture(future.Future): + """A Future implementation based around Timer objects.""" + + def __init__(self, compute_time, computation): + """Constructor. + + Args: + compute_time: The time after which to begin this future's computation. + computation: The computation to be performed within this Future. + """ + self._lock = threading.Lock() + self._compute_time = compute_time + self._computation = computation + self._timer = None + self._computing = False + self._computed = False + self._cancelled = False + self._result = None + self._exception = None + self._traceback = None + self._waiting = [] + + def _compute(self): + """Performs the computation embedded in this Future. + + Or doesn't, if the time to perform it has not yet arrived. + """ + with self._lock: + time_remaining = self._compute_time - time.time() + if 0 < time_remaining: + self._timer = threading.Timer(time_remaining, self._compute) + self._timer.start() + return + else: + self._computing = True + + try: + return_value = self._computation() + exception = None + traceback = None + except Exception as e: # pylint: disable=broad-except + return_value = None + exception = e + traceback = sys.exc_info()[2] + + with self._lock: + self._computing = False + self._computed = True + self._return_value = return_value + self._exception = exception + self._traceback = traceback + waiting = self._waiting + + for callback in waiting: + callback(self) + + def start(self): + """Starts this Future. + + This must be called exactly once, immediately after construction. + """ + with self._lock: + self._timer = threading.Timer( + self._compute_time - time.time(), self._compute) + self._timer.start() + + def cancel(self): + """See future.Future.cancel for specification.""" + with self._lock: + if self._computing or self._computed: + return False + elif self._cancelled: + return True + else: + self._timer.cancel() + self._cancelled = True + waiting = self._waiting + + for callback in waiting: + try: + callback(self) + except Exception: # pylint: disable=broad-except + pass + + return True + + def cancelled(self): + """See future.Future.cancelled for specification.""" + with self._lock: + return self._cancelled + + def running(self): + """See future.Future.running for specification.""" + with self._lock: + return not self._computed and not self._cancelled + + def done(self): + """See future.Future.done for specification.""" + with self._lock: + return self._computed or self._cancelled + + def result(self, timeout=None): + """See future.Future.result for specification.""" + with self._lock: + if self._cancelled: + raise future.CancelledError() + elif self._computed: + if self._exception is None: + return self._return_value + else: + raise self._exception # pylint: disable=raising-bad-type + + condition = threading.Condition() + def notify_condition(unused_future): + with condition: + condition.notify() + self._waiting.append(notify_condition) + + with condition: + condition.wait(timeout=timeout) + + with self._lock: + if self._cancelled: + raise future.CancelledError() + elif self._computed: + if self._exception is None: + return self._return_value + else: + raise self._exception # pylint: disable=raising-bad-type + else: + raise future.TimeoutError() + + def exception(self, timeout=None): + """See future.Future.exception for specification.""" + with self._lock: + if self._cancelled: + raise future.CancelledError() + elif self._computed: + return self._exception + + condition = threading.Condition() + def notify_condition(unused_future): + with condition: + condition.notify() + self._waiting.append(notify_condition) + + with condition: + condition.wait(timeout=timeout) + + with self._lock: + if self._cancelled: + raise future.CancelledError() + elif self._computed: + return self._exception + else: + raise future.TimeoutError() + + def traceback(self, timeout=None): + """See future.Future.traceback for specification.""" + with self._lock: + if self._cancelled: + raise future.CancelledError() + elif self._computed: + return self._traceback + + condition = threading.Condition() + def notify_condition(unused_future): + with condition: + condition.notify() + self._waiting.append(notify_condition) + + with condition: + condition.wait(timeout=timeout) + + with self._lock: + if self._cancelled: + raise future.CancelledError() + elif self._computed: + return self._traceback + else: + raise future.TimeoutError() + + def add_done_callback(self, fn): + """See future.Future.add_done_callback for specification.""" + with self._lock: + if not self._computed and not self._cancelled: + self._waiting.append(fn) + return + + fn(self) diff --git a/src/python/grpcio/grpc/framework/foundation/abandonment.py b/src/python/grpcio/grpc/framework/foundation/abandonment.py new file mode 100644 index 00000000..960b4d06 --- /dev/null +++ b/src/python/grpcio/grpc/framework/foundation/abandonment.py @@ -0,0 +1,38 @@ +# Copyright 2015, 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. + +"""Utilities for indicating abandonment of computation.""" + + +class Abandoned(Exception): + """Indicates that some computation is being abandoned. + + Abandoning a computation is different than returning a value or raising + an exception indicating some operational or programming defect. + """ diff --git a/src/python/grpcio/grpc/framework/foundation/activated.py b/src/python/grpcio/grpc/framework/foundation/activated.py new file mode 100644 index 00000000..426a71c7 --- /dev/null +++ b/src/python/grpcio/grpc/framework/foundation/activated.py @@ -0,0 +1,65 @@ +# Copyright 2015, 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. + +"""Interfaces related to streams of values or objects.""" + +import abc + + +class Activated(object): + """Interface for objects that may be started and stopped. + + Values implementing this type must also implement the context manager + protocol. + """ + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def __enter__(self): + """See the context manager protocol for specification.""" + raise NotImplementedError() + + @abc.abstractmethod + def __exit__(self, exc_type, exc_val, exc_tb): + """See the context manager protocol for specification.""" + raise NotImplementedError() + + @abc.abstractmethod + def start(self): + """Activates this object. + + Returns: + A value equal to the value returned by this object's __enter__ method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def stop(self): + """Deactivates this object.""" + raise NotImplementedError() diff --git a/src/python/grpcio/grpc/framework/foundation/callable_util.py b/src/python/grpcio/grpc/framework/foundation/callable_util.py new file mode 100644 index 00000000..32b0751a --- /dev/null +++ b/src/python/grpcio/grpc/framework/foundation/callable_util.py @@ -0,0 +1,107 @@ +# Copyright 2015, 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. + +"""Utilities for working with callables.""" + +import abc +import collections +import enum +import functools +import logging + + +class Outcome(object): + """A sum type describing the outcome of some call. + + Attributes: + kind: One of Kind.RETURNED or Kind.RAISED respectively indicating that the + call returned a value or raised an exception. + return_value: The value returned by the call. Must be present if kind is + Kind.RETURNED. + exception: The exception raised by the call. Must be present if kind is + Kind.RAISED. + """ + __metaclass__ = abc.ABCMeta + + @enum.unique + class Kind(enum.Enum): + """Identifies the general kind of the outcome of some call.""" + + RETURNED = object() + RAISED = object() + + +class _EasyOutcome( + collections.namedtuple( + '_EasyOutcome', ['kind', 'return_value', 'exception']), + Outcome): + """A trivial implementation of Outcome.""" + + +def _call_logging_exceptions(behavior, message, *args, **kwargs): + try: + return _EasyOutcome(Outcome.Kind.RETURNED, behavior(*args, **kwargs), None) + except Exception as e: # pylint: disable=broad-except + logging.exception(message) + return _EasyOutcome(Outcome.Kind.RAISED, None, e) + + +def with_exceptions_logged(behavior, message): + """Wraps a callable in a try-except that logs any exceptions it raises. + + Args: + behavior: Any callable. + message: A string to log if the behavior raises an exception. + + Returns: + A callable that when executed invokes the given behavior. The returned + callable takes the same arguments as the given behavior but returns a + future.Outcome describing whether the given behavior returned a value or + raised an exception. + """ + @functools.wraps(behavior) + def wrapped_behavior(*args, **kwargs): + return _call_logging_exceptions(behavior, message, *args, **kwargs) + return wrapped_behavior + + +def call_logging_exceptions(behavior, message, *args, **kwargs): + """Calls a behavior in a try-except that logs any exceptions it raises. + + Args: + behavior: Any callable. + message: A string to log if the behavior raises an exception. + *args: Positional arguments to pass to the given behavior. + **kwargs: Keyword arguments to pass to the given behavior. + + Returns: + An Outcome describing whether the given behavior returned a value or raised + an exception. + """ + return _call_logging_exceptions(behavior, message, *args, **kwargs) diff --git a/src/python/grpcio/grpc/framework/foundation/future.py b/src/python/grpcio/grpc/framework/foundation/future.py new file mode 100644 index 00000000..bfc16fc1 --- /dev/null +++ b/src/python/grpcio/grpc/framework/foundation/future.py @@ -0,0 +1,236 @@ +# Copyright 2015, 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. + +"""A Future interface. + +Python doesn't have a Future interface in its standard library. In the absence +of such a standard, three separate, incompatible implementations +(concurrent.futures.Future, ndb.Future, and asyncio.Future) have appeared. This +interface attempts to be as compatible as possible with +concurrent.futures.Future. From ndb.Future it adopts a traceback-object accessor +method. + +Unlike the concrete and implemented Future classes listed above, the Future +class defined in this module is an entirely abstract interface that anyone may +implement and use. + +The one known incompatibility between this interface and the interface of +concurrent.futures.Future is that this interface defines its own CancelledError +and TimeoutError exceptions rather than raising the implementation-private +concurrent.futures._base.CancelledError and the +built-in-but-only-in-3.3-and-later TimeoutError. +""" + +import abc + + +class TimeoutError(Exception): + """Indicates that a particular call timed out.""" + + +class CancelledError(Exception): + """Indicates that the computation underlying a Future was cancelled.""" + + +class Future(object): + """A representation of a computation in another control flow. + + Computations represented by a Future may be yet to be begun, may be ongoing, + or may have already completed. + """ + __metaclass__ = abc.ABCMeta + + # NOTE(nathaniel): This isn't the return type that I would want to have if it + # were up to me. Were this interface being written from scratch, the return + # type of this method would probably be a sum type like: + # + # NOT_COMMENCED + # COMMENCED_AND_NOT_COMPLETED + # PARTIAL_RESULT + # COMPLETED + # UNCANCELLABLE + # NOT_IMMEDIATELY_DETERMINABLE + @abc.abstractmethod + def cancel(self): + """Attempts to cancel the computation. + + This method does not block. + + Returns: + True if the computation has not yet begun, will not be allowed to take + place, and determination of both was possible without blocking. False + under all other circumstances including but not limited to the + computation's already having begun, the computation's already having + finished, and the computation's having been scheduled for execution on a + remote system for which a determination of whether or not it commenced + before being cancelled cannot be made without blocking. + """ + raise NotImplementedError() + + # NOTE(nathaniel): Here too this isn't the return type that I'd want this + # method to have if it were up to me. I think I'd go with another sum type + # like: + # + # NOT_CANCELLED (this object's cancel method hasn't been called) + # NOT_COMMENCED + # COMMENCED_AND_NOT_COMPLETED + # PARTIAL_RESULT + # COMPLETED + # UNCANCELLABLE + # NOT_IMMEDIATELY_DETERMINABLE + # + # Notice how giving the cancel method the right semantics obviates most + # reasons for this method to exist. + @abc.abstractmethod + def cancelled(self): + """Describes whether the computation was cancelled. + + This method does not block. + + Returns: + True if the computation was cancelled any time before its result became + immediately available. False under all other circumstances including but + not limited to this object's cancel method not having been called and + the computation's result having become immediately available. + """ + raise NotImplementedError() + + @abc.abstractmethod + def running(self): + """Describes whether the computation is taking place. + + This method does not block. + + Returns: + True if the computation is scheduled to take place in the future or is + taking place now, or False if the computation took place in the past or + was cancelled. + """ + raise NotImplementedError() + + # NOTE(nathaniel): These aren't quite the semantics I'd like here either. I + # would rather this only returned True in cases in which the underlying + # computation completed successfully. A computation's having been cancelled + # conflicts with considering that computation "done". + @abc.abstractmethod + def done(self): + """Describes whether the computation has taken place. + + This method does not block. + + Returns: + True if the computation is known to have either completed or have been + unscheduled or interrupted. False if the computation may possibly be + executing or scheduled to execute later. + """ + raise NotImplementedError() + + @abc.abstractmethod + def result(self, timeout=None): + """Accesses the outcome of the computation or raises its exception. + + This method may return immediately or may block. + + Args: + timeout: The length of time in seconds to wait for the computation to + finish or be cancelled, or None if this method should block until the + computation has finished or is cancelled no matter how long that takes. + + Returns: + The return value of the computation. + + Raises: + TimeoutError: If a timeout value is passed and the computation does not + terminate within the allotted time. + CancelledError: If the computation was cancelled. + Exception: If the computation raised an exception, this call will raise + the same exception. + """ + raise NotImplementedError() + + @abc.abstractmethod + def exception(self, timeout=None): + """Return the exception raised by the computation. + + This method may return immediately or may block. + + Args: + timeout: The length of time in seconds to wait for the computation to + terminate or be cancelled, or None if this method should block until + the computation is terminated or is cancelled no matter how long that + takes. + + Returns: + The exception raised by the computation, or None if the computation did + not raise an exception. + + Raises: + TimeoutError: If a timeout value is passed and the computation does not + terminate within the allotted time. + CancelledError: If the computation was cancelled. + """ + raise NotImplementedError() + + @abc.abstractmethod + def traceback(self, timeout=None): + """Access the traceback of the exception raised by the computation. + + This method may return immediately or may block. + + Args: + timeout: The length of time in seconds to wait for the computation to + terminate or be cancelled, or None if this method should block until + the computation is terminated or is cancelled no matter how long that + takes. + + Returns: + The traceback of the exception raised by the computation, or None if the + computation did not raise an exception. + + Raises: + TimeoutError: If a timeout value is passed and the computation does not + terminate within the allotted time. + CancelledError: If the computation was cancelled. + """ + raise NotImplementedError() + + @abc.abstractmethod + def add_done_callback(self, fn): + """Adds a function to be called at completion of the computation. + + The callback will be passed this Future object describing the outcome of + the computation. + + If the computation has already completed, the callback will be called + immediately. + + Args: + fn: A callable taking a this Future object as its single parameter. + """ + raise NotImplementedError() diff --git a/src/python/grpcio/grpc/framework/foundation/later.py b/src/python/grpcio/grpc/framework/foundation/later.py new file mode 100644 index 00000000..1d1e0650 --- /dev/null +++ b/src/python/grpcio/grpc/framework/foundation/later.py @@ -0,0 +1,51 @@ +# Copyright 2015, 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. + +"""Enables scheduling execution at a later time.""" + +import time + +from grpc.framework.foundation import _timer_future + + +def later(delay, computation): + """Schedules later execution of a callable. + + Args: + delay: Any numeric value. Represents the minimum length of time in seconds + to allow to pass before beginning the computation. No guarantees are made + about the maximum length of time that will pass. + computation: A callable that accepts no arguments. + + Returns: + A Future representing the scheduled computation. + """ + timer_future = _timer_future.TimerFuture(time.time() + delay, computation) + timer_future.start() + return timer_future diff --git a/src/python/grpcio/grpc/framework/foundation/logging_pool.py b/src/python/grpcio/grpc/framework/foundation/logging_pool.py new file mode 100644 index 00000000..7c7a6eeb --- /dev/null +++ b/src/python/grpcio/grpc/framework/foundation/logging_pool.py @@ -0,0 +1,83 @@ +# Copyright 2015, 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. + +"""A thread pool that logs exceptions raised by tasks executed within it.""" + +import functools +import logging + +from concurrent import futures + + +def _wrap(behavior): + """Wraps an arbitrary callable behavior in exception-logging.""" + @functools.wraps(behavior) + def _wrapping(*args, **kwargs): + try: + return behavior(*args, **kwargs) + except Exception as e: + logging.exception('Unexpected exception from task run in logging pool!') + raise + return _wrapping + + +class _LoggingPool(object): + """An exception-logging futures.ThreadPoolExecutor-compatible thread pool.""" + + def __init__(self, backing_pool): + self._backing_pool = backing_pool + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self._backing_pool.shutdown(wait=True) + + def submit(self, fn, *args, **kwargs): + return self._backing_pool.submit(_wrap(fn), *args, **kwargs) + + def map(self, func, *iterables, **kwargs): + return self._backing_pool.map( + _wrap(func), *iterables, timeout=kwargs.get('timeout', None)) + + def shutdown(self, wait=True): + self._backing_pool.shutdown(wait=wait) + + +def pool(max_workers): + """Creates a thread pool that logs exceptions raised by the tasks within it. + + Args: + max_workers: The maximum number of worker threads to allow the pool. + + Returns: + A futures.ThreadPoolExecutor-compatible thread pool that logs exceptions + raised by the tasks executed within it. + """ + return _LoggingPool(futures.ThreadPoolExecutor(max_workers)) diff --git a/src/python/grpcio/grpc/framework/foundation/relay.py b/src/python/grpcio/grpc/framework/foundation/relay.py new file mode 100644 index 00000000..9c239465 --- /dev/null +++ b/src/python/grpcio/grpc/framework/foundation/relay.py @@ -0,0 +1,175 @@ +# Copyright 2015, 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. + +"""Implementations of in-order work deference.""" + +import abc +import enum +import threading + +from grpc.framework.foundation import activated +from grpc.framework.foundation import logging_pool + +_NULL_BEHAVIOR = lambda unused_value: None + + +class Relay(object): + """Performs work submitted to it in another thread. + + Performs work in the order in which work was submitted to it; otherwise there + would be no reason to use an implementation of this interface instead of a + thread pool. + """ + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def add_value(self, value): + """Adds a value to be passed to the behavior registered with this Relay. + + Args: + value: A value that will be passed to a call made in another thread to the + behavior registered with this Relay. + """ + raise NotImplementedError() + + @abc.abstractmethod + def set_behavior(self, behavior): + """Sets the behavior that this Relay should call when passed values. + + Args: + behavior: The behavior that this Relay should call in another thread when + passed a value, or None to have passed values ignored. + """ + raise NotImplementedError() + + +class _PoolRelay(activated.Activated, Relay): + + @enum.unique + class _State(enum.Enum): + INACTIVE = 'inactive' + IDLE = 'idle' + SPINNING = 'spinning' + + def __init__(self, pool, behavior): + self._condition = threading.Condition() + self._pool = pool + self._own_pool = pool is None + self._state = _PoolRelay._State.INACTIVE + self._activated = False + self._spinning = False + self._values = [] + self._behavior = _NULL_BEHAVIOR if behavior is None else behavior + + def _spin(self, behavior, value): + while True: + behavior(value) + with self._condition: + if self._values: + value = self._values.pop(0) + behavior = self._behavior + else: + self._state = _PoolRelay._State.IDLE + self._condition.notify_all() + break + + def add_value(self, value): + with self._condition: + if self._state is _PoolRelay._State.INACTIVE: + raise ValueError('add_value not valid on inactive Relay!') + elif self._state is _PoolRelay._State.IDLE: + self._pool.submit(self._spin, self._behavior, value) + self._state = _PoolRelay._State.SPINNING + else: + self._values.append(value) + + def set_behavior(self, behavior): + with self._condition: + self._behavior = _NULL_BEHAVIOR if behavior is None else behavior + + def _start(self): + with self._condition: + self._state = _PoolRelay._State.IDLE + if self._own_pool: + self._pool = logging_pool.pool(1) + return self + + def _stop(self): + with self._condition: + while self._state is _PoolRelay._State.SPINNING: + self._condition.wait() + if self._own_pool: + self._pool.shutdown(wait=True) + self._state = _PoolRelay._State.INACTIVE + + def __enter__(self): + return self._start() + + def __exit__(self, exc_type, exc_val, exc_tb): + self._stop() + return False + + def start(self): + return self._start() + + def stop(self): + self._stop() + + +def relay(behavior): + """Creates a Relay. + + Args: + behavior: The behavior to be called by the created Relay, or None to have + passed values dropped until a different behavior is given to the returned + Relay later. + + Returns: + An object that is both an activated.Activated and a Relay. The object is + only valid for use as a Relay when activated. + """ + return _PoolRelay(None, behavior) + + +def pool_relay(pool, behavior): + """Creates a Relay that uses a given thread pool. + + This object will make use of at most one thread in the given pool. + + Args: + pool: A futures.ThreadPoolExecutor for use by the created Relay. + behavior: The behavior to be called by the created Relay, or None to have + passed values dropped until a different behavior is given to the returned + Relay later. + + Returns: + An object that is both an activated.Activated and a Relay. The object is + only valid for use as a Relay when activated. + """ + return _PoolRelay(pool, behavior) diff --git a/src/python/grpcio/grpc/framework/foundation/stream.py b/src/python/grpcio/grpc/framework/foundation/stream.py new file mode 100644 index 00000000..75c0cf14 --- /dev/null +++ b/src/python/grpcio/grpc/framework/foundation/stream.py @@ -0,0 +1,60 @@ +# Copyright 2015, 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. + +"""Interfaces related to streams of values or objects.""" + +import abc + + +class Consumer(object): + """Interface for consumers of finite streams of values or objects.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def consume(self, value): + """Accepts a value. + + Args: + value: Any value accepted by this Consumer. + """ + raise NotImplementedError() + + @abc.abstractmethod + def terminate(self): + """Indicates to this Consumer that no more values will be supplied.""" + raise NotImplementedError() + + @abc.abstractmethod + def consume_and_terminate(self, value): + """Supplies a value and signals that no more values will be supplied. + + Args: + value: Any value accepted by this Consumer. + """ + raise NotImplementedError() diff --git a/src/python/grpcio/grpc/framework/foundation/stream_util.py b/src/python/grpcio/grpc/framework/foundation/stream_util.py new file mode 100644 index 00000000..2210e4ef --- /dev/null +++ b/src/python/grpcio/grpc/framework/foundation/stream_util.py @@ -0,0 +1,160 @@ +# Copyright 2015, 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. + +"""Helpful utilities related to the stream module.""" + +import logging +import threading + +from grpc.framework.foundation import stream + +_NO_VALUE = object() + + +class TransformingConsumer(stream.Consumer): + """A stream.Consumer that passes a transformation of its input to another.""" + + def __init__(self, transformation, downstream): + self._transformation = transformation + self._downstream = downstream + + def consume(self, value): + self._downstream.consume(self._transformation(value)) + + def terminate(self): + self._downstream.terminate() + + def consume_and_terminate(self, value): + self._downstream.consume_and_terminate(self._transformation(value)) + + +class IterableConsumer(stream.Consumer): + """A Consumer that when iterated over emits the values it has consumed.""" + + def __init__(self): + self._condition = threading.Condition() + self._values = [] + self._active = True + + def consume(self, stock_reply): + with self._condition: + if self._active: + self._values.append(stock_reply) + self._condition.notify() + + def terminate(self): + with self._condition: + self._active = False + self._condition.notify() + + def consume_and_terminate(self, stock_reply): + with self._condition: + if self._active: + self._values.append(stock_reply) + self._active = False + self._condition.notify() + + def __iter__(self): + return self + + def next(self): + with self._condition: + while self._active and not self._values: + self._condition.wait() + if self._values: + return self._values.pop(0) + else: + raise StopIteration() + + +class ThreadSwitchingConsumer(stream.Consumer): + """A Consumer decorator that affords serialization and asynchrony.""" + + def __init__(self, sink, pool): + self._lock = threading.Lock() + self._sink = sink + self._pool = pool + # True if self._spin has been submitted to the pool to be called once and + # that call has not yet returned, False otherwise. + self._spinning = False + self._values = [] + self._active = True + + def _spin(self, sink, value, terminate): + while True: + try: + if value is _NO_VALUE: + sink.terminate() + elif terminate: + sink.consume_and_terminate(value) + else: + sink.consume(value) + except Exception as e: # pylint:disable=broad-except + logging.exception(e) + + with self._lock: + if terminate: + self._spinning = False + return + elif self._values: + value = self._values.pop(0) + terminate = not self._values and not self._active + elif not self._active: + value = _NO_VALUE + terminate = True + else: + self._spinning = False + return + + def consume(self, value): + with self._lock: + if self._active: + if self._spinning: + self._values.append(value) + else: + self._pool.submit(self._spin, self._sink, value, False) + self._spinning = True + + def terminate(self): + with self._lock: + if self._active: + self._active = False + if not self._spinning: + self._pool.submit(self._spin, self._sink, _NO_VALUE, True) + self._spinning = True + + def consume_and_terminate(self, value): + with self._lock: + if self._active: + self._active = False + if self._spinning: + self._values.append(value) + else: + self._pool.submit(self._spin, self._sink, value, True) + self._spinning = True diff --git a/src/python/grpcio/grpc/framework/interfaces/__init__.py b/src/python/grpcio/grpc/framework/interfaces/__init__.py new file mode 100644 index 00000000..70865191 --- /dev/null +++ b/src/python/grpcio/grpc/framework/interfaces/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, 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. + + diff --git a/src/python/grpcio/grpc/framework/interfaces/base/__init__.py b/src/python/grpcio/grpc/framework/interfaces/base/__init__.py new file mode 100644 index 00000000..70865191 --- /dev/null +++ b/src/python/grpcio/grpc/framework/interfaces/base/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, 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. + + diff --git a/src/python/grpcio/grpc/framework/interfaces/base/base.py b/src/python/grpcio/grpc/framework/interfaces/base/base.py new file mode 100644 index 00000000..a1e70be5 --- /dev/null +++ b/src/python/grpcio/grpc/framework/interfaces/base/base.py @@ -0,0 +1,339 @@ +# Copyright 2015, 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. + +"""The base interface of RPC Framework. + +Implementations of this interface support the conduct of "operations": +exchanges between two distinct ends of an arbitrary number of data payloads +and metadata such as a name for the operation, initial and terminal metadata +in each direction, and flow control. These operations may be used for transfers +of data, remote procedure calls, status indication, or anything else +applications choose. +""" + +# threading is referenced from specification in this module. +import abc +import enum +import threading # pylint: disable=unused-import + +# abandonment is referenced from specification in this module. +from grpc.framework.foundation import abandonment # pylint: disable=unused-import + + +class NoSuchMethodError(Exception): + """Indicates that an unrecognized operation has been called. + + Attributes: + code: A code value to communicate to the other side of the operation along + with indication of operation termination. May be None. + details: A details value to communicate to the other side of the operation + along with indication of operation termination. May be None. + """ + + def __init__(self, code, details): + """Constructor. + + Args: + code: A code value to communicate to the other side of the operation + along with indication of operation termination. May be None. + details: A details value to communicate to the other side of the + operation along with indication of operation termination. May be None. + """ + self.code = code + self.details = details + + +class Outcome(object): + """The outcome of an operation. + + Attributes: + kind: A Kind value coarsely identifying how the operation terminated. + code: An application-specific code value or None if no such value was + provided. + details: An application-specific details value or None if no such value was + provided. + """ + + @enum.unique + class Kind(enum.Enum): + """Ways in which an operation can terminate.""" + + COMPLETED = 'completed' + CANCELLED = 'cancelled' + EXPIRED = 'expired' + LOCAL_SHUTDOWN = 'local shutdown' + REMOTE_SHUTDOWN = 'remote shutdown' + RECEPTION_FAILURE = 'reception failure' + TRANSMISSION_FAILURE = 'transmission failure' + LOCAL_FAILURE = 'local failure' + REMOTE_FAILURE = 'remote failure' + + +class Completion(object): + """An aggregate of the values exchanged upon operation completion. + + Attributes: + terminal_metadata: A terminal metadata value for the operaton. + code: A code value for the operation. + message: A message value for the operation. + """ + __metaclass__ = abc.ABCMeta + + +class OperationContext(object): + """Provides operation-related information and action.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def outcome(self): + """Indicates the operation's outcome (or that the operation is ongoing). + + Returns: + None if the operation is still active or the Outcome value for the + operation if it has terminated. + """ + raise NotImplementedError() + + @abc.abstractmethod + def add_termination_callback(self, callback): + """Adds a function to be called upon operation termination. + + Args: + callback: A callable to be passed an Outcome value on operation + termination. + + Returns: + None if the operation has not yet terminated and the passed callback will + later be called when it does terminate, or if the operation has already + terminated an Outcome value describing the operation termination and the + passed callback will not be called as a result of this method call. + """ + raise NotImplementedError() + + @abc.abstractmethod + def time_remaining(self): + """Describes the length of allowed time remaining for the operation. + + Returns: + A nonnegative float indicating the length of allowed time in seconds + remaining for the operation to complete before it is considered to have + timed out. Zero is returned if the operation has terminated. + """ + raise NotImplementedError() + + @abc.abstractmethod + def cancel(self): + """Cancels the operation if the operation has not yet terminated.""" + raise NotImplementedError() + + @abc.abstractmethod + def fail(self, exception): + """Indicates that the operation has failed. + + Args: + exception: An exception germane to the operation failure. May be None. + """ + raise NotImplementedError() + + +class Operator(object): + """An interface through which to participate in an operation.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def advance( + self, initial_metadata=None, payload=None, completion=None, + allowance=None): + """Progresses the operation. + + Args: + initial_metadata: An initial metadata value. Only one may ever be + communicated in each direction for an operation, and they must be + communicated no later than either the first payload or the completion. + payload: A payload value. + completion: A Completion value. May only ever be non-None once in either + direction, and no payloads may be passed after it has been communicated. + allowance: A positive integer communicating the number of additional + payloads allowed to be passed by the remote side of the operation. + """ + raise NotImplementedError() + +class ProtocolReceiver(object): + """A means of receiving protocol values during an operation.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def context(self, protocol_context): + """Accepts the protocol context object for the operation. + + Args: + protocol_context: The protocol context object for the operation. + """ + raise NotImplementedError() + + +class Subscription(object): + """Describes customer code's interest in values from the other side. + + Attributes: + kind: A Kind value describing the overall kind of this value. + termination_callback: A callable to be passed the Outcome associated with + the operation after it has terminated. Must be non-None if kind is + Kind.TERMINATION_ONLY. Must be None otherwise. + allowance: A callable behavior that accepts positive integers representing + the number of additional payloads allowed to be passed to the other side + of the operation. Must be None if kind is Kind.FULL. Must not be None + otherwise. + operator: An Operator to be passed values from the other side of the + operation. Must be non-None if kind is Kind.FULL. Must be None otherwise. + protocol_receiver: A ProtocolReceiver to be passed protocol objects as they + become available during the operation. Must be non-None if kind is + Kind.FULL. + """ + __metaclass__ = abc.ABCMeta + + @enum.unique + class Kind(enum.Enum): + + NONE = 'none' + TERMINATION_ONLY = 'termination only' + FULL = 'full' + + +class Servicer(object): + """Interface for service implementations.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def service(self, group, method, context, output_operator): + """Services an operation. + + Args: + group: The group identifier of the operation to be serviced. + method: The method identifier of the operation to be serviced. + context: An OperationContext object affording contextual information and + actions. + output_operator: An Operator that will accept output values of the + operation. + + Returns: + A Subscription via which this object may or may not accept more values of + the operation. + + Raises: + NoSuchMethodError: If this Servicer does not handle operations with the + given group and method. + abandonment.Abandoned: If the operation has been aborted and there no + longer is any reason to service the operation. + """ + raise NotImplementedError() + + +class End(object): + """Common type for entry-point objects on both sides of an operation.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def start(self): + """Starts this object's service of operations.""" + raise NotImplementedError() + + @abc.abstractmethod + def stop(self, grace): + """Stops this object's service of operations. + + This object will refuse service of new operations as soon as this method is + called but operations under way at the time of the call may be given a + grace period during which they are allowed to finish. + + Args: + grace: A duration of time in seconds to allow ongoing operations to + terminate before being forcefully terminated by the stopping of this + End. May be zero to terminate all ongoing operations and immediately + stop. + + Returns: + A threading.Event that will be set to indicate all operations having + terminated and this End having completely stopped. The returned event + may not be set until after the full grace period (if some ongoing + operation continues for the full length of the period) or it may be set + much sooner (if for example this End had no operations in progress at + the time its stop method was called). + """ + raise NotImplementedError() + + @abc.abstractmethod + def operate( + self, group, method, subscription, timeout, initial_metadata=None, + payload=None, completion=None, protocol_options=None): + """Commences an operation. + + Args: + group: The group identifier of the invoked operation. + method: The method identifier of the invoked operation. + subscription: A Subscription to which the results of the operation will be + passed. + timeout: A length of time in seconds to allow for the operation. + initial_metadata: An initial metadata value to be sent to the other side + of the operation. May be None if the initial metadata will be later + passed via the returned operator or if there will be no initial metadata + passed at all. + payload: An initial payload for the operation. + completion: A Completion value indicating the end of transmission to the + other side of the operation. + protocol_options: A value specified by the provider of a Base interface + implementation affording custom state and behavior. + + Returns: + A pair of objects affording information about the operation and action + continuing the operation. The first element of the returned pair is an + OperationContext for the operation and the second element of the + returned pair is an Operator to which operation values not passed in + this call should later be passed. + """ + raise NotImplementedError() + + @abc.abstractmethod + def operation_stats(self): + """Reports the number of terminated operations broken down by outcome. + + Returns: + A dictionary from Outcome.Kind value to an integer identifying the number + of operations that terminated with that outcome kind. + """ + raise NotImplementedError() + + @abc.abstractmethod + def add_idle_action(self, action): + """Adds an action to be called when this End has no ongoing operations. + + Args: + action: A callable that accepts no arguments. + """ + raise NotImplementedError() diff --git a/src/python/grpcio/grpc/framework/interfaces/base/utilities.py b/src/python/grpcio/grpc/framework/interfaces/base/utilities.py new file mode 100644 index 00000000..87a85018 --- /dev/null +++ b/src/python/grpcio/grpc/framework/interfaces/base/utilities.py @@ -0,0 +1,82 @@ +# Copyright 2015, 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. + +"""Utilities for use with the base interface of RPC Framework.""" + +import collections + +from grpc.framework.interfaces.base import base + + +class _Completion( + base.Completion, + collections.namedtuple( + '_Completion', ('terminal_metadata', 'code', 'message',))): + """A trivial implementation of base.Completion.""" + + +class _Subscription( + base.Subscription, + collections.namedtuple( + '_Subscription', + ('kind', 'termination_callback', 'allowance', 'operator', + 'protocol_receiver',))): + """A trivial implementation of base.Subscription.""" + +_NONE_SUBSCRIPTION = _Subscription( + base.Subscription.Kind.NONE, None, None, None, None) + + +def completion(terminal_metadata, code, message): + """Creates a base.Completion aggregating the given operation values. + + Args: + terminal_metadata: A terminal metadata value for an operaton. + code: A code value for an operation. + message: A message value for an operation. + + Returns: + A base.Completion aggregating the given operation values. + """ + return _Completion(terminal_metadata, code, message) + + +def full_subscription(operator, protocol_receiver): + """Creates a "full" base.Subscription for the given base.Operator. + + Args: + operator: A base.Operator to be used in an operation. + protocol_receiver: A base.ProtocolReceiver to be used in an operation. + + Returns: + A base.Subscription of kind base.Subscription.Kind.FULL wrapping the given + base.Operator and base.ProtocolReceiver. + """ + return _Subscription( + base.Subscription.Kind.FULL, None, None, operator, protocol_receiver) diff --git a/src/python/grpcio/grpc/framework/interfaces/face/__init__.py b/src/python/grpcio/grpc/framework/interfaces/face/__init__.py new file mode 100644 index 00000000..70865191 --- /dev/null +++ b/src/python/grpcio/grpc/framework/interfaces/face/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, 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. + + diff --git a/src/python/grpcio/grpc/framework/interfaces/face/face.py b/src/python/grpcio/grpc/framework/interfaces/face/face.py new file mode 100644 index 00000000..bc9a434a --- /dev/null +++ b/src/python/grpcio/grpc/framework/interfaces/face/face.py @@ -0,0 +1,992 @@ +# Copyright 2015, 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. + +"""Interfaces defining the Face layer of RPC Framework.""" + +import abc +import collections +import enum + +# cardinality, style, abandonment, future, and stream are +# referenced from specification in this module. +from grpc.framework.common import cardinality # pylint: disable=unused-import +from grpc.framework.common import style # pylint: disable=unused-import +from grpc.framework.foundation import abandonment # pylint: disable=unused-import +from grpc.framework.foundation import future # pylint: disable=unused-import +from grpc.framework.foundation import stream # pylint: disable=unused-import + + +class NoSuchMethodError(Exception): + """Raised by customer code to indicate an unrecognized method. + + Attributes: + group: The group of the unrecognized method. + name: The name of the unrecognized method. + """ + + def __init__(self, group, method): + """Constructor. + + Args: + group: The group identifier of the unrecognized RPC name. + method: The method identifier of the unrecognized RPC name. + """ + super(NoSuchMethodError, self).__init__() + self.group = group + self.method = method + + def __repr__(self): + return 'face.NoSuchMethodError(%s, %s)' % (self.group, self.method,) + + +class Abortion( + collections.namedtuple( + 'Abortion', + ('kind', 'initial_metadata', 'terminal_metadata', 'code', 'details',))): + """A value describing RPC abortion. + + Attributes: + kind: A Kind value identifying how the RPC failed. + initial_metadata: The initial metadata from the other side of the RPC or + None if no initial metadata value was received. + terminal_metadata: The terminal metadata from the other side of the RPC or + None if no terminal metadata value was received. + code: The code value from the other side of the RPC or None if no code value + was received. + details: The details value from the other side of the RPC or None if no + details value was received. + """ + + @enum.unique + class Kind(enum.Enum): + """Types of RPC abortion.""" + + CANCELLED = 'cancelled' + EXPIRED = 'expired' + LOCAL_SHUTDOWN = 'local shutdown' + REMOTE_SHUTDOWN = 'remote shutdown' + NETWORK_FAILURE = 'network failure' + LOCAL_FAILURE = 'local failure' + REMOTE_FAILURE = 'remote failure' + + +class AbortionError(Exception): + """Common super type for exceptions indicating RPC abortion. + + initial_metadata: The initial metadata from the other side of the RPC or + None if no initial metadata value was received. + terminal_metadata: The terminal metadata from the other side of the RPC or + None if no terminal metadata value was received. + code: The code value from the other side of the RPC or None if no code value + was received. + details: The details value from the other side of the RPC or None if no + details value was received. + """ + __metaclass__ = abc.ABCMeta + + def __init__(self, initial_metadata, terminal_metadata, code, details): + super(AbortionError, self).__init__() + self.initial_metadata = initial_metadata + self.terminal_metadata = terminal_metadata + self.code = code + self.details = details + + +class CancellationError(AbortionError): + """Indicates that an RPC has been cancelled.""" + + +class ExpirationError(AbortionError): + """Indicates that an RPC has expired ("timed out").""" + + +class LocalShutdownError(AbortionError): + """Indicates that an RPC has terminated due to local shutdown of RPCs.""" + + +class RemoteShutdownError(AbortionError): + """Indicates that an RPC has terminated due to remote shutdown of RPCs.""" + + +class NetworkError(AbortionError): + """Indicates that some error occurred on the network.""" + + +class LocalError(AbortionError): + """Indicates that an RPC has terminated due to a local defect.""" + + +class RemoteError(AbortionError): + """Indicates that an RPC has terminated due to a remote defect.""" + + +class RpcContext(object): + """Provides RPC-related information and action.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def is_active(self): + """Describes whether the RPC is active or has terminated.""" + raise NotImplementedError() + + @abc.abstractmethod + def time_remaining(self): + """Describes the length of allowed time remaining for the RPC. + + Returns: + A nonnegative float indicating the length of allowed time in seconds + remaining for the RPC to complete before it is considered to have timed + out. + """ + raise NotImplementedError() + + @abc.abstractmethod + def add_abortion_callback(self, abortion_callback): + """Registers a callback to be called if the RPC is aborted. + + Args: + abortion_callback: A callable to be called and passed an Abortion value + in the event of RPC abortion. + """ + raise NotImplementedError() + + @abc.abstractmethod + def cancel(self): + """Cancels the RPC. + + Idempotent and has no effect if the RPC has already terminated. + """ + raise NotImplementedError() + + @abc.abstractmethod + def protocol_context(self): + """Accesses a custom object specified by an implementation provider. + + Returns: + A value specified by the provider of a Face interface implementation + affording custom state and behavior. + """ + raise NotImplementedError() + + +class Call(RpcContext): + """Invocation-side utility object for an RPC.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def initial_metadata(self): + """Accesses the initial metadata from the service-side of the RPC. + + This method blocks until the value is available or is known not to have been + emitted from the service-side of the RPC. + + Returns: + The initial metadata object emitted by the service-side of the RPC, or + None if there was no such value. + """ + raise NotImplementedError() + + @abc.abstractmethod + def terminal_metadata(self): + """Accesses the terminal metadata from the service-side of the RPC. + + This method blocks until the value is available or is known not to have been + emitted from the service-side of the RPC. + + Returns: + The terminal metadata object emitted by the service-side of the RPC, or + None if there was no such value. + """ + raise NotImplementedError() + + @abc.abstractmethod + def code(self): + """Accesses the code emitted by the service-side of the RPC. + + This method blocks until the value is available or is known not to have been + emitted from the service-side of the RPC. + + Returns: + The code object emitted by the service-side of the RPC, or None if there + was no such value. + """ + raise NotImplementedError() + + @abc.abstractmethod + def details(self): + """Accesses the details value emitted by the service-side of the RPC. + + This method blocks until the value is available or is known not to have been + emitted from the service-side of the RPC. + + Returns: + The details value emitted by the service-side of the RPC, or None if there + was no such value. + """ + raise NotImplementedError() + + +class ServicerContext(RpcContext): + """A context object passed to method implementations.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def invocation_metadata(self): + """Accesses the metadata from the invocation-side of the RPC. + + This method blocks until the value is available or is known not to have been + emitted from the invocation-side of the RPC. + + Returns: + The metadata object emitted by the invocation-side of the RPC, or None if + there was no such value. + """ + raise NotImplementedError() + + @abc.abstractmethod + def initial_metadata(self, initial_metadata): + """Accepts the service-side initial metadata value of the RPC. + + This method need not be called by method implementations if they have no + service-side initial metadata to transmit. + + Args: + initial_metadata: The service-side initial metadata value of the RPC to + be transmitted to the invocation side of the RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def terminal_metadata(self, terminal_metadata): + """Accepts the service-side terminal metadata value of the RPC. + + This method need not be called by method implementations if they have no + service-side terminal metadata to transmit. + + Args: + terminal_metadata: The service-side terminal metadata value of the RPC to + be transmitted to the invocation side of the RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def code(self, code): + """Accepts the service-side code of the RPC. + + This method need not be called by method implementations if they have no + code to transmit. + + Args: + code: The code of the RPC to be transmitted to the invocation side of the + RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def details(self, details): + """Accepts the service-side details of the RPC. + + This method need not be called by method implementations if they have no + service-side details to transmit. + + Args: + details: The service-side details value of the RPC to be transmitted to + the invocation side of the RPC. + """ + raise NotImplementedError() + + +class ResponseReceiver(object): + """Invocation-side object used to accept the output of an RPC.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def initial_metadata(self, initial_metadata): + """Receives the initial metadata from the service-side of the RPC. + + Args: + initial_metadata: The initial metadata object emitted from the + service-side of the RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def response(self, response): + """Receives a response from the service-side of the RPC. + + Args: + response: A response object emitted from the service-side of the RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def complete(self, terminal_metadata, code, details): + """Receives the completion values emitted from the service-side of the RPC. + + Args: + terminal_metadata: The terminal metadata object emitted from the + service-side of the RPC. + code: The code object emitted from the service-side of the RPC. + details: The details object emitted from the service-side of the RPC. + """ + raise NotImplementedError() + + +class UnaryUnaryMultiCallable(object): + """Affords invoking a unary-unary RPC in any call style.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def __call__( + self, request, timeout, metadata=None, with_call=False, + protocol_options=None): + """Synchronously invokes the underlying RPC. + + Args: + request: The request value for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + metadata: A metadata value to be passed to the service-side of + the RPC. + with_call: Whether or not to include return a Call for the RPC in addition + to the reponse. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. + + Returns: + The response value for the RPC, and a Call for the RPC if with_call was + set to True at invocation. + + Raises: + AbortionError: Indicating that the RPC was aborted. + """ + raise NotImplementedError() + + @abc.abstractmethod + def future(self, request, timeout, metadata=None, protocol_options=None): + """Asynchronously invokes the underlying RPC. + + Args: + request: The request value for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + metadata: A metadata value to be passed to the service-side of + the RPC. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. + + Returns: + An object that is both a Call for the RPC and a future.Future. In the + event of RPC completion, the return Future's result value will be the + response value of the RPC. In the event of RPC abortion, the returned + Future's exception value will be an AbortionError. + """ + raise NotImplementedError() + + @abc.abstractmethod + def event( + self, request, receiver, abortion_callback, timeout, + metadata=None, protocol_options=None): + """Asynchronously invokes the underlying RPC. + + Args: + request: The request value for the RPC. + receiver: A ResponseReceiver to be passed the response data of the RPC. + abortion_callback: A callback to be called and passed an Abortion value + in the event of RPC abortion. + timeout: A duration of time in seconds to allow for the RPC. + metadata: A metadata value to be passed to the service-side of + the RPC. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. + + Returns: + A Call for the RPC. + """ + raise NotImplementedError() + + +class UnaryStreamMultiCallable(object): + """Affords invoking a unary-stream RPC in any call style.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def __call__(self, request, timeout, metadata=None, protocol_options=None): + """Invokes the underlying RPC. + + Args: + request: The request value for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + metadata: A metadata value to be passed to the service-side of + the RPC. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. + + Returns: + An object that is both a Call for the RPC and an iterator of response + values. Drawing response values from the returned iterator may raise + AbortionError indicating abortion of the RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def event( + self, request, receiver, abortion_callback, timeout, + metadata=None, protocol_options=None): + """Asynchronously invokes the underlying RPC. + + Args: + request: The request value for the RPC. + receiver: A ResponseReceiver to be passed the response data of the RPC. + abortion_callback: A callback to be called and passed an Abortion value + in the event of RPC abortion. + timeout: A duration of time in seconds to allow for the RPC. + metadata: A metadata value to be passed to the service-side of + the RPC. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. + + Returns: + A Call object for the RPC. + """ + raise NotImplementedError() + + +class StreamUnaryMultiCallable(object): + """Affords invoking a stream-unary RPC in any call style.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def __call__( + self, request_iterator, timeout, metadata=None, + with_call=False, protocol_options=None): + """Synchronously invokes the underlying RPC. + + Args: + request_iterator: An iterator that yields request values for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + metadata: A metadata value to be passed to the service-side of + the RPC. + with_call: Whether or not to include return a Call for the RPC in addition + to the reponse. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. + + Returns: + The response value for the RPC, and a Call for the RPC if with_call was + set to True at invocation. + + Raises: + AbortionError: Indicating that the RPC was aborted. + """ + raise NotImplementedError() + + @abc.abstractmethod + def future( + self, request_iterator, timeout, metadata=None, protocol_options=None): + """Asynchronously invokes the underlying RPC. + + Args: + request_iterator: An iterator that yields request values for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + metadata: A metadata value to be passed to the service-side of + the RPC. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. + + Returns: + An object that is both a Call for the RPC and a future.Future. In the + event of RPC completion, the return Future's result value will be the + response value of the RPC. In the event of RPC abortion, the returned + Future's exception value will be an AbortionError. + """ + raise NotImplementedError() + + @abc.abstractmethod + def event( + self, receiver, abortion_callback, timeout, metadata=None, + protocol_options=None): + """Asynchronously invokes the underlying RPC. + + Args: + receiver: A ResponseReceiver to be passed the response data of the RPC. + abortion_callback: A callback to be called and passed an Abortion value + in the event of RPC abortion. + timeout: A duration of time in seconds to allow for the RPC. + metadata: A metadata value to be passed to the service-side of + the RPC. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. + + Returns: + A single object that is both a Call object for the RPC and a + stream.Consumer to which the request values of the RPC should be passed. + """ + raise NotImplementedError() + + +class StreamStreamMultiCallable(object): + """Affords invoking a stream-stream RPC in any call style.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def __call__( + self, request_iterator, timeout, metadata=None, protocol_options=None): + """Invokes the underlying RPC. + + Args: + request_iterator: An iterator that yields request values for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + metadata: A metadata value to be passed to the service-side of + the RPC. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. + + Returns: + An object that is both a Call for the RPC and an iterator of response + values. Drawing response values from the returned iterator may raise + AbortionError indicating abortion of the RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def event( + self, receiver, abortion_callback, timeout, metadata=None, + protocol_options=None): + """Asynchronously invokes the underlying RPC. + + Args: + receiver: A ResponseReceiver to be passed the response data of the RPC. + abortion_callback: A callback to be called and passed an Abortion value + in the event of RPC abortion. + timeout: A duration of time in seconds to allow for the RPC. + metadata: A metadata value to be passed to the service-side of + the RPC. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. + + Returns: + A single object that is both a Call object for the RPC and a + stream.Consumer to which the request values of the RPC should be passed. + """ + raise NotImplementedError() + + +class MethodImplementation(object): + """A sum type that describes a method implementation. + + Attributes: + cardinality: A cardinality.Cardinality value. + style: A style.Service value. + unary_unary_inline: The implementation of the method as a callable value + that takes a request value and a ServicerContext object and returns a + response value. Only non-None if cardinality is + cardinality.Cardinality.UNARY_UNARY and style is style.Service.INLINE. + unary_stream_inline: The implementation of the method as a callable value + that takes a request value and a ServicerContext object and returns an + iterator of response values. Only non-None if cardinality is + cardinality.Cardinality.UNARY_STREAM and style is style.Service.INLINE. + stream_unary_inline: The implementation of the method as a callable value + that takes an iterator of request values and a ServicerContext object and + returns a response value. Only non-None if cardinality is + cardinality.Cardinality.STREAM_UNARY and style is style.Service.INLINE. + stream_stream_inline: The implementation of the method as a callable value + that takes an iterator of request values and a ServicerContext object and + returns an iterator of response values. Only non-None if cardinality is + cardinality.Cardinality.STREAM_STREAM and style is style.Service.INLINE. + unary_unary_event: The implementation of the method as a callable value that + takes a request value, a response callback to which to pass the response + value of the RPC, and a ServicerContext. Only non-None if cardinality is + cardinality.Cardinality.UNARY_UNARY and style is style.Service.EVENT. + unary_stream_event: The implementation of the method as a callable value + that takes a request value, a stream.Consumer to which to pass the + response values of the RPC, and a ServicerContext. Only non-None if + cardinality is cardinality.Cardinality.UNARY_STREAM and style is + style.Service.EVENT. + stream_unary_event: The implementation of the method as a callable value + that takes a response callback to which to pass the response value of the + RPC and a ServicerContext and returns a stream.Consumer to which the + request values of the RPC should be passed. Only non-None if cardinality + is cardinality.Cardinality.STREAM_UNARY and style is style.Service.EVENT. + stream_stream_event: The implementation of the method as a callable value + that takes a stream.Consumer to which to pass the response values of the + RPC and a ServicerContext and returns a stream.Consumer to which the + request values of the RPC should be passed. Only non-None if cardinality + is cardinality.Cardinality.STREAM_STREAM and style is + style.Service.EVENT. + """ + __metaclass__ = abc.ABCMeta + + +class MultiMethodImplementation(object): + """A general type able to service many methods.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def service(self, group, method, response_consumer, context): + """Services an RPC. + + Args: + group: The group identifier of the RPC. + method: The method identifier of the RPC. + response_consumer: A stream.Consumer to be called to accept the response + values of the RPC. + context: a ServicerContext object. + + Returns: + A stream.Consumer with which to accept the request values of the RPC. The + consumer returned from this method may or may not be invoked to + completion: in the case of RPC abortion, RPC Framework will simply stop + passing values to this object. Implementations must not assume that this + object will be called to completion of the request stream or even called + at all. + + Raises: + abandonment.Abandoned: May or may not be raised when the RPC has been + aborted. + NoSuchMethodError: If this MultiMethod does not recognize the given group + and name for the RPC and is not able to service the RPC. + """ + raise NotImplementedError() + + +class GenericStub(object): + """Affords RPC invocation via generic methods.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def blocking_unary_unary( + self, group, method, request, timeout, metadata=None, + with_call=False, protocol_options=None): + """Invokes a unary-request-unary-response method. + + This method blocks until either returning the response value of the RPC + (in the event of RPC completion) or raising an exception (in the event of + RPC abortion). + + Args: + group: The group identifier of the RPC. + method: The method identifier of the RPC. + request: The request value for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + metadata: A metadata value to be passed to the service-side of the RPC. + with_call: Whether or not to include return a Call for the RPC in addition + to the reponse. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. + + Returns: + The response value for the RPC, and a Call for the RPC if with_call was + set to True at invocation. + + Raises: + AbortionError: Indicating that the RPC was aborted. + """ + raise NotImplementedError() + + @abc.abstractmethod + def future_unary_unary( + self, group, method, request, timeout, metadata=None, + protocol_options=None): + """Invokes a unary-request-unary-response method. + + Args: + group: The group identifier of the RPC. + method: The method identifier of the RPC. + request: The request value for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + metadata: A metadata value to be passed to the service-side of the RPC. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. + + Returns: + An object that is both a Call for the RPC and a future.Future. In the + event of RPC completion, the return Future's result value will be the + response value of the RPC. In the event of RPC abortion, the returned + Future's exception value will be an AbortionError. + """ + raise NotImplementedError() + + @abc.abstractmethod + def inline_unary_stream( + self, group, method, request, timeout, metadata=None, + protocol_options=None): + """Invokes a unary-request-stream-response method. + + Args: + group: The group identifier of the RPC. + method: The method identifier of the RPC. + request: The request value for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + metadata: A metadata value to be passed to the service-side of the RPC. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. + + Returns: + An object that is both a Call for the RPC and an iterator of response + values. Drawing response values from the returned iterator may raise + AbortionError indicating abortion of the RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def blocking_stream_unary( + self, group, method, request_iterator, timeout, metadata=None, + with_call=False, protocol_options=None): + """Invokes a stream-request-unary-response method. + + This method blocks until either returning the response value of the RPC + (in the event of RPC completion) or raising an exception (in the event of + RPC abortion). + + Args: + group: The group identifier of the RPC. + method: The method identifier of the RPC. + request_iterator: An iterator that yields request values for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + metadata: A metadata value to be passed to the service-side of the RPC. + with_call: Whether or not to include return a Call for the RPC in addition + to the reponse. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. + + Returns: + The response value for the RPC, and a Call for the RPC if with_call was + set to True at invocation. + + Raises: + AbortionError: Indicating that the RPC was aborted. + """ + raise NotImplementedError() + + @abc.abstractmethod + def future_stream_unary( + self, group, method, request_iterator, timeout, metadata=None, + protocol_options=None): + """Invokes a stream-request-unary-response method. + + Args: + group: The group identifier of the RPC. + method: The method identifier of the RPC. + request_iterator: An iterator that yields request values for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + metadata: A metadata value to be passed to the service-side of the RPC. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. + + Returns: + An object that is both a Call for the RPC and a future.Future. In the + event of RPC completion, the return Future's result value will be the + response value of the RPC. In the event of RPC abortion, the returned + Future's exception value will be an AbortionError. + """ + raise NotImplementedError() + + @abc.abstractmethod + def inline_stream_stream( + self, group, method, request_iterator, timeout, metadata=None, + protocol_options=None): + """Invokes a stream-request-stream-response method. + + Args: + group: The group identifier of the RPC. + method: The method identifier of the RPC. + request_iterator: An iterator that yields request values for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + metadata: A metadata value to be passed to the service-side of the RPC. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. + + Returns: + An object that is both a Call for the RPC and an iterator of response + values. Drawing response values from the returned iterator may raise + AbortionError indicating abortion of the RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def event_unary_unary( + self, group, method, request, receiver, abortion_callback, timeout, + metadata=None, protocol_options=None): + """Event-driven invocation of a unary-request-unary-response method. + + Args: + group: The group identifier of the RPC. + method: The method identifier of the RPC. + request: The request value for the RPC. + receiver: A ResponseReceiver to be passed the response data of the RPC. + abortion_callback: A callback to be called and passed an Abortion value + in the event of RPC abortion. + timeout: A duration of time in seconds to allow for the RPC. + metadata: A metadata value to be passed to the service-side of the RPC. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. + + Returns: + A Call for the RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def event_unary_stream( + self, group, method, request, receiver, abortion_callback, timeout, + metadata=None, protocol_options=None): + """Event-driven invocation of a unary-request-stream-response method. + + Args: + group: The group identifier of the RPC. + method: The method identifier of the RPC. + request: The request value for the RPC. + receiver: A ResponseReceiver to be passed the response data of the RPC. + abortion_callback: A callback to be called and passed an Abortion value + in the event of RPC abortion. + timeout: A duration of time in seconds to allow for the RPC. + metadata: A metadata value to be passed to the service-side of the RPC. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. + + Returns: + A Call for the RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def event_stream_unary( + self, group, method, receiver, abortion_callback, timeout, + metadata=None, protocol_options=None): + """Event-driven invocation of a unary-request-unary-response method. + + Args: + group: The group identifier of the RPC. + method: The method identifier of the RPC. + receiver: A ResponseReceiver to be passed the response data of the RPC. + abortion_callback: A callback to be called and passed an Abortion value + in the event of RPC abortion. + timeout: A duration of time in seconds to allow for the RPC. + metadata: A metadata value to be passed to the service-side of the RPC. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. + + Returns: + A pair of a Call object for the RPC and a stream.Consumer to which the + request values of the RPC should be passed. + """ + raise NotImplementedError() + + @abc.abstractmethod + def event_stream_stream( + self, group, method, receiver, abortion_callback, timeout, + metadata=None, protocol_options=None): + """Event-driven invocation of a unary-request-stream-response method. + + Args: + group: The group identifier of the RPC. + method: The method identifier of the RPC. + receiver: A ResponseReceiver to be passed the response data of the RPC. + abortion_callback: A callback to be called and passed an Abortion value + in the event of RPC abortion. + timeout: A duration of time in seconds to allow for the RPC. + metadata: A metadata value to be passed to the service-side of the RPC. + protocol_options: A value specified by the provider of a Face interface + implementation affording custom state and behavior. + + Returns: + A pair of a Call object for the RPC and a stream.Consumer to which the + request values of the RPC should be passed. + """ + raise NotImplementedError() + + @abc.abstractmethod + def unary_unary(self, group, method): + """Creates a UnaryUnaryMultiCallable for a unary-unary method. + + Args: + group: The group identifier of the RPC. + method: The method identifier of the RPC. + + Returns: + A UnaryUnaryMultiCallable value for the named unary-unary method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def unary_stream(self, group, method): + """Creates a UnaryStreamMultiCallable for a unary-stream method. + + Args: + group: The group identifier of the RPC. + method: The method identifier of the RPC. + + Returns: + A UnaryStreamMultiCallable value for the name unary-stream method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def stream_unary(self, group, method): + """Creates a StreamUnaryMultiCallable for a stream-unary method. + + Args: + group: The group identifier of the RPC. + method: The method identifier of the RPC. + + Returns: + A StreamUnaryMultiCallable value for the named stream-unary method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def stream_stream(self, group, method): + """Creates a StreamStreamMultiCallable for a stream-stream method. + + Args: + group: The group identifier of the RPC. + method: The method identifier of the RPC. + + Returns: + A StreamStreamMultiCallable value for the named stream-stream method. + """ + raise NotImplementedError() + + +class DynamicStub(object): + """Affords RPC invocation via attributes corresponding to afforded methods. + + Instances of this type may be scoped to a single group so that attribute + access is unambiguous. + + Instances of this type respond to attribute access as follows: if the + requested attribute is the name of a unary-unary method, the value of the + attribute will be a UnaryUnaryMultiCallable with which to invoke an RPC; if + the requested attribute is the name of a unary-stream method, the value of the + attribute will be a UnaryStreamMultiCallable with which to invoke an RPC; if + the requested attribute is the name of a stream-unary method, the value of the + attribute will be a StreamUnaryMultiCallable with which to invoke an RPC; and + if the requested attribute is the name of a stream-stream method, the value of + the attribute will be a StreamStreamMultiCallable with which to invoke an RPC. + """ + __metaclass__ = abc.ABCMeta diff --git a/src/python/grpcio/grpc/framework/interfaces/face/utilities.py b/src/python/grpcio/grpc/framework/interfaces/face/utilities.py new file mode 100644 index 00000000..db2ec6ed --- /dev/null +++ b/src/python/grpcio/grpc/framework/interfaces/face/utilities.py @@ -0,0 +1,178 @@ +# Copyright 2015, 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. + +"""Utilities for RPC Framework's Face interface.""" + +import collections + +# stream is referenced from specification in this module. +from grpc.framework.common import cardinality +from grpc.framework.common import style +from grpc.framework.foundation import stream # pylint: disable=unused-import +from grpc.framework.interfaces.face import face + + +class _MethodImplementation( + face.MethodImplementation, + collections.namedtuple( + '_MethodImplementation', + ['cardinality', 'style', 'unary_unary_inline', 'unary_stream_inline', + 'stream_unary_inline', 'stream_stream_inline', 'unary_unary_event', + 'unary_stream_event', 'stream_unary_event', 'stream_stream_event',])): + pass + + +def unary_unary_inline(behavior): + """Creates an face.MethodImplementation for the given behavior. + + Args: + behavior: The implementation of a unary-unary RPC method as a callable value + that takes a request value and an face.ServicerContext object and + returns a response value. + + Returns: + An face.MethodImplementation derived from the given behavior. + """ + return _MethodImplementation( + cardinality.Cardinality.UNARY_UNARY, style.Service.INLINE, behavior, + None, None, None, None, None, None, None) + + +def unary_stream_inline(behavior): + """Creates an face.MethodImplementation for the given behavior. + + Args: + behavior: The implementation of a unary-stream RPC method as a callable + value that takes a request value and an face.ServicerContext object and + returns an iterator of response values. + + Returns: + An face.MethodImplementation derived from the given behavior. + """ + return _MethodImplementation( + cardinality.Cardinality.UNARY_STREAM, style.Service.INLINE, None, + behavior, None, None, None, None, None, None) + + +def stream_unary_inline(behavior): + """Creates an face.MethodImplementation for the given behavior. + + Args: + behavior: The implementation of a stream-unary RPC method as a callable + value that takes an iterator of request values and an + face.ServicerContext object and returns a response value. + + Returns: + An face.MethodImplementation derived from the given behavior. + """ + return _MethodImplementation( + cardinality.Cardinality.STREAM_UNARY, style.Service.INLINE, None, None, + behavior, None, None, None, None, None) + + +def stream_stream_inline(behavior): + """Creates an face.MethodImplementation for the given behavior. + + Args: + behavior: The implementation of a stream-stream RPC method as a callable + value that takes an iterator of request values and an + face.ServicerContext object and returns an iterator of response values. + + Returns: + An face.MethodImplementation derived from the given behavior. + """ + return _MethodImplementation( + cardinality.Cardinality.STREAM_STREAM, style.Service.INLINE, None, None, + None, behavior, None, None, None, None) + + +def unary_unary_event(behavior): + """Creates an face.MethodImplementation for the given behavior. + + Args: + behavior: The implementation of a unary-unary RPC method as a callable + value that takes a request value, a response callback to which to pass + the response value of the RPC, and an face.ServicerContext. + + Returns: + An face.MethodImplementation derived from the given behavior. + """ + return _MethodImplementation( + cardinality.Cardinality.UNARY_UNARY, style.Service.EVENT, None, None, + None, None, behavior, None, None, None) + + +def unary_stream_event(behavior): + """Creates an face.MethodImplementation for the given behavior. + + Args: + behavior: The implementation of a unary-stream RPC method as a callable + value that takes a request value, a stream.Consumer to which to pass the + the response values of the RPC, and an face.ServicerContext. + + Returns: + An face.MethodImplementation derived from the given behavior. + """ + return _MethodImplementation( + cardinality.Cardinality.UNARY_STREAM, style.Service.EVENT, None, None, + None, None, None, behavior, None, None) + + +def stream_unary_event(behavior): + """Creates an face.MethodImplementation for the given behavior. + + Args: + behavior: The implementation of a stream-unary RPC method as a callable + value that takes a response callback to which to pass the response value + of the RPC and an face.ServicerContext and returns a stream.Consumer to + which the request values of the RPC should be passed. + + Returns: + An face.MethodImplementation derived from the given behavior. + """ + return _MethodImplementation( + cardinality.Cardinality.STREAM_UNARY, style.Service.EVENT, None, None, + None, None, None, None, behavior, None) + + +def stream_stream_event(behavior): + """Creates an face.MethodImplementation for the given behavior. + + Args: + behavior: The implementation of a stream-stream RPC method as a callable + value that takes a stream.Consumer to which to pass the response values + of the RPC and an face.ServicerContext and returns a stream.Consumer to + which the request values of the RPC should be passed. + + Returns: + An face.MethodImplementation derived from the given behavior. + """ + return _MethodImplementation( + cardinality.Cardinality.STREAM_STREAM, style.Service.EVENT, None, None, + None, None, None, None, None, behavior) diff --git a/src/python/grpcio/grpc/framework/interfaces/links/__init__.py b/src/python/grpcio/grpc/framework/interfaces/links/__init__.py new file mode 100644 index 00000000..70865191 --- /dev/null +++ b/src/python/grpcio/grpc/framework/interfaces/links/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, 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. + + diff --git a/src/python/grpcio/grpc/framework/interfaces/links/links.py b/src/python/grpcio/grpc/framework/interfaces/links/links.py new file mode 100644 index 00000000..24f0e3b3 --- /dev/null +++ b/src/python/grpcio/grpc/framework/interfaces/links/links.py @@ -0,0 +1,142 @@ +# Copyright 2015, 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. + +"""The low-level ticket-exchanging-links interface of RPC Framework.""" + +import abc +import collections +import enum + + +class Protocol(collections.namedtuple('Protocol', ('kind', 'value',))): + """A sum type for handles to a system that transmits tickets. + + Attributes: + kind: A Kind value identifying the kind of value being passed. + value: The value being passed between the high-level application and the + system affording ticket transport. + """ + + @enum.unique + class Kind(enum.Enum): + CALL_OPTION = 'call option' + SERVICER_CONTEXT = 'servicer context' + INVOCATION_CONTEXT = 'invocation context' + + +class Ticket( + collections.namedtuple( + 'Ticket', + ('operation_id', 'sequence_number', 'group', 'method', 'subscription', + 'timeout', 'allowance', 'initial_metadata', 'payload', + 'terminal_metadata', 'code', 'message', 'termination', 'protocol',))): + """A sum type for all values sent from a front to a back. + + Attributes: + operation_id: A unique-with-respect-to-equality hashable object identifying + a particular operation. + sequence_number: A zero-indexed integer sequence number identifying the + ticket's place in the stream of tickets sent in one direction for the + particular operation. + group: The group to which the method of the operation belongs. Must be + present in the first ticket from invocation side to service side. Ignored + for all other tickets exchanged during the operation. + method: The name of an operation. Must be present in the first ticket from + invocation side to service side. Ignored for all other tickets exchanged + during the operation. + subscription: A Subscription value describing the interest one side has in + receiving information from the other side. Must be present in the first + ticket from either side. Ignored for all other tickets exchanged during + the operation. + timeout: A nonzero length of time (measured from the beginning of the + operation) to allow for the entire operation. Must be present in the first + ticket from invocation side to service side. Optional for all other + tickets exchanged during the operation. Receipt of a value from the other + side of the operation indicates the value in use by that side. Setting a + value on a later ticket allows either side to request time extensions (or + even time reductions!) on in-progress operations. + allowance: A positive integer granting permission for a number of payloads + to be transmitted to the communicating side of the operation, or None if + no additional allowance is being granted with this ticket. + initial_metadata: An optional metadata value communicated from one side to + the other at the beginning of the operation. May be non-None in at most + one ticket from each side. Any non-None value must appear no later than + the first payload value. + payload: A customer payload object. May be None. + terminal_metadata: A metadata value comminicated from one side to the other + at the end of the operation. May be non-None in the same ticket as + the code and message, but must be None for all earlier tickets. + code: A value communicated at operation completion. May be None. + message: A value communicated at operation completion. May be None. + termination: A Termination value describing the end of the operation, or + None if the operation has not yet terminated. If set, no further tickets + may be sent in the same direction. + protocol: A Protocol value or None, with further semantics being a matter + between high-level application and underlying ticket transport. + """ + + @enum.unique + class Subscription(enum.Enum): + """Identifies the level of subscription of a side of an operation.""" + + NONE = 'none' + TERMINATION = 'termination' + FULL = 'full' + + @enum.unique + class Termination(enum.Enum): + """Identifies the termination of an operation.""" + + COMPLETION = 'completion' + CANCELLATION = 'cancellation' + EXPIRATION = 'expiration' + SHUTDOWN = 'shutdown' + RECEPTION_FAILURE = 'reception failure' + TRANSMISSION_FAILURE = 'transmission failure' + LOCAL_FAILURE = 'local failure' + REMOTE_FAILURE = 'remote failure' + + +class Link(object): + """Accepts and emits tickets.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def accept_ticket(self, ticket): + """Accept a Ticket. + + Args: + ticket: Any Ticket. + """ + raise NotImplementedError() + + @abc.abstractmethod + def join_link(self, link): + """Mates this object with a peer with which it will exchange tickets.""" + raise NotImplementedError() diff --git a/src/python/grpcio/grpc/framework/interfaces/links/utilities.py b/src/python/grpcio/grpc/framework/interfaces/links/utilities.py new file mode 100644 index 00000000..6e4fd76d --- /dev/null +++ b/src/python/grpcio/grpc/framework/interfaces/links/utilities.py @@ -0,0 +1,44 @@ +# Copyright 2015, 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. + +"""Utilities provided as part of the links interface.""" + +from grpc.framework.interfaces.links import links + + +class _NullLink(links.Link): + """A do-nothing links.Link.""" + + def accept_ticket(self, ticket): + pass + + def join_link(self, link): + pass + +NULL_LINK = _NullLink() diff --git a/src/python/grpcio/requirements.txt b/src/python/grpcio/requirements.txt new file mode 100644 index 00000000..77356e0a --- /dev/null +++ b/src/python/grpcio/requirements.txt @@ -0,0 +1,2 @@ +enum34>=1.0.4 +futures>=2.2.0 diff --git a/src/python/grpcio/setup.cfg b/src/python/grpcio/setup.cfg new file mode 100644 index 00000000..8f696136 --- /dev/null +++ b/src/python/grpcio/setup.cfg @@ -0,0 +1,2 @@ +[build_ext] +inplace=1 diff --git a/src/python/grpcio/setup.py b/src/python/grpcio/setup.py new file mode 100644 index 00000000..8b87c09d --- /dev/null +++ b/src/python/grpcio/setup.py @@ -0,0 +1,113 @@ +# Copyright 2015, 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. + +"""A setup module for the GRPC Python package.""" + +import os +import os.path +import sys + +from distutils import core as _core +import setuptools + +# Ensure we're in the proper directory whether or not we're being used by pip. +os.chdir(os.path.dirname(os.path.abspath(__file__))) + +# Break import-style to ensure we can actually find our commands module. +import commands + +# Use environment variables to determine whether or not the Cython extension +# should *use* Cython or use the generated C files. Note that this requires the +# C files to have been generated by building first *with* Cython support. +_BUILD_WITH_CYTHON = os.environ.get('GRPC_PYTHON_BUILD_WITH_CYTHON', False) + +_C_EXTENSION_SOURCES = ( + 'grpc/_adapter/_c/module.c', + 'grpc/_adapter/_c/types.c', + 'grpc/_adapter/_c/utility.c', + 'grpc/_adapter/_c/types/client_credentials.c', + 'grpc/_adapter/_c/types/server_credentials.c', + 'grpc/_adapter/_c/types/completion_queue.c', + 'grpc/_adapter/_c/types/call.c', + 'grpc/_adapter/_c/types/channel.c', + 'grpc/_adapter/_c/types/server.c', +) + +_EXTENSION_INCLUDE_DIRECTORIES = ( + '.', +) + +_EXTENSION_LIBRARIES = ( + 'grpc', + 'gpr', +) +if not "darwin" in sys.platform: + _EXTENSION_LIBRARIES += ('rt',) + + +_C_EXTENSION_MODULE = _core.Extension( + 'grpc._adapter._c', sources=list(_C_EXTENSION_SOURCES), + include_dirs=list(_EXTENSION_INCLUDE_DIRECTORIES), + libraries=list(_EXTENSION_LIBRARIES), +) +_EXTENSION_MODULES = [_C_EXTENSION_MODULE] + +_PACKAGES = ( + setuptools.find_packages('.', exclude=['*._cython', '*._cython.*']) +) + +_PACKAGE_DIRECTORIES = { + '': '.', +} + +_INSTALL_REQUIRES = ( + 'enum34>=1.0.4', + 'futures>=2.2.0', +) + +_SETUP_REQUIRES = ( + 'sphinx>=1.3', +) + _INSTALL_REQUIRES + +_COMMAND_CLASS = { + 'doc': commands.SphinxDocumentation, + 'build_project_metadata': commands.BuildProjectMetadata, + 'build_py': commands.BuildPy, +} + +setuptools.setup( + name='grpcio', + version='0.11.0b1', + ext_modules=_EXTENSION_MODULES, + packages=list(_PACKAGES), + package_dir=_PACKAGE_DIRECTORIES, + install_requires=_INSTALL_REQUIRES, + setup_requires=_SETUP_REQUIRES, + cmdclass=_COMMAND_CLASS +) diff --git a/src/python/grpcio_health_checking/MANIFEST.in b/src/python/grpcio_health_checking/MANIFEST.in new file mode 100644 index 00000000..498b55f2 --- /dev/null +++ b/src/python/grpcio_health_checking/MANIFEST.in @@ -0,0 +1,2 @@ +graft grpc +include commands.py diff --git a/src/python/grpcio_health_checking/README.rst b/src/python/grpcio_health_checking/README.rst new file mode 100644 index 00000000..600734e5 --- /dev/null +++ b/src/python/grpcio_health_checking/README.rst @@ -0,0 +1,9 @@ +gRPC Python Health Checking +=========================== + +Reference package for GRPC Python health checking. + +Dependencies +------------ + +Depends on the `grpcio` package, available from PyPI via `pip install grpcio`. diff --git a/src/python/grpcio_health_checking/commands.py b/src/python/grpcio_health_checking/commands.py new file mode 100644 index 00000000..6a95e679 --- /dev/null +++ b/src/python/grpcio_health_checking/commands.py @@ -0,0 +1,80 @@ +# Copyright 2015, 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. + +"""Provides distutils command classes for the GRPC Python setup process.""" + +import distutils +import glob +import os +import os.path +import subprocess +import sys + +import setuptools +from setuptools.command import build_py + + +class BuildProtoModules(setuptools.Command): + """Command to generate project *_pb2.py modules from proto files.""" + + description = '' + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + self.protoc_command = 'protoc' + self.grpc_python_plugin_command = distutils.spawn.find_executable( + 'grpc_python_plugin') + + def run(self): + paths = [] + root_directory = os.getcwd() + for walk_root, directories, filenames in os.walk(root_directory): + for filename in filenames: + if filename.endswith('.proto'): + paths.append(os.path.join(walk_root, filename)) + command = [ + self.protoc_command, + '--plugin=protoc-gen-python-grpc={}'.format( + self.grpc_python_plugin_command), + '-I {}'.format(root_directory), + '--python_out={}'.format(root_directory), + '--python-grpc_out={}'.format(root_directory), + ] + paths + subprocess.check_call(' '.join(command), cwd=root_directory, shell=True) + + +class BuildPy(build_py.build_py): + """Custom project build command.""" + + def run(self): + self.run_command('build_proto_modules') + build_py.build_py.run(self) diff --git a/src/python/grpcio_health_checking/grpc/__init__.py b/src/python/grpcio_health_checking/grpc/__init__.py new file mode 100644 index 00000000..70865191 --- /dev/null +++ b/src/python/grpcio_health_checking/grpc/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, 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. + + diff --git a/src/python/grpcio_health_checking/grpc/health/__init__.py b/src/python/grpcio_health_checking/grpc/health/__init__.py new file mode 100644 index 00000000..70865191 --- /dev/null +++ b/src/python/grpcio_health_checking/grpc/health/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, 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. + + diff --git a/src/python/grpcio_health_checking/grpc/health/v1alpha/__init__.py b/src/python/grpcio_health_checking/grpc/health/v1alpha/__init__.py new file mode 100644 index 00000000..70865191 --- /dev/null +++ b/src/python/grpcio_health_checking/grpc/health/v1alpha/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, 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. + + diff --git a/src/python/grpcio_health_checking/grpc/health/v1alpha/health.proto b/src/python/grpcio_health_checking/grpc/health/v1alpha/health.proto new file mode 100644 index 00000000..57f4aaa9 --- /dev/null +++ b/src/python/grpcio_health_checking/grpc/health/v1alpha/health.proto @@ -0,0 +1,49 @@ +// Copyright 2015, 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. + +syntax = "proto3"; + +package grpc.health.v1alpha; + +message HealthCheckRequest { + string service = 1; +} + +message HealthCheckResponse { + enum ServingStatus { + UNKNOWN = 0; + SERVING = 1; + NOT_SERVING = 2; + } + ServingStatus status = 1; +} + +service Health { + rpc Check(HealthCheckRequest) returns (HealthCheckResponse); +} diff --git a/src/python/grpcio_health_checking/grpc/health/v1alpha/health.py b/src/python/grpcio_health_checking/grpc/health/v1alpha/health.py new file mode 100644 index 00000000..9dfcd962 --- /dev/null +++ b/src/python/grpcio_health_checking/grpc/health/v1alpha/health.py @@ -0,0 +1,129 @@ +# Copyright 2015, 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. + +"""Reference implementation for health checking in gRPC Python.""" + +import abc +import enum +import threading + +from grpc.health.v1alpha import health_pb2 + + +@enum.unique +class HealthStatus(enum.Enum): + """Statuses for a service mirroring the reference health.proto's values.""" + UNKNOWN = health_pb2.HealthCheckResponse.UNKNOWN + SERVING = health_pb2.HealthCheckResponse.SERVING + NOT_SERVING = health_pb2.HealthCheckResponse.NOT_SERVING + + +class _HealthServicer(health_pb2.EarlyAdopterHealthServicer): + """Servicer handling RPCs for service statuses.""" + + def __init__(self): + self._server_status_lock = threading.Lock() + self._server_status = {} + + def Check(self, request, context): + with self._server_status_lock: + if request.service not in self._server_status: + # TODO(atash): once the Python API has a way of setting the server + # status, bring us into conformance with the health check spec by + # returning the NOT_FOUND status here. + raise NotImplementedError() + else: + return health_pb2.HealthCheckResponse( + status=self._server_status[request.service].value) + + def set(service, status): + if not isinstance(status, HealthStatus): + raise TypeError('expected grpc.health.v1alpha.health.HealthStatus ' + 'for argument `status` but got {}'.format(status)) + with self._server_status_lock: + self._server_status[service] = status + + +class HealthServer(health_pb2.EarlyAdopterHealthServer): + """Interface for the reference gRPC Python health server.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def start(self): + raise NotImplementedError() + + @abc.abstractmethod + def stop(self): + raise NotImplementedError() + + @abc.abstractmethod + def set(self, service, status): + """Set the status of the given service. + + Args: + service (str): service name of the service to set the reported status of + status (HealthStatus): status to set for the specified service + """ + raise NotImplementedError() + + +class _HealthServerImplementation(HealthServer): + """Implementation for the reference gRPC Python health server.""" + + def __init__(self, server, servicer): + self._server = server + self._servicer = servicer + + def start(self): + self._server.start() + + def stop(self): + self._server.stop() + + def set(self, service, status): + self._servicer.set(service, status) + + +def create_Health_server(port, private_key=None, certificate_chain=None): + """Get a HealthServer instance. + + Args: + port (int): port number passed through to health_pb2 server creation + routine. + private_key (str): to-be-created server's desired private key + certificate_chain (str): to-be-created server's desired certificate chain + + Returns: + An instance of HealthServer (conforming thus to + EarlyAdopterHealthServer and providing a method to set server status).""" + servicer = _HealthServicer() + server = health_pb2.early_adopter_create_Health_server( + servicer, port=port, private_key=private_key, + certificate_chain=certificate_chain) + return _HealthServerImplementation(server, servicer) diff --git a/src/python/grpcio_health_checking/setup.py b/src/python/grpcio_health_checking/setup.py new file mode 100644 index 00000000..35253ba3 --- /dev/null +++ b/src/python/grpcio_health_checking/setup.py @@ -0,0 +1,72 @@ +# Copyright 2015, 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. + +"""Setup module for the GRPC Python package's optional health checking.""" + +import os +import os.path +import sys + +from distutils import core as _core +import setuptools + +# Ensure we're in the proper directory whether or not we're being used by pip. +os.chdir(os.path.dirname(os.path.abspath(__file__))) + +# Break import-style to ensure we can actually find our commands module. +import commands + +_PACKAGES = ( + setuptools.find_packages('.') +) + +_PACKAGE_DIRECTORIES = { + '': '.', +} + +_INSTALL_REQUIRES = ( + 'grpcio>=0.11.0b0', +) + +_SETUP_REQUIRES = _INSTALL_REQUIRES + +_COMMAND_CLASS = { + 'build_proto_modules': commands.BuildProtoModules, + 'build_py': commands.BuildPy, +} + +setuptools.setup( + name='grpcio_health_checking', + version='0.11.0b0', + packages=list(_PACKAGES), + package_dir=_PACKAGE_DIRECTORIES, + install_requires=_INSTALL_REQUIRES, + setup_requires=_SETUP_REQUIRES, + cmdclass=_COMMAND_CLASS +) diff --git a/src/python/grpcio_test/.gitignore b/src/python/grpcio_test/.gitignore new file mode 100644 index 00000000..218e3a15 --- /dev/null +++ b/src/python/grpcio_test/.gitignore @@ -0,0 +1,10 @@ +MANIFEST +*.egg-info/ +build/ +dist/ +*.egg +*.egg/ +*.eggs/ +.coverage +.coverage.* +nosetests.xml diff --git a/src/python/grpcio_test/MANIFEST.in b/src/python/grpcio_test/MANIFEST.in new file mode 100644 index 00000000..c9327307 --- /dev/null +++ b/src/python/grpcio_test/MANIFEST.in @@ -0,0 +1,4 @@ +graft grpc_interop +graft grpc_test +include commands.py +include requirements.txt diff --git a/src/python/grpcio_test/commands.py b/src/python/grpcio_test/commands.py new file mode 100644 index 00000000..c796d94c --- /dev/null +++ b/src/python/grpcio_test/commands.py @@ -0,0 +1,57 @@ +# Copyright 2015, 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. + +"""Provides distutils command classes for the GRPC Python test setup process.""" + +import os +import os.path +import sys + +import setuptools + + +class RunTests(setuptools.Command): + """Command to run all tests via py.test.""" + + description = '' + user_options = [('pytest-args=', 'a', 'arguments to pass to py.test')] + + def initialize_options(self): + self.pytest_args = [] + + def finalize_options(self): + pass + + def run(self): + # We import here to ensure that setup.py has had a chance to install the + # relevant package eggs first. + import pytest + result = pytest.main(self.pytest_args) + if result != 0: + raise SystemExit(result) diff --git a/src/python/grpcio_test/grpc_interop/__init__.py b/src/python/grpcio_test/grpc_interop/__init__.py new file mode 100644 index 00000000..70865191 --- /dev/null +++ b/src/python/grpcio_test/grpc_interop/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, 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. + + diff --git a/src/python/grpcio_test/grpc_interop/_insecure_interop_test.py b/src/python/grpcio_test/grpc_interop/_insecure_interop_test.py new file mode 100644 index 00000000..825988a0 --- /dev/null +++ b/src/python/grpcio_test/grpc_interop/_insecure_interop_test.py @@ -0,0 +1,57 @@ +# Copyright 2015, 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. + +"""Insecure client-server interoperability as a unit test.""" + +import unittest + +from grpc.early_adopter import implementations + +from grpc_interop import _interop_test_case +from grpc_interop import methods + + +class InsecureInteropTest( + _interop_test_case.InteropTestCase, + unittest.TestCase): + + def setUp(self): + self.server = implementations.server( + methods.SERVICE_NAME, methods.SERVER_METHODS, 0) + self.server.start() + port = self.server.port() + self.stub = implementations.stub( + methods.SERVICE_NAME, methods.CLIENT_METHODS, 'localhost', port) + + def tearDown(self): + self.server.stop() + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_interop/_interop_test_case.py b/src/python/grpcio_test/grpc_interop/_interop_test_case.py new file mode 100644 index 00000000..b6d06b30 --- /dev/null +++ b/src/python/grpcio_test/grpc_interop/_interop_test_case.py @@ -0,0 +1,64 @@ +# Copyright 2015, 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. + +"""Common code for unit tests of the interoperability test code.""" + +from grpc_interop import methods + + +class InteropTestCase(object): + """Unit test methods. + + This class must be mixed in with unittest.TestCase and a class that defines + setUp and tearDown methods that manage a stub attribute. + """ + + def testEmptyUnary(self): + methods.TestCase.EMPTY_UNARY.test_interoperability(self.stub, None) + + def testLargeUnary(self): + methods.TestCase.LARGE_UNARY.test_interoperability(self.stub, None) + + def testServerStreaming(self): + methods.TestCase.SERVER_STREAMING.test_interoperability(self.stub, None) + + def testClientStreaming(self): + methods.TestCase.CLIENT_STREAMING.test_interoperability(self.stub, None) + + def testPingPong(self): + methods.TestCase.PING_PONG.test_interoperability(self.stub, None) + + def testCancelAfterBegin(self): + methods.TestCase.CANCEL_AFTER_BEGIN.test_interoperability(self.stub, None) + + def testCancelAfterFirstResponse(self): + methods.TestCase.CANCEL_AFTER_FIRST_RESPONSE.test_interoperability(self.stub, None) + + def testTimeoutOnSleepingServer(self): + methods.TestCase.TIMEOUT_ON_SLEEPING_SERVER.test_interoperability(self.stub, None) diff --git a/src/python/grpcio_test/grpc_interop/_secure_interop_test.py b/src/python/grpcio_test/grpc_interop/_secure_interop_test.py new file mode 100644 index 00000000..a2682dee --- /dev/null +++ b/src/python/grpcio_test/grpc_interop/_secure_interop_test.py @@ -0,0 +1,64 @@ +# Copyright 2015, 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. + +"""Secure client-server interoperability as a unit test.""" + +import unittest + +from grpc.early_adopter import implementations + +from grpc_interop import _interop_test_case +from grpc_interop import methods +from grpc_interop import resources + +_SERVER_HOST_OVERRIDE = 'foo.test.google.fr' + + +class SecureInteropTest( + _interop_test_case.InteropTestCase, + unittest.TestCase): + + def setUp(self): + self.server = implementations.server( + methods.SERVICE_NAME, methods.SERVER_METHODS, 0, + private_key=resources.private_key(), + certificate_chain=resources.certificate_chain()) + self.server.start() + port = self.server.port() + self.stub = implementations.stub( + methods.SERVICE_NAME, methods.CLIENT_METHODS, 'localhost', port, + secure=True, root_certificates=resources.test_root_certificates(), + server_host_override=_SERVER_HOST_OVERRIDE) + + def tearDown(self): + self.server.stop() + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_interop/client.py b/src/python/grpcio_test/grpc_interop/client.py new file mode 100644 index 00000000..36afe6c0 --- /dev/null +++ b/src/python/grpcio_test/grpc_interop/client.py @@ -0,0 +1,116 @@ +# Copyright 2015, 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. + +"""The Python implementation of the GRPC interoperability test client.""" + +import argparse +from oauth2client import client as oauth2client_client + +from grpc.early_adopter import implementations + +from grpc_interop import methods +from grpc_interop import resources + +_ONE_DAY_IN_SECONDS = 60 * 60 * 24 + + +def _args(): + parser = argparse.ArgumentParser() + parser.add_argument( + '--server_host', help='the host to which to connect', type=str) + parser.add_argument( + '--server_port', help='the port to which to connect', type=int) + parser.add_argument( + '--test_case', help='the test case to execute', type=str) + parser.add_argument( + '--use_tls', help='require a secure connection', dest='use_tls', + action='store_true') + parser.add_argument( + '--use_test_ca', help='replace platform root CAs with ca.pem', + action='store_true') + parser.add_argument( + '--server_host_override', + help='the server host to which to claim to connect', type=str) + parser.add_argument('--oauth_scope', help='scope for OAuth tokens', type=str) + parser.add_argument( + '--default_service_account', + help='email address of the default service account', type=str) + return parser.parse_args() + +def _oauth_access_token(args): + credentials = oauth2client_client.GoogleCredentials.get_application_default() + scoped_credentials = credentials.create_scoped([args.oauth_scope]) + return scoped_credentials.get_access_token().access_token + +def _stub(args): + if args.oauth_scope: + if args.test_case == 'oauth2_auth_token': + access_token = _oauth_access_token(args) + metadata_transformer = lambda x: [ + ('Authorization', 'Bearer %s' % access_token)] + else: + metadata_transformer = lambda x: [ + ('Authorization', 'Bearer %s' % _oauth_access_token(args))] + else: + metadata_transformer = lambda x: [] + if args.use_tls: + if args.use_test_ca: + root_certificates = resources.test_root_certificates() + else: + root_certificates = resources.prod_root_certificates() + + stub = implementations.stub( + methods.SERVICE_NAME, methods.CLIENT_METHODS, args.server_host, + args.server_port, metadata_transformer=metadata_transformer, + secure=True, root_certificates=root_certificates, + server_host_override=args.server_host_override) + else: + stub = implementations.stub( + methods.SERVICE_NAME, methods.CLIENT_METHODS, args.server_host, + args.server_port, secure=False) + return stub + + +def _test_case_from_arg(test_case_arg): + for test_case in methods.TestCase: + if test_case_arg == test_case.value: + return test_case + else: + raise ValueError('No test case "%s"!' % test_case_arg) + + +def _test_interoperability(): + args = _args() + stub = _stub(args) + test_case = _test_case_from_arg(args.test_case) + test_case.test_interoperability(stub, args) + + +if __name__ == '__main__': + _test_interoperability() diff --git a/src/python/grpcio_test/grpc_interop/credentials/README b/src/python/grpcio_test/grpc_interop/credentials/README new file mode 100644 index 00000000..cb20dcb4 --- /dev/null +++ b/src/python/grpcio_test/grpc_interop/credentials/README @@ -0,0 +1 @@ +These are test keys *NOT* to be used in production. diff --git a/src/python/grpcio_test/grpc_interop/credentials/ca.pem b/src/python/grpcio_test/grpc_interop/credentials/ca.pem new file mode 100755 index 00000000..6c8511a7 --- /dev/null +++ b/src/python/grpcio_test/grpc_interop/credentials/ca.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICSjCCAbOgAwIBAgIJAJHGGR4dGioHMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQxDzANBgNVBAMTBnRlc3RjYTAeFw0xNDExMTEyMjMxMjla +Fw0yNDExMDgyMjMxMjlaMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0 +YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMT +BnRlc3RjYTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwEDfBV5MYdlHVHJ7 ++L4nxrZy7mBfAVXpOc5vMYztssUI7mL2/iYujiIXM+weZYNTEpLdjyJdu7R5gGUu +g1jSVK/EPHfc74O7AyZU34PNIP4Sh33N+/A5YexrNgJlPY+E3GdVYi4ldWJjgkAd +Qah2PH5ACLrIIC6tRka9hcaBlIECAwEAAaMgMB4wDAYDVR0TBAUwAwEB/zAOBgNV +HQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQELBQADgYEAHzC7jdYlzAVmddi/gdAeKPau +sPBG/C2HCWqHzpCUHcKuvMzDVkY/MP2o6JIW2DBbY64bO/FceExhjcykgaYtCH/m +oIU63+CFOTtR7otyQAWHqXa7q4SbCDlG7DyRFxqG0txPtGvy12lgldA2+RgcigQG +Dfcog5wrJytaQ6UA0wE= +-----END CERTIFICATE----- diff --git a/src/python/grpcio_test/grpc_interop/credentials/server1.key b/src/python/grpcio_test/grpc_interop/credentials/server1.key new file mode 100755 index 00000000..143a5b87 --- /dev/null +++ b/src/python/grpcio_test/grpc_interop/credentials/server1.key @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAOHDFScoLCVJpYDD +M4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1BgzkWF+slf +3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd9N8YwbBY +AckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAECgYAn7qGnM2vbjJNBm0VZCkOkTIWm +V10okw7EPJrdL2mkre9NasghNXbE1y5zDshx5Nt3KsazKOxTT8d0Jwh/3KbaN+YY +tTCbKGW0pXDRBhwUHRcuRzScjli8Rih5UOCiZkhefUTcRb6xIhZJuQy71tjaSy0p +dHZRmYyBYO2YEQ8xoQJBAPrJPhMBkzmEYFtyIEqAxQ/o/A6E+E4w8i+KM7nQCK7q +K4JXzyXVAjLfyBZWHGM2uro/fjqPggGD6QH1qXCkI4MCQQDmdKeb2TrKRh5BY1LR +81aJGKcJ2XbcDu6wMZK4oqWbTX2KiYn9GB0woM6nSr/Y6iy1u145YzYxEV/iMwff +DJULAkB8B2MnyzOg0pNFJqBJuH29bKCcHa8gHJzqXhNO5lAlEbMK95p/P2Wi+4Hd +aiEIAF1BF326QJcvYKmwSmrORp85AkAlSNxRJ50OWrfMZnBgzVjDx3xG6KsFQVk2 +ol6VhqL6dFgKUORFUWBvnKSyhjJxurlPEahV6oo6+A+mPhFY8eUvAkAZQyTdupP3 +XEFQKctGz+9+gKkemDp7LBBMEMBXrGTLPhpEfcjv/7KPdnFHYmhYeBTBnuVmTVWe +F98XJ7tIFfJq +-----END PRIVATE KEY----- diff --git a/src/python/grpcio_test/grpc_interop/credentials/server1.pem b/src/python/grpcio_test/grpc_interop/credentials/server1.pem new file mode 100755 index 00000000..8e582e57 --- /dev/null +++ b/src/python/grpcio_test/grpc_interop/credentials/server1.pem @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICmzCCAgSgAwIBAgIBAzANBgkqhkiG9w0BAQUFADBWMQswCQYDVQQGEwJBVTET +MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ +dHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0Y2EwHhcNMTQwNzIyMDYwMDU3WhcNMjQwNzE5 +MDYwMDU3WjBkMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV +BAcTB0NoaWNhZ28xFDASBgNVBAoTC0dvb2dsZSBJbmMuMRowGAYDVQQDFBEqLnRl +c3QuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA4cMVJygs +JUmlgMMzgdi0h1XoCR7+ww1pop04OMMyy7H/i0PJ2W6Y35+b4CM8QrkYeEafUGDO +RYX6yV/cHGGsD/x02ye6ey1UDtkGAD/mpDEx8YCrjAc1Vfvt8Fk6Cn1WVIxV/J30 +3xjBsFgByQ55RBp1OLZfVLo6AleBDSbcxaECAwEAAaNrMGkwCQYDVR0TBAIwADAL +BgNVHQ8EBAMCBeAwTwYDVR0RBEgwRoIQKi50ZXN0Lmdvb2dsZS5mcoIYd2F0ZXJ6 +b29pLnRlc3QuZ29vZ2xlLmJlghIqLnRlc3QueW91dHViZS5jb22HBMCoAQMwDQYJ +KoZIhvcNAQEFBQADgYEAM2Ii0LgTGbJ1j4oqX9bxVcxm+/R5Yf8oi0aZqTJlnLYS +wXcBykxTx181s7WyfJ49WwrYXo78zTDAnf1ma0fPq3e4mpspvyndLh1a+OarHa1e +aT0DIIYk7qeEa1YcVljx2KyLd0r1BBAfrwyGaEPVeJQVYWaOJRU2we/KD4ojf9s= +-----END CERTIFICATE----- diff --git a/src/python/grpcio_test/grpc_interop/empty_pb2.py b/src/python/grpcio_test/grpc_interop/empty_pb2.py new file mode 100644 index 00000000..8c1ce2f1 --- /dev/null +++ b/src/python/grpcio_test/grpc_interop/empty_pb2.py @@ -0,0 +1,63 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: test/cpp/interop/empty.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='test/cpp/interop/empty.proto', + package='grpc.testing', + serialized_pb=_b('\n\x1ctest/cpp/interop/empty.proto\x12\x0cgrpc.testing\"\x07\n\x05\x45mpty') +) +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + + + + +_EMPTY = _descriptor.Descriptor( + name='Empty', + full_name='grpc.testing.Empty', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=46, + serialized_end=53, +) + +DESCRIPTOR.message_types_by_name['Empty'] = _EMPTY + +Empty = _reflection.GeneratedProtocolMessageType('Empty', (_message.Message,), dict( + DESCRIPTOR = _EMPTY, + __module__ = 'test.cpp.interop.empty_pb2' + # @@protoc_insertion_point(class_scope:grpc.testing.Empty) + )) +_sym_db.RegisterMessage(Empty) + + +import abc +from grpc.early_adopter import implementations +from grpc.framework.alpha import utilities +# @@protoc_insertion_point(module_scope) diff --git a/src/python/grpcio_test/grpc_interop/messages_pb2.py b/src/python/grpcio_test/grpc_interop/messages_pb2.py new file mode 100644 index 00000000..0bf3d86a --- /dev/null +++ b/src/python/grpcio_test/grpc_interop/messages_pb2.py @@ -0,0 +1,447 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: test/cpp/interop/messages.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf.internal import enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='test/cpp/interop/messages.proto', + package='grpc.testing', + serialized_pb=_b('\n\x1ftest/cpp/interop/messages.proto\x12\x0cgrpc.testing\"@\n\x07Payload\x12\'\n\x04type\x18\x01 \x01(\x0e\x32\x19.grpc.testing.PayloadType\x12\x0c\n\x04\x62ody\x18\x02 \x01(\x0c\"\xb1\x01\n\rSimpleRequest\x12\x30\n\rresponse_type\x18\x01 \x01(\x0e\x32\x19.grpc.testing.PayloadType\x12\x15\n\rresponse_size\x18\x02 \x01(\x05\x12&\n\x07payload\x18\x03 \x01(\x0b\x32\x15.grpc.testing.Payload\x12\x15\n\rfill_username\x18\x04 \x01(\x08\x12\x18\n\x10\x66ill_oauth_scope\x18\x05 \x01(\x08\"_\n\x0eSimpleResponse\x12&\n\x07payload\x18\x01 \x01(\x0b\x32\x15.grpc.testing.Payload\x12\x10\n\x08username\x18\x02 \x01(\t\x12\x13\n\x0boauth_scope\x18\x03 \x01(\t\"C\n\x19StreamingInputCallRequest\x12&\n\x07payload\x18\x01 \x01(\x0b\x32\x15.grpc.testing.Payload\"=\n\x1aStreamingInputCallResponse\x12\x1f\n\x17\x61ggregated_payload_size\x18\x01 \x01(\x05\"7\n\x12ResponseParameters\x12\x0c\n\x04size\x18\x01 \x01(\x05\x12\x13\n\x0binterval_us\x18\x02 \x01(\x05\"\xb5\x01\n\x1aStreamingOutputCallRequest\x12\x30\n\rresponse_type\x18\x01 \x01(\x0e\x32\x19.grpc.testing.PayloadType\x12=\n\x13response_parameters\x18\x02 \x03(\x0b\x32 .grpc.testing.ResponseParameters\x12&\n\x07payload\x18\x03 \x01(\x0b\x32\x15.grpc.testing.Payload\"E\n\x1bStreamingOutputCallResponse\x12&\n\x07payload\x18\x01 \x01(\x0b\x32\x15.grpc.testing.Payload*?\n\x0bPayloadType\x12\x10\n\x0c\x43OMPRESSABLE\x10\x00\x12\x12\n\x0eUNCOMPRESSABLE\x10\x01\x12\n\n\x06RANDOM\x10\x02') +) +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +_PAYLOADTYPE = _descriptor.EnumDescriptor( + name='PayloadType', + full_name='grpc.testing.PayloadType', + filename=None, + file=DESCRIPTOR, + values=[ + _descriptor.EnumValueDescriptor( + name='COMPRESSABLE', index=0, number=0, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='UNCOMPRESSABLE', index=1, number=1, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='RANDOM', index=2, number=2, + options=None, + type=None), + ], + containing_type=None, + options=None, + serialized_start=836, + serialized_end=899, +) +_sym_db.RegisterEnumDescriptor(_PAYLOADTYPE) + +PayloadType = enum_type_wrapper.EnumTypeWrapper(_PAYLOADTYPE) +COMPRESSABLE = 0 +UNCOMPRESSABLE = 1 +RANDOM = 2 + + + +_PAYLOAD = _descriptor.Descriptor( + name='Payload', + full_name='grpc.testing.Payload', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='type', full_name='grpc.testing.Payload.type', index=0, + number=1, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='body', full_name='grpc.testing.Payload.body', index=1, + number=2, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=_b(""), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=49, + serialized_end=113, +) + + +_SIMPLEREQUEST = _descriptor.Descriptor( + name='SimpleRequest', + full_name='grpc.testing.SimpleRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='response_type', full_name='grpc.testing.SimpleRequest.response_type', index=0, + number=1, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='response_size', full_name='grpc.testing.SimpleRequest.response_size', index=1, + number=2, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='payload', full_name='grpc.testing.SimpleRequest.payload', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='fill_username', full_name='grpc.testing.SimpleRequest.fill_username', index=3, + number=4, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='fill_oauth_scope', full_name='grpc.testing.SimpleRequest.fill_oauth_scope', index=4, + number=5, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=116, + serialized_end=293, +) + + +_SIMPLERESPONSE = _descriptor.Descriptor( + name='SimpleResponse', + full_name='grpc.testing.SimpleResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='payload', full_name='grpc.testing.SimpleResponse.payload', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='username', full_name='grpc.testing.SimpleResponse.username', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='oauth_scope', full_name='grpc.testing.SimpleResponse.oauth_scope', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=295, + serialized_end=390, +) + + +_STREAMINGINPUTCALLREQUEST = _descriptor.Descriptor( + name='StreamingInputCallRequest', + full_name='grpc.testing.StreamingInputCallRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='payload', full_name='grpc.testing.StreamingInputCallRequest.payload', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=392, + serialized_end=459, +) + + +_STREAMINGINPUTCALLRESPONSE = _descriptor.Descriptor( + name='StreamingInputCallResponse', + full_name='grpc.testing.StreamingInputCallResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='aggregated_payload_size', full_name='grpc.testing.StreamingInputCallResponse.aggregated_payload_size', index=0, + number=1, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=461, + serialized_end=522, +) + + +_RESPONSEPARAMETERS = _descriptor.Descriptor( + name='ResponseParameters', + full_name='grpc.testing.ResponseParameters', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='size', full_name='grpc.testing.ResponseParameters.size', index=0, + number=1, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='interval_us', full_name='grpc.testing.ResponseParameters.interval_us', index=1, + number=2, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=524, + serialized_end=579, +) + + +_STREAMINGOUTPUTCALLREQUEST = _descriptor.Descriptor( + name='StreamingOutputCallRequest', + full_name='grpc.testing.StreamingOutputCallRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='response_type', full_name='grpc.testing.StreamingOutputCallRequest.response_type', index=0, + number=1, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='response_parameters', full_name='grpc.testing.StreamingOutputCallRequest.response_parameters', index=1, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='payload', full_name='grpc.testing.StreamingOutputCallRequest.payload', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=582, + serialized_end=763, +) + + +_STREAMINGOUTPUTCALLRESPONSE = _descriptor.Descriptor( + name='StreamingOutputCallResponse', + full_name='grpc.testing.StreamingOutputCallResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='payload', full_name='grpc.testing.StreamingOutputCallResponse.payload', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=765, + serialized_end=834, +) + +_PAYLOAD.fields_by_name['type'].enum_type = _PAYLOADTYPE +_SIMPLEREQUEST.fields_by_name['response_type'].enum_type = _PAYLOADTYPE +_SIMPLEREQUEST.fields_by_name['payload'].message_type = _PAYLOAD +_SIMPLERESPONSE.fields_by_name['payload'].message_type = _PAYLOAD +_STREAMINGINPUTCALLREQUEST.fields_by_name['payload'].message_type = _PAYLOAD +_STREAMINGOUTPUTCALLREQUEST.fields_by_name['response_type'].enum_type = _PAYLOADTYPE +_STREAMINGOUTPUTCALLREQUEST.fields_by_name['response_parameters'].message_type = _RESPONSEPARAMETERS +_STREAMINGOUTPUTCALLREQUEST.fields_by_name['payload'].message_type = _PAYLOAD +_STREAMINGOUTPUTCALLRESPONSE.fields_by_name['payload'].message_type = _PAYLOAD +DESCRIPTOR.message_types_by_name['Payload'] = _PAYLOAD +DESCRIPTOR.message_types_by_name['SimpleRequest'] = _SIMPLEREQUEST +DESCRIPTOR.message_types_by_name['SimpleResponse'] = _SIMPLERESPONSE +DESCRIPTOR.message_types_by_name['StreamingInputCallRequest'] = _STREAMINGINPUTCALLREQUEST +DESCRIPTOR.message_types_by_name['StreamingInputCallResponse'] = _STREAMINGINPUTCALLRESPONSE +DESCRIPTOR.message_types_by_name['ResponseParameters'] = _RESPONSEPARAMETERS +DESCRIPTOR.message_types_by_name['StreamingOutputCallRequest'] = _STREAMINGOUTPUTCALLREQUEST +DESCRIPTOR.message_types_by_name['StreamingOutputCallResponse'] = _STREAMINGOUTPUTCALLRESPONSE +DESCRIPTOR.enum_types_by_name['PayloadType'] = _PAYLOADTYPE + +Payload = _reflection.GeneratedProtocolMessageType('Payload', (_message.Message,), dict( + DESCRIPTOR = _PAYLOAD, + __module__ = 'test.cpp.interop.messages_pb2' + # @@protoc_insertion_point(class_scope:grpc.testing.Payload) + )) +_sym_db.RegisterMessage(Payload) + +SimpleRequest = _reflection.GeneratedProtocolMessageType('SimpleRequest', (_message.Message,), dict( + DESCRIPTOR = _SIMPLEREQUEST, + __module__ = 'test.cpp.interop.messages_pb2' + # @@protoc_insertion_point(class_scope:grpc.testing.SimpleRequest) + )) +_sym_db.RegisterMessage(SimpleRequest) + +SimpleResponse = _reflection.GeneratedProtocolMessageType('SimpleResponse', (_message.Message,), dict( + DESCRIPTOR = _SIMPLERESPONSE, + __module__ = 'test.cpp.interop.messages_pb2' + # @@protoc_insertion_point(class_scope:grpc.testing.SimpleResponse) + )) +_sym_db.RegisterMessage(SimpleResponse) + +StreamingInputCallRequest = _reflection.GeneratedProtocolMessageType('StreamingInputCallRequest', (_message.Message,), dict( + DESCRIPTOR = _STREAMINGINPUTCALLREQUEST, + __module__ = 'test.cpp.interop.messages_pb2' + # @@protoc_insertion_point(class_scope:grpc.testing.StreamingInputCallRequest) + )) +_sym_db.RegisterMessage(StreamingInputCallRequest) + +StreamingInputCallResponse = _reflection.GeneratedProtocolMessageType('StreamingInputCallResponse', (_message.Message,), dict( + DESCRIPTOR = _STREAMINGINPUTCALLRESPONSE, + __module__ = 'test.cpp.interop.messages_pb2' + # @@protoc_insertion_point(class_scope:grpc.testing.StreamingInputCallResponse) + )) +_sym_db.RegisterMessage(StreamingInputCallResponse) + +ResponseParameters = _reflection.GeneratedProtocolMessageType('ResponseParameters', (_message.Message,), dict( + DESCRIPTOR = _RESPONSEPARAMETERS, + __module__ = 'test.cpp.interop.messages_pb2' + # @@protoc_insertion_point(class_scope:grpc.testing.ResponseParameters) + )) +_sym_db.RegisterMessage(ResponseParameters) + +StreamingOutputCallRequest = _reflection.GeneratedProtocolMessageType('StreamingOutputCallRequest', (_message.Message,), dict( + DESCRIPTOR = _STREAMINGOUTPUTCALLREQUEST, + __module__ = 'test.cpp.interop.messages_pb2' + # @@protoc_insertion_point(class_scope:grpc.testing.StreamingOutputCallRequest) + )) +_sym_db.RegisterMessage(StreamingOutputCallRequest) + +StreamingOutputCallResponse = _reflection.GeneratedProtocolMessageType('StreamingOutputCallResponse', (_message.Message,), dict( + DESCRIPTOR = _STREAMINGOUTPUTCALLRESPONSE, + __module__ = 'test.cpp.interop.messages_pb2' + # @@protoc_insertion_point(class_scope:grpc.testing.StreamingOutputCallResponse) + )) +_sym_db.RegisterMessage(StreamingOutputCallResponse) + + +import abc +from grpc.early_adopter import implementations +from grpc.framework.alpha import utilities +# @@protoc_insertion_point(module_scope) diff --git a/src/python/grpcio_test/grpc_interop/methods.py b/src/python/grpcio_test/grpc_interop/methods.py new file mode 100644 index 00000000..52b800af --- /dev/null +++ b/src/python/grpcio_test/grpc_interop/methods.py @@ -0,0 +1,397 @@ +# Copyright 2015, 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. + +"""Implementations of interoperability test methods.""" + +import enum +import json +import os +import threading +import time + +from oauth2client import client as oauth2client_client + +from grpc.framework.alpha import utilities +from grpc.framework.alpha import exceptions + +from grpc_interop import empty_pb2 +from grpc_interop import messages_pb2 + +_TIMEOUT = 7 + + +def _empty_call(request, unused_context): + return empty_pb2.Empty() + +_CLIENT_EMPTY_CALL = utilities.unary_unary_invocation_description( + empty_pb2.Empty.SerializeToString, empty_pb2.Empty.FromString) +_SERVER_EMPTY_CALL = utilities.unary_unary_service_description( + _empty_call, empty_pb2.Empty.FromString, + empty_pb2.Empty.SerializeToString) + + +def _unary_call(request, unused_context): + return messages_pb2.SimpleResponse( + payload=messages_pb2.Payload( + type=messages_pb2.COMPRESSABLE, + body=b'\x00' * request.response_size)) + +_CLIENT_UNARY_CALL = utilities.unary_unary_invocation_description( + messages_pb2.SimpleRequest.SerializeToString, + messages_pb2.SimpleResponse.FromString) +_SERVER_UNARY_CALL = utilities.unary_unary_service_description( + _unary_call, messages_pb2.SimpleRequest.FromString, + messages_pb2.SimpleResponse.SerializeToString) + + +def _streaming_output_call(request, unused_context): + for response_parameters in request.response_parameters: + yield messages_pb2.StreamingOutputCallResponse( + payload=messages_pb2.Payload( + type=request.response_type, + body=b'\x00' * response_parameters.size)) + +_CLIENT_STREAMING_OUTPUT_CALL = utilities.unary_stream_invocation_description( + messages_pb2.StreamingOutputCallRequest.SerializeToString, + messages_pb2.StreamingOutputCallResponse.FromString) +_SERVER_STREAMING_OUTPUT_CALL = utilities.unary_stream_service_description( + _streaming_output_call, + messages_pb2.StreamingOutputCallRequest.FromString, + messages_pb2.StreamingOutputCallResponse.SerializeToString) + + +def _streaming_input_call(request_iterator, unused_context): + aggregate_size = 0 + for request in request_iterator: + if request.payload and request.payload.body: + aggregate_size += len(request.payload.body) + return messages_pb2.StreamingInputCallResponse( + aggregated_payload_size=aggregate_size) + +_CLIENT_STREAMING_INPUT_CALL = utilities.stream_unary_invocation_description( + messages_pb2.StreamingInputCallRequest.SerializeToString, + messages_pb2.StreamingInputCallResponse.FromString) +_SERVER_STREAMING_INPUT_CALL = utilities.stream_unary_service_description( + _streaming_input_call, + messages_pb2.StreamingInputCallRequest.FromString, + messages_pb2.StreamingInputCallResponse.SerializeToString) + + +def _full_duplex_call(request_iterator, unused_context): + for request in request_iterator: + yield messages_pb2.StreamingOutputCallResponse( + payload=messages_pb2.Payload( + type=request.payload.type, + body=b'\x00' * request.response_parameters[0].size)) + +_CLIENT_FULL_DUPLEX_CALL = utilities.stream_stream_invocation_description( + messages_pb2.StreamingOutputCallRequest.SerializeToString, + messages_pb2.StreamingOutputCallResponse.FromString) +_SERVER_FULL_DUPLEX_CALL = utilities.stream_stream_service_description( + _full_duplex_call, + messages_pb2.StreamingOutputCallRequest.FromString, + messages_pb2.StreamingOutputCallResponse.SerializeToString) + +# NOTE(nathaniel): Apparently this is the same as the full-duplex call? +_CLIENT_HALF_DUPLEX_CALL = utilities.stream_stream_invocation_description( + messages_pb2.StreamingOutputCallRequest.SerializeToString, + messages_pb2.StreamingOutputCallResponse.FromString) +_SERVER_HALF_DUPLEX_CALL = utilities.stream_stream_service_description( + _full_duplex_call, + messages_pb2.StreamingOutputCallRequest.FromString, + messages_pb2.StreamingOutputCallResponse.SerializeToString) + + +SERVICE_NAME = 'grpc.testing.TestService' + +_EMPTY_CALL_METHOD_NAME = 'EmptyCall' +_UNARY_CALL_METHOD_NAME = 'UnaryCall' +_STREAMING_OUTPUT_CALL_METHOD_NAME = 'StreamingOutputCall' +_STREAMING_INPUT_CALL_METHOD_NAME = 'StreamingInputCall' +_FULL_DUPLEX_CALL_METHOD_NAME = 'FullDuplexCall' +_HALF_DUPLEX_CALL_METHOD_NAME = 'HalfDuplexCall' + +CLIENT_METHODS = { + _EMPTY_CALL_METHOD_NAME: _CLIENT_EMPTY_CALL, + _UNARY_CALL_METHOD_NAME: _CLIENT_UNARY_CALL, + _STREAMING_OUTPUT_CALL_METHOD_NAME: _CLIENT_STREAMING_OUTPUT_CALL, + _STREAMING_INPUT_CALL_METHOD_NAME: _CLIENT_STREAMING_INPUT_CALL, + _FULL_DUPLEX_CALL_METHOD_NAME: _CLIENT_FULL_DUPLEX_CALL, + _HALF_DUPLEX_CALL_METHOD_NAME: _CLIENT_HALF_DUPLEX_CALL, +} + +SERVER_METHODS = { + _EMPTY_CALL_METHOD_NAME: _SERVER_EMPTY_CALL, + _UNARY_CALL_METHOD_NAME: _SERVER_UNARY_CALL, + _STREAMING_OUTPUT_CALL_METHOD_NAME: _SERVER_STREAMING_OUTPUT_CALL, + _STREAMING_INPUT_CALL_METHOD_NAME: _SERVER_STREAMING_INPUT_CALL, + _FULL_DUPLEX_CALL_METHOD_NAME: _SERVER_FULL_DUPLEX_CALL, + _HALF_DUPLEX_CALL_METHOD_NAME: _SERVER_HALF_DUPLEX_CALL, +} + + +def _large_unary_common_behavior(stub, fill_username, fill_oauth_scope): + with stub: + request = messages_pb2.SimpleRequest( + response_type=messages_pb2.COMPRESSABLE, response_size=314159, + payload=messages_pb2.Payload(body=b'\x00' * 271828), + fill_username=fill_username, fill_oauth_scope=fill_oauth_scope) + response_future = stub.UnaryCall.async(request, _TIMEOUT) + response = response_future.result() + if response.payload.type is not messages_pb2.COMPRESSABLE: + raise ValueError( + 'response payload type is "%s"!' % type(response.payload.type)) + if len(response.payload.body) != 314159: + raise ValueError( + 'response body of incorrect size %d!' % len(response.payload.body)) + return response + + +def _empty_unary(stub): + with stub: + response = stub.EmptyCall(empty_pb2.Empty(), _TIMEOUT) + if not isinstance(response, empty_pb2.Empty): + raise TypeError( + 'response is of type "%s", not empty_pb2.Empty!', type(response)) + + +def _large_unary(stub): + _large_unary_common_behavior(stub, False, False) + + +def _client_streaming(stub): + with stub: + payload_body_sizes = (27182, 8, 1828, 45904) + payloads = ( + messages_pb2.Payload(body=b'\x00' * size) + for size in payload_body_sizes) + requests = ( + messages_pb2.StreamingInputCallRequest(payload=payload) + for payload in payloads) + response = stub.StreamingInputCall(requests, _TIMEOUT) + if response.aggregated_payload_size != 74922: + raise ValueError( + 'incorrect size %d!' % response.aggregated_payload_size) + + +def _server_streaming(stub): + sizes = (31415, 9, 2653, 58979) + + with stub: + request = messages_pb2.StreamingOutputCallRequest( + response_type=messages_pb2.COMPRESSABLE, + response_parameters=( + messages_pb2.ResponseParameters(size=sizes[0]), + messages_pb2.ResponseParameters(size=sizes[1]), + messages_pb2.ResponseParameters(size=sizes[2]), + messages_pb2.ResponseParameters(size=sizes[3]), + )) + response_iterator = stub.StreamingOutputCall(request, _TIMEOUT) + for index, response in enumerate(response_iterator): + if response.payload.type != messages_pb2.COMPRESSABLE: + raise ValueError( + 'response body of invalid type %s!' % response.payload.type) + if len(response.payload.body) != sizes[index]: + raise ValueError( + 'response body of invalid size %d!' % len(response.payload.body)) + +def _cancel_after_begin(stub): + with stub: + sizes = (27182, 8, 1828, 45904) + payloads = [messages_pb2.Payload(body=b'\x00' * size) for size in sizes] + requests = [messages_pb2.StreamingInputCallRequest(payload=payload) + for payload in payloads] + responses = stub.StreamingInputCall.async(requests, _TIMEOUT) + responses.cancel() + if not responses.cancelled(): + raise ValueError('expected call to be cancelled') + + +class _Pipe(object): + + def __init__(self): + self._condition = threading.Condition() + self._values = [] + self._open = True + + def __iter__(self): + return self + + def next(self): + with self._condition: + while not self._values and self._open: + self._condition.wait() + if self._values: + return self._values.pop(0) + else: + raise StopIteration() + + def add(self, value): + with self._condition: + self._values.append(value) + self._condition.notify() + + def close(self): + with self._condition: + self._open = False + self._condition.notify() + + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + self.close() + + +def _ping_pong(stub): + request_response_sizes = (31415, 9, 2653, 58979) + request_payload_sizes = (27182, 8, 1828, 45904) + + with stub, _Pipe() as pipe: + response_iterator = stub.FullDuplexCall(pipe, _TIMEOUT) + print 'Starting ping-pong with response iterator %s' % response_iterator + for response_size, payload_size in zip( + request_response_sizes, request_payload_sizes): + request = messages_pb2.StreamingOutputCallRequest( + response_type=messages_pb2.COMPRESSABLE, + response_parameters=(messages_pb2.ResponseParameters( + size=response_size),), + payload=messages_pb2.Payload(body=b'\x00' * payload_size)) + pipe.add(request) + response = next(response_iterator) + if response.payload.type != messages_pb2.COMPRESSABLE: + raise ValueError( + 'response body of invalid type %s!' % response.payload.type) + if len(response.payload.body) != response_size: + raise ValueError( + 'response body of invalid size %d!' % len(response.payload.body)) + + +def _cancel_after_first_response(stub): + request_response_sizes = (31415, 9, 2653, 58979) + request_payload_sizes = (27182, 8, 1828, 45904) + with stub, _Pipe() as pipe: + response_iterator = stub.FullDuplexCall(pipe, _TIMEOUT) + + response_size = request_response_sizes[0] + payload_size = request_payload_sizes[0] + request = messages_pb2.StreamingOutputCallRequest( + response_type=messages_pb2.COMPRESSABLE, + response_parameters=(messages_pb2.ResponseParameters( + size=response_size),), + payload=messages_pb2.Payload(body=b'\x00' * payload_size)) + pipe.add(request) + response = next(response_iterator) + # We test the contents of `response` in the Ping Pong test - don't check + # them here. + response_iterator.cancel() + + try: + next(response_iterator) + except Exception: + pass + else: + raise ValueError('expected call to be cancelled') + + +def _timeout_on_sleeping_server(stub): + request_payload_size = 27182 + with stub, _Pipe() as pipe: + response_iterator = stub.FullDuplexCall(pipe, 0.001) + + request = messages_pb2.StreamingOutputCallRequest( + response_type=messages_pb2.COMPRESSABLE, + payload=messages_pb2.Payload(body=b'\x00' * request_payload_size)) + pipe.add(request) + time.sleep(0.1) + try: + next(response_iterator) + except exceptions.ExpirationError: + pass + else: + raise ValueError('expected call to exceed deadline') + + +def _compute_engine_creds(stub, args): + response = _large_unary_common_behavior(stub, True, True) + if args.default_service_account != response.username: + raise ValueError( + 'expected username %s, got %s' % (args.default_service_account, + response.username)) + + +def _oauth2_auth_token(stub, args): + json_key_filename = os.environ[ + oauth2client_client.GOOGLE_APPLICATION_CREDENTIALS] + wanted_email = json.load(open(json_key_filename, 'rb'))['client_email'] + response = _large_unary_common_behavior(stub, True, True) + if wanted_email != response.username: + raise ValueError( + 'expected username %s, got %s' % (wanted_email, response.username)) + if args.oauth_scope.find(response.oauth_scope) == -1: + raise ValueError( + 'expected to find oauth scope "%s" in received "%s"' % + (response.oauth_scope, args.oauth_scope)) + +@enum.unique +class TestCase(enum.Enum): + EMPTY_UNARY = 'empty_unary' + LARGE_UNARY = 'large_unary' + SERVER_STREAMING = 'server_streaming' + CLIENT_STREAMING = 'client_streaming' + PING_PONG = 'ping_pong' + CANCEL_AFTER_BEGIN = 'cancel_after_begin' + CANCEL_AFTER_FIRST_RESPONSE = 'cancel_after_first_response' + COMPUTE_ENGINE_CREDS = 'compute_engine_creds' + OAUTH2_AUTH_TOKEN = 'oauth2_auth_token' + TIMEOUT_ON_SLEEPING_SERVER = 'timeout_on_sleeping_server' + + def test_interoperability(self, stub, args): + if self is TestCase.EMPTY_UNARY: + _empty_unary(stub) + elif self is TestCase.LARGE_UNARY: + _large_unary(stub) + elif self is TestCase.SERVER_STREAMING: + _server_streaming(stub) + elif self is TestCase.CLIENT_STREAMING: + _client_streaming(stub) + elif self is TestCase.PING_PONG: + _ping_pong(stub) + elif self is TestCase.CANCEL_AFTER_BEGIN: + _cancel_after_begin(stub) + elif self is TestCase.CANCEL_AFTER_FIRST_RESPONSE: + _cancel_after_first_response(stub) + elif self is TestCase.TIMEOUT_ON_SLEEPING_SERVER: + _timeout_on_sleeping_server(stub) + elif self is TestCase.COMPUTE_ENGINE_CREDS: + _compute_engine_creds(stub, args) + elif self is TestCase.OAUTH2_AUTH_TOKEN: + _oauth2_auth_token(stub, args) + else: + raise NotImplementedError('Test case "%s" not implemented!' % self.name) diff --git a/src/python/grpcio_test/grpc_interop/resources.py b/src/python/grpcio_test/grpc_interop/resources.py new file mode 100644 index 00000000..2c304531 --- /dev/null +++ b/src/python/grpcio_test/grpc_interop/resources.py @@ -0,0 +1,56 @@ +# Copyright 2015, 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. + +"""Constants and functions for data used in interoperability testing.""" + +import os + +import pkg_resources + +_ROOT_CERTIFICATES_RESOURCE_PATH = 'credentials/ca.pem' +_PRIVATE_KEY_RESOURCE_PATH = 'credentials/server1.key' +_CERTIFICATE_CHAIN_RESOURCE_PATH = 'credentials/server1.pem' + + +def test_root_certificates(): + return pkg_resources.resource_string( + __name__, _ROOT_CERTIFICATES_RESOURCE_PATH) + + +def prod_root_certificates(): + return open(os.environ['SSL_CERT_FILE'], mode='rb').read() + + +def private_key(): + return pkg_resources.resource_string(__name__, _PRIVATE_KEY_RESOURCE_PATH) + + +def certificate_chain(): + return pkg_resources.resource_string( + __name__, _CERTIFICATE_CHAIN_RESOURCE_PATH) diff --git a/src/python/grpcio_test/grpc_interop/server.py b/src/python/grpcio_test/grpc_interop/server.py new file mode 100644 index 00000000..60f630a6 --- /dev/null +++ b/src/python/grpcio_test/grpc_interop/server.py @@ -0,0 +1,74 @@ +# Copyright 2015, 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. + +"""The Python implementation of the GRPC interoperability test server.""" + +import argparse +import logging +import time + +from grpc.early_adopter import implementations + +from grpc_interop import methods +from grpc_interop import resources + +_ONE_DAY_IN_SECONDS = 60 * 60 * 24 + + +def serve(): + parser = argparse.ArgumentParser() + parser.add_argument( + '--port', help='the port on which to serve', type=int) + parser.add_argument( + '--use_tls', help='require a secure connection', dest='use_tls', + action='store_true') + args = parser.parse_args() + + if args.use_tls: + private_key = resources.private_key() + certificate_chain = resources.certificate_chain() + server = implementations.server( + methods.SERVICE_NAME, methods.SERVER_METHODS, args.port, + private_key=private_key, certificate_chain=certificate_chain) + else: + server = implementations.server( + methods.SERVICE_NAME, methods.SERVER_METHODS, args.port) + + server.start() + logging.info('Server serving.') + try: + while True: + time.sleep(_ONE_DAY_IN_SECONDS) + except BaseException as e: + logging.info('Caught exception "%s"; stopping server...', e) + server.stop() + logging.info('Server stopped; exiting.') + +if __name__ == '__main__': + serve() diff --git a/src/python/grpcio_test/grpc_interop/test_pb2.py b/src/python/grpcio_test/grpc_interop/test_pb2.py new file mode 100644 index 00000000..71325d5a --- /dev/null +++ b/src/python/grpcio_test/grpc_interop/test_pb2.py @@ -0,0 +1,178 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: test/cpp/interop/test.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from test.cpp.interop import empty_pb2 as test_dot_cpp_dot_interop_dot_empty__pb2 +from test.cpp.interop import messages_pb2 as test_dot_cpp_dot_interop_dot_messages__pb2 + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='test/cpp/interop/test.proto', + package='grpc.testing', + serialized_pb=_b('\n\x1btest/cpp/interop/test.proto\x12\x0cgrpc.testing\x1a\x1ctest/cpp/interop/empty.proto\x1a\x1ftest/cpp/interop/messages.proto2\xbb\x04\n\x0bTestService\x12\x35\n\tEmptyCall\x12\x13.grpc.testing.Empty\x1a\x13.grpc.testing.Empty\x12\x46\n\tUnaryCall\x12\x1b.grpc.testing.SimpleRequest\x1a\x1c.grpc.testing.SimpleResponse\x12l\n\x13StreamingOutputCall\x12(.grpc.testing.StreamingOutputCallRequest\x1a).grpc.testing.StreamingOutputCallResponse0\x01\x12i\n\x12StreamingInputCall\x12\'.grpc.testing.StreamingInputCallRequest\x1a(.grpc.testing.StreamingInputCallResponse(\x01\x12i\n\x0e\x46ullDuplexCall\x12(.grpc.testing.StreamingOutputCallRequest\x1a).grpc.testing.StreamingOutputCallResponse(\x01\x30\x01\x12i\n\x0eHalfDuplexCall\x12(.grpc.testing.StreamingOutputCallRequest\x1a).grpc.testing.StreamingOutputCallResponse(\x01\x30\x01') + , + dependencies=[test_dot_cpp_dot_interop_dot_empty__pb2.DESCRIPTOR,test_dot_cpp_dot_interop_dot_messages__pb2.DESCRIPTOR,]) +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + + + + + +import abc +from grpc.early_adopter import implementations +from grpc.framework.alpha import utilities +class EarlyAdopterTestServiceServicer(object): + """""" + __metaclass__ = abc.ABCMeta + @abc.abstractmethod + def EmptyCall(self, request, context): + raise NotImplementedError() + @abc.abstractmethod + def UnaryCall(self, request, context): + raise NotImplementedError() + @abc.abstractmethod + def StreamingOutputCall(self, request, context): + raise NotImplementedError() + @abc.abstractmethod + def StreamingInputCall(self, request_iterator, context): + raise NotImplementedError() + @abc.abstractmethod + def FullDuplexCall(self, request_iterator, context): + raise NotImplementedError() + @abc.abstractmethod + def HalfDuplexCall(self, request_iterator, context): + raise NotImplementedError() +class EarlyAdopterTestServiceServer(object): + """""" + __metaclass__ = abc.ABCMeta + @abc.abstractmethod + def start(self): + raise NotImplementedError() + @abc.abstractmethod + def stop(self): + raise NotImplementedError() +class EarlyAdopterTestServiceStub(object): + """""" + __metaclass__ = abc.ABCMeta + @abc.abstractmethod + def EmptyCall(self, request): + raise NotImplementedError() + EmptyCall.async = None + @abc.abstractmethod + def UnaryCall(self, request): + raise NotImplementedError() + UnaryCall.async = None + @abc.abstractmethod + def StreamingOutputCall(self, request): + raise NotImplementedError() + StreamingOutputCall.async = None + @abc.abstractmethod + def StreamingInputCall(self, request_iterator): + raise NotImplementedError() + StreamingInputCall.async = None + @abc.abstractmethod + def FullDuplexCall(self, request_iterator): + raise NotImplementedError() + FullDuplexCall.async = None + @abc.abstractmethod + def HalfDuplexCall(self, request_iterator): + raise NotImplementedError() + HalfDuplexCall.async = None +def early_adopter_create_TestService_server(servicer, port, private_key=None, certificate_chain=None): + import test.cpp.interop.empty_pb2 + import test.cpp.interop.empty_pb2 + import test.cpp.interop.messages_pb2 + import test.cpp.interop.messages_pb2 + import test.cpp.interop.messages_pb2 + import test.cpp.interop.messages_pb2 + import test.cpp.interop.messages_pb2 + import test.cpp.interop.messages_pb2 + import test.cpp.interop.messages_pb2 + import test.cpp.interop.messages_pb2 + import test.cpp.interop.messages_pb2 + import test.cpp.interop.messages_pb2 + method_service_descriptions = { + "EmptyCall": utilities.unary_unary_service_description( + servicer.EmptyCall, + test.cpp.interop.empty_pb2.Empty.FromString, + test.cpp.interop.empty_pb2.Empty.SerializeToString, + ), + "FullDuplexCall": utilities.stream_stream_service_description( + servicer.FullDuplexCall, + test.cpp.interop.messages_pb2.StreamingOutputCallRequest.FromString, + test.cpp.interop.messages_pb2.StreamingOutputCallResponse.SerializeToString, + ), + "HalfDuplexCall": utilities.stream_stream_service_description( + servicer.HalfDuplexCall, + test.cpp.interop.messages_pb2.StreamingOutputCallRequest.FromString, + test.cpp.interop.messages_pb2.StreamingOutputCallResponse.SerializeToString, + ), + "StreamingInputCall": utilities.stream_unary_service_description( + servicer.StreamingInputCall, + test.cpp.interop.messages_pb2.StreamingInputCallRequest.FromString, + test.cpp.interop.messages_pb2.StreamingInputCallResponse.SerializeToString, + ), + "StreamingOutputCall": utilities.unary_stream_service_description( + servicer.StreamingOutputCall, + test.cpp.interop.messages_pb2.StreamingOutputCallRequest.FromString, + test.cpp.interop.messages_pb2.StreamingOutputCallResponse.SerializeToString, + ), + "UnaryCall": utilities.unary_unary_service_description( + servicer.UnaryCall, + test.cpp.interop.messages_pb2.SimpleRequest.FromString, + test.cpp.interop.messages_pb2.SimpleResponse.SerializeToString, + ), + } + return implementations.server("grpc.testing.TestService", method_service_descriptions, port, private_key=private_key, certificate_chain=certificate_chain) +def early_adopter_create_TestService_stub(host, port, metadata_transformer=None, secure=False, root_certificates=None, private_key=None, certificate_chain=None, server_host_override=None): + import test.cpp.interop.empty_pb2 + import test.cpp.interop.empty_pb2 + import test.cpp.interop.messages_pb2 + import test.cpp.interop.messages_pb2 + import test.cpp.interop.messages_pb2 + import test.cpp.interop.messages_pb2 + import test.cpp.interop.messages_pb2 + import test.cpp.interop.messages_pb2 + import test.cpp.interop.messages_pb2 + import test.cpp.interop.messages_pb2 + import test.cpp.interop.messages_pb2 + import test.cpp.interop.messages_pb2 + method_invocation_descriptions = { + "EmptyCall": utilities.unary_unary_invocation_description( + test.cpp.interop.empty_pb2.Empty.SerializeToString, + test.cpp.interop.empty_pb2.Empty.FromString, + ), + "FullDuplexCall": utilities.stream_stream_invocation_description( + test.cpp.interop.messages_pb2.StreamingOutputCallRequest.SerializeToString, + test.cpp.interop.messages_pb2.StreamingOutputCallResponse.FromString, + ), + "HalfDuplexCall": utilities.stream_stream_invocation_description( + test.cpp.interop.messages_pb2.StreamingOutputCallRequest.SerializeToString, + test.cpp.interop.messages_pb2.StreamingOutputCallResponse.FromString, + ), + "StreamingInputCall": utilities.stream_unary_invocation_description( + test.cpp.interop.messages_pb2.StreamingInputCallRequest.SerializeToString, + test.cpp.interop.messages_pb2.StreamingInputCallResponse.FromString, + ), + "StreamingOutputCall": utilities.unary_stream_invocation_description( + test.cpp.interop.messages_pb2.StreamingOutputCallRequest.SerializeToString, + test.cpp.interop.messages_pb2.StreamingOutputCallResponse.FromString, + ), + "UnaryCall": utilities.unary_unary_invocation_description( + test.cpp.interop.messages_pb2.SimpleRequest.SerializeToString, + test.cpp.interop.messages_pb2.SimpleResponse.FromString, + ), + } + return implementations.stub("grpc.testing.TestService", method_invocation_descriptions, host, port, metadata_transformer=metadata_transformer, secure=secure, root_certificates=root_certificates, private_key=private_key, certificate_chain=certificate_chain, server_host_override=server_host_override) +# @@protoc_insertion_point(module_scope) diff --git a/src/python/grpcio_test/grpc_protoc_plugin/__init__.py b/src/python/grpcio_test/grpc_protoc_plugin/__init__.py new file mode 100644 index 00000000..70865191 --- /dev/null +++ b/src/python/grpcio_test/grpc_protoc_plugin/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, 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. + + diff --git a/src/python/grpcio_test/grpc_protoc_plugin/alpha_python_plugin_test.py b/src/python/grpcio_test/grpc_protoc_plugin/alpha_python_plugin_test.py new file mode 100644 index 00000000..b200d129 --- /dev/null +++ b/src/python/grpcio_test/grpc_protoc_plugin/alpha_python_plugin_test.py @@ -0,0 +1,541 @@ +# Copyright 2015, 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. + +import argparse +import contextlib +import distutils.spawn +import errno +import itertools +import os +import pkg_resources +import shutil +import subprocess +import sys +import tempfile +import threading +import time +import unittest + +from grpc.framework.alpha import exceptions +from grpc.framework.foundation import future + +# Identifiers of entities we expect to find in the generated module. +SERVICER_IDENTIFIER = 'EarlyAdopterTestServiceServicer' +SERVER_IDENTIFIER = 'EarlyAdopterTestServiceServer' +STUB_IDENTIFIER = 'EarlyAdopterTestServiceStub' +SERVER_FACTORY_IDENTIFIER = 'early_adopter_create_TestService_server' +STUB_FACTORY_IDENTIFIER = 'early_adopter_create_TestService_stub' + +# The timeout used in tests of RPCs that are supposed to expire. +SHORT_TIMEOUT = 2 +# The timeout used in tests of RPCs that are not supposed to expire. The +# absurdly large value doesn't matter since no passing execution of this test +# module will ever wait the duration. +LONG_TIMEOUT = 600 +NO_DELAY = 0 + + +class _ServicerMethods(object): + + def __init__(self, test_pb2, delay): + self._condition = threading.Condition() + self._delay = delay + self._paused = False + self._fail = False + self._test_pb2 = test_pb2 + + @contextlib.contextmanager + def pause(self): # pylint: disable=invalid-name + with self._condition: + self._paused = True + yield + with self._condition: + self._paused = False + self._condition.notify_all() + + @contextlib.contextmanager + def fail(self): # pylint: disable=invalid-name + with self._condition: + self._fail = True + yield + with self._condition: + self._fail = False + + def _control(self): # pylint: disable=invalid-name + with self._condition: + if self._fail: + raise ValueError() + while self._paused: + self._condition.wait() + time.sleep(self._delay) + + def UnaryCall(self, request, unused_rpc_context): + response = self._test_pb2.SimpleResponse() + response.payload.payload_type = self._test_pb2.COMPRESSABLE + response.payload.payload_compressable = 'a' * request.response_size + self._control() + return response + + def StreamingOutputCall(self, request, unused_rpc_context): + for parameter in request.response_parameters: + response = self._test_pb2.StreamingOutputCallResponse() + response.payload.payload_type = self._test_pb2.COMPRESSABLE + response.payload.payload_compressable = 'a' * parameter.size + self._control() + yield response + + def StreamingInputCall(self, request_iter, unused_rpc_context): + response = self._test_pb2.StreamingInputCallResponse() + aggregated_payload_size = 0 + for request in request_iter: + aggregated_payload_size += len(request.payload.payload_compressable) + response.aggregated_payload_size = aggregated_payload_size + self._control() + return response + + def FullDuplexCall(self, request_iter, unused_rpc_context): + for request in request_iter: + for parameter in request.response_parameters: + response = self._test_pb2.StreamingOutputCallResponse() + response.payload.payload_type = self._test_pb2.COMPRESSABLE + response.payload.payload_compressable = 'a' * parameter.size + self._control() + yield response + + def HalfDuplexCall(self, request_iter, unused_rpc_context): + responses = [] + for request in request_iter: + for parameter in request.response_parameters: + response = self._test_pb2.StreamingOutputCallResponse() + response.payload.payload_type = self._test_pb2.COMPRESSABLE + response.payload.payload_compressable = 'a' * parameter.size + self._control() + responses.append(response) + for response in responses: + yield response + + +@contextlib.contextmanager +def _CreateService(test_pb2, delay): + """Provides a servicer backend and a stub. + + The servicer is just the implementation + of the actual servicer passed to the face player of the python RPC + implementation; the two are detached. + + Non-zero delay puts a delay on each call to the servicer, representative of + communication latency. Timeout is the default timeout for the stub while + waiting for the service. + + Args: + test_pb2: The test_pb2 module generated by this test. + delay: Delay in seconds per response from the servicer. + + Yields: + A (servicer_methods, servicer, stub) three-tuple where servicer_methods is + the back-end of the service bound to the stub and the server and stub + are both activated and ready for use. + """ + servicer_methods = _ServicerMethods(test_pb2, delay) + + class Servicer(getattr(test_pb2, SERVICER_IDENTIFIER)): + + def UnaryCall(self, request, context): + return servicer_methods.UnaryCall(request, context) + + def StreamingOutputCall(self, request, context): + return servicer_methods.StreamingOutputCall(request, context) + + def StreamingInputCall(self, request_iter, context): + return servicer_methods.StreamingInputCall(request_iter, context) + + def FullDuplexCall(self, request_iter, context): + return servicer_methods.FullDuplexCall(request_iter, context) + + def HalfDuplexCall(self, request_iter, context): + return servicer_methods.HalfDuplexCall(request_iter, context) + + servicer = Servicer() + server = getattr( + test_pb2, SERVER_FACTORY_IDENTIFIER)(servicer, 0) + with server: + port = server.port() + stub = getattr(test_pb2, STUB_FACTORY_IDENTIFIER)('localhost', port) + with stub: + yield servicer_methods, stub, server + + +def _streaming_input_request_iterator(test_pb2): + for _ in range(3): + request = test_pb2.StreamingInputCallRequest() + request.payload.payload_type = test_pb2.COMPRESSABLE + request.payload.payload_compressable = 'a' + yield request + + +def _streaming_output_request(test_pb2): + request = test_pb2.StreamingOutputCallRequest() + sizes = [1, 2, 3] + request.response_parameters.add(size=sizes[0], interval_us=0) + request.response_parameters.add(size=sizes[1], interval_us=0) + request.response_parameters.add(size=sizes[2], interval_us=0) + return request + + +def _full_duplex_request_iterator(test_pb2): + request = test_pb2.StreamingOutputCallRequest() + request.response_parameters.add(size=1, interval_us=0) + yield request + request = test_pb2.StreamingOutputCallRequest() + request.response_parameters.add(size=2, interval_us=0) + request.response_parameters.add(size=3, interval_us=0) + yield request + + +class PythonPluginTest(unittest.TestCase): + """Test case for the gRPC Python protoc-plugin. + + While reading these tests, remember that the futures API + (`stub.method.async()`) only gives futures for the *non-streaming* responses, + else it behaves like its blocking cousin. + """ + + def setUp(self): + # Assume that the appropriate protoc and grpc_python_plugins are on the + # path. + protoc_command = 'protoc' + protoc_plugin_filename = distutils.spawn.find_executable( + 'grpc_python_plugin') + test_proto_filename = pkg_resources.resource_filename( + 'grpc_protoc_plugin', 'test.proto') + if not os.path.isfile(protoc_command): + # Assume that if we haven't built protoc that it's on the system. + protoc_command = 'protoc' + + # Ensure that the output directory exists. + self.outdir = tempfile.mkdtemp() + + # Invoke protoc with the plugin. + cmd = [ + protoc_command, + '--plugin=protoc-gen-python-grpc=%s' % protoc_plugin_filename, + '-I .', + '--python_out=%s' % self.outdir, + '--python-grpc_out=%s' % self.outdir, + os.path.basename(test_proto_filename), + ] + subprocess.check_call(' '.join(cmd), shell=True, env=os.environ, + cwd=os.path.dirname(test_proto_filename)) + sys.path.append(self.outdir) + + def tearDown(self): + try: + shutil.rmtree(self.outdir) + except OSError as exc: + if exc.errno != errno.ENOENT: + raise + + # TODO(atash): Figure out which of these tests is hanging flakily with small + # probability. + + def testImportAttributes(self): + # check that we can access the generated module and its members. + import test_pb2 # pylint: disable=g-import-not-at-top + self.assertIsNotNone(getattr(test_pb2, SERVICER_IDENTIFIER, None)) + self.assertIsNotNone(getattr(test_pb2, SERVER_IDENTIFIER, None)) + self.assertIsNotNone(getattr(test_pb2, STUB_IDENTIFIER, None)) + self.assertIsNotNone(getattr(test_pb2, SERVER_FACTORY_IDENTIFIER, None)) + self.assertIsNotNone(getattr(test_pb2, STUB_FACTORY_IDENTIFIER, None)) + + def testUpDown(self): + import test_pb2 + with _CreateService( + test_pb2, NO_DELAY) as (servicer, stub, unused_server): + request = test_pb2.SimpleRequest(response_size=13) + + def testUnaryCall(self): + import test_pb2 # pylint: disable=g-import-not-at-top + with _CreateService(test_pb2, NO_DELAY) as (methods, stub, unused_server): + timeout = 6 # TODO(issue 2039): LONG_TIMEOUT like the other methods. + request = test_pb2.SimpleRequest(response_size=13) + response = stub.UnaryCall(request, timeout) + expected_response = methods.UnaryCall(request, 'not a real RpcContext!') + self.assertEqual(expected_response, response) + + def testUnaryCallAsync(self): + import test_pb2 # pylint: disable=g-import-not-at-top + request = test_pb2.SimpleRequest(response_size=13) + with _CreateService(test_pb2, NO_DELAY) as ( + methods, stub, unused_server): + # Check that the call does not block waiting for the server to respond. + with methods.pause(): + response_future = stub.UnaryCall.async(request, LONG_TIMEOUT) + response = response_future.result() + expected_response = methods.UnaryCall(request, 'not a real RpcContext!') + self.assertEqual(expected_response, response) + + def testUnaryCallAsyncExpired(self): + import test_pb2 # pylint: disable=g-import-not-at-top + with _CreateService(test_pb2, NO_DELAY) as ( + methods, stub, unused_server): + request = test_pb2.SimpleRequest(response_size=13) + with methods.pause(): + response_future = stub.UnaryCall.async(request, SHORT_TIMEOUT) + with self.assertRaises(exceptions.ExpirationError): + response_future.result() + + @unittest.skip('TODO(atash,nathaniel): figure out why this flakily hangs ' + 'forever and fix.') + def testUnaryCallAsyncCancelled(self): + import test_pb2 # pylint: disable=g-import-not-at-top + request = test_pb2.SimpleRequest(response_size=13) + with _CreateService(test_pb2, NO_DELAY) as ( + methods, stub, unused_server): + with methods.pause(): + response_future = stub.UnaryCall.async(request, 1) + response_future.cancel() + self.assertTrue(response_future.cancelled()) + + def testUnaryCallAsyncFailed(self): + import test_pb2 # pylint: disable=g-import-not-at-top + request = test_pb2.SimpleRequest(response_size=13) + with _CreateService(test_pb2, NO_DELAY) as ( + methods, stub, unused_server): + with methods.fail(): + response_future = stub.UnaryCall.async(request, LONG_TIMEOUT) + self.assertIsNotNone(response_future.exception()) + + def testStreamingOutputCall(self): + import test_pb2 # pylint: disable=g-import-not-at-top + request = _streaming_output_request(test_pb2) + with _CreateService(test_pb2, NO_DELAY) as (methods, stub, unused_server): + responses = stub.StreamingOutputCall(request, LONG_TIMEOUT) + expected_responses = methods.StreamingOutputCall( + request, 'not a real RpcContext!') + for expected_response, response in itertools.izip_longest( + expected_responses, responses): + self.assertEqual(expected_response, response) + + @unittest.skip('TODO(atash,nathaniel): figure out why this flakily hangs ' + 'forever and fix.') + def testStreamingOutputCallExpired(self): + import test_pb2 # pylint: disable=g-import-not-at-top + request = _streaming_output_request(test_pb2) + with _CreateService(test_pb2, NO_DELAY) as ( + methods, stub, unused_server): + with methods.pause(): + responses = stub.StreamingOutputCall(request, SHORT_TIMEOUT) + with self.assertRaises(exceptions.ExpirationError): + list(responses) + + @unittest.skip('TODO(atash,nathaniel): figure out why this flakily hangs ' + 'forever and fix.') + def testStreamingOutputCallCancelled(self): + import test_pb2 # pylint: disable=g-import-not-at-top + request = _streaming_output_request(test_pb2) + with _CreateService(test_pb2, NO_DELAY) as ( + unused_methods, stub, unused_server): + responses = stub.StreamingOutputCall(request, SHORT_TIMEOUT) + next(responses) + responses.cancel() + with self.assertRaises(future.CancelledError): + next(responses) + + @unittest.skip('TODO(atash,nathaniel): figure out why this times out ' + 'instead of raising the proper error.') + def testStreamingOutputCallFailed(self): + import test_pb2 # pylint: disable=g-import-not-at-top + request = _streaming_output_request(test_pb2) + with _CreateService(test_pb2, NO_DELAY) as ( + methods, stub, unused_server): + with methods.fail(): + responses = stub.StreamingOutputCall(request, 1) + self.assertIsNotNone(responses) + with self.assertRaises(exceptions.ServicerError): + next(responses) + + @unittest.skip('TODO(atash,nathaniel): figure out why this flakily hangs ' + 'forever and fix.') + def testStreamingInputCall(self): + import test_pb2 # pylint: disable=g-import-not-at-top + with _CreateService(test_pb2, NO_DELAY) as (methods, stub, unused_server): + response = stub.StreamingInputCall( + _streaming_input_request_iterator(test_pb2), LONG_TIMEOUT) + expected_response = methods.StreamingInputCall( + _streaming_input_request_iterator(test_pb2), 'not a real RpcContext!') + self.assertEqual(expected_response, response) + + def testStreamingInputCallAsync(self): + import test_pb2 # pylint: disable=g-import-not-at-top + with _CreateService(test_pb2, NO_DELAY) as ( + methods, stub, unused_server): + with methods.pause(): + response_future = stub.StreamingInputCall.async( + _streaming_input_request_iterator(test_pb2), LONG_TIMEOUT) + response = response_future.result() + expected_response = methods.StreamingInputCall( + _streaming_input_request_iterator(test_pb2), 'not a real RpcContext!') + self.assertEqual(expected_response, response) + + def testStreamingInputCallAsyncExpired(self): + import test_pb2 # pylint: disable=g-import-not-at-top + with _CreateService(test_pb2, NO_DELAY) as ( + methods, stub, unused_server): + with methods.pause(): + response_future = stub.StreamingInputCall.async( + _streaming_input_request_iterator(test_pb2), SHORT_TIMEOUT) + with self.assertRaises(exceptions.ExpirationError): + response_future.result() + self.assertIsInstance( + response_future.exception(), exceptions.ExpirationError) + + def testStreamingInputCallAsyncCancelled(self): + import test_pb2 # pylint: disable=g-import-not-at-top + with _CreateService(test_pb2, NO_DELAY) as ( + methods, stub, unused_server): + with methods.pause(): + timeout = 6 # TODO(issue 2039): LONG_TIMEOUT like the other methods. + response_future = stub.StreamingInputCall.async( + _streaming_input_request_iterator(test_pb2), timeout) + response_future.cancel() + self.assertTrue(response_future.cancelled()) + with self.assertRaises(future.CancelledError): + response_future.result() + + def testStreamingInputCallAsyncFailed(self): + import test_pb2 # pylint: disable=g-import-not-at-top + with _CreateService(test_pb2, NO_DELAY) as ( + methods, stub, unused_server): + with methods.fail(): + response_future = stub.StreamingInputCall.async( + _streaming_input_request_iterator(test_pb2), SHORT_TIMEOUT) + self.assertIsNotNone(response_future.exception()) + + def testFullDuplexCall(self): + import test_pb2 # pylint: disable=g-import-not-at-top + with _CreateService(test_pb2, NO_DELAY) as (methods, stub, unused_server): + responses = stub.FullDuplexCall( + _full_duplex_request_iterator(test_pb2), LONG_TIMEOUT) + expected_responses = methods.FullDuplexCall( + _full_duplex_request_iterator(test_pb2), 'not a real RpcContext!') + for expected_response, response in itertools.izip_longest( + expected_responses, responses): + self.assertEqual(expected_response, response) + + @unittest.skip('TODO(atash,nathaniel): figure out why this flakily hangs ' + 'forever and fix.') + def testFullDuplexCallExpired(self): + import test_pb2 # pylint: disable=g-import-not-at-top + request_iterator = _full_duplex_request_iterator(test_pb2) + with _CreateService(test_pb2, NO_DELAY) as ( + methods, stub, unused_server): + with methods.pause(): + responses = stub.FullDuplexCall(request_iterator, SHORT_TIMEOUT) + with self.assertRaises(exceptions.ExpirationError): + list(responses) + + @unittest.skip('TODO(atash,nathaniel): figure out why this flakily hangs ' + 'forever and fix.') + def testFullDuplexCallCancelled(self): + import test_pb2 # pylint: disable=g-import-not-at-top + with _CreateService(test_pb2, NO_DELAY) as (methods, stub, unused_server): + request_iterator = _full_duplex_request_iterator(test_pb2) + responses = stub.FullDuplexCall(request_iterator, LONG_TIMEOUT) + next(responses) + responses.cancel() + with self.assertRaises(future.CancelledError): + next(responses) + + @unittest.skip('TODO(atash,nathaniel): figure out why this hangs forever ' + 'and fix.') + def testFullDuplexCallFailed(self): + import test_pb2 # pylint: disable=g-import-not-at-top + request_iterator = _full_duplex_request_iterator(test_pb2) + with _CreateService(test_pb2, NO_DELAY) as ( + methods, stub, unused_server): + with methods.fail(): + responses = stub.FullDuplexCall(request_iterator, LONG_TIMEOUT) + self.assertIsNotNone(responses) + with self.assertRaises(exceptions.ServicerError): + next(responses) + + @unittest.skip('TODO(atash,nathaniel): figure out why this flakily hangs ' + 'forever and fix.') + def testHalfDuplexCall(self): + import test_pb2 # pylint: disable=g-import-not-at-top + with _CreateService(test_pb2, NO_DELAY) as ( + methods, stub, unused_server): + def half_duplex_request_iterator(): + request = test_pb2.StreamingOutputCallRequest() + request.response_parameters.add(size=1, interval_us=0) + yield request + request = test_pb2.StreamingOutputCallRequest() + request.response_parameters.add(size=2, interval_us=0) + request.response_parameters.add(size=3, interval_us=0) + yield request + responses = stub.HalfDuplexCall( + half_duplex_request_iterator(), LONG_TIMEOUT) + expected_responses = methods.HalfDuplexCall( + half_duplex_request_iterator(), 'not a real RpcContext!') + for check in itertools.izip_longest(expected_responses, responses): + expected_response, response = check + self.assertEqual(expected_response, response) + + def testHalfDuplexCallWedged(self): + import test_pb2 # pylint: disable=g-import-not-at-top + condition = threading.Condition() + wait_cell = [False] + @contextlib.contextmanager + def wait(): # pylint: disable=invalid-name + # Where's Python 3's 'nonlocal' statement when you need it? + with condition: + wait_cell[0] = True + yield + with condition: + wait_cell[0] = False + condition.notify_all() + def half_duplex_request_iterator(): + request = test_pb2.StreamingOutputCallRequest() + request.response_parameters.add(size=1, interval_us=0) + yield request + with condition: + while wait_cell[0]: + condition.wait() + with _CreateService(test_pb2, NO_DELAY) as (methods, stub, unused_server): + with wait(): + responses = stub.HalfDuplexCall( + half_duplex_request_iterator(), SHORT_TIMEOUT) + # half-duplex waits for the client to send all info + with self.assertRaises(exceptions.ExpirationError): + next(responses) + + +if __name__ == '__main__': + os.chdir(os.path.dirname(sys.argv[0])) + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_protoc_plugin/beta_python_plugin_test.py b/src/python/grpcio_test/grpc_protoc_plugin/beta_python_plugin_test.py new file mode 100644 index 00000000..259b978d --- /dev/null +++ b/src/python/grpcio_test/grpc_protoc_plugin/beta_python_plugin_test.py @@ -0,0 +1,501 @@ +# Copyright 2015, 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. + +import argparse +import contextlib +import distutils.spawn +import errno +import itertools +import os +import pkg_resources +import shutil +import subprocess +import sys +import tempfile +import threading +import time +import unittest + +from grpc.beta import implementations +from grpc.framework.foundation import future +from grpc.framework.interfaces.face import face +from grpc_test.framework.common import test_constants + +# Identifiers of entities we expect to find in the generated module. +SERVICER_IDENTIFIER = 'BetaTestServiceServicer' +STUB_IDENTIFIER = 'BetaTestServiceStub' +SERVER_FACTORY_IDENTIFIER = 'beta_create_TestService_server' +STUB_FACTORY_IDENTIFIER = 'beta_create_TestService_stub' + + +class _ServicerMethods(object): + + def __init__(self, test_pb2): + self._condition = threading.Condition() + self._paused = False + self._fail = False + self._test_pb2 = test_pb2 + + @contextlib.contextmanager + def pause(self): # pylint: disable=invalid-name + with self._condition: + self._paused = True + yield + with self._condition: + self._paused = False + self._condition.notify_all() + + @contextlib.contextmanager + def fail(self): # pylint: disable=invalid-name + with self._condition: + self._fail = True + yield + with self._condition: + self._fail = False + + def _control(self): # pylint: disable=invalid-name + with self._condition: + if self._fail: + raise ValueError() + while self._paused: + self._condition.wait() + + def UnaryCall(self, request, unused_rpc_context): + response = self._test_pb2.SimpleResponse() + response.payload.payload_type = self._test_pb2.COMPRESSABLE + response.payload.payload_compressable = 'a' * request.response_size + self._control() + return response + + def StreamingOutputCall(self, request, unused_rpc_context): + for parameter in request.response_parameters: + response = self._test_pb2.StreamingOutputCallResponse() + response.payload.payload_type = self._test_pb2.COMPRESSABLE + response.payload.payload_compressable = 'a' * parameter.size + self._control() + yield response + + def StreamingInputCall(self, request_iter, unused_rpc_context): + response = self._test_pb2.StreamingInputCallResponse() + aggregated_payload_size = 0 + for request in request_iter: + aggregated_payload_size += len(request.payload.payload_compressable) + response.aggregated_payload_size = aggregated_payload_size + self._control() + return response + + def FullDuplexCall(self, request_iter, unused_rpc_context): + for request in request_iter: + for parameter in request.response_parameters: + response = self._test_pb2.StreamingOutputCallResponse() + response.payload.payload_type = self._test_pb2.COMPRESSABLE + response.payload.payload_compressable = 'a' * parameter.size + self._control() + yield response + + def HalfDuplexCall(self, request_iter, unused_rpc_context): + responses = [] + for request in request_iter: + for parameter in request.response_parameters: + response = self._test_pb2.StreamingOutputCallResponse() + response.payload.payload_type = self._test_pb2.COMPRESSABLE + response.payload.payload_compressable = 'a' * parameter.size + self._control() + responses.append(response) + for response in responses: + yield response + + +@contextlib.contextmanager +def _CreateService(test_pb2): + """Provides a servicer backend and a stub. + + The servicer is just the implementation of the actual servicer passed to the + face player of the python RPC implementation; the two are detached. + + Args: + test_pb2: The test_pb2 module generated by this test. + + Yields: + A (servicer_methods, stub) pair where servicer_methods is the back-end of + the service bound to the stub and and stub is the stub on which to invoke + RPCs. + """ + servicer_methods = _ServicerMethods(test_pb2) + + class Servicer(getattr(test_pb2, SERVICER_IDENTIFIER)): + + def UnaryCall(self, request, context): + return servicer_methods.UnaryCall(request, context) + + def StreamingOutputCall(self, request, context): + return servicer_methods.StreamingOutputCall(request, context) + + def StreamingInputCall(self, request_iter, context): + return servicer_methods.StreamingInputCall(request_iter, context) + + def FullDuplexCall(self, request_iter, context): + return servicer_methods.FullDuplexCall(request_iter, context) + + def HalfDuplexCall(self, request_iter, context): + return servicer_methods.HalfDuplexCall(request_iter, context) + + servicer = Servicer() + server = getattr(test_pb2, SERVER_FACTORY_IDENTIFIER)(servicer) + port = server.add_insecure_port('[::]:0') + server.start() + channel = implementations.insecure_channel('localhost', port) + stub = getattr(test_pb2, STUB_FACTORY_IDENTIFIER)(channel) + yield servicer_methods, stub + server.stop(0) + + +def _streaming_input_request_iterator(test_pb2): + for _ in range(3): + request = test_pb2.StreamingInputCallRequest() + request.payload.payload_type = test_pb2.COMPRESSABLE + request.payload.payload_compressable = 'a' + yield request + + +def _streaming_output_request(test_pb2): + request = test_pb2.StreamingOutputCallRequest() + sizes = [1, 2, 3] + request.response_parameters.add(size=sizes[0], interval_us=0) + request.response_parameters.add(size=sizes[1], interval_us=0) + request.response_parameters.add(size=sizes[2], interval_us=0) + return request + + +def _full_duplex_request_iterator(test_pb2): + request = test_pb2.StreamingOutputCallRequest() + request.response_parameters.add(size=1, interval_us=0) + yield request + request = test_pb2.StreamingOutputCallRequest() + request.response_parameters.add(size=2, interval_us=0) + request.response_parameters.add(size=3, interval_us=0) + yield request + + +class PythonPluginTest(unittest.TestCase): + """Test case for the gRPC Python protoc-plugin. + + While reading these tests, remember that the futures API + (`stub.method.future()`) only gives futures for the *response-unary* + methods and does not exist for response-streaming methods. + """ + + def setUp(self): + # Assume that the appropriate protoc and grpc_python_plugins are on the + # path. + protoc_command = 'protoc' + protoc_plugin_filename = distutils.spawn.find_executable( + 'grpc_python_plugin') + test_proto_filename = pkg_resources.resource_filename( + 'grpc_protoc_plugin', 'test.proto') + if not os.path.isfile(protoc_command): + # Assume that if we haven't built protoc that it's on the system. + protoc_command = 'protoc' + + # Ensure that the output directory exists. + self.outdir = tempfile.mkdtemp() + + # Invoke protoc with the plugin. + cmd = [ + protoc_command, + '--plugin=protoc-gen-python-grpc=%s' % protoc_plugin_filename, + '-I .', + '--python_out=%s' % self.outdir, + '--python-grpc_out=%s' % self.outdir, + os.path.basename(test_proto_filename), + ] + subprocess.check_call(' '.join(cmd), shell=True, env=os.environ, + cwd=os.path.dirname(test_proto_filename)) + sys.path.append(self.outdir) + + def tearDown(self): + try: + shutil.rmtree(self.outdir) + except OSError as exc: + if exc.errno != errno.ENOENT: + raise + + def testImportAttributes(self): + # check that we can access the generated module and its members. + import test_pb2 # pylint: disable=g-import-not-at-top + self.assertIsNotNone(getattr(test_pb2, SERVICER_IDENTIFIER, None)) + self.assertIsNotNone(getattr(test_pb2, STUB_IDENTIFIER, None)) + self.assertIsNotNone(getattr(test_pb2, SERVER_FACTORY_IDENTIFIER, None)) + self.assertIsNotNone(getattr(test_pb2, STUB_FACTORY_IDENTIFIER, None)) + + def testUpDown(self): + import test_pb2 + with _CreateService(test_pb2) as (servicer, stub): + request = test_pb2.SimpleRequest(response_size=13) + + def testUnaryCall(self): + import test_pb2 # pylint: disable=g-import-not-at-top + with _CreateService(test_pb2) as (methods, stub): + request = test_pb2.SimpleRequest(response_size=13) + response = stub.UnaryCall(request, test_constants.LONG_TIMEOUT) + expected_response = methods.UnaryCall(request, 'not a real context!') + self.assertEqual(expected_response, response) + + def testUnaryCallFuture(self): + import test_pb2 # pylint: disable=g-import-not-at-top + request = test_pb2.SimpleRequest(response_size=13) + with _CreateService(test_pb2) as (methods, stub): + # Check that the call does not block waiting for the server to respond. + with methods.pause(): + response_future = stub.UnaryCall.future( + request, test_constants.LONG_TIMEOUT) + response = response_future.result() + expected_response = methods.UnaryCall(request, 'not a real RpcContext!') + self.assertEqual(expected_response, response) + + def testUnaryCallFutureExpired(self): + import test_pb2 # pylint: disable=g-import-not-at-top + with _CreateService(test_pb2) as (methods, stub): + request = test_pb2.SimpleRequest(response_size=13) + with methods.pause(): + response_future = stub.UnaryCall.future( + request, test_constants.SHORT_TIMEOUT) + with self.assertRaises(face.ExpirationError): + response_future.result() + + def testUnaryCallFutureCancelled(self): + import test_pb2 # pylint: disable=g-import-not-at-top + request = test_pb2.SimpleRequest(response_size=13) + with _CreateService(test_pb2) as (methods, stub): + with methods.pause(): + response_future = stub.UnaryCall.future(request, 1) + response_future.cancel() + self.assertTrue(response_future.cancelled()) + + def testUnaryCallFutureFailed(self): + import test_pb2 # pylint: disable=g-import-not-at-top + request = test_pb2.SimpleRequest(response_size=13) + with _CreateService(test_pb2) as (methods, stub): + with methods.fail(): + response_future = stub.UnaryCall.future( + request, test_constants.LONG_TIMEOUT) + self.assertIsNotNone(response_future.exception()) + + def testStreamingOutputCall(self): + import test_pb2 # pylint: disable=g-import-not-at-top + request = _streaming_output_request(test_pb2) + with _CreateService(test_pb2) as (methods, stub): + responses = stub.StreamingOutputCall( + request, test_constants.LONG_TIMEOUT) + expected_responses = methods.StreamingOutputCall( + request, 'not a real RpcContext!') + for expected_response, response in itertools.izip_longest( + expected_responses, responses): + self.assertEqual(expected_response, response) + + def testStreamingOutputCallExpired(self): + import test_pb2 # pylint: disable=g-import-not-at-top + request = _streaming_output_request(test_pb2) + with _CreateService(test_pb2) as (methods, stub): + with methods.pause(): + responses = stub.StreamingOutputCall( + request, test_constants.SHORT_TIMEOUT) + with self.assertRaises(face.ExpirationError): + list(responses) + + def testStreamingOutputCallCancelled(self): + import test_pb2 # pylint: disable=g-import-not-at-top + request = _streaming_output_request(test_pb2) + with _CreateService(test_pb2) as (unused_methods, stub): + responses = stub.StreamingOutputCall( + request, test_constants.LONG_TIMEOUT) + next(responses) + responses.cancel() + with self.assertRaises(face.CancellationError): + next(responses) + + def testStreamingOutputCallFailed(self): + import test_pb2 # pylint: disable=g-import-not-at-top + request = _streaming_output_request(test_pb2) + with _CreateService(test_pb2) as (methods, stub): + with methods.fail(): + responses = stub.StreamingOutputCall(request, 1) + self.assertIsNotNone(responses) + with self.assertRaises(face.RemoteError): + next(responses) + + def testStreamingInputCall(self): + import test_pb2 # pylint: disable=g-import-not-at-top + with _CreateService(test_pb2) as (methods, stub): + response = stub.StreamingInputCall( + _streaming_input_request_iterator(test_pb2), + test_constants.LONG_TIMEOUT) + expected_response = methods.StreamingInputCall( + _streaming_input_request_iterator(test_pb2), 'not a real RpcContext!') + self.assertEqual(expected_response, response) + + def testStreamingInputCallFuture(self): + import test_pb2 # pylint: disable=g-import-not-at-top + with _CreateService(test_pb2) as (methods, stub): + with methods.pause(): + response_future = stub.StreamingInputCall.future( + _streaming_input_request_iterator(test_pb2), + test_constants.LONG_TIMEOUT) + response = response_future.result() + expected_response = methods.StreamingInputCall( + _streaming_input_request_iterator(test_pb2), 'not a real RpcContext!') + self.assertEqual(expected_response, response) + + def testStreamingInputCallFutureExpired(self): + import test_pb2 # pylint: disable=g-import-not-at-top + with _CreateService(test_pb2) as (methods, stub): + with methods.pause(): + response_future = stub.StreamingInputCall.future( + _streaming_input_request_iterator(test_pb2), + test_constants.SHORT_TIMEOUT) + with self.assertRaises(face.ExpirationError): + response_future.result() + self.assertIsInstance( + response_future.exception(), face.ExpirationError) + + def testStreamingInputCallFutureCancelled(self): + import test_pb2 # pylint: disable=g-import-not-at-top + with _CreateService(test_pb2) as (methods, stub): + with methods.pause(): + response_future = stub.StreamingInputCall.future( + _streaming_input_request_iterator(test_pb2), + test_constants.LONG_TIMEOUT) + response_future.cancel() + self.assertTrue(response_future.cancelled()) + with self.assertRaises(future.CancelledError): + response_future.result() + + def testStreamingInputCallFutureFailed(self): + import test_pb2 # pylint: disable=g-import-not-at-top + with _CreateService(test_pb2) as (methods, stub): + with methods.fail(): + response_future = stub.StreamingInputCall.future( + _streaming_input_request_iterator(test_pb2), + test_constants.LONG_TIMEOUT) + self.assertIsNotNone(response_future.exception()) + + def testFullDuplexCall(self): + import test_pb2 # pylint: disable=g-import-not-at-top + with _CreateService(test_pb2) as (methods, stub): + responses = stub.FullDuplexCall( + _full_duplex_request_iterator(test_pb2), test_constants.LONG_TIMEOUT) + expected_responses = methods.FullDuplexCall( + _full_duplex_request_iterator(test_pb2), 'not a real RpcContext!') + for expected_response, response in itertools.izip_longest( + expected_responses, responses): + self.assertEqual(expected_response, response) + + def testFullDuplexCallExpired(self): + import test_pb2 # pylint: disable=g-import-not-at-top + request_iterator = _full_duplex_request_iterator(test_pb2) + with _CreateService(test_pb2) as (methods, stub): + with methods.pause(): + responses = stub.FullDuplexCall( + request_iterator, test_constants.SHORT_TIMEOUT) + with self.assertRaises(face.ExpirationError): + list(responses) + + def testFullDuplexCallCancelled(self): + import test_pb2 # pylint: disable=g-import-not-at-top + with _CreateService(test_pb2) as (methods, stub): + request_iterator = _full_duplex_request_iterator(test_pb2) + responses = stub.FullDuplexCall( + request_iterator, test_constants.LONG_TIMEOUT) + next(responses) + responses.cancel() + with self.assertRaises(face.CancellationError): + next(responses) + + def testFullDuplexCallFailed(self): + import test_pb2 # pylint: disable=g-import-not-at-top + request_iterator = _full_duplex_request_iterator(test_pb2) + with _CreateService(test_pb2) as (methods, stub): + with methods.fail(): + responses = stub.FullDuplexCall( + request_iterator, test_constants.LONG_TIMEOUT) + self.assertIsNotNone(responses) + with self.assertRaises(face.RemoteError): + next(responses) + + def testHalfDuplexCall(self): + import test_pb2 # pylint: disable=g-import-not-at-top + with _CreateService(test_pb2) as (methods, stub): + def half_duplex_request_iterator(): + request = test_pb2.StreamingOutputCallRequest() + request.response_parameters.add(size=1, interval_us=0) + yield request + request = test_pb2.StreamingOutputCallRequest() + request.response_parameters.add(size=2, interval_us=0) + request.response_parameters.add(size=3, interval_us=0) + yield request + responses = stub.HalfDuplexCall( + half_duplex_request_iterator(), test_constants.LONG_TIMEOUT) + expected_responses = methods.HalfDuplexCall( + half_duplex_request_iterator(), 'not a real RpcContext!') + for check in itertools.izip_longest(expected_responses, responses): + expected_response, response = check + self.assertEqual(expected_response, response) + + def testHalfDuplexCallWedged(self): + import test_pb2 # pylint: disable=g-import-not-at-top + condition = threading.Condition() + wait_cell = [False] + @contextlib.contextmanager + def wait(): # pylint: disable=invalid-name + # Where's Python 3's 'nonlocal' statement when you need it? + with condition: + wait_cell[0] = True + yield + with condition: + wait_cell[0] = False + condition.notify_all() + def half_duplex_request_iterator(): + request = test_pb2.StreamingOutputCallRequest() + request.response_parameters.add(size=1, interval_us=0) + yield request + with condition: + while wait_cell[0]: + condition.wait() + with _CreateService(test_pb2) as (methods, stub): + with wait(): + responses = stub.HalfDuplexCall( + half_duplex_request_iterator(), test_constants.SHORT_TIMEOUT) + # half-duplex waits for the client to send all info + with self.assertRaises(face.ExpirationError): + next(responses) + + +if __name__ == '__main__': + os.chdir(os.path.dirname(sys.argv[0])) + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_protoc_plugin/python_plugin_test.py b/src/python/grpcio_test/grpc_protoc_plugin/python_plugin_test.py new file mode 100644 index 00000000..b200d129 --- /dev/null +++ b/src/python/grpcio_test/grpc_protoc_plugin/python_plugin_test.py @@ -0,0 +1,541 @@ +# Copyright 2015, 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. + +import argparse +import contextlib +import distutils.spawn +import errno +import itertools +import os +import pkg_resources +import shutil +import subprocess +import sys +import tempfile +import threading +import time +import unittest + +from grpc.framework.alpha import exceptions +from grpc.framework.foundation import future + +# Identifiers of entities we expect to find in the generated module. +SERVICER_IDENTIFIER = 'EarlyAdopterTestServiceServicer' +SERVER_IDENTIFIER = 'EarlyAdopterTestServiceServer' +STUB_IDENTIFIER = 'EarlyAdopterTestServiceStub' +SERVER_FACTORY_IDENTIFIER = 'early_adopter_create_TestService_server' +STUB_FACTORY_IDENTIFIER = 'early_adopter_create_TestService_stub' + +# The timeout used in tests of RPCs that are supposed to expire. +SHORT_TIMEOUT = 2 +# The timeout used in tests of RPCs that are not supposed to expire. The +# absurdly large value doesn't matter since no passing execution of this test +# module will ever wait the duration. +LONG_TIMEOUT = 600 +NO_DELAY = 0 + + +class _ServicerMethods(object): + + def __init__(self, test_pb2, delay): + self._condition = threading.Condition() + self._delay = delay + self._paused = False + self._fail = False + self._test_pb2 = test_pb2 + + @contextlib.contextmanager + def pause(self): # pylint: disable=invalid-name + with self._condition: + self._paused = True + yield + with self._condition: + self._paused = False + self._condition.notify_all() + + @contextlib.contextmanager + def fail(self): # pylint: disable=invalid-name + with self._condition: + self._fail = True + yield + with self._condition: + self._fail = False + + def _control(self): # pylint: disable=invalid-name + with self._condition: + if self._fail: + raise ValueError() + while self._paused: + self._condition.wait() + time.sleep(self._delay) + + def UnaryCall(self, request, unused_rpc_context): + response = self._test_pb2.SimpleResponse() + response.payload.payload_type = self._test_pb2.COMPRESSABLE + response.payload.payload_compressable = 'a' * request.response_size + self._control() + return response + + def StreamingOutputCall(self, request, unused_rpc_context): + for parameter in request.response_parameters: + response = self._test_pb2.StreamingOutputCallResponse() + response.payload.payload_type = self._test_pb2.COMPRESSABLE + response.payload.payload_compressable = 'a' * parameter.size + self._control() + yield response + + def StreamingInputCall(self, request_iter, unused_rpc_context): + response = self._test_pb2.StreamingInputCallResponse() + aggregated_payload_size = 0 + for request in request_iter: + aggregated_payload_size += len(request.payload.payload_compressable) + response.aggregated_payload_size = aggregated_payload_size + self._control() + return response + + def FullDuplexCall(self, request_iter, unused_rpc_context): + for request in request_iter: + for parameter in request.response_parameters: + response = self._test_pb2.StreamingOutputCallResponse() + response.payload.payload_type = self._test_pb2.COMPRESSABLE + response.payload.payload_compressable = 'a' * parameter.size + self._control() + yield response + + def HalfDuplexCall(self, request_iter, unused_rpc_context): + responses = [] + for request in request_iter: + for parameter in request.response_parameters: + response = self._test_pb2.StreamingOutputCallResponse() + response.payload.payload_type = self._test_pb2.COMPRESSABLE + response.payload.payload_compressable = 'a' * parameter.size + self._control() + responses.append(response) + for response in responses: + yield response + + +@contextlib.contextmanager +def _CreateService(test_pb2, delay): + """Provides a servicer backend and a stub. + + The servicer is just the implementation + of the actual servicer passed to the face player of the python RPC + implementation; the two are detached. + + Non-zero delay puts a delay on each call to the servicer, representative of + communication latency. Timeout is the default timeout for the stub while + waiting for the service. + + Args: + test_pb2: The test_pb2 module generated by this test. + delay: Delay in seconds per response from the servicer. + + Yields: + A (servicer_methods, servicer, stub) three-tuple where servicer_methods is + the back-end of the service bound to the stub and the server and stub + are both activated and ready for use. + """ + servicer_methods = _ServicerMethods(test_pb2, delay) + + class Servicer(getattr(test_pb2, SERVICER_IDENTIFIER)): + + def UnaryCall(self, request, context): + return servicer_methods.UnaryCall(request, context) + + def StreamingOutputCall(self, request, context): + return servicer_methods.StreamingOutputCall(request, context) + + def StreamingInputCall(self, request_iter, context): + return servicer_methods.StreamingInputCall(request_iter, context) + + def FullDuplexCall(self, request_iter, context): + return servicer_methods.FullDuplexCall(request_iter, context) + + def HalfDuplexCall(self, request_iter, context): + return servicer_methods.HalfDuplexCall(request_iter, context) + + servicer = Servicer() + server = getattr( + test_pb2, SERVER_FACTORY_IDENTIFIER)(servicer, 0) + with server: + port = server.port() + stub = getattr(test_pb2, STUB_FACTORY_IDENTIFIER)('localhost', port) + with stub: + yield servicer_methods, stub, server + + +def _streaming_input_request_iterator(test_pb2): + for _ in range(3): + request = test_pb2.StreamingInputCallRequest() + request.payload.payload_type = test_pb2.COMPRESSABLE + request.payload.payload_compressable = 'a' + yield request + + +def _streaming_output_request(test_pb2): + request = test_pb2.StreamingOutputCallRequest() + sizes = [1, 2, 3] + request.response_parameters.add(size=sizes[0], interval_us=0) + request.response_parameters.add(size=sizes[1], interval_us=0) + request.response_parameters.add(size=sizes[2], interval_us=0) + return request + + +def _full_duplex_request_iterator(test_pb2): + request = test_pb2.StreamingOutputCallRequest() + request.response_parameters.add(size=1, interval_us=0) + yield request + request = test_pb2.StreamingOutputCallRequest() + request.response_parameters.add(size=2, interval_us=0) + request.response_parameters.add(size=3, interval_us=0) + yield request + + +class PythonPluginTest(unittest.TestCase): + """Test case for the gRPC Python protoc-plugin. + + While reading these tests, remember that the futures API + (`stub.method.async()`) only gives futures for the *non-streaming* responses, + else it behaves like its blocking cousin. + """ + + def setUp(self): + # Assume that the appropriate protoc and grpc_python_plugins are on the + # path. + protoc_command = 'protoc' + protoc_plugin_filename = distutils.spawn.find_executable( + 'grpc_python_plugin') + test_proto_filename = pkg_resources.resource_filename( + 'grpc_protoc_plugin', 'test.proto') + if not os.path.isfile(protoc_command): + # Assume that if we haven't built protoc that it's on the system. + protoc_command = 'protoc' + + # Ensure that the output directory exists. + self.outdir = tempfile.mkdtemp() + + # Invoke protoc with the plugin. + cmd = [ + protoc_command, + '--plugin=protoc-gen-python-grpc=%s' % protoc_plugin_filename, + '-I .', + '--python_out=%s' % self.outdir, + '--python-grpc_out=%s' % self.outdir, + os.path.basename(test_proto_filename), + ] + subprocess.check_call(' '.join(cmd), shell=True, env=os.environ, + cwd=os.path.dirname(test_proto_filename)) + sys.path.append(self.outdir) + + def tearDown(self): + try: + shutil.rmtree(self.outdir) + except OSError as exc: + if exc.errno != errno.ENOENT: + raise + + # TODO(atash): Figure out which of these tests is hanging flakily with small + # probability. + + def testImportAttributes(self): + # check that we can access the generated module and its members. + import test_pb2 # pylint: disable=g-import-not-at-top + self.assertIsNotNone(getattr(test_pb2, SERVICER_IDENTIFIER, None)) + self.assertIsNotNone(getattr(test_pb2, SERVER_IDENTIFIER, None)) + self.assertIsNotNone(getattr(test_pb2, STUB_IDENTIFIER, None)) + self.assertIsNotNone(getattr(test_pb2, SERVER_FACTORY_IDENTIFIER, None)) + self.assertIsNotNone(getattr(test_pb2, STUB_FACTORY_IDENTIFIER, None)) + + def testUpDown(self): + import test_pb2 + with _CreateService( + test_pb2, NO_DELAY) as (servicer, stub, unused_server): + request = test_pb2.SimpleRequest(response_size=13) + + def testUnaryCall(self): + import test_pb2 # pylint: disable=g-import-not-at-top + with _CreateService(test_pb2, NO_DELAY) as (methods, stub, unused_server): + timeout = 6 # TODO(issue 2039): LONG_TIMEOUT like the other methods. + request = test_pb2.SimpleRequest(response_size=13) + response = stub.UnaryCall(request, timeout) + expected_response = methods.UnaryCall(request, 'not a real RpcContext!') + self.assertEqual(expected_response, response) + + def testUnaryCallAsync(self): + import test_pb2 # pylint: disable=g-import-not-at-top + request = test_pb2.SimpleRequest(response_size=13) + with _CreateService(test_pb2, NO_DELAY) as ( + methods, stub, unused_server): + # Check that the call does not block waiting for the server to respond. + with methods.pause(): + response_future = stub.UnaryCall.async(request, LONG_TIMEOUT) + response = response_future.result() + expected_response = methods.UnaryCall(request, 'not a real RpcContext!') + self.assertEqual(expected_response, response) + + def testUnaryCallAsyncExpired(self): + import test_pb2 # pylint: disable=g-import-not-at-top + with _CreateService(test_pb2, NO_DELAY) as ( + methods, stub, unused_server): + request = test_pb2.SimpleRequest(response_size=13) + with methods.pause(): + response_future = stub.UnaryCall.async(request, SHORT_TIMEOUT) + with self.assertRaises(exceptions.ExpirationError): + response_future.result() + + @unittest.skip('TODO(atash,nathaniel): figure out why this flakily hangs ' + 'forever and fix.') + def testUnaryCallAsyncCancelled(self): + import test_pb2 # pylint: disable=g-import-not-at-top + request = test_pb2.SimpleRequest(response_size=13) + with _CreateService(test_pb2, NO_DELAY) as ( + methods, stub, unused_server): + with methods.pause(): + response_future = stub.UnaryCall.async(request, 1) + response_future.cancel() + self.assertTrue(response_future.cancelled()) + + def testUnaryCallAsyncFailed(self): + import test_pb2 # pylint: disable=g-import-not-at-top + request = test_pb2.SimpleRequest(response_size=13) + with _CreateService(test_pb2, NO_DELAY) as ( + methods, stub, unused_server): + with methods.fail(): + response_future = stub.UnaryCall.async(request, LONG_TIMEOUT) + self.assertIsNotNone(response_future.exception()) + + def testStreamingOutputCall(self): + import test_pb2 # pylint: disable=g-import-not-at-top + request = _streaming_output_request(test_pb2) + with _CreateService(test_pb2, NO_DELAY) as (methods, stub, unused_server): + responses = stub.StreamingOutputCall(request, LONG_TIMEOUT) + expected_responses = methods.StreamingOutputCall( + request, 'not a real RpcContext!') + for expected_response, response in itertools.izip_longest( + expected_responses, responses): + self.assertEqual(expected_response, response) + + @unittest.skip('TODO(atash,nathaniel): figure out why this flakily hangs ' + 'forever and fix.') + def testStreamingOutputCallExpired(self): + import test_pb2 # pylint: disable=g-import-not-at-top + request = _streaming_output_request(test_pb2) + with _CreateService(test_pb2, NO_DELAY) as ( + methods, stub, unused_server): + with methods.pause(): + responses = stub.StreamingOutputCall(request, SHORT_TIMEOUT) + with self.assertRaises(exceptions.ExpirationError): + list(responses) + + @unittest.skip('TODO(atash,nathaniel): figure out why this flakily hangs ' + 'forever and fix.') + def testStreamingOutputCallCancelled(self): + import test_pb2 # pylint: disable=g-import-not-at-top + request = _streaming_output_request(test_pb2) + with _CreateService(test_pb2, NO_DELAY) as ( + unused_methods, stub, unused_server): + responses = stub.StreamingOutputCall(request, SHORT_TIMEOUT) + next(responses) + responses.cancel() + with self.assertRaises(future.CancelledError): + next(responses) + + @unittest.skip('TODO(atash,nathaniel): figure out why this times out ' + 'instead of raising the proper error.') + def testStreamingOutputCallFailed(self): + import test_pb2 # pylint: disable=g-import-not-at-top + request = _streaming_output_request(test_pb2) + with _CreateService(test_pb2, NO_DELAY) as ( + methods, stub, unused_server): + with methods.fail(): + responses = stub.StreamingOutputCall(request, 1) + self.assertIsNotNone(responses) + with self.assertRaises(exceptions.ServicerError): + next(responses) + + @unittest.skip('TODO(atash,nathaniel): figure out why this flakily hangs ' + 'forever and fix.') + def testStreamingInputCall(self): + import test_pb2 # pylint: disable=g-import-not-at-top + with _CreateService(test_pb2, NO_DELAY) as (methods, stub, unused_server): + response = stub.StreamingInputCall( + _streaming_input_request_iterator(test_pb2), LONG_TIMEOUT) + expected_response = methods.StreamingInputCall( + _streaming_input_request_iterator(test_pb2), 'not a real RpcContext!') + self.assertEqual(expected_response, response) + + def testStreamingInputCallAsync(self): + import test_pb2 # pylint: disable=g-import-not-at-top + with _CreateService(test_pb2, NO_DELAY) as ( + methods, stub, unused_server): + with methods.pause(): + response_future = stub.StreamingInputCall.async( + _streaming_input_request_iterator(test_pb2), LONG_TIMEOUT) + response = response_future.result() + expected_response = methods.StreamingInputCall( + _streaming_input_request_iterator(test_pb2), 'not a real RpcContext!') + self.assertEqual(expected_response, response) + + def testStreamingInputCallAsyncExpired(self): + import test_pb2 # pylint: disable=g-import-not-at-top + with _CreateService(test_pb2, NO_DELAY) as ( + methods, stub, unused_server): + with methods.pause(): + response_future = stub.StreamingInputCall.async( + _streaming_input_request_iterator(test_pb2), SHORT_TIMEOUT) + with self.assertRaises(exceptions.ExpirationError): + response_future.result() + self.assertIsInstance( + response_future.exception(), exceptions.ExpirationError) + + def testStreamingInputCallAsyncCancelled(self): + import test_pb2 # pylint: disable=g-import-not-at-top + with _CreateService(test_pb2, NO_DELAY) as ( + methods, stub, unused_server): + with methods.pause(): + timeout = 6 # TODO(issue 2039): LONG_TIMEOUT like the other methods. + response_future = stub.StreamingInputCall.async( + _streaming_input_request_iterator(test_pb2), timeout) + response_future.cancel() + self.assertTrue(response_future.cancelled()) + with self.assertRaises(future.CancelledError): + response_future.result() + + def testStreamingInputCallAsyncFailed(self): + import test_pb2 # pylint: disable=g-import-not-at-top + with _CreateService(test_pb2, NO_DELAY) as ( + methods, stub, unused_server): + with methods.fail(): + response_future = stub.StreamingInputCall.async( + _streaming_input_request_iterator(test_pb2), SHORT_TIMEOUT) + self.assertIsNotNone(response_future.exception()) + + def testFullDuplexCall(self): + import test_pb2 # pylint: disable=g-import-not-at-top + with _CreateService(test_pb2, NO_DELAY) as (methods, stub, unused_server): + responses = stub.FullDuplexCall( + _full_duplex_request_iterator(test_pb2), LONG_TIMEOUT) + expected_responses = methods.FullDuplexCall( + _full_duplex_request_iterator(test_pb2), 'not a real RpcContext!') + for expected_response, response in itertools.izip_longest( + expected_responses, responses): + self.assertEqual(expected_response, response) + + @unittest.skip('TODO(atash,nathaniel): figure out why this flakily hangs ' + 'forever and fix.') + def testFullDuplexCallExpired(self): + import test_pb2 # pylint: disable=g-import-not-at-top + request_iterator = _full_duplex_request_iterator(test_pb2) + with _CreateService(test_pb2, NO_DELAY) as ( + methods, stub, unused_server): + with methods.pause(): + responses = stub.FullDuplexCall(request_iterator, SHORT_TIMEOUT) + with self.assertRaises(exceptions.ExpirationError): + list(responses) + + @unittest.skip('TODO(atash,nathaniel): figure out why this flakily hangs ' + 'forever and fix.') + def testFullDuplexCallCancelled(self): + import test_pb2 # pylint: disable=g-import-not-at-top + with _CreateService(test_pb2, NO_DELAY) as (methods, stub, unused_server): + request_iterator = _full_duplex_request_iterator(test_pb2) + responses = stub.FullDuplexCall(request_iterator, LONG_TIMEOUT) + next(responses) + responses.cancel() + with self.assertRaises(future.CancelledError): + next(responses) + + @unittest.skip('TODO(atash,nathaniel): figure out why this hangs forever ' + 'and fix.') + def testFullDuplexCallFailed(self): + import test_pb2 # pylint: disable=g-import-not-at-top + request_iterator = _full_duplex_request_iterator(test_pb2) + with _CreateService(test_pb2, NO_DELAY) as ( + methods, stub, unused_server): + with methods.fail(): + responses = stub.FullDuplexCall(request_iterator, LONG_TIMEOUT) + self.assertIsNotNone(responses) + with self.assertRaises(exceptions.ServicerError): + next(responses) + + @unittest.skip('TODO(atash,nathaniel): figure out why this flakily hangs ' + 'forever and fix.') + def testHalfDuplexCall(self): + import test_pb2 # pylint: disable=g-import-not-at-top + with _CreateService(test_pb2, NO_DELAY) as ( + methods, stub, unused_server): + def half_duplex_request_iterator(): + request = test_pb2.StreamingOutputCallRequest() + request.response_parameters.add(size=1, interval_us=0) + yield request + request = test_pb2.StreamingOutputCallRequest() + request.response_parameters.add(size=2, interval_us=0) + request.response_parameters.add(size=3, interval_us=0) + yield request + responses = stub.HalfDuplexCall( + half_duplex_request_iterator(), LONG_TIMEOUT) + expected_responses = methods.HalfDuplexCall( + half_duplex_request_iterator(), 'not a real RpcContext!') + for check in itertools.izip_longest(expected_responses, responses): + expected_response, response = check + self.assertEqual(expected_response, response) + + def testHalfDuplexCallWedged(self): + import test_pb2 # pylint: disable=g-import-not-at-top + condition = threading.Condition() + wait_cell = [False] + @contextlib.contextmanager + def wait(): # pylint: disable=invalid-name + # Where's Python 3's 'nonlocal' statement when you need it? + with condition: + wait_cell[0] = True + yield + with condition: + wait_cell[0] = False + condition.notify_all() + def half_duplex_request_iterator(): + request = test_pb2.StreamingOutputCallRequest() + request.response_parameters.add(size=1, interval_us=0) + yield request + with condition: + while wait_cell[0]: + condition.wait() + with _CreateService(test_pb2, NO_DELAY) as (methods, stub, unused_server): + with wait(): + responses = stub.HalfDuplexCall( + half_duplex_request_iterator(), SHORT_TIMEOUT) + # half-duplex waits for the client to send all info + with self.assertRaises(exceptions.ExpirationError): + next(responses) + + +if __name__ == '__main__': + os.chdir(os.path.dirname(sys.argv[0])) + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_protoc_plugin/test.proto b/src/python/grpcio_test/grpc_protoc_plugin/test.proto new file mode 100644 index 00000000..ed7c6a7b --- /dev/null +++ b/src/python/grpcio_test/grpc_protoc_plugin/test.proto @@ -0,0 +1,139 @@ +// Copyright 2015, 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. + +// An integration test service that covers all the method signature permutations +// of unary/streaming requests/responses. +// This file is duplicated around the code base. See GitHub issue #526. +syntax = "proto2"; + +package grpc.testing; + +enum PayloadType { + // Compressable text format. + COMPRESSABLE= 1; + + // Uncompressable binary format. + UNCOMPRESSABLE = 2; + + // Randomly chosen from all other formats defined in this enum. + RANDOM = 3; +} + +message Payload { + required PayloadType payload_type = 1; + oneof payload_body { + string payload_compressable = 2; + bytes payload_uncompressable = 3; + } +} + +message SimpleRequest { + // Desired payload type in the response from the server. + // If response_type is RANDOM, server randomly chooses one from other formats. + optional PayloadType response_type = 1 [default=COMPRESSABLE]; + + // Desired payload size in the response from the server. + // If response_type is COMPRESSABLE, this denotes the size before compression. + optional int32 response_size = 2; + + // Optional input payload sent along with the request. + optional Payload payload = 3; +} + +message SimpleResponse { + optional Payload payload = 1; +} + +message StreamingInputCallRequest { + // Optional input payload sent along with the request. + optional Payload payload = 1; + + // Not expecting any payload from the response. +} + +message StreamingInputCallResponse { + // Aggregated size of payloads received from the client. + optional int32 aggregated_payload_size = 1; +} + +message ResponseParameters { + // Desired payload sizes in responses from the server. + // If response_type is COMPRESSABLE, this denotes the size before compression. + required int32 size = 1; + + // Desired interval between consecutive responses in the response stream in + // microseconds. + required int32 interval_us = 2; +} + +message StreamingOutputCallRequest { + // Desired payload type in the response from the server. + // If response_type is RANDOM, the payload from each response in the stream + // might be of different types. This is to simulate a mixed type of payload + // stream. + optional PayloadType response_type = 1 [default=COMPRESSABLE]; + + repeated ResponseParameters response_parameters = 2; + + // Optional input payload sent along with the request. + optional Payload payload = 3; +} + +message StreamingOutputCallResponse { + optional Payload payload = 1; +} + +service TestService { + // One request followed by one response. + // The server returns the client payload as-is. + rpc UnaryCall(SimpleRequest) returns (SimpleResponse); + + // One request followed by a sequence of responses (streamed download). + // The server returns the payload with client desired type and sizes. + rpc StreamingOutputCall(StreamingOutputCallRequest) + returns (stream StreamingOutputCallResponse); + + // A sequence of requests followed by one response (streamed upload). + // The server returns the aggregated size of client payload as the result. + rpc StreamingInputCall(stream StreamingInputCallRequest) + returns (StreamingInputCallResponse); + + // A sequence of requests with each request served by the server immediately. + // As one request could lead to multiple responses, this interface + // demonstrates the idea of full duplexing. + rpc FullDuplexCall(stream StreamingOutputCallRequest) + returns (stream StreamingOutputCallResponse); + + // A sequence of requests followed by a sequence of responses. + // The server buffers all the client requests and then serves them in order. A + // stream of responses are returned to the client when the server starts with + // first request. + rpc HalfDuplexCall(stream StreamingOutputCallRequest) + returns (stream StreamingOutputCallResponse); +} diff --git a/src/python/grpcio_test/grpc_test/__init__.py b/src/python/grpcio_test/grpc_test/__init__.py new file mode 100644 index 00000000..70865191 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, 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. + + diff --git a/src/python/grpcio_test/grpc_test/_adapter/.gitignore b/src/python/grpcio_test/grpc_test/_adapter/.gitignore new file mode 100644 index 00000000..a6f96cd6 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_adapter/.gitignore @@ -0,0 +1,5 @@ +*.a +*.so +*.dll +*.pyc +*.pyd diff --git a/src/python/grpcio_test/grpc_test/_adapter/__init__.py b/src/python/grpcio_test/grpc_test/_adapter/__init__.py new file mode 100644 index 00000000..70865191 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_adapter/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, 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. + + diff --git a/src/python/grpcio_test/grpc_test/_adapter/_blocking_invocation_inline_service_test.py b/src/python/grpcio_test/grpc_test/_adapter/_blocking_invocation_inline_service_test.py new file mode 100644 index 00000000..a1f77621 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_adapter/_blocking_invocation_inline_service_test.py @@ -0,0 +1,46 @@ +# Copyright 2015, 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. + +"""One of the tests of the Face layer of RPC Framework.""" + +import unittest + +from grpc_test._adapter import _face_test_case +from grpc_test.framework.face.testing import blocking_invocation_inline_service_test_case as test_case + + +class BlockingInvocationInlineServiceTest( + _face_test_case.FaceTestCase, + test_case.BlockingInvocationInlineServiceTestCase, + unittest.TestCase): + pass + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/_adapter/_c_test.py b/src/python/grpcio_test/grpc_test/_adapter/_c_test.py new file mode 100644 index 00000000..fe020e2a --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_adapter/_c_test.py @@ -0,0 +1,55 @@ +# Copyright 2015, 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. + +import time +import unittest + +from grpc._adapter import _c +from grpc._adapter import _types + + +class CTypeSmokeTest(unittest.TestCase): + + def testCompletionQueueUpDown(self): + completion_queue = _c.CompletionQueue() + del completion_queue + + def testServerUpDown(self): + completion_queue = _c.CompletionQueue() + serv = _c.Server(completion_queue, []) + del serv + del completion_queue + + def testChannelUpDown(self): + channel = _c.Channel('[::]:0', []) + del channel + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/_adapter/_event_invocation_synchronous_event_service_test.py b/src/python/grpcio_test/grpc_test/_adapter/_event_invocation_synchronous_event_service_test.py new file mode 100644 index 00000000..0d01ebc8 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_adapter/_event_invocation_synchronous_event_service_test.py @@ -0,0 +1,46 @@ +# Copyright 2015, 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. + +"""One of the tests of the Face layer of RPC Framework.""" + +import unittest + +from grpc_test._adapter import _face_test_case +from grpc_test.framework.face.testing import event_invocation_synchronous_event_service_test_case as test_case + + +class EventInvocationSynchronousEventServiceTest( + _face_test_case.FaceTestCase, + test_case.EventInvocationSynchronousEventServiceTestCase, + unittest.TestCase): + pass + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/_adapter/_face_test_case.py b/src/python/grpcio_test/grpc_test/_adapter/_face_test_case.py new file mode 100644 index 00000000..dfbd0b60 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_adapter/_face_test_case.py @@ -0,0 +1,106 @@ +# Copyright 2015, 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. + +"""Common construction and destruction for GRPC-backed Face-layer tests.""" + +import unittest + +from grpc._adapter import fore +from grpc._adapter import rear +from grpc.framework.base import util +from grpc.framework.base import implementations as base_implementations +from grpc.framework.face import implementations as face_implementations +from grpc.framework.foundation import logging_pool +from grpc_test.framework.face.testing import coverage +from grpc_test.framework.face.testing import serial +from grpc_test.framework.face.testing import test_case + +_TIMEOUT = 3 +_MAXIMUM_TIMEOUT = 90 +_MAXIMUM_POOL_SIZE = 4 + + +class FaceTestCase(test_case.FaceTestCase, coverage.BlockingCoverage): + """Provides abstract Face-layer tests a GRPC-backed implementation.""" + + def set_up_implementation( + self, name, methods, method_implementations, + multi_method_implementation): + pool = logging_pool.pool(_MAXIMUM_POOL_SIZE) + + servicer = face_implementations.servicer( + pool, method_implementations, multi_method_implementation) + + serialization = serial.serialization(methods) + + fore_link = fore.ForeLink( + pool, serialization.request_deserializers, + serialization.response_serializers, None, ()) + fore_link.start() + port = fore_link.port() + rear_link = rear.RearLink( + 'localhost', port, pool, + serialization.request_serializers, + serialization.response_deserializers, False, None, None, None) + rear_link.start() + front = base_implementations.front_link(pool, pool, pool) + back = base_implementations.back_link( + servicer, pool, pool, pool, _TIMEOUT, _MAXIMUM_TIMEOUT) + fore_link.join_rear_link(back) + back.join_fore_link(fore_link) + rear_link.join_fore_link(front) + front.join_rear_link(rear_link) + + stub = face_implementations.generic_stub(front, pool) + return stub, (rear_link, fore_link, front, back) + + def tear_down_implementation(self, memo): + rear_link, fore_link, front, back = memo + # TODO(nathaniel): Waiting for the front and back to idle possibly should + # not be necessary - investigate as part of graceful shutdown work. + util.wait_for_idle(front) + util.wait_for_idle(back) + rear_link.stop() + fore_link.stop() + + @unittest.skip('Service-side failure not transmitted by GRPC.') + def testFailedUnaryRequestUnaryResponse(self): + raise NotImplementedError() + + @unittest.skip('Service-side failure not transmitted by GRPC.') + def testFailedUnaryRequestStreamResponse(self): + raise NotImplementedError() + + @unittest.skip('Service-side failure not transmitted by GRPC.') + def testFailedStreamRequestUnaryResponse(self): + raise NotImplementedError() + + @unittest.skip('Service-side failure not transmitted by GRPC.') + def testFailedStreamRequestStreamResponse(self): + raise NotImplementedError() diff --git a/src/python/grpcio_test/grpc_test/_adapter/_future_invocation_asynchronous_event_service_test.py b/src/python/grpcio_test/grpc_test/_adapter/_future_invocation_asynchronous_event_service_test.py new file mode 100644 index 00000000..ea4a6a0b --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_adapter/_future_invocation_asynchronous_event_service_test.py @@ -0,0 +1,46 @@ +# Copyright 2015, 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. + +"""One of the tests of the Face layer of RPC Framework.""" + +import unittest + +from grpc_test._adapter import _face_test_case +from grpc_test.framework.face.testing import future_invocation_asynchronous_event_service_test_case as test_case + + +class FutureInvocationAsynchronousEventServiceTest( + _face_test_case.FaceTestCase, + test_case.FutureInvocationAsynchronousEventServiceTestCase, + unittest.TestCase): + pass + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/_adapter/_intermediary_low_test.py b/src/python/grpcio_test/grpc_test/_adapter/_intermediary_low_test.py new file mode 100644 index 00000000..90ad0b9b --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_adapter/_intermediary_low_test.py @@ -0,0 +1,434 @@ +# Copyright 2015, 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. + +"""Tests for the old '_low'.""" + +import Queue +import threading +import time +import unittest + +from grpc._adapter import _intermediary_low as _low + +_STREAM_LENGTH = 300 +_TIMEOUT = 5 +_AFTER_DELAY = 2 +_FUTURE = time.time() + 60 * 60 * 24 +_BYTE_SEQUENCE = b'\abcdefghijklmnopqrstuvwxyz0123456789' * 200 +_BYTE_SEQUENCE_SEQUENCE = tuple( + bytes(bytearray((row + column) % 256 for column in range(row))) + for row in range(_STREAM_LENGTH)) + + +class LonelyClientTest(unittest.TestCase): + + def testLonelyClient(self): + host = 'nosuchhostexists' + port = 54321 + method = 'test method' + deadline = time.time() + _TIMEOUT + after_deadline = deadline + _AFTER_DELAY + metadata_tag = object() + finish_tag = object() + + completion_queue = _low.CompletionQueue() + channel = _low.Channel('%s:%d' % (host, port), None) + client_call = _low.Call(channel, completion_queue, method, host, deadline) + + client_call.invoke(completion_queue, metadata_tag, finish_tag) + first_event = completion_queue.get(after_deadline) + self.assertIsNotNone(first_event) + second_event = completion_queue.get(after_deadline) + self.assertIsNotNone(second_event) + kinds = [event.kind for event in (first_event, second_event)] + self.assertItemsEqual( + (_low.Event.Kind.METADATA_ACCEPTED, _low.Event.Kind.FINISH), + kinds) + + self.assertIsNone(completion_queue.get(after_deadline)) + + completion_queue.stop() + stop_event = completion_queue.get(_FUTURE) + self.assertEqual(_low.Event.Kind.STOP, stop_event.kind) + + del client_call + del channel + del completion_queue + + +def _drive_completion_queue(completion_queue, event_queue): + while True: + event = completion_queue.get(_FUTURE) + if event.kind is _low.Event.Kind.STOP: + break + event_queue.put(event) + + +class EchoTest(unittest.TestCase): + + def setUp(self): + self.host = 'localhost' + + self.server_completion_queue = _low.CompletionQueue() + self.server = _low.Server(self.server_completion_queue) + port = self.server.add_http2_addr('[::]:0') + self.server.start() + self.server_events = Queue.Queue() + self.server_completion_queue_thread = threading.Thread( + target=_drive_completion_queue, + args=(self.server_completion_queue, self.server_events)) + self.server_completion_queue_thread.start() + + self.client_completion_queue = _low.CompletionQueue() + self.channel = _low.Channel('%s:%d' % (self.host, port), None) + self.client_events = Queue.Queue() + self.client_completion_queue_thread = threading.Thread( + target=_drive_completion_queue, + args=(self.client_completion_queue, self.client_events)) + self.client_completion_queue_thread.start() + + def tearDown(self): + self.server.stop() + self.server_completion_queue.stop() + self.client_completion_queue.stop() + self.server_completion_queue_thread.join() + self.client_completion_queue_thread.join() + del self.server + + def _perform_echo_test(self, test_data): + method = 'test method' + details = 'test details' + server_leading_metadata_key = 'my_server_leading_key' + server_leading_metadata_value = 'my_server_leading_value' + server_trailing_metadata_key = 'my_server_trailing_key' + server_trailing_metadata_value = 'my_server_trailing_value' + client_metadata_key = 'my_client_key' + client_metadata_value = 'my_client_value' + server_leading_binary_metadata_key = 'my_server_leading_key-bin' + server_leading_binary_metadata_value = b'\0'*2047 + server_trailing_binary_metadata_key = 'my_server_trailing_key-bin' + server_trailing_binary_metadata_value = b'\0'*2047 + client_binary_metadata_key = 'my_client_key-bin' + client_binary_metadata_value = b'\0'*2047 + deadline = _FUTURE + metadata_tag = object() + finish_tag = object() + write_tag = object() + complete_tag = object() + service_tag = object() + read_tag = object() + status_tag = object() + + server_data = [] + client_data = [] + + client_call = _low.Call(self.channel, self.client_completion_queue, + method, self.host, deadline) + client_call.add_metadata(client_metadata_key, client_metadata_value) + client_call.add_metadata(client_binary_metadata_key, + client_binary_metadata_value) + + client_call.invoke(self.client_completion_queue, metadata_tag, finish_tag) + + self.server.service(service_tag) + service_accepted = self.server_events.get() + self.assertIsNotNone(service_accepted) + self.assertIs(service_accepted.kind, _low.Event.Kind.SERVICE_ACCEPTED) + self.assertIs(service_accepted.tag, service_tag) + self.assertEqual(method, service_accepted.service_acceptance.method) + self.assertEqual(self.host, service_accepted.service_acceptance.host) + self.assertIsNotNone(service_accepted.service_acceptance.call) + metadata = dict(service_accepted.metadata) + self.assertIn(client_metadata_key, metadata) + self.assertEqual(client_metadata_value, metadata[client_metadata_key]) + self.assertIn(client_binary_metadata_key, metadata) + self.assertEqual(client_binary_metadata_value, + metadata[client_binary_metadata_key]) + server_call = service_accepted.service_acceptance.call + server_call.accept(self.server_completion_queue, finish_tag) + server_call.add_metadata(server_leading_metadata_key, + server_leading_metadata_value) + server_call.add_metadata(server_leading_binary_metadata_key, + server_leading_binary_metadata_value) + server_call.premetadata() + + metadata_accepted = self.client_events.get() + self.assertIsNotNone(metadata_accepted) + self.assertEqual(_low.Event.Kind.METADATA_ACCEPTED, metadata_accepted.kind) + self.assertEqual(metadata_tag, metadata_accepted.tag) + metadata = dict(metadata_accepted.metadata) + self.assertIn(server_leading_metadata_key, metadata) + self.assertEqual(server_leading_metadata_value, + metadata[server_leading_metadata_key]) + self.assertIn(server_leading_binary_metadata_key, metadata) + self.assertEqual(server_leading_binary_metadata_value, + metadata[server_leading_binary_metadata_key]) + + for datum in test_data: + client_call.write(datum, write_tag, _low.WriteFlags.WRITE_NO_COMPRESS) + write_accepted = self.client_events.get() + self.assertIsNotNone(write_accepted) + self.assertIs(write_accepted.kind, _low.Event.Kind.WRITE_ACCEPTED) + self.assertIs(write_accepted.tag, write_tag) + self.assertIs(write_accepted.write_accepted, True) + + server_call.read(read_tag) + read_accepted = self.server_events.get() + self.assertIsNotNone(read_accepted) + self.assertEqual(_low.Event.Kind.READ_ACCEPTED, read_accepted.kind) + self.assertEqual(read_tag, read_accepted.tag) + self.assertIsNotNone(read_accepted.bytes) + server_data.append(read_accepted.bytes) + + server_call.write(read_accepted.bytes, write_tag, 0) + write_accepted = self.server_events.get() + self.assertIsNotNone(write_accepted) + self.assertEqual(_low.Event.Kind.WRITE_ACCEPTED, write_accepted.kind) + self.assertEqual(write_tag, write_accepted.tag) + self.assertTrue(write_accepted.write_accepted) + + client_call.read(read_tag) + read_accepted = self.client_events.get() + self.assertIsNotNone(read_accepted) + self.assertEqual(_low.Event.Kind.READ_ACCEPTED, read_accepted.kind) + self.assertEqual(read_tag, read_accepted.tag) + self.assertIsNotNone(read_accepted.bytes) + client_data.append(read_accepted.bytes) + + client_call.complete(complete_tag) + complete_accepted = self.client_events.get() + self.assertIsNotNone(complete_accepted) + self.assertIs(complete_accepted.kind, _low.Event.Kind.COMPLETE_ACCEPTED) + self.assertIs(complete_accepted.tag, complete_tag) + self.assertIs(complete_accepted.complete_accepted, True) + + server_call.read(read_tag) + read_accepted = self.server_events.get() + self.assertIsNotNone(read_accepted) + self.assertEqual(_low.Event.Kind.READ_ACCEPTED, read_accepted.kind) + self.assertEqual(read_tag, read_accepted.tag) + self.assertIsNone(read_accepted.bytes) + + server_call.add_metadata(server_trailing_metadata_key, + server_trailing_metadata_value) + server_call.add_metadata(server_trailing_binary_metadata_key, + server_trailing_binary_metadata_value) + + server_call.status(_low.Status(_low.Code.OK, details), status_tag) + server_terminal_event_one = self.server_events.get() + server_terminal_event_two = self.server_events.get() + if server_terminal_event_one.kind == _low.Event.Kind.COMPLETE_ACCEPTED: + status_accepted = server_terminal_event_one + rpc_accepted = server_terminal_event_two + else: + status_accepted = server_terminal_event_two + rpc_accepted = server_terminal_event_one + self.assertIsNotNone(status_accepted) + self.assertIsNotNone(rpc_accepted) + self.assertEqual(_low.Event.Kind.COMPLETE_ACCEPTED, status_accepted.kind) + self.assertEqual(status_tag, status_accepted.tag) + self.assertTrue(status_accepted.complete_accepted) + self.assertEqual(_low.Event.Kind.FINISH, rpc_accepted.kind) + self.assertEqual(finish_tag, rpc_accepted.tag) + self.assertEqual(_low.Status(_low.Code.OK, ''), rpc_accepted.status) + + client_call.read(read_tag) + client_terminal_event_one = self.client_events.get() + client_terminal_event_two = self.client_events.get() + if client_terminal_event_one.kind == _low.Event.Kind.READ_ACCEPTED: + read_accepted = client_terminal_event_one + finish_accepted = client_terminal_event_two + else: + read_accepted = client_terminal_event_two + finish_accepted = client_terminal_event_one + self.assertIsNotNone(read_accepted) + self.assertIsNotNone(finish_accepted) + self.assertEqual(_low.Event.Kind.READ_ACCEPTED, read_accepted.kind) + self.assertEqual(read_tag, read_accepted.tag) + self.assertIsNone(read_accepted.bytes) + self.assertEqual(_low.Event.Kind.FINISH, finish_accepted.kind) + self.assertEqual(finish_tag, finish_accepted.tag) + self.assertEqual(_low.Status(_low.Code.OK, details), finish_accepted.status) + metadata = dict(finish_accepted.metadata) + self.assertIn(server_trailing_metadata_key, metadata) + self.assertEqual(server_trailing_metadata_value, + metadata[server_trailing_metadata_key]) + self.assertIn(server_trailing_binary_metadata_key, metadata) + self.assertEqual(server_trailing_binary_metadata_value, + metadata[server_trailing_binary_metadata_key]) + self.assertSetEqual(set(key for key, _ in finish_accepted.metadata), + set((server_trailing_metadata_key, + server_trailing_binary_metadata_key,))) + + server_timeout_none_event = self.server_completion_queue.get(0) + self.assertIsNone(server_timeout_none_event) + client_timeout_none_event = self.client_completion_queue.get(0) + self.assertIsNone(client_timeout_none_event) + + self.assertSequenceEqual(test_data, server_data) + self.assertSequenceEqual(test_data, client_data) + + def testNoEcho(self): + self._perform_echo_test(()) + + def testOneByteEcho(self): + self._perform_echo_test([b'\x07']) + + def testOneManyByteEcho(self): + self._perform_echo_test([_BYTE_SEQUENCE]) + + def testManyOneByteEchoes(self): + self._perform_echo_test(_BYTE_SEQUENCE) + + def testManyManyByteEchoes(self): + self._perform_echo_test(_BYTE_SEQUENCE_SEQUENCE) + + +class CancellationTest(unittest.TestCase): + + def setUp(self): + self.host = 'localhost' + + self.server_completion_queue = _low.CompletionQueue() + self.server = _low.Server(self.server_completion_queue) + port = self.server.add_http2_addr('[::]:0') + self.server.start() + self.server_events = Queue.Queue() + self.server_completion_queue_thread = threading.Thread( + target=_drive_completion_queue, + args=(self.server_completion_queue, self.server_events)) + self.server_completion_queue_thread.start() + + self.client_completion_queue = _low.CompletionQueue() + self.channel = _low.Channel('%s:%d' % (self.host, port), None) + self.client_events = Queue.Queue() + self.client_completion_queue_thread = threading.Thread( + target=_drive_completion_queue, + args=(self.client_completion_queue, self.client_events)) + self.client_completion_queue_thread.start() + + def tearDown(self): + self.server.stop() + self.server_completion_queue.stop() + self.client_completion_queue.stop() + self.server_completion_queue_thread.join() + self.client_completion_queue_thread.join() + del self.server + + def testCancellation(self): + method = 'test method' + deadline = _FUTURE + metadata_tag = object() + finish_tag = object() + write_tag = object() + service_tag = object() + read_tag = object() + test_data = _BYTE_SEQUENCE_SEQUENCE + + server_data = [] + client_data = [] + + client_call = _low.Call(self.channel, self.client_completion_queue, + method, self.host, deadline) + + client_call.invoke(self.client_completion_queue, metadata_tag, finish_tag) + + self.server.service(service_tag) + service_accepted = self.server_events.get() + server_call = service_accepted.service_acceptance.call + + server_call.accept(self.server_completion_queue, finish_tag) + server_call.premetadata() + + metadata_accepted = self.client_events.get() + self.assertIsNotNone(metadata_accepted) + + for datum in test_data: + client_call.write(datum, write_tag, 0) + write_accepted = self.client_events.get() + + server_call.read(read_tag) + read_accepted = self.server_events.get() + server_data.append(read_accepted.bytes) + + server_call.write(read_accepted.bytes, write_tag, 0) + write_accepted = self.server_events.get() + self.assertIsNotNone(write_accepted) + + client_call.read(read_tag) + read_accepted = self.client_events.get() + client_data.append(read_accepted.bytes) + + client_call.cancel() + # cancel() is idempotent. + client_call.cancel() + client_call.cancel() + client_call.cancel() + + server_call.read(read_tag) + + server_terminal_event_one = self.server_events.get() + server_terminal_event_two = self.server_events.get() + if server_terminal_event_one.kind == _low.Event.Kind.READ_ACCEPTED: + read_accepted = server_terminal_event_one + rpc_accepted = server_terminal_event_two + else: + read_accepted = server_terminal_event_two + rpc_accepted = server_terminal_event_one + self.assertIsNotNone(read_accepted) + self.assertIsNotNone(rpc_accepted) + self.assertEqual(_low.Event.Kind.READ_ACCEPTED, read_accepted.kind) + self.assertIsNone(read_accepted.bytes) + self.assertEqual(_low.Event.Kind.FINISH, rpc_accepted.kind) + self.assertEqual(_low.Status(_low.Code.CANCELLED, ''), rpc_accepted.status) + + finish_event = self.client_events.get() + self.assertEqual(_low.Event.Kind.FINISH, finish_event.kind) + self.assertEqual(_low.Status(_low.Code.CANCELLED, 'Cancelled'), + finish_event.status) + + server_timeout_none_event = self.server_completion_queue.get(0) + self.assertIsNone(server_timeout_none_event) + client_timeout_none_event = self.client_completion_queue.get(0) + self.assertIsNone(client_timeout_none_event) + + self.assertSequenceEqual(test_data, server_data) + self.assertSequenceEqual(test_data, client_data) + + +class ExpirationTest(unittest.TestCase): + + @unittest.skip('TODO(nathaniel): Expiration test!') + def testExpiration(self): + pass + + +if __name__ == '__main__': + unittest.main(verbosity=2) + diff --git a/src/python/grpcio_test/grpc_test/_adapter/_links_test.py b/src/python/grpcio_test/grpc_test/_adapter/_links_test.py new file mode 100644 index 00000000..4077b8e3 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_adapter/_links_test.py @@ -0,0 +1,277 @@ +# Copyright 2015, 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. + +"""Test of the GRPC-backed ForeLink and RearLink.""" + +import threading +import unittest + +from grpc._adapter import fore +from grpc._adapter import rear +from grpc.framework.base import interfaces +from grpc.framework.foundation import logging_pool +from grpc_test._adapter import _proto_scenarios +from grpc_test._adapter import _test_links + +_IDENTITY = lambda x: x +_TIMEOUT = 32 + + +# TODO(nathaniel): End-to-end metadata testing. +def _transform_metadata(unused_metadata): + return ( + ('one_unused_key', 'one unused value'), + ('another_unused_key', 'another unused value'), +) + + +class RoundTripTest(unittest.TestCase): + + def setUp(self): + self.fore_link_pool = logging_pool.pool(8) + self.rear_link_pool = logging_pool.pool(8) + + def tearDown(self): + self.rear_link_pool.shutdown(wait=True) + self.fore_link_pool.shutdown(wait=True) + + def testZeroMessageRoundTrip(self): + test_operation_id = object() + test_method = 'test method' + test_fore_link = _test_links.ForeLink(None, None) + def rear_action(front_to_back_ticket, fore_link): + if front_to_back_ticket.kind in ( + interfaces.FrontToBackTicket.Kind.COMPLETION, + interfaces.FrontToBackTicket.Kind.ENTIRE): + back_to_front_ticket = interfaces.BackToFrontTicket( + front_to_back_ticket.operation_id, 0, + interfaces.BackToFrontTicket.Kind.COMPLETION, None) + fore_link.accept_back_to_front_ticket(back_to_front_ticket) + test_rear_link = _test_links.RearLink(rear_action, None) + + fore_link = fore.ForeLink( + self.fore_link_pool, {test_method: None}, {test_method: None}, None, ()) + fore_link.join_rear_link(test_rear_link) + test_rear_link.join_fore_link(fore_link) + fore_link.start() + port = fore_link.port() + + rear_link = rear.RearLink( + 'localhost', port, self.rear_link_pool, {test_method: None}, + {test_method: None}, False, None, None, None, + metadata_transformer=_transform_metadata) + rear_link.join_fore_link(test_fore_link) + test_fore_link.join_rear_link(rear_link) + rear_link.start() + + front_to_back_ticket = interfaces.FrontToBackTicket( + test_operation_id, 0, interfaces.FrontToBackTicket.Kind.ENTIRE, + test_method, interfaces.ServicedSubscription.Kind.FULL, None, None, + _TIMEOUT) + rear_link.accept_front_to_back_ticket(front_to_back_ticket) + + with test_fore_link.condition: + while (not test_fore_link.tickets or + test_fore_link.tickets[-1].kind is + interfaces.BackToFrontTicket.Kind.CONTINUATION): + test_fore_link.condition.wait() + + rear_link.stop() + fore_link.stop() + + with test_fore_link.condition: + self.assertIs( + test_fore_link.tickets[-1].kind, + interfaces.BackToFrontTicket.Kind.COMPLETION) + + def testEntireRoundTrip(self): + test_operation_id = object() + test_method = 'test method' + test_front_to_back_datum = b'\x07' + test_back_to_front_datum = b'\x08' + test_fore_link = _test_links.ForeLink(None, None) + rear_sequence_number = [0] + def rear_action(front_to_back_ticket, fore_link): + if front_to_back_ticket.payload is None: + payload = None + else: + payload = test_back_to_front_datum + terminal = front_to_back_ticket.kind in ( + interfaces.FrontToBackTicket.Kind.COMPLETION, + interfaces.FrontToBackTicket.Kind.ENTIRE) + if payload is not None or terminal: + if terminal: + kind = interfaces.BackToFrontTicket.Kind.COMPLETION + else: + kind = interfaces.BackToFrontTicket.Kind.CONTINUATION + back_to_front_ticket = interfaces.BackToFrontTicket( + front_to_back_ticket.operation_id, rear_sequence_number[0], kind, + payload) + rear_sequence_number[0] += 1 + fore_link.accept_back_to_front_ticket(back_to_front_ticket) + test_rear_link = _test_links.RearLink(rear_action, None) + + fore_link = fore.ForeLink( + self.fore_link_pool, {test_method: _IDENTITY}, + {test_method: _IDENTITY}, None, ()) + fore_link.join_rear_link(test_rear_link) + test_rear_link.join_fore_link(fore_link) + fore_link.start() + port = fore_link.port() + + rear_link = rear.RearLink( + 'localhost', port, self.rear_link_pool, {test_method: _IDENTITY}, + {test_method: _IDENTITY}, False, None, None, None) + rear_link.join_fore_link(test_fore_link) + test_fore_link.join_rear_link(rear_link) + rear_link.start() + + front_to_back_ticket = interfaces.FrontToBackTicket( + test_operation_id, 0, interfaces.FrontToBackTicket.Kind.ENTIRE, + test_method, interfaces.ServicedSubscription.Kind.FULL, None, + test_front_to_back_datum, _TIMEOUT) + rear_link.accept_front_to_back_ticket(front_to_back_ticket) + + with test_fore_link.condition: + while (not test_fore_link.tickets or + test_fore_link.tickets[-1].kind is not + interfaces.BackToFrontTicket.Kind.COMPLETION): + test_fore_link.condition.wait() + + rear_link.stop() + fore_link.stop() + + with test_rear_link.condition: + front_to_back_payloads = tuple( + ticket.payload for ticket in test_rear_link.tickets + if ticket.payload is not None) + with test_fore_link.condition: + back_to_front_payloads = tuple( + ticket.payload for ticket in test_fore_link.tickets + if ticket.payload is not None) + self.assertTupleEqual((test_front_to_back_datum,), front_to_back_payloads) + self.assertTupleEqual((test_back_to_front_datum,), back_to_front_payloads) + + def _perform_scenario_test(self, scenario): + test_operation_id = object() + test_method = scenario.method() + test_fore_link = _test_links.ForeLink(None, None) + rear_lock = threading.Lock() + rear_sequence_number = [0] + def rear_action(front_to_back_ticket, fore_link): + with rear_lock: + if front_to_back_ticket.payload is not None: + response = scenario.response_for_request(front_to_back_ticket.payload) + else: + response = None + terminal = front_to_back_ticket.kind in ( + interfaces.FrontToBackTicket.Kind.COMPLETION, + interfaces.FrontToBackTicket.Kind.ENTIRE) + if response is not None or terminal: + if terminal: + kind = interfaces.BackToFrontTicket.Kind.COMPLETION + else: + kind = interfaces.BackToFrontTicket.Kind.CONTINUATION + back_to_front_ticket = interfaces.BackToFrontTicket( + front_to_back_ticket.operation_id, rear_sequence_number[0], kind, + response) + rear_sequence_number[0] += 1 + fore_link.accept_back_to_front_ticket(back_to_front_ticket) + test_rear_link = _test_links.RearLink(rear_action, None) + + fore_link = fore.ForeLink( + self.fore_link_pool, {test_method: scenario.deserialize_request}, + {test_method: scenario.serialize_response}, None, ()) + fore_link.join_rear_link(test_rear_link) + test_rear_link.join_fore_link(fore_link) + fore_link.start() + port = fore_link.port() + + rear_link = rear.RearLink( + 'localhost', port, self.rear_link_pool, + {test_method: scenario.serialize_request}, + {test_method: scenario.deserialize_response}, False, None, None, None) + rear_link.join_fore_link(test_fore_link) + test_fore_link.join_rear_link(rear_link) + rear_link.start() + + commencement_ticket = interfaces.FrontToBackTicket( + test_operation_id, 0, + interfaces.FrontToBackTicket.Kind.COMMENCEMENT, test_method, + interfaces.ServicedSubscription.Kind.FULL, None, None, + _TIMEOUT) + fore_sequence_number = 1 + rear_link.accept_front_to_back_ticket(commencement_ticket) + for request in scenario.requests(): + continuation_ticket = interfaces.FrontToBackTicket( + test_operation_id, fore_sequence_number, + interfaces.FrontToBackTicket.Kind.CONTINUATION, None, None, None, + request, None) + fore_sequence_number += 1 + rear_link.accept_front_to_back_ticket(continuation_ticket) + completion_ticket = interfaces.FrontToBackTicket( + test_operation_id, fore_sequence_number, + interfaces.FrontToBackTicket.Kind.COMPLETION, None, None, None, None, + None) + fore_sequence_number += 1 + rear_link.accept_front_to_back_ticket(completion_ticket) + + with test_fore_link.condition: + while (not test_fore_link.tickets or + test_fore_link.tickets[-1].kind is not + interfaces.BackToFrontTicket.Kind.COMPLETION): + test_fore_link.condition.wait() + + rear_link.stop() + fore_link.stop() + + with test_rear_link.condition: + requests = tuple( + ticket.payload for ticket in test_rear_link.tickets + if ticket.payload is not None) + with test_fore_link.condition: + responses = tuple( + ticket.payload for ticket in test_fore_link.tickets + if ticket.payload is not None) + self.assertTrue(scenario.verify_requests(requests)) + self.assertTrue(scenario.verify_responses(responses)) + + def testEmptyScenario(self): + self._perform_scenario_test(_proto_scenarios.EmptyScenario()) + + def testBidirectionallyUnaryScenario(self): + self._perform_scenario_test(_proto_scenarios.BidirectionallyUnaryScenario()) + + def testBidirectionallyStreamingScenario(self): + self._perform_scenario_test( + _proto_scenarios.BidirectionallyStreamingScenario()) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/_adapter/_lonely_rear_link_test.py b/src/python/grpcio_test/grpc_test/_adapter/_lonely_rear_link_test.py new file mode 100644 index 00000000..9b5758f6 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_adapter/_lonely_rear_link_test.py @@ -0,0 +1,100 @@ +# Copyright 2015, 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. + +"""A test of invocation-side code unconnected to an RPC server.""" + +import unittest + +from grpc._adapter import rear +from grpc.framework.base import interfaces +from grpc.framework.foundation import logging_pool +from grpc_test._adapter import _test_links + +_IDENTITY = lambda x: x +_TIMEOUT = 2 + + +class LonelyRearLinkTest(unittest.TestCase): + + def setUp(self): + self.pool = logging_pool.pool(8) + + def tearDown(self): + self.pool.shutdown(wait=True) + + def testUpAndDown(self): + rear_link = rear.RearLink( + 'nonexistent', 54321, self.pool, {}, {}, False, None, None, None) + + rear_link.start() + rear_link.stop() + + def _perform_lonely_client_test_with_ticket_kind( + self, front_to_back_ticket_kind): + test_operation_id = object() + test_method = 'test method' + fore_link = _test_links.ForeLink(None, None) + + rear_link = rear.RearLink( + 'nonexistent', 54321, self.pool, {test_method: None}, + {test_method: None}, False, None, None, None) + rear_link.join_fore_link(fore_link) + rear_link.start() + + front_to_back_ticket = interfaces.FrontToBackTicket( + test_operation_id, 0, front_to_back_ticket_kind, test_method, + interfaces.ServicedSubscription.Kind.FULL, None, None, _TIMEOUT) + rear_link.accept_front_to_back_ticket(front_to_back_ticket) + + with fore_link.condition: + while True: + if (fore_link.tickets and + fore_link.tickets[-1].kind is not + interfaces.BackToFrontTicket.Kind.CONTINUATION): + break + fore_link.condition.wait() + + rear_link.stop() + + with fore_link.condition: + self.assertIsNot( + fore_link.tickets[-1].kind, + interfaces.BackToFrontTicket.Kind.COMPLETION) + + def testLonelyClientCommencementTicket(self): + self._perform_lonely_client_test_with_ticket_kind( + interfaces.FrontToBackTicket.Kind.COMMENCEMENT) + + def testLonelyClientEntireTicket(self): + self._perform_lonely_client_test_with_ticket_kind( + interfaces.FrontToBackTicket.Kind.ENTIRE) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/_adapter/_low_test.py b/src/python/grpcio_test/grpc_test/_adapter/_low_test.py new file mode 100644 index 00000000..70149127 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_adapter/_low_test.py @@ -0,0 +1,312 @@ +# Copyright 2015, 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. + +import threading +import time +import unittest + +from grpc import _grpcio_metadata +from grpc._adapter import _types +from grpc._adapter import _low + + +def wait_for_events(completion_queues, deadline): + """ + Args: + completion_queues: list of completion queues to wait for events on + deadline: absolute deadline to wait until + + Returns: + a sequence of events of length len(completion_queues). + """ + + results = [None] * len(completion_queues) + lock = threading.Lock() + threads = [] + def set_ith_result(i, completion_queue): + result = completion_queue.next(deadline) + with lock: + results[i] = result + for i, completion_queue in enumerate(completion_queues): + thread = threading.Thread(target=set_ith_result, + args=[i, completion_queue]) + thread.start() + threads.append(thread) + for thread in threads: + thread.join() + return results + + +class InsecureServerInsecureClient(unittest.TestCase): + + def setUp(self): + self.server_completion_queue = _low.CompletionQueue() + self.server = _low.Server(self.server_completion_queue, []) + self.port = self.server.add_http2_port('[::]:0') + self.client_completion_queue = _low.CompletionQueue() + self.client_channel = _low.Channel('localhost:%d'%self.port, []) + + self.server.start() + + def tearDown(self): + self.server.shutdown() + del self.client_channel + + self.client_completion_queue.shutdown() + while (self.client_completion_queue.next().type != + _types.EventType.QUEUE_SHUTDOWN): + pass + self.server_completion_queue.shutdown() + while (self.server_completion_queue.next().type != + _types.EventType.QUEUE_SHUTDOWN): + pass + + del self.client_completion_queue + del self.server_completion_queue + del self.server + + def testEcho(self): + deadline = time.time() + 5 + event_time_tolerance = 2 + deadline_tolerance = 0.25 + client_metadata_ascii_key = 'key' + client_metadata_ascii_value = 'val' + client_metadata_bin_key = 'key-bin' + client_metadata_bin_value = b'\0'*1000 + server_initial_metadata_key = 'init_me_me_me' + server_initial_metadata_value = 'whodawha?' + server_trailing_metadata_key = 'california_is_in_a_drought' + server_trailing_metadata_value = 'zomg it is' + server_status_code = _types.StatusCode.OK + server_status_details = 'our work is never over' + request = 'blarghaflargh' + response = 'his name is robert paulson' + method = 'twinkies' + host = 'hostess' + server_request_tag = object() + request_call_result = self.server.request_call(self.server_completion_queue, + server_request_tag) + + self.assertEqual(_types.CallError.OK, request_call_result) + + client_call_tag = object() + client_call = self.client_channel.create_call( + self.client_completion_queue, method, host, deadline) + client_initial_metadata = [ + (client_metadata_ascii_key, client_metadata_ascii_value), + (client_metadata_bin_key, client_metadata_bin_value) + ] + client_start_batch_result = client_call.start_batch([ + _types.OpArgs.send_initial_metadata(client_initial_metadata), + _types.OpArgs.send_message(request, 0), + _types.OpArgs.send_close_from_client(), + _types.OpArgs.recv_initial_metadata(), + _types.OpArgs.recv_message(), + _types.OpArgs.recv_status_on_client() + ], client_call_tag) + self.assertEqual(_types.CallError.OK, client_start_batch_result) + + client_no_event, request_event, = wait_for_events( + [self.client_completion_queue, self.server_completion_queue], + time.time() + event_time_tolerance) + self.assertEqual(client_no_event, None) + self.assertEqual(_types.EventType.OP_COMPLETE, request_event.type) + self.assertIsInstance(request_event.call, _low.Call) + self.assertIs(server_request_tag, request_event.tag) + self.assertEqual(1, len(request_event.results)) + received_initial_metadata = dict(request_event.results[0].initial_metadata) + # Check that our metadata were transmitted + self.assertEqual( + dict(client_initial_metadata), + dict((x, received_initial_metadata[x]) + for x in zip(*client_initial_metadata)[0])) + # Check that Python's user agent string is a part of the full user agent + # string + self.assertIn('Python-gRPC-{}'.format(_grpcio_metadata.__version__), + received_initial_metadata['user-agent']) + self.assertEqual(method, request_event.call_details.method) + self.assertEqual(host, request_event.call_details.host) + self.assertLess(abs(deadline - request_event.call_details.deadline), + deadline_tolerance) + + # Check that the channel is connected, and that both it and the call have + # the proper target and peer; do this after the first flurry of messages to + # avoid the possibility that connection was delayed by the core until the + # first message was sent. + self.assertEqual(_types.ConnectivityState.READY, + self.client_channel.check_connectivity_state(False)) + self.assertIsNotNone(self.client_channel.target()) + self.assertIsNotNone(client_call.peer()) + + server_call_tag = object() + server_call = request_event.call + server_initial_metadata = [ + (server_initial_metadata_key, server_initial_metadata_value) + ] + server_trailing_metadata = [ + (server_trailing_metadata_key, server_trailing_metadata_value) + ] + server_start_batch_result = server_call.start_batch([ + _types.OpArgs.send_initial_metadata(server_initial_metadata), + _types.OpArgs.recv_message(), + _types.OpArgs.send_message(response, 0), + _types.OpArgs.recv_close_on_server(), + _types.OpArgs.send_status_from_server( + server_trailing_metadata, server_status_code, server_status_details) + ], server_call_tag) + self.assertEqual(_types.CallError.OK, server_start_batch_result) + + client_event, server_event, = wait_for_events( + [self.client_completion_queue, self.server_completion_queue], + time.time() + event_time_tolerance) + + self.assertEqual(6, len(client_event.results)) + found_client_op_types = set() + for client_result in client_event.results: + # we expect each op type to be unique + self.assertNotIn(client_result.type, found_client_op_types) + found_client_op_types.add(client_result.type) + if client_result.type == _types.OpType.RECV_INITIAL_METADATA: + self.assertEqual(dict(server_initial_metadata), + dict(client_result.initial_metadata)) + elif client_result.type == _types.OpType.RECV_MESSAGE: + self.assertEqual(response, client_result.message) + elif client_result.type == _types.OpType.RECV_STATUS_ON_CLIENT: + self.assertEqual(dict(server_trailing_metadata), + dict(client_result.trailing_metadata)) + self.assertEqual(server_status_details, client_result.status.details) + self.assertEqual(server_status_code, client_result.status.code) + self.assertEqual(set([ + _types.OpType.SEND_INITIAL_METADATA, + _types.OpType.SEND_MESSAGE, + _types.OpType.SEND_CLOSE_FROM_CLIENT, + _types.OpType.RECV_INITIAL_METADATA, + _types.OpType.RECV_MESSAGE, + _types.OpType.RECV_STATUS_ON_CLIENT + ]), found_client_op_types) + + self.assertEqual(5, len(server_event.results)) + found_server_op_types = set() + for server_result in server_event.results: + self.assertNotIn(client_result.type, found_server_op_types) + found_server_op_types.add(server_result.type) + if server_result.type == _types.OpType.RECV_MESSAGE: + self.assertEqual(request, server_result.message) + elif server_result.type == _types.OpType.RECV_CLOSE_ON_SERVER: + self.assertFalse(server_result.cancelled) + self.assertEqual(set([ + _types.OpType.SEND_INITIAL_METADATA, + _types.OpType.RECV_MESSAGE, + _types.OpType.SEND_MESSAGE, + _types.OpType.RECV_CLOSE_ON_SERVER, + _types.OpType.SEND_STATUS_FROM_SERVER + ]), found_server_op_types) + + del client_call + del server_call + + +class HangingServerShutdown(unittest.TestCase): + + def setUp(self): + self.server_completion_queue = _low.CompletionQueue() + self.server = _low.Server(self.server_completion_queue, []) + self.port = self.server.add_http2_port('[::]:0') + self.client_completion_queue = _low.CompletionQueue() + self.client_channel = _low.Channel('localhost:%d'%self.port, []) + + self.server.start() + + def tearDown(self): + self.server.shutdown() + del self.client_channel + + self.client_completion_queue.shutdown() + self.server_completion_queue.shutdown() + while True: + client_event, server_event = wait_for_events( + [self.client_completion_queue, self.server_completion_queue], + float("+inf")) + if (client_event.type == _types.EventType.QUEUE_SHUTDOWN and + server_event.type == _types.EventType.QUEUE_SHUTDOWN): + break + + del self.client_completion_queue + del self.server_completion_queue + del self.server + + def testHangingServerCall(self): + deadline = time.time() + 5 + deadline_tolerance = 0.25 + event_time_tolerance = 2 + cancel_all_calls_time_tolerance = 0.5 + request = 'blarghaflargh' + method = 'twinkies' + host = 'hostess' + server_request_tag = object() + request_call_result = self.server.request_call(self.server_completion_queue, + server_request_tag) + + client_call_tag = object() + client_call = self.client_channel.create_call(self.client_completion_queue, + method, host, deadline) + client_start_batch_result = client_call.start_batch([ + _types.OpArgs.send_initial_metadata([]), + _types.OpArgs.send_message(request, 0), + _types.OpArgs.send_close_from_client(), + _types.OpArgs.recv_initial_metadata(), + _types.OpArgs.recv_message(), + _types.OpArgs.recv_status_on_client() + ], client_call_tag) + + client_no_event, request_event, = wait_for_events( + [self.client_completion_queue, self.server_completion_queue], + time.time() + event_time_tolerance) + + # Now try to shutdown the server and expect that we see server shutdown + # almost immediately after calling cancel_all_calls. + with self.assertRaises(RuntimeError): + self.server.cancel_all_calls() + shutdown_tag = object() + self.server.shutdown(shutdown_tag) + pre_cancel_timestamp = time.time() + self.server.cancel_all_calls() + finish_shutdown_timestamp = None + client_call_event, server_shutdown_event = wait_for_events( + [self.client_completion_queue, self.server_completion_queue], + time.time() + event_time_tolerance) + self.assertIs(shutdown_tag, server_shutdown_event.tag) + self.assertGreater(pre_cancel_timestamp + cancel_all_calls_time_tolerance, + time.time()) + + del client_call + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/_adapter/_proto_scenarios.py b/src/python/grpcio_test/grpc_test/_adapter/_proto_scenarios.py new file mode 100644 index 00000000..b3d6ec86 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_adapter/_proto_scenarios.py @@ -0,0 +1,261 @@ +# Copyright 2015, 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. + +"""Test scenarios using protocol buffers.""" + +import abc +import threading + +from grpc_test._junkdrawer import math_pb2 + + +class ProtoScenario(object): + """An RPC test scenario using protocol buffers.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def method(self): + """Access the test method name. + + Returns: + The test method name. + """ + raise NotImplementedError() + + @abc.abstractmethod + def serialize_request(self, request): + """Serialize a request protocol buffer. + + Args: + request: A request protocol buffer. + + Returns: + The bytestring serialization of the given request protocol buffer. + """ + raise NotImplementedError() + + @abc.abstractmethod + def deserialize_request(self, request_bytestring): + """Deserialize a request protocol buffer. + + Args: + request_bytestring: The bytestring serialization of a request protocol + buffer. + + Returns: + The request protocol buffer deserialized from the given byte string. + """ + raise NotImplementedError() + + @abc.abstractmethod + def serialize_response(self, response): + """Serialize a response protocol buffer. + + Args: + response: A response protocol buffer. + + Returns: + The bytestring serialization of the given response protocol buffer. + """ + raise NotImplementedError() + + @abc.abstractmethod + def deserialize_response(self, response_bytestring): + """Deserialize a response protocol buffer. + + Args: + response_bytestring: The bytestring serialization of a response protocol + buffer. + + Returns: + The response protocol buffer deserialized from the given byte string. + """ + raise NotImplementedError() + + @abc.abstractmethod + def requests(self): + """Access the sequence of requests for this scenario. + + Returns: + A sequence of request protocol buffers. + """ + raise NotImplementedError() + + @abc.abstractmethod + def response_for_request(self, request): + """Access the response for a particular request. + + Args: + request: A request protocol buffer. + + Returns: + The response protocol buffer appropriate for the given request. + """ + raise NotImplementedError() + + @abc.abstractmethod + def verify_requests(self, experimental_requests): + """Verify the requests transmitted through the system under test. + + Args: + experimental_requests: The request protocol buffers transmitted through + the system under test. + + Returns: + True if the requests satisfy this test scenario; False otherwise. + """ + raise NotImplementedError() + + @abc.abstractmethod + def verify_responses(self, experimental_responses): + """Verify the responses transmitted through the system under test. + + Args: + experimental_responses: The response protocol buffers transmitted through + the system under test. + + Returns: + True if the responses satisfy this test scenario; False otherwise. + """ + raise NotImplementedError() + + +class EmptyScenario(ProtoScenario): + """A scenario that transmits no protocol buffers in either direction.""" + + def method(self): + return 'DivMany' + + def serialize_request(self, request): + raise ValueError('This should not be necessary to call!') + + def deserialize_request(self, request_bytestring): + raise ValueError('This should not be necessary to call!') + + def serialize_response(self, response): + raise ValueError('This should not be necessary to call!') + + def deserialize_response(self, response_bytestring): + raise ValueError('This should not be necessary to call!') + + def requests(self): + return () + + def response_for_request(self, request): + raise ValueError('This should not be necessary to call!') + + def verify_requests(self, experimental_requests): + return not experimental_requests + + def verify_responses(self, experimental_responses): + return not experimental_responses + + +class BidirectionallyUnaryScenario(ProtoScenario): + """A scenario that transmits no protocol buffers in either direction.""" + + _DIVIDEND = 59 + _DIVISOR = 7 + _QUOTIENT = 8 + _REMAINDER = 3 + + _REQUEST = math_pb2.DivArgs(dividend=_DIVIDEND, divisor=_DIVISOR) + _RESPONSE = math_pb2.DivReply(quotient=_QUOTIENT, remainder=_REMAINDER) + + def method(self): + return 'Div' + + def serialize_request(self, request): + return request.SerializeToString() + + def deserialize_request(self, request_bytestring): + return math_pb2.DivArgs.FromString(request_bytestring) + + def serialize_response(self, response): + return response.SerializeToString() + + def deserialize_response(self, response_bytestring): + return math_pb2.DivReply.FromString(response_bytestring) + + def requests(self): + return [self._REQUEST] + + def response_for_request(self, request): + return self._RESPONSE + + def verify_requests(self, experimental_requests): + return tuple(experimental_requests) == (self._REQUEST,) + + def verify_responses(self, experimental_responses): + return tuple(experimental_responses) == (self._RESPONSE,) + + +class BidirectionallyStreamingScenario(ProtoScenario): + """A scenario that transmits no protocol buffers in either direction.""" + + _STREAM_LENGTH = 200 + _REQUESTS = tuple( + math_pb2.DivArgs(dividend=59 + index, divisor=7 + index) + for index in range(_STREAM_LENGTH)) + + def __init__(self): + self._lock = threading.Lock() + self._responses = [] + + def method(self): + return 'DivMany' + + def serialize_request(self, request): + return request.SerializeToString() + + def deserialize_request(self, request_bytestring): + return math_pb2.DivArgs.FromString(request_bytestring) + + def serialize_response(self, response): + return response.SerializeToString() + + def deserialize_response(self, response_bytestring): + return math_pb2.DivReply.FromString(response_bytestring) + + def requests(self): + return self._REQUESTS + + def response_for_request(self, request): + quotient, remainder = divmod(request.dividend, request.divisor) + response = math_pb2.DivReply(quotient=quotient, remainder=remainder) + with self._lock: + self._responses.append(response) + return response + + def verify_requests(self, experimental_requests): + return tuple(experimental_requests) == self._REQUESTS + + def verify_responses(self, experimental_responses): + with self._lock: + return tuple(experimental_responses) == tuple(self._responses) diff --git a/src/python/grpcio_test/grpc_test/_adapter/_test_links.py b/src/python/grpcio_test/grpc_test/_adapter/_test_links.py new file mode 100644 index 00000000..86c7e61b --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_adapter/_test_links.py @@ -0,0 +1,80 @@ +# Copyright 2015, 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. + +"""Links suitable for use in tests.""" + +import threading + +from grpc.framework.base import interfaces + + +class ForeLink(interfaces.ForeLink): + """A ForeLink suitable for use in tests of RearLinks.""" + + def __init__(self, action, rear_link): + self.condition = threading.Condition() + self.tickets = [] + self.action = action + self.rear_link = rear_link + + def accept_back_to_front_ticket(self, ticket): + with self.condition: + self.tickets.append(ticket) + self.condition.notify_all() + action, rear_link = self.action, self.rear_link + + if action is not None: + action(ticket, rear_link) + + def join_rear_link(self, rear_link): + with self.condition: + self.rear_link = rear_link + + +class RearLink(interfaces.RearLink): + """A RearLink suitable for use in tests of ForeLinks.""" + + def __init__(self, action, fore_link): + self.condition = threading.Condition() + self.tickets = [] + self.action = action + self.fore_link = fore_link + + def accept_front_to_back_ticket(self, ticket): + with self.condition: + self.tickets.append(ticket) + self.condition.notify_all() + action, fore_link = self.action, self.fore_link + + if action is not None: + action(ticket, fore_link) + + def join_fore_link(self, fore_link): + with self.condition: + self.fore_link = fore_link diff --git a/src/python/grpcio_test/grpc_test/_core_over_links_base_interface_test.py b/src/python/grpcio_test/grpc_test/_core_over_links_base_interface_test.py new file mode 100644 index 00000000..cafb6b6e --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_core_over_links_base_interface_test.py @@ -0,0 +1,155 @@ +# Copyright 2015, 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. + +"""Tests Base interface compliance of the core-over-gRPC-links stack.""" + +import collections +import logging +import random +import time +import unittest + +from grpc._adapter import _intermediary_low +from grpc._links import invocation +from grpc._links import service +from grpc.beta import interfaces as beta_interfaces +from grpc.framework.core import implementations +from grpc.framework.interfaces.base import utilities +from grpc_test import test_common as grpc_test_common +from grpc_test.framework.common import test_constants +from grpc_test.framework.interfaces.base import test_cases +from grpc_test.framework.interfaces.base import test_interfaces + + +class _SerializationBehaviors( + collections.namedtuple( + '_SerializationBehaviors', + ('request_serializers', 'request_deserializers', 'response_serializers', + 'response_deserializers',))): + pass + + +class _Links( + collections.namedtuple( + '_Links', + ('invocation_end_link', 'invocation_grpc_link', 'service_grpc_link', + 'service_end_link'))): + pass + + +def _serialization_behaviors_from_serializations(serializations): + request_serializers = {} + request_deserializers = {} + response_serializers = {} + response_deserializers = {} + for (group, method), serialization in serializations.iteritems(): + request_serializers[group, method] = serialization.serialize_request + request_deserializers[group, method] = serialization.deserialize_request + response_serializers[group, method] = serialization.serialize_response + response_deserializers[group, method] = serialization.deserialize_response + return _SerializationBehaviors( + request_serializers, request_deserializers, response_serializers, + response_deserializers) + + +class _Implementation(test_interfaces.Implementation): + + def instantiate(self, serializations, servicer): + serialization_behaviors = _serialization_behaviors_from_serializations( + serializations) + invocation_end_link = implementations.invocation_end_link() + service_end_link = implementations.service_end_link( + servicer, test_constants.DEFAULT_TIMEOUT, + test_constants.MAXIMUM_TIMEOUT) + service_grpc_link = service.service_link( + serialization_behaviors.request_deserializers, + serialization_behaviors.response_serializers) + port = service_grpc_link.add_port('[::]:0', None) + channel = _intermediary_low.Channel('localhost:%d' % port, None) + invocation_grpc_link = invocation.invocation_link( + channel, b'localhost', None, + serialization_behaviors.request_serializers, + serialization_behaviors.response_deserializers) + + invocation_end_link.join_link(invocation_grpc_link) + invocation_grpc_link.join_link(invocation_end_link) + service_end_link.join_link(service_grpc_link) + service_grpc_link.join_link(service_end_link) + invocation_grpc_link.start() + service_grpc_link.start() + return invocation_end_link, service_end_link, ( + invocation_grpc_link, service_grpc_link) + + def destantiate(self, memo): + invocation_grpc_link, service_grpc_link = memo + invocation_grpc_link.stop() + service_grpc_link.begin_stop() + service_grpc_link.end_stop() + + def invocation_initial_metadata(self): + return grpc_test_common.INVOCATION_INITIAL_METADATA + + def service_initial_metadata(self): + return grpc_test_common.SERVICE_INITIAL_METADATA + + def invocation_completion(self): + return utilities.completion(None, None, None) + + def service_completion(self): + return utilities.completion( + grpc_test_common.SERVICE_TERMINAL_METADATA, + beta_interfaces.StatusCode.OK, grpc_test_common.DETAILS) + + def metadata_transmitted(self, original_metadata, transmitted_metadata): + return original_metadata is None or grpc_test_common.metadata_transmitted( + original_metadata, transmitted_metadata) + + def completion_transmitted(self, original_completion, transmitted_completion): + if (original_completion.terminal_metadata is not None and + not grpc_test_common.metadata_transmitted( + original_completion.terminal_metadata, + transmitted_completion.terminal_metadata)): + return False + elif original_completion.code is not transmitted_completion.code: + return False + elif original_completion.message != transmitted_completion.message: + return False + else: + return True + + +def load_tests(loader, tests, pattern): + return unittest.TestSuite( + tests=tuple( + loader.loadTestsFromTestCase(test_case_class) + for test_case_class in test_cases.test_cases(_Implementation()))) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/_crust_over_core_over_links_face_interface_test.py b/src/python/grpcio_test/grpc_test/_crust_over_core_over_links_face_interface_test.py new file mode 100644 index 00000000..a4d4dee3 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_crust_over_core_over_links_face_interface_test.py @@ -0,0 +1,161 @@ +# Copyright 2015, 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. + +"""Tests Face compliance of the crust-over-core-over-gRPC-links stack.""" + +import collections +import unittest + +from grpc._adapter import _intermediary_low +from grpc._links import invocation +from grpc._links import service +from grpc.beta import interfaces as beta_interfaces +from grpc.framework.core import implementations as core_implementations +from grpc.framework.crust import implementations as crust_implementations +from grpc.framework.foundation import logging_pool +from grpc.framework.interfaces.links import utilities +from grpc_test import test_common as grpc_test_common +from grpc_test.framework.common import test_constants +from grpc_test.framework.interfaces.face import test_cases +from grpc_test.framework.interfaces.face import test_interfaces + + +class _SerializationBehaviors( + collections.namedtuple( + '_SerializationBehaviors', + ('request_serializers', 'request_deserializers', 'response_serializers', + 'response_deserializers',))): + pass + + +def _serialization_behaviors_from_test_methods(test_methods): + request_serializers = {} + request_deserializers = {} + response_serializers = {} + response_deserializers = {} + for (group, method), test_method in test_methods.iteritems(): + request_serializers[group, method] = test_method.serialize_request + request_deserializers[group, method] = test_method.deserialize_request + response_serializers[group, method] = test_method.serialize_response + response_deserializers[group, method] = test_method.deserialize_response + return _SerializationBehaviors( + request_serializers, request_deserializers, response_serializers, + response_deserializers) + + +class _Implementation(test_interfaces.Implementation): + + def instantiate( + self, methods, method_implementations, multi_method_implementation): + pool = logging_pool.pool(test_constants.POOL_SIZE) + servicer = crust_implementations.servicer( + method_implementations, multi_method_implementation, pool) + serialization_behaviors = _serialization_behaviors_from_test_methods( + methods) + invocation_end_link = core_implementations.invocation_end_link() + service_end_link = core_implementations.service_end_link( + servicer, test_constants.DEFAULT_TIMEOUT, + test_constants.MAXIMUM_TIMEOUT) + service_grpc_link = service.service_link( + serialization_behaviors.request_deserializers, + serialization_behaviors.response_serializers) + port = service_grpc_link.add_port('[::]:0', None) + channel = _intermediary_low.Channel('localhost:%d' % port, None) + invocation_grpc_link = invocation.invocation_link( + channel, b'localhost', None, + serialization_behaviors.request_serializers, + serialization_behaviors.response_deserializers) + + invocation_end_link.join_link(invocation_grpc_link) + invocation_grpc_link.join_link(invocation_end_link) + service_grpc_link.join_link(service_end_link) + service_end_link.join_link(service_grpc_link) + service_end_link.start() + invocation_end_link.start() + invocation_grpc_link.start() + service_grpc_link.start() + + generic_stub = crust_implementations.generic_stub(invocation_end_link, pool) + # TODO(nathaniel): Add a "groups" attribute to _digest.TestServiceDigest. + group = next(iter(methods))[0] + # TODO(nathaniel): Add a "cardinalities_by_group" attribute to + # _digest.TestServiceDigest. + cardinalities = { + method: method_object.cardinality() + for (group, method), method_object in methods.iteritems()} + dynamic_stub = crust_implementations.dynamic_stub( + invocation_end_link, group, cardinalities, pool) + + return generic_stub, {group: dynamic_stub}, ( + invocation_end_link, invocation_grpc_link, service_grpc_link, + service_end_link, pool) + + def destantiate(self, memo): + (invocation_end_link, invocation_grpc_link, service_grpc_link, + service_end_link, pool) = memo + invocation_end_link.stop(0).wait() + invocation_grpc_link.stop() + service_grpc_link.begin_stop() + service_end_link.stop(0).wait() + service_grpc_link.end_stop() + invocation_end_link.join_link(utilities.NULL_LINK) + invocation_grpc_link.join_link(utilities.NULL_LINK) + service_grpc_link.join_link(utilities.NULL_LINK) + service_end_link.join_link(utilities.NULL_LINK) + pool.shutdown(wait=True) + + def invocation_metadata(self): + return grpc_test_common.INVOCATION_INITIAL_METADATA + + def initial_metadata(self): + return grpc_test_common.SERVICE_INITIAL_METADATA + + def terminal_metadata(self): + return grpc_test_common.SERVICE_TERMINAL_METADATA + + def code(self): + return beta_interfaces.StatusCode.OK + + def details(self): + return grpc_test_common.DETAILS + + def metadata_transmitted(self, original_metadata, transmitted_metadata): + return original_metadata is None or grpc_test_common.metadata_transmitted( + original_metadata, transmitted_metadata) + + +def load_tests(loader, tests, pattern): + return unittest.TestSuite( + tests=tuple( + loader.loadTestsFromTestCase(test_case_class) + for test_case_class in test_cases.test_cases(_Implementation()))) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/_cython/.gitignore b/src/python/grpcio_test/grpc_test/_cython/.gitignore new file mode 100644 index 00000000..c3150292 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_cython/.gitignore @@ -0,0 +1,7 @@ +*.h +*.c +*.a +*.so +*.dll +*.pyc +*.pyd diff --git a/src/python/grpcio_test/grpc_test/_cython/__init__.py b/src/python/grpcio_test/grpc_test/_cython/__init__.py new file mode 100644 index 00000000..b8939880 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_cython/__init__.py @@ -0,0 +1,28 @@ +# Copyright 2015, 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. diff --git a/src/python/grpcio_test/grpc_test/_cython/adapter_low_test.py b/src/python/grpcio_test/grpc_test/_cython/adapter_low_test.py new file mode 100644 index 00000000..f1bec238 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_cython/adapter_low_test.py @@ -0,0 +1,187 @@ +# Copyright 2015, 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. + +# Fork of grpc._adapter._low_test; the grpc._cython.types adapter in +# grpc._cython.low should transparently support the semantics expected of +# grpc._adapter._low. + +import time +import unittest + +from grpc._adapter import _types +from grpc._cython import adapter_low as _low + + +class InsecureServerInsecureClient(unittest.TestCase): + + def setUp(self): + self.server_completion_queue = _low.CompletionQueue() + self.server = _low.Server(self.server_completion_queue, []) + self.port = self.server.add_http2_port('[::]:0') + self.client_completion_queue = _low.CompletionQueue() + self.client_channel = _low.Channel('localhost:%d'%self.port, []) + + self.server.start() + + def tearDown(self): + self.server.shutdown() + del self.client_channel + + self.client_completion_queue.shutdown() + while (self.client_completion_queue.next().type != + _types.EventType.QUEUE_SHUTDOWN): + pass + self.server_completion_queue.shutdown() + while (self.server_completion_queue.next().type != + _types.EventType.QUEUE_SHUTDOWN): + pass + + del self.client_completion_queue + del self.server_completion_queue + del self.server + + @unittest.skip('TODO(atash): implement grpc._cython.adapter_low') + def testEcho(self): + DEADLINE = time.time()+5 + DEADLINE_TOLERANCE = 0.25 + CLIENT_METADATA_ASCII_KEY = 'key' + CLIENT_METADATA_ASCII_VALUE = 'val' + CLIENT_METADATA_BIN_KEY = 'key-bin' + CLIENT_METADATA_BIN_VALUE = b'\0'*1000 + SERVER_INITIAL_METADATA_KEY = 'init_me_me_me' + SERVER_INITIAL_METADATA_VALUE = 'whodawha?' + SERVER_TRAILING_METADATA_KEY = 'california_is_in_a_drought' + SERVER_TRAILING_METADATA_VALUE = 'zomg it is' + SERVER_STATUS_CODE = _types.StatusCode.OK + SERVER_STATUS_DETAILS = 'our work is never over' + REQUEST = 'in death a member of project mayhem has a name' + RESPONSE = 'his name is robert paulson' + METHOD = 'twinkies' + HOST = 'hostess' + server_request_tag = object() + request_call_result = self.server.request_call(self.server_completion_queue, + server_request_tag) + + self.assertEqual(_types.CallError.OK, request_call_result) + + client_call_tag = object() + client_call = self.client_channel.create_call(self.client_completion_queue, + METHOD, HOST, DEADLINE) + client_initial_metadata = [ + (CLIENT_METADATA_ASCII_KEY, CLIENT_METADATA_ASCII_VALUE), + (CLIENT_METADATA_BIN_KEY, CLIENT_METADATA_BIN_VALUE)] + client_start_batch_result = client_call.start_batch([ + _types.OpArgs.send_initial_metadata(client_initial_metadata), + _types.OpArgs.send_message(REQUEST), + _types.OpArgs.send_close_from_client(), + _types.OpArgs.recv_initial_metadata(), + _types.OpArgs.recv_message(), + _types.OpArgs.recv_status_on_client() + ], client_call_tag) + self.assertEqual(_types.CallError.OK, client_start_batch_result) + + request_event = self.server_completion_queue.next(DEADLINE) + self.assertEqual(_types.EventType.OP_COMPLETE, request_event.type) + self.assertIsInstance(request_event.call, _low.Call) + self.assertIs(server_request_tag, request_event.tag) + self.assertEqual(1, len(request_event.results)) + self.assertEqual(dict(client_initial_metadata), + dict(request_event.results[0].initial_metadata)) + self.assertEqual(METHOD, request_event.call_details.method) + self.assertEqual(HOST, request_event.call_details.host) + self.assertLess(abs(DEADLINE - request_event.call_details.deadline), + DEADLINE_TOLERANCE) + + server_call_tag = object() + server_call = request_event.call + server_initial_metadata = [ + (SERVER_INITIAL_METADATA_KEY, SERVER_INITIAL_METADATA_VALUE)] + server_trailing_metadata = [ + (SERVER_TRAILING_METADATA_KEY, SERVER_TRAILING_METADATA_VALUE)] + server_start_batch_result = server_call.start_batch([ + _types.OpArgs.send_initial_metadata(server_initial_metadata), + _types.OpArgs.recv_message(), + _types.OpArgs.send_message(RESPONSE), + _types.OpArgs.recv_close_on_server(), + _types.OpArgs.send_status_from_server( + server_trailing_metadata, SERVER_STATUS_CODE, SERVER_STATUS_DETAILS) + ], server_call_tag) + self.assertEqual(_types.CallError.OK, server_start_batch_result) + + client_event = self.client_completion_queue.next(DEADLINE) + server_event = self.server_completion_queue.next(DEADLINE) + + self.assertEqual(6, len(client_event.results)) + found_client_op_types = set() + for client_result in client_event.results: + # we expect each op type to be unique + self.assertNotIn(client_result.type, found_client_op_types) + found_client_op_types.add(client_result.type) + if client_result.type == _types.OpType.RECV_INITIAL_METADATA: + self.assertEqual(dict(server_initial_metadata), + dict(client_result.initial_metadata)) + elif client_result.type == _types.OpType.RECV_MESSAGE: + self.assertEqual(RESPONSE, client_result.message) + elif client_result.type == _types.OpType.RECV_STATUS_ON_CLIENT: + self.assertEqual(dict(server_trailing_metadata), + dict(client_result.trailing_metadata)) + self.assertEqual(SERVER_STATUS_DETAILS, client_result.status.details) + self.assertEqual(SERVER_STATUS_CODE, client_result.status.code) + self.assertEqual(set([ + _types.OpType.SEND_INITIAL_METADATA, + _types.OpType.SEND_MESSAGE, + _types.OpType.SEND_CLOSE_FROM_CLIENT, + _types.OpType.RECV_INITIAL_METADATA, + _types.OpType.RECV_MESSAGE, + _types.OpType.RECV_STATUS_ON_CLIENT + ]), found_client_op_types) + + self.assertEqual(5, len(server_event.results)) + found_server_op_types = set() + for server_result in server_event.results: + self.assertNotIn(client_result.type, found_server_op_types) + found_server_op_types.add(server_result.type) + if server_result.type == _types.OpType.RECV_MESSAGE: + self.assertEqual(REQUEST, server_result.message) + elif server_result.type == _types.OpType.RECV_CLOSE_ON_SERVER: + self.assertFalse(server_result.cancelled) + self.assertEqual(set([ + _types.OpType.SEND_INITIAL_METADATA, + _types.OpType.RECV_MESSAGE, + _types.OpType.SEND_MESSAGE, + _types.OpType.RECV_CLOSE_ON_SERVER, + _types.OpType.SEND_STATUS_FROM_SERVER + ]), found_server_op_types) + + del client_call + del server_call + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/_cython/cygrpc_test.py b/src/python/grpcio_test/grpc_test/_cython/cygrpc_test.py new file mode 100644 index 00000000..637506b4 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_cython/cygrpc_test.py @@ -0,0 +1,262 @@ +# Copyright 2015, 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. + +import time +import unittest + +from grpc._cython import cygrpc +from grpc_test._cython import test_utilities + + +class TypeSmokeTest(unittest.TestCase): + + def testStringsInUtilitiesUpDown(self): + self.assertEqual(0, cygrpc.StatusCode.ok) + metadatum = cygrpc.Metadatum('a', 'b') + self.assertEqual('a'.encode(), metadatum.key) + self.assertEqual('b'.encode(), metadatum.value) + metadata = cygrpc.Metadata([metadatum]) + self.assertEqual(1, len(metadata)) + self.assertEqual(metadatum.key, metadata[0].key) + + def testMetadataIteration(self): + metadata = cygrpc.Metadata([ + cygrpc.Metadatum('a', 'b'), cygrpc.Metadatum('c', 'd')]) + iterator = iter(metadata) + metadatum = next(iterator) + self.assertIsInstance(metadatum, cygrpc.Metadatum) + self.assertEqual(metadatum.key, 'a'.encode()) + self.assertEqual(metadatum.value, 'b'.encode()) + metadatum = next(iterator) + self.assertIsInstance(metadatum, cygrpc.Metadatum) + self.assertEqual(metadatum.key, 'c'.encode()) + self.assertEqual(metadatum.value, 'd'.encode()) + with self.assertRaises(StopIteration): + next(iterator) + + def testOperationsIteration(self): + operations = cygrpc.Operations([ + cygrpc.operation_send_message('asdf')]) + iterator = iter(operations) + operation = next(iterator) + self.assertIsInstance(operation, cygrpc.Operation) + # `Operation`s are write-only structures; can't directly debug anything out + # of them. Just check that we stop iterating. + with self.assertRaises(StopIteration): + next(iterator) + + def testTimespec(self): + now = time.time() + timespec = cygrpc.Timespec(now) + self.assertAlmostEqual(now, float(timespec), places=8) + + def testCompletionQueueUpDown(self): + completion_queue = cygrpc.CompletionQueue() + del completion_queue + + def testServerUpDown(self): + server = cygrpc.Server(cygrpc.ChannelArgs([])) + del server + + def testChannelUpDown(self): + channel = cygrpc.Channel('[::]:0', cygrpc.ChannelArgs([])) + del channel + + @unittest.skip('TODO(atash): undo skip after #2229 is merged') + def testServerStartNoExplicitShutdown(self): + server = cygrpc.Server() + completion_queue = cygrpc.CompletionQueue() + server.register_completion_queue(completion_queue) + port = server.add_http2_port('[::]:0') + self.assertIsInstance(port, int) + server.start() + del server + + @unittest.skip('TODO(atash): undo skip after #2229 is merged') + def testServerStartShutdown(self): + completion_queue = cygrpc.CompletionQueue() + server = cygrpc.Server() + server.add_http2_port('[::]:0') + server.register_completion_queue(completion_queue) + server.start() + shutdown_tag = object() + server.shutdown(completion_queue, shutdown_tag) + event = completion_queue.poll() + self.assertEqual(cygrpc.CompletionType.operation_complete, event.type) + self.assertIs(shutdown_tag, event.tag) + del server + del completion_queue + + +class InsecureServerInsecureClient(unittest.TestCase): + + def setUp(self): + self.server_completion_queue = cygrpc.CompletionQueue() + self.server = cygrpc.Server() + self.server.register_completion_queue(self.server_completion_queue) + self.port = self.server.add_http2_port('[::]:0') + self.server.start() + self.client_completion_queue = cygrpc.CompletionQueue() + self.client_channel = cygrpc.Channel('localhost:{}'.format(self.port)) + + def tearDown(self): + del self.server + del self.client_completion_queue + del self.server_completion_queue + + def testEcho(self): + DEADLINE = time.time()+5 + DEADLINE_TOLERANCE = 0.25 + CLIENT_METADATA_ASCII_KEY = b'key' + CLIENT_METADATA_ASCII_VALUE = b'val' + CLIENT_METADATA_BIN_KEY = b'key-bin' + CLIENT_METADATA_BIN_VALUE = b'\0'*1000 + SERVER_INITIAL_METADATA_KEY = b'init_me_me_me' + SERVER_INITIAL_METADATA_VALUE = b'whodawha?' + SERVER_TRAILING_METADATA_KEY = b'California_is_in_a_drought' + SERVER_TRAILING_METADATA_VALUE = b'zomg it is' + SERVER_STATUS_CODE = cygrpc.StatusCode.ok + SERVER_STATUS_DETAILS = b'our work is never over' + REQUEST = b'in death a member of project mayhem has a name' + RESPONSE = b'his name is robert paulson' + METHOD = b'twinkies' + HOST = b'hostess' + + cygrpc_deadline = cygrpc.Timespec(DEADLINE) + + server_request_tag = object() + request_call_result = self.server.request_call( + self.server_completion_queue, self.server_completion_queue, + server_request_tag) + + self.assertEqual(cygrpc.CallError.ok, request_call_result) + + client_call_tag = object() + client_call = self.client_channel.create_call(self.client_completion_queue, + METHOD, HOST, cygrpc_deadline) + client_initial_metadata = cygrpc.Metadata([ + cygrpc.Metadatum(CLIENT_METADATA_ASCII_KEY, + CLIENT_METADATA_ASCII_VALUE), + cygrpc.Metadatum(CLIENT_METADATA_BIN_KEY, CLIENT_METADATA_BIN_VALUE)]) + client_start_batch_result = client_call.start_batch(cygrpc.Operations([ + cygrpc.operation_send_initial_metadata(client_initial_metadata), + cygrpc.operation_send_message(REQUEST), + cygrpc.operation_send_close_from_client(), + cygrpc.operation_receive_initial_metadata(), + cygrpc.operation_receive_message(), + cygrpc.operation_receive_status_on_client() + ]), client_call_tag) + self.assertEqual(cygrpc.CallError.ok, client_start_batch_result) + client_event_future = test_utilities.CompletionQueuePollFuture( + self.client_completion_queue, cygrpc_deadline) + + request_event = self.server_completion_queue.poll(cygrpc_deadline) + self.assertEqual(cygrpc.CompletionType.operation_complete, + request_event.type) + self.assertIsInstance(request_event.operation_call, cygrpc.Call) + self.assertIs(server_request_tag, request_event.tag) + self.assertEqual(0, len(request_event.batch_operations)) + self.assertEqual(dict(client_initial_metadata), + dict(request_event.request_metadata)) + self.assertEqual(METHOD, request_event.request_call_details.method) + self.assertEqual(HOST, request_event.request_call_details.host) + self.assertLess( + abs(DEADLINE - float(request_event.request_call_details.deadline)), + DEADLINE_TOLERANCE) + + server_call_tag = object() + server_call = request_event.operation_call + server_initial_metadata = cygrpc.Metadata([ + cygrpc.Metadatum(SERVER_INITIAL_METADATA_KEY, + SERVER_INITIAL_METADATA_VALUE)]) + server_trailing_metadata = cygrpc.Metadata([ + cygrpc.Metadatum(SERVER_TRAILING_METADATA_KEY, + SERVER_TRAILING_METADATA_VALUE)]) + server_start_batch_result = server_call.start_batch([ + cygrpc.operation_send_initial_metadata(server_initial_metadata), + cygrpc.operation_receive_message(), + cygrpc.operation_send_message(RESPONSE), + cygrpc.operation_receive_close_on_server(), + cygrpc.operation_send_status_from_server( + server_trailing_metadata, SERVER_STATUS_CODE, SERVER_STATUS_DETAILS) + ], server_call_tag) + self.assertEqual(cygrpc.CallError.ok, server_start_batch_result) + + client_event = client_event_future.result() + server_event = self.server_completion_queue.poll(cygrpc_deadline) + + self.assertEqual(6, len(client_event.batch_operations)) + found_client_op_types = set() + for client_result in client_event.batch_operations: + # we expect each op type to be unique + self.assertNotIn(client_result.type, found_client_op_types) + found_client_op_types.add(client_result.type) + if client_result.type == cygrpc.OperationType.receive_initial_metadata: + self.assertEqual(dict(server_initial_metadata), + dict(client_result.received_metadata)) + elif client_result.type == cygrpc.OperationType.receive_message: + self.assertEqual(RESPONSE, client_result.received_message.bytes()) + elif client_result.type == cygrpc.OperationType.receive_status_on_client: + self.assertEqual(dict(server_trailing_metadata), + dict(client_result.received_metadata)) + self.assertEqual(SERVER_STATUS_DETAILS, + client_result.received_status_details) + self.assertEqual(SERVER_STATUS_CODE, client_result.received_status_code) + self.assertEqual(set([ + cygrpc.OperationType.send_initial_metadata, + cygrpc.OperationType.send_message, + cygrpc.OperationType.send_close_from_client, + cygrpc.OperationType.receive_initial_metadata, + cygrpc.OperationType.receive_message, + cygrpc.OperationType.receive_status_on_client + ]), found_client_op_types) + + self.assertEqual(5, len(server_event.batch_operations)) + found_server_op_types = set() + for server_result in server_event.batch_operations: + self.assertNotIn(client_result.type, found_server_op_types) + found_server_op_types.add(server_result.type) + if server_result.type == cygrpc.OperationType.receive_message: + self.assertEqual(REQUEST, server_result.received_message.bytes()) + elif server_result.type == cygrpc.OperationType.receive_close_on_server: + self.assertFalse(server_result.received_cancelled) + self.assertEqual(set([ + cygrpc.OperationType.send_initial_metadata, + cygrpc.OperationType.receive_message, + cygrpc.OperationType.send_message, + cygrpc.OperationType.receive_close_on_server, + cygrpc.OperationType.send_status_from_server + ]), found_server_op_types) + + del client_call + del server_call + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/_cython/test_utilities.py b/src/python/grpcio_test/grpc_test/_cython/test_utilities.py new file mode 100644 index 00000000..21ea3075 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_cython/test_utilities.py @@ -0,0 +1,46 @@ +# Copyright 2015, 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. + +import threading + +from grpc._cython._cygrpc import completion_queue + + +class CompletionQueuePollFuture: + + def __init__(self, completion_queue, deadline): + def poller_function(): + self._event_result = completion_queue.poll(deadline) + self._event_result = None + self._thread = threading.Thread(target=poller_function) + self._thread.start() + + def result(self): + self._thread.join() + return self._event_result diff --git a/src/python/grpcio_test/grpc_test/_junkdrawer/__init__.py b/src/python/grpcio_test/grpc_test/_junkdrawer/__init__.py new file mode 100644 index 00000000..70865191 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_junkdrawer/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, 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. + + diff --git a/src/python/grpcio_test/grpc_test/_junkdrawer/math_pb2.py b/src/python/grpcio_test/grpc_test/_junkdrawer/math_pb2.py new file mode 100644 index 00000000..20165955 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_junkdrawer/math_pb2.py @@ -0,0 +1,266 @@ +# Copyright 2015, 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. + +# TODO(nathaniel): Remove this from source control after having made +# generation from the math.proto source part of GRPC's build-and-test +# process. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: math.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='math.proto', + package='math', + serialized_pb=_b('\n\nmath.proto\x12\x04math\",\n\x07\x44ivArgs\x12\x10\n\x08\x64ividend\x18\x01 \x02(\x03\x12\x0f\n\x07\x64ivisor\x18\x02 \x02(\x03\"/\n\x08\x44ivReply\x12\x10\n\x08quotient\x18\x01 \x02(\x03\x12\x11\n\tremainder\x18\x02 \x02(\x03\"\x18\n\x07\x46ibArgs\x12\r\n\x05limit\x18\x01 \x01(\x03\"\x12\n\x03Num\x12\x0b\n\x03num\x18\x01 \x02(\x03\"\x19\n\x08\x46ibReply\x12\r\n\x05\x63ount\x18\x01 \x02(\x03\x32\xa4\x01\n\x04Math\x12&\n\x03\x44iv\x12\r.math.DivArgs\x1a\x0e.math.DivReply\"\x00\x12.\n\x07\x44ivMany\x12\r.math.DivArgs\x1a\x0e.math.DivReply\"\x00(\x01\x30\x01\x12#\n\x03\x46ib\x12\r.math.FibArgs\x1a\t.math.Num\"\x00\x30\x01\x12\x1f\n\x03Sum\x12\t.math.Num\x1a\t.math.Num\"\x00(\x01') +) +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + + + + +_DIVARGS = _descriptor.Descriptor( + name='DivArgs', + full_name='math.DivArgs', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='dividend', full_name='math.DivArgs.dividend', index=0, + number=1, type=3, cpp_type=2, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='divisor', full_name='math.DivArgs.divisor', index=1, + number=2, type=3, cpp_type=2, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=20, + serialized_end=64, +) + + +_DIVREPLY = _descriptor.Descriptor( + name='DivReply', + full_name='math.DivReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='quotient', full_name='math.DivReply.quotient', index=0, + number=1, type=3, cpp_type=2, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='remainder', full_name='math.DivReply.remainder', index=1, + number=2, type=3, cpp_type=2, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=66, + serialized_end=113, +) + + +_FIBARGS = _descriptor.Descriptor( + name='FibArgs', + full_name='math.FibArgs', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='limit', full_name='math.FibArgs.limit', index=0, + number=1, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=115, + serialized_end=139, +) + + +_NUM = _descriptor.Descriptor( + name='Num', + full_name='math.Num', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='num', full_name='math.Num.num', index=0, + number=1, type=3, cpp_type=2, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=141, + serialized_end=159, +) + + +_FIBREPLY = _descriptor.Descriptor( + name='FibReply', + full_name='math.FibReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='count', full_name='math.FibReply.count', index=0, + number=1, type=3, cpp_type=2, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=161, + serialized_end=186, +) + +DESCRIPTOR.message_types_by_name['DivArgs'] = _DIVARGS +DESCRIPTOR.message_types_by_name['DivReply'] = _DIVREPLY +DESCRIPTOR.message_types_by_name['FibArgs'] = _FIBARGS +DESCRIPTOR.message_types_by_name['Num'] = _NUM +DESCRIPTOR.message_types_by_name['FibReply'] = _FIBREPLY + +DivArgs = _reflection.GeneratedProtocolMessageType('DivArgs', (_message.Message,), dict( + DESCRIPTOR = _DIVARGS, + __module__ = 'math_pb2' + # @@protoc_insertion_point(class_scope:math.DivArgs) + )) +_sym_db.RegisterMessage(DivArgs) + +DivReply = _reflection.GeneratedProtocolMessageType('DivReply', (_message.Message,), dict( + DESCRIPTOR = _DIVREPLY, + __module__ = 'math_pb2' + # @@protoc_insertion_point(class_scope:math.DivReply) + )) +_sym_db.RegisterMessage(DivReply) + +FibArgs = _reflection.GeneratedProtocolMessageType('FibArgs', (_message.Message,), dict( + DESCRIPTOR = _FIBARGS, + __module__ = 'math_pb2' + # @@protoc_insertion_point(class_scope:math.FibArgs) + )) +_sym_db.RegisterMessage(FibArgs) + +Num = _reflection.GeneratedProtocolMessageType('Num', (_message.Message,), dict( + DESCRIPTOR = _NUM, + __module__ = 'math_pb2' + # @@protoc_insertion_point(class_scope:math.Num) + )) +_sym_db.RegisterMessage(Num) + +FibReply = _reflection.GeneratedProtocolMessageType('FibReply', (_message.Message,), dict( + DESCRIPTOR = _FIBREPLY, + __module__ = 'math_pb2' + # @@protoc_insertion_point(class_scope:math.FibReply) + )) +_sym_db.RegisterMessage(FibReply) + + +# @@protoc_insertion_point(module_scope) diff --git a/src/python/grpcio_test/grpc_test/_junkdrawer/stock_pb2.py b/src/python/grpcio_test/grpc_test/_junkdrawer/stock_pb2.py new file mode 100644 index 00000000..eef18f82 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_junkdrawer/stock_pb2.py @@ -0,0 +1,152 @@ +# Copyright 2015, 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. + +# TODO(nathaniel): Remove this from source control after having made +# generation from the stock.proto source part of GRPC's build-and-test +# process. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: stock.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='stock.proto', + package='stock', + serialized_pb=_b('\n\x0bstock.proto\x12\x05stock\">\n\x0cStockRequest\x12\x0e\n\x06symbol\x18\x01 \x01(\t\x12\x1e\n\x13num_trades_to_watch\x18\x02 \x01(\x05:\x01\x30\"+\n\nStockReply\x12\r\n\x05price\x18\x01 \x01(\x02\x12\x0e\n\x06symbol\x18\x02 \x01(\t2\x96\x02\n\x05Stock\x12=\n\x11GetLastTradePrice\x12\x13.stock.StockRequest\x1a\x11.stock.StockReply\"\x00\x12I\n\x19GetLastTradePriceMultiple\x12\x13.stock.StockRequest\x1a\x11.stock.StockReply\"\x00(\x01\x30\x01\x12?\n\x11WatchFutureTrades\x12\x13.stock.StockRequest\x1a\x11.stock.StockReply\"\x00\x30\x01\x12\x42\n\x14GetHighestTradePrice\x12\x13.stock.StockRequest\x1a\x11.stock.StockReply\"\x00(\x01') +) +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + + + + +_STOCKREQUEST = _descriptor.Descriptor( + name='StockRequest', + full_name='stock.StockRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='symbol', full_name='stock.StockRequest.symbol', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='num_trades_to_watch', full_name='stock.StockRequest.num_trades_to_watch', index=1, + number=2, type=5, cpp_type=1, label=1, + has_default_value=True, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=22, + serialized_end=84, +) + + +_STOCKREPLY = _descriptor.Descriptor( + name='StockReply', + full_name='stock.StockReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='price', full_name='stock.StockReply.price', index=0, + number=1, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='symbol', full_name='stock.StockReply.symbol', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=86, + serialized_end=129, +) + +DESCRIPTOR.message_types_by_name['StockRequest'] = _STOCKREQUEST +DESCRIPTOR.message_types_by_name['StockReply'] = _STOCKREPLY + +StockRequest = _reflection.GeneratedProtocolMessageType('StockRequest', (_message.Message,), dict( + DESCRIPTOR = _STOCKREQUEST, + __module__ = 'stock_pb2' + # @@protoc_insertion_point(class_scope:stock.StockRequest) + )) +_sym_db.RegisterMessage(StockRequest) + +StockReply = _reflection.GeneratedProtocolMessageType('StockReply', (_message.Message,), dict( + DESCRIPTOR = _STOCKREPLY, + __module__ = 'stock_pb2' + # @@protoc_insertion_point(class_scope:stock.StockReply) + )) +_sym_db.RegisterMessage(StockReply) + + +# @@protoc_insertion_point(module_scope) diff --git a/src/python/grpcio_test/grpc_test/_links/__init__.py b/src/python/grpcio_test/grpc_test/_links/__init__.py new file mode 100644 index 00000000..70865191 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_links/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, 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. + + diff --git a/src/python/grpcio_test/grpc_test/_links/_lonely_invocation_link_test.py b/src/python/grpcio_test/grpc_test/_links/_lonely_invocation_link_test.py new file mode 100644 index 00000000..8e12e8cc --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_links/_lonely_invocation_link_test.py @@ -0,0 +1,88 @@ +# Copyright 2015, 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. + +"""A test of invocation-side code unconnected to an RPC server.""" + +import unittest + +from grpc._adapter import _intermediary_low +from grpc._links import invocation +from grpc.framework.interfaces.links import links +from grpc_test.framework.common import test_constants +from grpc_test.framework.interfaces.links import test_cases +from grpc_test.framework.interfaces.links import test_utilities + +_NULL_BEHAVIOR = lambda unused_argument: None + + +class LonelyInvocationLinkTest(unittest.TestCase): + + def testUpAndDown(self): + channel = _intermediary_low.Channel('nonexistent:54321', None) + invocation_link = invocation.invocation_link( + channel, 'nonexistent', None, {}, {}) + + invocation_link.start() + invocation_link.stop() + + def _test_lonely_invocation_with_termination(self, termination): + test_operation_id = object() + test_group = 'test package.Test Service' + test_method = 'test method' + invocation_link_mate = test_utilities.RecordingLink() + + channel = _intermediary_low.Channel('nonexistent:54321', None) + invocation_link = invocation.invocation_link( + channel, 'nonexistent', None, {}, {}) + invocation_link.join_link(invocation_link_mate) + invocation_link.start() + + ticket = links.Ticket( + test_operation_id, 0, test_group, test_method, + links.Ticket.Subscription.FULL, test_constants.SHORT_TIMEOUT, 1, None, + None, None, None, None, termination, None) + invocation_link.accept_ticket(ticket) + invocation_link_mate.block_until_tickets_satisfy(test_cases.terminated) + + invocation_link.stop() + + self.assertIsNot( + invocation_link_mate.tickets()[-1].termination, + links.Ticket.Termination.COMPLETION) + + def testLonelyInvocationLinkWithCommencementTicket(self): + self._test_lonely_invocation_with_termination(None) + + def testLonelyInvocationLinkWithEntireTicket(self): + self._test_lonely_invocation_with_termination( + links.Ticket.Termination.COMPLETION) + + +if __name__ == '__main__': + unittest.main() diff --git a/src/python/grpcio_test/grpc_test/_links/_proto_scenarios.py b/src/python/grpcio_test/grpc_test/_links/_proto_scenarios.py new file mode 100644 index 00000000..0d74d662 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_links/_proto_scenarios.py @@ -0,0 +1,261 @@ +# Copyright 2015, 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. + +"""Test scenarios using protocol buffers.""" + +import abc +import threading + +from grpc_test._junkdrawer import math_pb2 +from grpc_test.framework.common import test_constants + + +class ProtoScenario(object): + """An RPC test scenario using protocol buffers.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def group_and_method(self): + """Access the test group and method. + + Returns: + The test group and method as a pair. + """ + raise NotImplementedError() + + @abc.abstractmethod + def serialize_request(self, request): + """Serialize a request protocol buffer. + + Args: + request: A request protocol buffer. + + Returns: + The bytestring serialization of the given request protocol buffer. + """ + raise NotImplementedError() + + @abc.abstractmethod + def deserialize_request(self, request_bytestring): + """Deserialize a request protocol buffer. + + Args: + request_bytestring: The bytestring serialization of a request protocol + buffer. + + Returns: + The request protocol buffer deserialized from the given byte string. + """ + raise NotImplementedError() + + @abc.abstractmethod + def serialize_response(self, response): + """Serialize a response protocol buffer. + + Args: + response: A response protocol buffer. + + Returns: + The bytestring serialization of the given response protocol buffer. + """ + raise NotImplementedError() + + @abc.abstractmethod + def deserialize_response(self, response_bytestring): + """Deserialize a response protocol buffer. + + Args: + response_bytestring: The bytestring serialization of a response protocol + buffer. + + Returns: + The response protocol buffer deserialized from the given byte string. + """ + raise NotImplementedError() + + @abc.abstractmethod + def requests(self): + """Access the sequence of requests for this scenario. + + Returns: + A sequence of request protocol buffers. + """ + raise NotImplementedError() + + @abc.abstractmethod + def response_for_request(self, request): + """Access the response for a particular request. + + Args: + request: A request protocol buffer. + + Returns: + The response protocol buffer appropriate for the given request. + """ + raise NotImplementedError() + + @abc.abstractmethod + def verify_requests(self, experimental_requests): + """Verify the requests transmitted through the system under test. + + Args: + experimental_requests: The request protocol buffers transmitted through + the system under test. + + Returns: + True if the requests satisfy this test scenario; False otherwise. + """ + raise NotImplementedError() + + @abc.abstractmethod + def verify_responses(self, experimental_responses): + """Verify the responses transmitted through the system under test. + + Args: + experimental_responses: The response protocol buffers transmitted through + the system under test. + + Returns: + True if the responses satisfy this test scenario; False otherwise. + """ + raise NotImplementedError() + + +class EmptyScenario(ProtoScenario): + """A scenario that transmits no protocol buffers in either direction.""" + + def group_and_method(self): + return 'math.Math', 'DivMany' + + def serialize_request(self, request): + raise ValueError('This should not be necessary to call!') + + def deserialize_request(self, request_bytestring): + raise ValueError('This should not be necessary to call!') + + def serialize_response(self, response): + raise ValueError('This should not be necessary to call!') + + def deserialize_response(self, response_bytestring): + raise ValueError('This should not be necessary to call!') + + def requests(self): + return () + + def response_for_request(self, request): + raise ValueError('This should not be necessary to call!') + + def verify_requests(self, experimental_requests): + return not experimental_requests + + def verify_responses(self, experimental_responses): + return not experimental_responses + + +class BidirectionallyUnaryScenario(ProtoScenario): + """A scenario that transmits no protocol buffers in either direction.""" + + _DIVIDEND = 59 + _DIVISOR = 7 + _QUOTIENT = 8 + _REMAINDER = 3 + + _REQUEST = math_pb2.DivArgs(dividend=_DIVIDEND, divisor=_DIVISOR) + _RESPONSE = math_pb2.DivReply(quotient=_QUOTIENT, remainder=_REMAINDER) + + def group_and_method(self): + return 'math.Math', 'Div' + + def serialize_request(self, request): + return request.SerializeToString() + + def deserialize_request(self, request_bytestring): + return math_pb2.DivArgs.FromString(request_bytestring) + + def serialize_response(self, response): + return response.SerializeToString() + + def deserialize_response(self, response_bytestring): + return math_pb2.DivReply.FromString(response_bytestring) + + def requests(self): + return [self._REQUEST] + + def response_for_request(self, request): + return self._RESPONSE + + def verify_requests(self, experimental_requests): + return tuple(experimental_requests) == (self._REQUEST,) + + def verify_responses(self, experimental_responses): + return tuple(experimental_responses) == (self._RESPONSE,) + + +class BidirectionallyStreamingScenario(ProtoScenario): + """A scenario that transmits no protocol buffers in either direction.""" + + _REQUESTS = tuple( + math_pb2.DivArgs(dividend=59 + index, divisor=7 + index) + for index in range(test_constants.STREAM_LENGTH)) + + def __init__(self): + self._lock = threading.Lock() + self._responses = [] + + def group_and_method(self): + return 'math.Math', 'DivMany' + + def serialize_request(self, request): + return request.SerializeToString() + + def deserialize_request(self, request_bytestring): + return math_pb2.DivArgs.FromString(request_bytestring) + + def serialize_response(self, response): + return response.SerializeToString() + + def deserialize_response(self, response_bytestring): + return math_pb2.DivReply.FromString(response_bytestring) + + def requests(self): + return self._REQUESTS + + def response_for_request(self, request): + quotient, remainder = divmod(request.dividend, request.divisor) + response = math_pb2.DivReply(quotient=quotient, remainder=remainder) + with self._lock: + self._responses.append(response) + return response + + def verify_requests(self, experimental_requests): + return tuple(experimental_requests) == self._REQUESTS + + def verify_responses(self, experimental_responses): + with self._lock: + return tuple(experimental_responses) == tuple(self._responses) diff --git a/src/python/grpcio_test/grpc_test/_links/_transmission_test.py b/src/python/grpcio_test/grpc_test/_links/_transmission_test.py new file mode 100644 index 00000000..77e83d55 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_links/_transmission_test.py @@ -0,0 +1,239 @@ +# Copyright 2015, 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. + +"""Tests transmission of tickets across gRPC-on-the-wire.""" + +import unittest + +from grpc._adapter import _intermediary_low +from grpc._links import invocation +from grpc._links import service +from grpc.beta import interfaces as beta_interfaces +from grpc.framework.interfaces.links import links +from grpc_test import test_common +from grpc_test._links import _proto_scenarios +from grpc_test.framework.common import test_constants +from grpc_test.framework.interfaces.links import test_cases +from grpc_test.framework.interfaces.links import test_utilities + +_IDENTITY = lambda x: x + + +class TransmissionTest(test_cases.TransmissionTest, unittest.TestCase): + + def create_transmitting_links(self): + service_link = service.service_link( + {self.group_and_method(): self.deserialize_request}, + {self.group_and_method(): self.serialize_response}) + port = service_link.add_port('[::]:0', None) + service_link.start() + channel = _intermediary_low.Channel('localhost:%d' % port, None) + invocation_link = invocation.invocation_link( + channel, 'localhost', None, + {self.group_and_method(): self.serialize_request}, + {self.group_and_method(): self.deserialize_response}) + invocation_link.start() + return invocation_link, service_link + + def destroy_transmitting_links(self, invocation_side_link, service_side_link): + invocation_side_link.stop() + service_side_link.begin_stop() + service_side_link.end_stop() + + def create_invocation_initial_metadata(self): + return ( + ('first_invocation_initial_metadata_key', 'just a string value'), + ('second_invocation_initial_metadata_key', '0123456789'), + ('third_invocation_initial_metadata_key-bin', '\x00\x57' * 100), + ) + + def create_invocation_terminal_metadata(self): + return None + + def create_service_initial_metadata(self): + return ( + ('first_service_initial_metadata_key', 'just another string value'), + ('second_service_initial_metadata_key', '9876543210'), + ('third_service_initial_metadata_key-bin', '\x00\x59\x02' * 100), + ) + + def create_service_terminal_metadata(self): + return ( + ('first_service_terminal_metadata_key', 'yet another string value'), + ('second_service_terminal_metadata_key', 'abcdefghij'), + ('third_service_terminal_metadata_key-bin', '\x00\x37' * 100), + ) + + def create_invocation_completion(self): + return None, None + + def create_service_completion(self): + return ( + beta_interfaces.StatusCode.OK, b'An exuberant test "details" message!') + + def assertMetadataTransmitted(self, original_metadata, transmitted_metadata): + self.assertTrue( + test_common.metadata_transmitted( + original_metadata, transmitted_metadata), + '%s erroneously transmitted as %s' % ( + original_metadata, transmitted_metadata)) + + +class RoundTripTest(unittest.TestCase): + + def testZeroMessageRoundTrip(self): + test_operation_id = object() + test_group = 'test package.Test Group' + test_method = 'test method' + identity_transformation = {(test_group, test_method): _IDENTITY} + test_code = beta_interfaces.StatusCode.OK + test_message = 'a test message' + + service_link = service.service_link( + identity_transformation, identity_transformation) + service_mate = test_utilities.RecordingLink() + service_link.join_link(service_mate) + port = service_link.add_port('[::]:0', None) + service_link.start() + channel = _intermediary_low.Channel('localhost:%d' % port, None) + invocation_link = invocation.invocation_link( + channel, None, None, identity_transformation, identity_transformation) + invocation_mate = test_utilities.RecordingLink() + invocation_link.join_link(invocation_mate) + invocation_link.start() + + invocation_ticket = links.Ticket( + test_operation_id, 0, test_group, test_method, + links.Ticket.Subscription.FULL, test_constants.LONG_TIMEOUT, None, None, + None, None, None, None, links.Ticket.Termination.COMPLETION, None) + invocation_link.accept_ticket(invocation_ticket) + service_mate.block_until_tickets_satisfy(test_cases.terminated) + + service_ticket = links.Ticket( + service_mate.tickets()[-1].operation_id, 0, None, None, None, None, + None, None, None, None, test_code, test_message, + links.Ticket.Termination.COMPLETION, None) + service_link.accept_ticket(service_ticket) + invocation_mate.block_until_tickets_satisfy(test_cases.terminated) + + invocation_link.stop() + service_link.begin_stop() + service_link.end_stop() + + self.assertIs( + service_mate.tickets()[-1].termination, + links.Ticket.Termination.COMPLETION) + self.assertIs( + invocation_mate.tickets()[-1].termination, + links.Ticket.Termination.COMPLETION) + self.assertIs(invocation_mate.tickets()[-1].code, test_code) + self.assertEqual(invocation_mate.tickets()[-1].message, test_message) + + def _perform_scenario_test(self, scenario): + test_operation_id = object() + test_group, test_method = scenario.group_and_method() + test_code = beta_interfaces.StatusCode.OK + test_message = 'a scenario test message' + + service_link = service.service_link( + {(test_group, test_method): scenario.deserialize_request}, + {(test_group, test_method): scenario.serialize_response}) + service_mate = test_utilities.RecordingLink() + service_link.join_link(service_mate) + port = service_link.add_port('[::]:0', None) + service_link.start() + channel = _intermediary_low.Channel('localhost:%d' % port, None) + invocation_link = invocation.invocation_link( + channel, 'localhost', None, + {(test_group, test_method): scenario.serialize_request}, + {(test_group, test_method): scenario.deserialize_response}) + invocation_mate = test_utilities.RecordingLink() + invocation_link.join_link(invocation_mate) + invocation_link.start() + + invocation_ticket = links.Ticket( + test_operation_id, 0, test_group, test_method, + links.Ticket.Subscription.FULL, test_constants.LONG_TIMEOUT, None, None, + None, None, None, None, None, None) + invocation_link.accept_ticket(invocation_ticket) + requests = scenario.requests() + for request_index, request in enumerate(requests): + request_ticket = links.Ticket( + test_operation_id, 1 + request_index, None, None, None, None, 1, None, + request, None, None, None, None, None) + invocation_link.accept_ticket(request_ticket) + service_mate.block_until_tickets_satisfy( + test_cases.at_least_n_payloads_received_predicate(1 + request_index)) + response_ticket = links.Ticket( + service_mate.tickets()[0].operation_id, request_index, None, None, + None, None, 1, None, scenario.response_for_request(request), None, + None, None, None, None) + service_link.accept_ticket(response_ticket) + invocation_mate.block_until_tickets_satisfy( + test_cases.at_least_n_payloads_received_predicate(1 + request_index)) + request_count = len(requests) + invocation_completion_ticket = links.Ticket( + test_operation_id, request_count + 1, None, None, None, None, None, + None, None, None, None, None, links.Ticket.Termination.COMPLETION, + None) + invocation_link.accept_ticket(invocation_completion_ticket) + service_mate.block_until_tickets_satisfy(test_cases.terminated) + service_completion_ticket = links.Ticket( + service_mate.tickets()[0].operation_id, request_count, None, None, None, + None, None, None, None, None, test_code, test_message, + links.Ticket.Termination.COMPLETION, None) + service_link.accept_ticket(service_completion_ticket) + invocation_mate.block_until_tickets_satisfy(test_cases.terminated) + + invocation_link.stop() + service_link.begin_stop() + service_link.end_stop() + + observed_requests = tuple( + ticket.payload for ticket in service_mate.tickets() + if ticket.payload is not None) + observed_responses = tuple( + ticket.payload for ticket in invocation_mate.tickets() + if ticket.payload is not None) + self.assertTrue(scenario.verify_requests(observed_requests)) + self.assertTrue(scenario.verify_responses(observed_responses)) + + def testEmptyScenario(self): + self._perform_scenario_test(_proto_scenarios.EmptyScenario()) + + def testBidirectionallyUnaryScenario(self): + self._perform_scenario_test(_proto_scenarios.BidirectionallyUnaryScenario()) + + def testBidirectionallyStreamingScenario(self): + self._perform_scenario_test( + _proto_scenarios.BidirectionallyStreamingScenario()) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/beta/__init__.py b/src/python/grpcio_test/grpc_test/beta/__init__.py new file mode 100644 index 00000000..70865191 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/beta/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, 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. + + diff --git a/src/python/grpcio_test/grpc_test/beta/_beta_features_test.py b/src/python/grpcio_test/grpc_test/beta/_beta_features_test.py new file mode 100644 index 00000000..fad57da9 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/beta/_beta_features_test.py @@ -0,0 +1,232 @@ +# Copyright 2015, 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. + +"""Tests Face interface compliance of the gRPC Python Beta API.""" + +import threading +import unittest + +from grpc.beta import implementations +from grpc.beta import interfaces +from grpc.framework.common import cardinality +from grpc.framework.interfaces.face import utilities +from grpc_test import resources +from grpc_test.beta import test_utilities +from grpc_test.framework.common import test_constants + +_SERVER_HOST_OVERRIDE = 'foo.test.google.fr' + +_GROUP = 'group' +_UNARY_UNARY = 'unary-unary' +_UNARY_STREAM = 'unary-stream' +_STREAM_UNARY = 'stream-unary' +_STREAM_STREAM = 'stream-stream' + +_REQUEST = b'abc' +_RESPONSE = b'123' + + +class _Servicer(object): + + def __init__(self): + self._condition = threading.Condition() + self._peer = None + self._serviced = False + + def unary_unary(self, request, context): + with self._condition: + self._request = request + self._peer = context.protocol_context().peer() + context.protocol_context().disable_next_response_compression() + self._serviced = True + self._condition.notify_all() + return _RESPONSE + + def unary_stream(self, request, context): + with self._condition: + self._request = request + self._peer = context.protocol_context().peer() + context.protocol_context().disable_next_response_compression() + self._serviced = True + self._condition.notify_all() + return + yield + + def stream_unary(self, request_iterator, context): + for request in request_iterator: + self._request = request + with self._condition: + self._peer = context.protocol_context().peer() + context.protocol_context().disable_next_response_compression() + self._serviced = True + self._condition.notify_all() + return _RESPONSE + + def stream_stream(self, request_iterator, context): + for request in request_iterator: + with self._condition: + self._peer = context.protocol_context().peer() + context.protocol_context().disable_next_response_compression() + yield _RESPONSE + with self._condition: + self._serviced = True + self._condition.notify_all() + + def peer(self): + with self._condition: + return self._peer + + def block_until_serviced(self): + with self._condition: + while not self._serviced: + self._condition.wait() + + +class _BlockingIterator(object): + + def __init__(self, upstream): + self._condition = threading.Condition() + self._upstream = upstream + self._allowed = [] + + def __iter__(self): + return self + + def next(self): + with self._condition: + while True: + if self._allowed is None: + raise StopIteration() + elif self._allowed: + return self._allowed.pop(0) + else: + self._condition.wait() + + def allow(self): + with self._condition: + try: + self._allowed.append(next(self._upstream)) + except StopIteration: + self._allowed = None + self._condition.notify_all() + + +class BetaFeaturesTest(unittest.TestCase): + + def setUp(self): + self._servicer = _Servicer() + method_implementations = { + (_GROUP, _UNARY_UNARY): + utilities.unary_unary_inline(self._servicer.unary_unary), + (_GROUP, _UNARY_STREAM): + utilities.unary_stream_inline(self._servicer.unary_stream), + (_GROUP, _STREAM_UNARY): + utilities.stream_unary_inline(self._servicer.stream_unary), + (_GROUP, _STREAM_STREAM): + utilities.stream_stream_inline(self._servicer.stream_stream), + } + + cardinalities = { + _UNARY_UNARY: cardinality.Cardinality.UNARY_UNARY, + _UNARY_STREAM: cardinality.Cardinality.UNARY_STREAM, + _STREAM_UNARY: cardinality.Cardinality.STREAM_UNARY, + _STREAM_STREAM: cardinality.Cardinality.STREAM_STREAM, + } + + server_options = implementations.server_options( + thread_pool_size=test_constants.POOL_SIZE) + self._server = implementations.server( + method_implementations, options=server_options) + server_credentials = implementations.ssl_server_credentials( + [(resources.private_key(), resources.certificate_chain(),),]) + port = self._server.add_secure_port('[::]:0', server_credentials) + self._server.start() + self._client_credentials = implementations.ssl_client_credentials( + resources.test_root_certificates(), None, None) + channel = test_utilities.not_really_secure_channel( + 'localhost', port, self._client_credentials, _SERVER_HOST_OVERRIDE) + stub_options = implementations.stub_options( + thread_pool_size=test_constants.POOL_SIZE) + self._dynamic_stub = implementations.dynamic_stub( + channel, _GROUP, cardinalities, options=stub_options) + + def tearDown(self): + self._dynamic_stub = None + self._server.stop(test_constants.SHORT_TIMEOUT).wait() + + def test_unary_unary(self): + call_options = interfaces.grpc_call_options( + disable_compression=True, credentials=self._client_credentials) + response = getattr(self._dynamic_stub, _UNARY_UNARY)( + _REQUEST, test_constants.LONG_TIMEOUT, protocol_options=call_options) + self.assertEqual(_RESPONSE, response) + self.assertIsNotNone(self._servicer.peer()) + + def test_unary_stream(self): + call_options = interfaces.grpc_call_options( + disable_compression=True, credentials=self._client_credentials) + response_iterator = getattr(self._dynamic_stub, _UNARY_STREAM)( + _REQUEST, test_constants.LONG_TIMEOUT, protocol_options=call_options) + self._servicer.block_until_serviced() + self.assertIsNotNone(self._servicer.peer()) + + def test_stream_unary(self): + call_options = interfaces.grpc_call_options( + credentials=self._client_credentials) + request_iterator = _BlockingIterator(iter((_REQUEST,))) + response_future = getattr(self._dynamic_stub, _STREAM_UNARY).future( + request_iterator, test_constants.LONG_TIMEOUT, + protocol_options=call_options) + response_future.protocol_context().disable_next_request_compression() + request_iterator.allow() + response_future.protocol_context().disable_next_request_compression() + request_iterator.allow() + self._servicer.block_until_serviced() + self.assertIsNotNone(self._servicer.peer()) + self.assertEqual(_RESPONSE, response_future.result()) + + def test_stream_stream(self): + call_options = interfaces.grpc_call_options( + credentials=self._client_credentials) + request_iterator = _BlockingIterator(iter((_REQUEST,))) + response_iterator = getattr(self._dynamic_stub, _STREAM_STREAM)( + request_iterator, test_constants.SHORT_TIMEOUT, + protocol_options=call_options) + response_iterator.protocol_context().disable_next_request_compression() + request_iterator.allow() + response = next(response_iterator) + response_iterator.protocol_context().disable_next_request_compression() + request_iterator.allow() + self._servicer.block_until_serviced() + self.assertIsNotNone(self._servicer.peer()) + self.assertEqual(_RESPONSE, response) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/beta/_connectivity_channel_test.py b/src/python/grpcio_test/grpc_test/beta/_connectivity_channel_test.py new file mode 100644 index 00000000..b3c05bdb --- /dev/null +++ b/src/python/grpcio_test/grpc_test/beta/_connectivity_channel_test.py @@ -0,0 +1,191 @@ +# Copyright 2015, 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. + +"""Tests of grpc.beta._connectivity_channel.""" + +import threading +import time +import unittest + +from grpc._adapter import _low +from grpc._adapter import _types +from grpc.beta import _connectivity_channel +from grpc.beta import interfaces +from grpc_test.framework.common import test_constants + + +def _drive_completion_queue(completion_queue): + while True: + event = completion_queue.next(time.time() + 24 * 60 * 60) + if event.type == _types.EventType.QUEUE_SHUTDOWN: + break + + +class _Callback(object): + + def __init__(self): + self._condition = threading.Condition() + self._connectivities = [] + + def update(self, connectivity): + with self._condition: + self._connectivities.append(connectivity) + self._condition.notify() + + def connectivities(self): + with self._condition: + return tuple(self._connectivities) + + def block_until_connectivities_satisfy(self, predicate): + with self._condition: + while True: + connectivities = tuple(self._connectivities) + if predicate(connectivities): + return connectivities + else: + self._condition.wait() + + +class ChannelConnectivityTest(unittest.TestCase): + + def test_lonely_channel_connectivity(self): + low_channel = _low.Channel('localhost:12345', ()) + callback = _Callback() + + connectivity_channel = _connectivity_channel.ConnectivityChannel( + low_channel) + connectivity_channel.subscribe(callback.update, try_to_connect=False) + first_connectivities = callback.block_until_connectivities_satisfy(bool) + connectivity_channel.subscribe(callback.update, try_to_connect=True) + second_connectivities = callback.block_until_connectivities_satisfy( + lambda connectivities: 2 <= len(connectivities)) + # Wait for a connection that will never happen. + time.sleep(test_constants.SHORT_TIMEOUT) + third_connectivities = callback.connectivities() + connectivity_channel.unsubscribe(callback.update) + fourth_connectivities = callback.connectivities() + connectivity_channel.unsubscribe(callback.update) + fifth_connectivities = callback.connectivities() + + self.assertSequenceEqual( + (interfaces.ChannelConnectivity.IDLE,), first_connectivities) + self.assertNotIn( + interfaces.ChannelConnectivity.READY, second_connectivities) + self.assertNotIn( + interfaces.ChannelConnectivity.READY, third_connectivities) + self.assertNotIn( + interfaces.ChannelConnectivity.READY, fourth_connectivities) + self.assertNotIn( + interfaces.ChannelConnectivity.READY, fifth_connectivities) + + def test_immediately_connectable_channel_connectivity(self): + server_completion_queue = _low.CompletionQueue() + server = _low.Server(server_completion_queue, []) + port = server.add_http2_port('[::]:0') + server.start() + server_completion_queue_thread = threading.Thread( + target=_drive_completion_queue, args=(server_completion_queue,)) + server_completion_queue_thread.start() + low_channel = _low.Channel('localhost:%d' % port, ()) + first_callback = _Callback() + second_callback = _Callback() + + connectivity_channel = _connectivity_channel.ConnectivityChannel( + low_channel) + connectivity_channel.subscribe(first_callback.update, try_to_connect=False) + first_connectivities = first_callback.block_until_connectivities_satisfy( + bool) + # Wait for a connection that will never happen because try_to_connect=True + # has not yet been passed. + time.sleep(test_constants.SHORT_TIMEOUT) + second_connectivities = first_callback.connectivities() + connectivity_channel.subscribe(second_callback.update, try_to_connect=True) + third_connectivities = first_callback.block_until_connectivities_satisfy( + lambda connectivities: 2 <= len(connectivities)) + fourth_connectivities = second_callback.block_until_connectivities_satisfy( + bool) + # Wait for a connection that will happen (or may already have happened). + first_callback.block_until_connectivities_satisfy( + lambda connectivities: + interfaces.ChannelConnectivity.READY in connectivities) + second_callback.block_until_connectivities_satisfy( + lambda connectivities: + interfaces.ChannelConnectivity.READY in connectivities) + connectivity_channel.unsubscribe(first_callback.update) + connectivity_channel.unsubscribe(second_callback.update) + + server.shutdown() + server_completion_queue.shutdown() + server_completion_queue_thread.join() + + self.assertSequenceEqual( + (interfaces.ChannelConnectivity.IDLE,), first_connectivities) + self.assertSequenceEqual( + (interfaces.ChannelConnectivity.IDLE,), second_connectivities) + self.assertNotIn( + interfaces.ChannelConnectivity.TRANSIENT_FAILURE, third_connectivities) + self.assertNotIn( + interfaces.ChannelConnectivity.FATAL_FAILURE, third_connectivities) + self.assertNotIn( + interfaces.ChannelConnectivity.TRANSIENT_FAILURE, + fourth_connectivities) + self.assertNotIn( + interfaces.ChannelConnectivity.FATAL_FAILURE, fourth_connectivities) + + def test_reachable_then_unreachable_channel_connectivity(self): + server_completion_queue = _low.CompletionQueue() + server = _low.Server(server_completion_queue, []) + port = server.add_http2_port('[::]:0') + server.start() + server_completion_queue_thread = threading.Thread( + target=_drive_completion_queue, args=(server_completion_queue,)) + server_completion_queue_thread.start() + low_channel = _low.Channel('localhost:%d' % port, ()) + callback = _Callback() + + connectivity_channel = _connectivity_channel.ConnectivityChannel( + low_channel) + connectivity_channel.subscribe(callback.update, try_to_connect=True) + callback.block_until_connectivities_satisfy( + lambda connectivities: + interfaces.ChannelConnectivity.READY in connectivities) + # Now take down the server and confirm that channel readiness is repudiated. + server.shutdown() + callback.block_until_connectivities_satisfy( + lambda connectivities: + connectivities[-1] is not interfaces.ChannelConnectivity.READY) + connectivity_channel.unsubscribe(callback.update) + + server.shutdown() + server_completion_queue.shutdown() + server_completion_queue_thread.join() + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/beta/_face_interface_test.py b/src/python/grpcio_test/grpc_test/beta/_face_interface_test.py new file mode 100644 index 00000000..aa33e1e6 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/beta/_face_interface_test.py @@ -0,0 +1,138 @@ +# Copyright 2015, 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. + +"""Tests Face interface compliance of the gRPC Python Beta API.""" + +import collections +import unittest + +from grpc.beta import implementations +from grpc.beta import interfaces +from grpc_test import resources +from grpc_test import test_common as grpc_test_common +from grpc_test.beta import test_utilities +from grpc_test.framework.common import test_constants +from grpc_test.framework.interfaces.face import test_cases +from grpc_test.framework.interfaces.face import test_interfaces + +_SERVER_HOST_OVERRIDE = 'foo.test.google.fr' + + +class _SerializationBehaviors( + collections.namedtuple( + '_SerializationBehaviors', + ('request_serializers', 'request_deserializers', 'response_serializers', + 'response_deserializers',))): + pass + + +def _serialization_behaviors_from_test_methods(test_methods): + request_serializers = {} + request_deserializers = {} + response_serializers = {} + response_deserializers = {} + for (group, method), test_method in test_methods.iteritems(): + request_serializers[group, method] = test_method.serialize_request + request_deserializers[group, method] = test_method.deserialize_request + response_serializers[group, method] = test_method.serialize_response + response_deserializers[group, method] = test_method.deserialize_response + return _SerializationBehaviors( + request_serializers, request_deserializers, response_serializers, + response_deserializers) + + +class _Implementation(test_interfaces.Implementation): + + def instantiate( + self, methods, method_implementations, multi_method_implementation): + serialization_behaviors = _serialization_behaviors_from_test_methods( + methods) + # TODO(nathaniel): Add a "groups" attribute to _digest.TestServiceDigest. + service = next(iter(methods))[0] + # TODO(nathaniel): Add a "cardinalities_by_group" attribute to + # _digest.TestServiceDigest. + cardinalities = { + method: method_object.cardinality() + for (group, method), method_object in methods.iteritems()} + + server_options = implementations.server_options( + request_deserializers=serialization_behaviors.request_deserializers, + response_serializers=serialization_behaviors.response_serializers, + thread_pool_size=test_constants.POOL_SIZE) + server = implementations.server( + method_implementations, options=server_options) + server_credentials = implementations.ssl_server_credentials( + [(resources.private_key(), resources.certificate_chain(),),]) + port = server.add_secure_port('[::]:0', server_credentials) + server.start() + client_credentials = implementations.ssl_client_credentials( + resources.test_root_certificates(), None, None) + channel = test_utilities.not_really_secure_channel( + 'localhost', port, client_credentials, _SERVER_HOST_OVERRIDE) + stub_options = implementations.stub_options( + request_serializers=serialization_behaviors.request_serializers, + response_deserializers=serialization_behaviors.response_deserializers, + thread_pool_size=test_constants.POOL_SIZE) + generic_stub = implementations.generic_stub(channel, options=stub_options) + dynamic_stub = implementations.dynamic_stub( + channel, service, cardinalities, options=stub_options) + return generic_stub, {service: dynamic_stub}, server + + def destantiate(self, memo): + memo.stop(test_constants.SHORT_TIMEOUT).wait() + + def invocation_metadata(self): + return grpc_test_common.INVOCATION_INITIAL_METADATA + + def initial_metadata(self): + return grpc_test_common.SERVICE_INITIAL_METADATA + + def terminal_metadata(self): + return grpc_test_common.SERVICE_TERMINAL_METADATA + + def code(self): + return interfaces.StatusCode.OK + + def details(self): + return grpc_test_common.DETAILS + + def metadata_transmitted(self, original_metadata, transmitted_metadata): + return original_metadata is None or grpc_test_common.metadata_transmitted( + original_metadata, transmitted_metadata) + + +def load_tests(loader, tests, pattern): + return unittest.TestSuite( + tests=tuple( + loader.loadTestsFromTestCase(test_case_class) + for test_case_class in test_cases.test_cases(_Implementation()))) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/beta/_not_found_test.py b/src/python/grpcio_test/grpc_test/beta/_not_found_test.py new file mode 100644 index 00000000..5feb997f --- /dev/null +++ b/src/python/grpcio_test/grpc_test/beta/_not_found_test.py @@ -0,0 +1,75 @@ +# Copyright 2015, 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. + +"""Tests of RPC-method-not-found behavior.""" + +import unittest + +from grpc.beta import implementations +from grpc.beta import interfaces +from grpc.framework.interfaces.face import face +from grpc_test.framework.common import test_constants + + +class NotFoundTest(unittest.TestCase): + + def setUp(self): + self._server = implementations.server({}) + port = self._server.add_insecure_port('[::]:0') + channel = implementations.insecure_channel('localhost', port) + self._generic_stub = implementations.generic_stub(channel) + self._server.start() + + def tearDown(self): + self._server.stop(0).wait() + self._generic_stub = None + + def test_blocking_unary_unary_not_found(self): + with self.assertRaises(face.LocalError) as exception_assertion_context: + self._generic_stub.blocking_unary_unary( + 'groop', 'meffod', b'abc', test_constants.LONG_TIMEOUT, + with_call=True) + self.assertIs( + exception_assertion_context.exception.code, + interfaces.StatusCode.UNIMPLEMENTED) + + def test_future_stream_unary_not_found(self): + rpc_future = self._generic_stub.future_stream_unary( + 'grupe', 'mevvod', b'def', test_constants.LONG_TIMEOUT) + with self.assertRaises(face.LocalError) as exception_assertion_context: + rpc_future.result() + self.assertIs( + exception_assertion_context.exception.code, + interfaces.StatusCode.UNIMPLEMENTED) + self.assertIs( + rpc_future.exception().code, interfaces.StatusCode.UNIMPLEMENTED) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/beta/_utilities_test.py b/src/python/grpcio_test/grpc_test/beta/_utilities_test.py new file mode 100644 index 00000000..996cea91 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/beta/_utilities_test.py @@ -0,0 +1,123 @@ +# Copyright 2015, 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. + +"""Tests of grpc.beta.utilities.""" + +import threading +import time +import unittest + +from grpc._adapter import _low +from grpc._adapter import _types +from grpc.beta import implementations +from grpc.beta import utilities +from grpc.framework.foundation import future +from grpc_test.framework.common import test_constants + + +def _drive_completion_queue(completion_queue): + while True: + event = completion_queue.next(time.time() + 24 * 60 * 60) + if event.type == _types.EventType.QUEUE_SHUTDOWN: + break + + +class _Callback(object): + + def __init__(self): + self._condition = threading.Condition() + self._value = None + + def accept_value(self, value): + with self._condition: + self._value = value + self._condition.notify_all() + + def block_until_called(self): + with self._condition: + while self._value is None: + self._condition.wait() + return self._value + + +class ChannelConnectivityTest(unittest.TestCase): + + def test_lonely_channel_connectivity(self): + channel = implementations.insecure_channel('localhost', 12345) + callback = _Callback() + + ready_future = utilities.channel_ready_future(channel) + ready_future.add_done_callback(callback.accept_value) + with self.assertRaises(future.TimeoutError): + ready_future.result(test_constants.SHORT_TIMEOUT) + self.assertFalse(ready_future.cancelled()) + self.assertFalse(ready_future.done()) + self.assertTrue(ready_future.running()) + ready_future.cancel() + value_passed_to_callback = callback.block_until_called() + self.assertIs(ready_future, value_passed_to_callback) + self.assertTrue(ready_future.cancelled()) + self.assertTrue(ready_future.done()) + self.assertFalse(ready_future.running()) + + def test_immediately_connectable_channel_connectivity(self): + server_completion_queue = _low.CompletionQueue() + server = _low.Server(server_completion_queue, []) + port = server.add_http2_port('[::]:0') + server.start() + server_completion_queue_thread = threading.Thread( + target=_drive_completion_queue, args=(server_completion_queue,)) + server_completion_queue_thread.start() + channel = implementations.insecure_channel('localhost', port) + callback = _Callback() + + try: + ready_future = utilities.channel_ready_future(channel) + ready_future.add_done_callback(callback.accept_value) + self.assertIsNone( + ready_future.result(test_constants.SHORT_TIMEOUT)) + value_passed_to_callback = callback.block_until_called() + self.assertIs(ready_future, value_passed_to_callback) + self.assertFalse(ready_future.cancelled()) + self.assertTrue(ready_future.done()) + self.assertFalse(ready_future.running()) + # Cancellation after maturity has no effect. + ready_future.cancel() + self.assertFalse(ready_future.cancelled()) + self.assertTrue(ready_future.done()) + self.assertFalse(ready_future.running()) + finally: + ready_future.cancel() + server.shutdown() + server_completion_queue.shutdown() + server_completion_queue_thread.join() + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/beta/test_utilities.py b/src/python/grpcio_test/grpc_test/beta/test_utilities.py new file mode 100644 index 00000000..24a8600e --- /dev/null +++ b/src/python/grpcio_test/grpc_test/beta/test_utilities.py @@ -0,0 +1,56 @@ +# Copyright 2015, 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. + +"""Test-appropriate entry points into the gRPC Python Beta API.""" + +from grpc._adapter import _intermediary_low +from grpc.beta import implementations + + +def not_really_secure_channel( + host, port, client_credentials, server_host_override): + """Creates an insecure Channel to a remote host. + + Args: + host: The name of the remote host to which to connect. + port: The port of the remote host to which to connect. + client_credentials: The implementations.ClientCredentials with which to + connect. + server_host_override: The target name used for SSL host name checking. + + Returns: + An implementations.Channel to the remote host through which RPCs may be + conducted. + """ + hostport = '%s:%d' % (host, port) + intermediary_low_channel = _intermediary_low.Channel( + hostport, client_credentials._intermediary_low_credentials, + server_host_override=server_host_override) + return implementations.Channel( + intermediary_low_channel._internal, intermediary_low_channel) diff --git a/src/python/grpcio_test/grpc_test/credentials/README b/src/python/grpcio_test/grpc_test/credentials/README new file mode 100644 index 00000000..cb20dcb4 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/credentials/README @@ -0,0 +1 @@ +These are test keys *NOT* to be used in production. diff --git a/src/python/grpcio_test/grpc_test/credentials/ca.pem b/src/python/grpcio_test/grpc_test/credentials/ca.pem new file mode 100755 index 00000000..6c8511a7 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/credentials/ca.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICSjCCAbOgAwIBAgIJAJHGGR4dGioHMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQxDzANBgNVBAMTBnRlc3RjYTAeFw0xNDExMTEyMjMxMjla +Fw0yNDExMDgyMjMxMjlaMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0 +YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMT +BnRlc3RjYTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwEDfBV5MYdlHVHJ7 ++L4nxrZy7mBfAVXpOc5vMYztssUI7mL2/iYujiIXM+weZYNTEpLdjyJdu7R5gGUu +g1jSVK/EPHfc74O7AyZU34PNIP4Sh33N+/A5YexrNgJlPY+E3GdVYi4ldWJjgkAd +Qah2PH5ACLrIIC6tRka9hcaBlIECAwEAAaMgMB4wDAYDVR0TBAUwAwEB/zAOBgNV +HQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQELBQADgYEAHzC7jdYlzAVmddi/gdAeKPau +sPBG/C2HCWqHzpCUHcKuvMzDVkY/MP2o6JIW2DBbY64bO/FceExhjcykgaYtCH/m +oIU63+CFOTtR7otyQAWHqXa7q4SbCDlG7DyRFxqG0txPtGvy12lgldA2+RgcigQG +Dfcog5wrJytaQ6UA0wE= +-----END CERTIFICATE----- diff --git a/src/python/grpcio_test/grpc_test/credentials/server1.key b/src/python/grpcio_test/grpc_test/credentials/server1.key new file mode 100755 index 00000000..143a5b87 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/credentials/server1.key @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAOHDFScoLCVJpYDD +M4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1BgzkWF+slf +3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd9N8YwbBY +AckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAECgYAn7qGnM2vbjJNBm0VZCkOkTIWm +V10okw7EPJrdL2mkre9NasghNXbE1y5zDshx5Nt3KsazKOxTT8d0Jwh/3KbaN+YY +tTCbKGW0pXDRBhwUHRcuRzScjli8Rih5UOCiZkhefUTcRb6xIhZJuQy71tjaSy0p +dHZRmYyBYO2YEQ8xoQJBAPrJPhMBkzmEYFtyIEqAxQ/o/A6E+E4w8i+KM7nQCK7q +K4JXzyXVAjLfyBZWHGM2uro/fjqPggGD6QH1qXCkI4MCQQDmdKeb2TrKRh5BY1LR +81aJGKcJ2XbcDu6wMZK4oqWbTX2KiYn9GB0woM6nSr/Y6iy1u145YzYxEV/iMwff +DJULAkB8B2MnyzOg0pNFJqBJuH29bKCcHa8gHJzqXhNO5lAlEbMK95p/P2Wi+4Hd +aiEIAF1BF326QJcvYKmwSmrORp85AkAlSNxRJ50OWrfMZnBgzVjDx3xG6KsFQVk2 +ol6VhqL6dFgKUORFUWBvnKSyhjJxurlPEahV6oo6+A+mPhFY8eUvAkAZQyTdupP3 +XEFQKctGz+9+gKkemDp7LBBMEMBXrGTLPhpEfcjv/7KPdnFHYmhYeBTBnuVmTVWe +F98XJ7tIFfJq +-----END PRIVATE KEY----- diff --git a/src/python/grpcio_test/grpc_test/credentials/server1.pem b/src/python/grpcio_test/grpc_test/credentials/server1.pem new file mode 100755 index 00000000..8e582e57 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/credentials/server1.pem @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICmzCCAgSgAwIBAgIBAzANBgkqhkiG9w0BAQUFADBWMQswCQYDVQQGEwJBVTET +MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ +dHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0Y2EwHhcNMTQwNzIyMDYwMDU3WhcNMjQwNzE5 +MDYwMDU3WjBkMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV +BAcTB0NoaWNhZ28xFDASBgNVBAoTC0dvb2dsZSBJbmMuMRowGAYDVQQDFBEqLnRl +c3QuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA4cMVJygs +JUmlgMMzgdi0h1XoCR7+ww1pop04OMMyy7H/i0PJ2W6Y35+b4CM8QrkYeEafUGDO +RYX6yV/cHGGsD/x02ye6ey1UDtkGAD/mpDEx8YCrjAc1Vfvt8Fk6Cn1WVIxV/J30 +3xjBsFgByQ55RBp1OLZfVLo6AleBDSbcxaECAwEAAaNrMGkwCQYDVR0TBAIwADAL +BgNVHQ8EBAMCBeAwTwYDVR0RBEgwRoIQKi50ZXN0Lmdvb2dsZS5mcoIYd2F0ZXJ6 +b29pLnRlc3QuZ29vZ2xlLmJlghIqLnRlc3QueW91dHViZS5jb22HBMCoAQMwDQYJ +KoZIhvcNAQEFBQADgYEAM2Ii0LgTGbJ1j4oqX9bxVcxm+/R5Yf8oi0aZqTJlnLYS +wXcBykxTx181s7WyfJ49WwrYXo78zTDAnf1ma0fPq3e4mpspvyndLh1a+OarHa1e +aT0DIIYk7qeEa1YcVljx2KyLd0r1BBAfrwyGaEPVeJQVYWaOJRU2we/KD4ojf9s= +-----END CERTIFICATE----- diff --git a/src/python/grpcio_test/grpc_test/early_adopter/__init__.py b/src/python/grpcio_test/grpc_test/early_adopter/__init__.py new file mode 100644 index 00000000..70865191 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/early_adopter/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, 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. + + diff --git a/src/python/grpcio_test/grpc_test/early_adopter/implementations_test.py b/src/python/grpcio_test/grpc_test/early_adopter/implementations_test.py new file mode 100644 index 00000000..611637e8 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/early_adopter/implementations_test.py @@ -0,0 +1,180 @@ +# Copyright 2015, 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. + +# TODO(nathaniel): Expand this test coverage. + +"""Test of the GRPC-backed ForeLink and RearLink.""" + +import unittest + +from grpc.early_adopter import implementations +from grpc.framework.alpha import utilities +from grpc_test._junkdrawer import math_pb2 + +SERVICE_NAME = 'math.Math' + +DIV = 'Div' +DIV_MANY = 'DivMany' +FIB = 'Fib' +SUM = 'Sum' + +def _fibbonacci(limit): + left, right = 0, 1 + for _ in xrange(limit): + yield left + left, right = right, left + right + + +def _div(request, unused_context): + return math_pb2.DivReply( + quotient=request.dividend / request.divisor, + remainder=request.dividend % request.divisor) + + +def _div_many(request_iterator, unused_context): + for request in request_iterator: + yield math_pb2.DivReply( + quotient=request.dividend / request.divisor, + remainder=request.dividend % request.divisor) + + +def _fib(request, unused_context): + for number in _fibbonacci(request.limit): + yield math_pb2.Num(num=number) + + +def _sum(request_iterator, unused_context): + accumulation = 0 + for request in request_iterator: + accumulation += request.num + return math_pb2.Num(num=accumulation) + + +_INVOCATION_DESCRIPTIONS = { + DIV: utilities.unary_unary_invocation_description( + math_pb2.DivArgs.SerializeToString, math_pb2.DivReply.FromString), + DIV_MANY: utilities.stream_stream_invocation_description( + math_pb2.DivArgs.SerializeToString, math_pb2.DivReply.FromString), + FIB: utilities.unary_stream_invocation_description( + math_pb2.FibArgs.SerializeToString, math_pb2.Num.FromString), + SUM: utilities.stream_unary_invocation_description( + math_pb2.Num.SerializeToString, math_pb2.Num.FromString), +} + +_SERVICE_DESCRIPTIONS = { + DIV: utilities.unary_unary_service_description( + _div, math_pb2.DivArgs.FromString, + math_pb2.DivReply.SerializeToString), + DIV_MANY: utilities.stream_stream_service_description( + _div_many, math_pb2.DivArgs.FromString, + math_pb2.DivReply.SerializeToString), + FIB: utilities.unary_stream_service_description( + _fib, math_pb2.FibArgs.FromString, math_pb2.Num.SerializeToString), + SUM: utilities.stream_unary_service_description( + _sum, math_pb2.Num.FromString, math_pb2.Num.SerializeToString), +} + +_TIMEOUT = 3 + + +class EarlyAdopterImplementationsTest(unittest.TestCase): + + def setUp(self): + self.server = implementations.server( + SERVICE_NAME, _SERVICE_DESCRIPTIONS, 0) + self.server.start() + port = self.server.port() + self.stub = implementations.stub( + SERVICE_NAME, _INVOCATION_DESCRIPTIONS, 'localhost', port) + + def tearDown(self): + self.server.stop() + + def testUpAndDown(self): + with self.stub: + pass + + def testUnaryUnary(self): + divisor = 59 + dividend = 973 + expected_quotient = dividend / divisor + expected_remainder = dividend % divisor + + with self.stub: + response = self.stub.Div( + math_pb2.DivArgs(divisor=divisor, dividend=dividend), _TIMEOUT) + self.assertEqual(expected_quotient, response.quotient) + self.assertEqual(expected_remainder, response.remainder) + + def testUnaryStream(self): + stream_length = 43 + + with self.stub: + response_iterator = self.stub.Fib( + math_pb2.FibArgs(limit=stream_length), _TIMEOUT) + numbers = tuple(response.num for response in response_iterator) + for early, middle, later in zip(numbers, numbers[:1], numbers[:2]): + self.assertEqual(early + middle, later) + self.assertEqual(stream_length, len(numbers)) + + def testStreamUnary(self): + stream_length = 127 + + with self.stub: + response_future = self.stub.Sum.async( + (math_pb2.Num(num=index) for index in range(stream_length)), + _TIMEOUT) + self.assertEqual( + (stream_length * (stream_length - 1)) / 2, + response_future.result().num) + + def testStreamStream(self): + stream_length = 179 + divisor_offset = 71 + dividend_offset = 1763 + + with self.stub: + response_iterator = self.stub.DivMany( + (math_pb2.DivArgs( + divisor=divisor_offset + index, + dividend=dividend_offset + index) + for index in range(stream_length)), + _TIMEOUT) + for index, response in enumerate(response_iterator): + self.assertEqual( + (dividend_offset + index) / (divisor_offset + index), + response.quotient) + self.assertEqual( + (dividend_offset + index) % (divisor_offset + index), + response.remainder) + self.assertEqual(stream_length, index + 1) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/framework/__init__.py b/src/python/grpcio_test/grpc_test/framework/__init__.py new file mode 100644 index 00000000..70865191 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, 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. + + diff --git a/src/python/grpcio_test/grpc_test/framework/_crust_over_core_face_interface_test.py b/src/python/grpcio_test/grpc_test/framework/_crust_over_core_face_interface_test.py new file mode 100644 index 00000000..30bb85f6 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/_crust_over_core_face_interface_test.py @@ -0,0 +1,111 @@ +# Copyright 2015, 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. + +"""Tests Face interface compliance of the crust-over-core stack.""" + +import collections +import unittest + +from grpc.framework.core import implementations as core_implementations +from grpc.framework.crust import implementations as crust_implementations +from grpc.framework.foundation import logging_pool +from grpc.framework.interfaces.links import utilities +from grpc_test.framework.common import test_constants +from grpc_test.framework.interfaces.face import test_cases +from grpc_test.framework.interfaces.face import test_interfaces +from grpc_test.framework.interfaces.links import test_utilities + + +class _Implementation(test_interfaces.Implementation): + + def instantiate( + self, methods, method_implementations, multi_method_implementation): + pool = logging_pool.pool(test_constants.POOL_SIZE) + servicer = crust_implementations.servicer( + method_implementations, multi_method_implementation, pool) + + service_end_link = core_implementations.service_end_link( + servicer, test_constants.DEFAULT_TIMEOUT, + test_constants.MAXIMUM_TIMEOUT) + invocation_end_link = core_implementations.invocation_end_link() + invocation_end_link.join_link(service_end_link) + service_end_link.join_link(invocation_end_link) + service_end_link.start() + invocation_end_link.start() + + generic_stub = crust_implementations.generic_stub(invocation_end_link, pool) + # TODO(nathaniel): Add a "groups" attribute to _digest.TestServiceDigest. + group = next(iter(methods))[0] + # TODO(nathaniel): Add a "cardinalities_by_group" attribute to + # _digest.TestServiceDigest. + cardinalities = { + method: method_object.cardinality() + for (group, method), method_object in methods.iteritems()} + dynamic_stub = crust_implementations.dynamic_stub( + invocation_end_link, group, cardinalities, pool) + + return generic_stub, {group: dynamic_stub}, ( + invocation_end_link, service_end_link, pool) + + def destantiate(self, memo): + invocation_end_link, service_end_link, pool = memo + invocation_end_link.stop(0).wait() + service_end_link.stop(0).wait() + invocation_end_link.join_link(utilities.NULL_LINK) + service_end_link.join_link(utilities.NULL_LINK) + pool.shutdown(wait=True) + + def invocation_metadata(self): + return object() + + def initial_metadata(self): + return object() + + def terminal_metadata(self): + return object() + + def code(self): + return object() + + def details(self): + return object() + + def metadata_transmitted(self, original_metadata, transmitted_metadata): + return original_metadata is transmitted_metadata + + +def load_tests(loader, tests, pattern): + return unittest.TestSuite( + tests=tuple( + loader.loadTestsFromTestCase(test_case_class) + for test_case_class in test_cases.test_cases(_Implementation()))) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/framework/base/__init__.py b/src/python/grpcio_test/grpc_test/framework/base/__init__.py new file mode 100644 index 00000000..70865191 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/base/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, 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. + + diff --git a/src/python/grpcio_test/grpc_test/framework/base/implementations_test.py b/src/python/grpcio_test/grpc_test/framework/base/implementations_test.py new file mode 100644 index 00000000..5a7d1398 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/base/implementations_test.py @@ -0,0 +1,80 @@ +# Copyright 2015, 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. + +"""Tests for grpc.framework.base.implementations.""" + +import unittest + +from grpc.framework.base import implementations +from grpc.framework.base import util +from grpc.framework.foundation import logging_pool +from grpc_test.framework.base import interfaces_test_case + +POOL_MAX_WORKERS = 10 +DEFAULT_TIMEOUT = 30 +MAXIMUM_TIMEOUT = 60 + + +class ImplementationsTest( + interfaces_test_case.FrontAndBackTest, unittest.TestCase): + + def setUp(self): + self.memory_transmission_pool = logging_pool.pool(POOL_MAX_WORKERS) + self.front_work_pool = logging_pool.pool(POOL_MAX_WORKERS) + self.front_transmission_pool = logging_pool.pool(POOL_MAX_WORKERS) + self.front_utility_pool = logging_pool.pool(POOL_MAX_WORKERS) + self.back_work_pool = logging_pool.pool(POOL_MAX_WORKERS) + self.back_transmission_pool = logging_pool.pool(POOL_MAX_WORKERS) + self.back_utility_pool = logging_pool.pool(POOL_MAX_WORKERS) + self.test_pool = logging_pool.pool(POOL_MAX_WORKERS) + self.test_servicer = interfaces_test_case.TestServicer(self.test_pool) + self.front = implementations.front_link( + self.front_work_pool, self.front_transmission_pool, + self.front_utility_pool) + self.back = implementations.back_link( + self.test_servicer, self.back_work_pool, self.back_transmission_pool, + self.back_utility_pool, DEFAULT_TIMEOUT, MAXIMUM_TIMEOUT) + self.front.join_rear_link(self.back) + self.back.join_fore_link(self.front) + + def tearDown(self): + util.wait_for_idle(self.back) + util.wait_for_idle(self.front) + self.memory_transmission_pool.shutdown(wait=True) + self.front_work_pool.shutdown(wait=True) + self.front_transmission_pool.shutdown(wait=True) + self.front_utility_pool.shutdown(wait=True) + self.back_work_pool.shutdown(wait=True) + self.back_transmission_pool.shutdown(wait=True) + self.back_utility_pool.shutdown(wait=True) + self.test_pool.shutdown(wait=True) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/framework/base/interfaces_test_case.py b/src/python/grpcio_test/grpc_test/framework/base/interfaces_test_case.py new file mode 100644 index 00000000..be775ad4 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/base/interfaces_test_case.py @@ -0,0 +1,307 @@ +# Copyright 2015, 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. + +"""Abstract tests against the interfaces of the base layer of RPC Framework.""" + +import threading +import time + +from grpc.framework.base import interfaces +from grpc.framework.base import util +from grpc.framework.foundation import stream +from grpc.framework.foundation import stream_util +from grpc_test.framework.foundation import stream_testing + +TICK = 0.1 +SMALL_TIMEOUT = TICK * 50 +STREAM_LENGTH = 100 + +SYNCHRONOUS_ECHO = 'synchronous echo' +ASYNCHRONOUS_ECHO = 'asynchronous echo' +IMMEDIATE_FAILURE = 'immediate failure' +TRIGGERED_FAILURE = 'triggered failure' +WAIT_ON_CONDITION = 'wait on condition' + +EMPTY_OUTCOME_DICT = { + interfaces.Outcome.COMPLETED: 0, + interfaces.Outcome.CANCELLED: 0, + interfaces.Outcome.EXPIRED: 0, + interfaces.Outcome.RECEPTION_FAILURE: 0, + interfaces.Outcome.TRANSMISSION_FAILURE: 0, + interfaces.Outcome.SERVICER_FAILURE: 0, + interfaces.Outcome.SERVICED_FAILURE: 0, + } + + +def _synchronous_echo(output_consumer): + return stream_util.TransformingConsumer(lambda x: x, output_consumer) + + +class AsynchronousEcho(stream.Consumer): + """A stream.Consumer that echoes its input to another stream.Consumer.""" + + def __init__(self, output_consumer, pool): + self._lock = threading.Lock() + self._output_consumer = output_consumer + self._pool = pool + + self._queue = [] + self._spinning = False + + def _spin(self, value, complete): + while True: + if value: + if complete: + self._output_consumer.consume_and_terminate(value) + else: + self._output_consumer.consume(value) + elif complete: + self._output_consumer.terminate() + with self._lock: + if self._queue: + value, complete = self._queue.pop(0) + else: + self._spinning = False + return + + def consume(self, value): + with self._lock: + if self._spinning: + self._queue.append((value, False)) + else: + self._spinning = True + self._pool.submit(self._spin, value, False) + + def terminate(self): + with self._lock: + if self._spinning: + self._queue.append((None, True)) + else: + self._spinning = True + self._pool.submit(self._spin, None, True) + + def consume_and_terminate(self, value): + with self._lock: + if self._spinning: + self._queue.append((value, True)) + else: + self._spinning = True + self._pool.submit(self._spin, value, True) + + +class TestServicer(interfaces.Servicer): + """An interfaces.Servicer with instrumented for testing.""" + + def __init__(self, pool): + self._pool = pool + self.condition = threading.Condition() + self._released = False + + def service(self, name, context, output_consumer): + if name == SYNCHRONOUS_ECHO: + return _synchronous_echo(output_consumer) + elif name == ASYNCHRONOUS_ECHO: + return AsynchronousEcho(output_consumer, self._pool) + elif name == IMMEDIATE_FAILURE: + raise ValueError() + elif name == TRIGGERED_FAILURE: + raise NotImplementedError + elif name == WAIT_ON_CONDITION: + with self.condition: + while not self._released: + self.condition.wait() + return _synchronous_echo(output_consumer) + else: + raise NotImplementedError() + + def release(self): + with self.condition: + self._released = True + self.condition.notify_all() + + +class EasyServicedIngestor(interfaces.ServicedIngestor): + """A trivial implementation of interfaces.ServicedIngestor.""" + + def __init__(self, consumer): + self._consumer = consumer + + def consumer(self, operation_context): + """See interfaces.ServicedIngestor.consumer for specification.""" + return self._consumer + + +class FrontAndBackTest(object): + """A test suite usable against any joined Front and Back.""" + + # Pylint doesn't know that this is a unittest.TestCase mix-in. + # pylint: disable=invalid-name + + def testSimplestCall(self): + """Tests the absolute simplest call - a one-ticket fire-and-forget.""" + self.front.operate( + SYNCHRONOUS_ECHO, None, True, SMALL_TIMEOUT, + util.none_serviced_subscription(), 'test trace ID') + util.wait_for_idle(self.front) + self.assertEqual( + 1, self.front.operation_stats()[interfaces.Outcome.COMPLETED]) + + # Assuming nothing really pathological (such as pauses on the order of + # SMALL_TIMEOUT interfering with this test) there are a two different ways + # the back could have experienced execution up to this point: + # (1) The ticket is still either in the front waiting to be transmitted + # or is somewhere on the link between the front and the back. The back has + # no idea that this test is even happening. Calling wait_for_idle on it + # would do no good because in this case the back is idle and the call would + # return with the ticket bound for it still in the front or on the link. + back_operation_stats = self.back.operation_stats() + first_back_possibility = EMPTY_OUTCOME_DICT + # (2) The ticket arrived at the back and the back completed the operation. + second_back_possibility = dict(EMPTY_OUTCOME_DICT) + second_back_possibility[interfaces.Outcome.COMPLETED] = 1 + self.assertIn( + back_operation_stats, (first_back_possibility, second_back_possibility)) + # It's true that if the ticket had arrived at the back and the back had + # begun processing that wait_for_idle could hold test execution until the + # back completed the operation, but that doesn't really collapse the + # possibility space down to one solution. + + def testEntireEcho(self): + """Tests a very simple one-ticket-each-way round-trip.""" + test_payload = 'test payload' + test_consumer = stream_testing.TestConsumer() + subscription = util.full_serviced_subscription( + EasyServicedIngestor(test_consumer)) + + self.front.operate( + ASYNCHRONOUS_ECHO, test_payload, True, SMALL_TIMEOUT, subscription, + 'test trace ID') + + util.wait_for_idle(self.front) + util.wait_for_idle(self.back) + self.assertEqual( + 1, self.front.operation_stats()[interfaces.Outcome.COMPLETED]) + self.assertEqual( + 1, self.back.operation_stats()[interfaces.Outcome.COMPLETED]) + self.assertListEqual([(test_payload, True)], test_consumer.calls) + + def testBidirectionalStreamingEcho(self): + """Tests sending multiple tickets each way.""" + test_payload_template = 'test_payload: %03d' + test_payloads = [test_payload_template % i for i in range(STREAM_LENGTH)] + test_consumer = stream_testing.TestConsumer() + subscription = util.full_serviced_subscription( + EasyServicedIngestor(test_consumer)) + + operation = self.front.operate( + SYNCHRONOUS_ECHO, None, False, SMALL_TIMEOUT, subscription, + 'test trace ID') + + for test_payload in test_payloads: + operation.consumer.consume(test_payload) + operation.consumer.terminate() + + util.wait_for_idle(self.front) + util.wait_for_idle(self.back) + self.assertEqual( + 1, self.front.operation_stats()[interfaces.Outcome.COMPLETED]) + self.assertEqual( + 1, self.back.operation_stats()[interfaces.Outcome.COMPLETED]) + self.assertListEqual(test_payloads, test_consumer.values()) + + def testCancellation(self): + """Tests cancelling a long-lived operation.""" + test_consumer = stream_testing.TestConsumer() + subscription = util.full_serviced_subscription( + EasyServicedIngestor(test_consumer)) + + operation = self.front.operate( + ASYNCHRONOUS_ECHO, None, False, SMALL_TIMEOUT, subscription, + 'test trace ID') + operation.cancel() + + util.wait_for_idle(self.front) + self.assertEqual( + 1, self.front.operation_stats()[interfaces.Outcome.CANCELLED]) + util.wait_for_idle(self.back) + self.assertListEqual([], test_consumer.calls) + + # Assuming nothing really pathological (such as pauses on the order of + # SMALL_TIMEOUT interfering with this test) there are a two different ways + # the back could have experienced execution up to this point: + # (1) Both tickets are still either in the front waiting to be transmitted + # or are somewhere on the link between the front and the back. The back has + # no idea that this test is even happening. Calling wait_for_idle on it + # would do no good because in this case the back is idle and the call would + # return with the tickets bound for it still in the front or on the link. + back_operation_stats = self.back.operation_stats() + first_back_possibility = EMPTY_OUTCOME_DICT + # (2) Both tickets arrived within SMALL_TIMEOUT of one another at the back. + # The back started processing based on the first ticket and then stopped + # upon receiving the cancellation ticket. + second_back_possibility = dict(EMPTY_OUTCOME_DICT) + second_back_possibility[interfaces.Outcome.CANCELLED] = 1 + self.assertIn( + back_operation_stats, (first_back_possibility, second_back_possibility)) + + def testExpiration(self): + """Tests that operations time out.""" + timeout = TICK * 2 + allowance = TICK # How much extra time to + condition = threading.Condition() + test_payload = 'test payload' + subscription = util.termination_only_serviced_subscription() + start_time = time.time() + + outcome_cell = [None] + termination_time_cell = [None] + def termination_action(outcome): + with condition: + outcome_cell[0] = outcome + termination_time_cell[0] = time.time() + condition.notify() + + with condition: + operation = self.front.operate( + SYNCHRONOUS_ECHO, test_payload, False, timeout, subscription, + 'test trace ID') + operation.context.add_termination_callback(termination_action) + while outcome_cell[0] is None: + condition.wait() + + duration = termination_time_cell[0] - start_time + self.assertLessEqual(timeout, duration) + self.assertLess(duration, timeout + allowance) + self.assertEqual(interfaces.Outcome.EXPIRED, outcome_cell[0]) + util.wait_for_idle(self.front) + self.assertEqual( + 1, self.front.operation_stats()[interfaces.Outcome.EXPIRED]) + util.wait_for_idle(self.back) + self.assertLessEqual( + 1, self.back.operation_stats()[interfaces.Outcome.EXPIRED]) diff --git a/src/python/grpcio_test/grpc_test/framework/common/__init__.py b/src/python/grpcio_test/grpc_test/framework/common/__init__.py new file mode 100644 index 00000000..70865191 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/common/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, 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. + + diff --git a/src/python/grpcio_test/grpc_test/framework/common/test_constants.py b/src/python/grpcio_test/grpc_test/framework/common/test_constants.py new file mode 100644 index 00000000..e1d3c270 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/common/test_constants.py @@ -0,0 +1,53 @@ +# Copyright 2015, 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. + +"""Constants shared among tests throughout RPC Framework.""" + +# Value for maximum duration in seconds that a test is allowed for its actual +# behavioral logic, excluding all time spent deliberately waiting in the test. +TIME_ALLOWANCE = 10 +# Value for maximum duration in seconds of RPCs that may time out as part of a +# test. +SHORT_TIMEOUT = 4 +# Absurdly large value for maximum duration in seconds for should-not-time-out +# RPCs made during tests. +LONG_TIMEOUT = 3000 +# Values to supply on construction of an object that will service RPCs; these +# should not be used as the actual timeout values of any RPCs made during tests. +DEFAULT_TIMEOUT = 300 +MAXIMUM_TIMEOUT = 3600 + +# The number of payloads to transmit in streaming tests. +STREAM_LENGTH = 200 + +# The size of payloads to transmit in tests. +PAYLOAD_SIZE = 256 * 1024 + 17 + +# The size of thread pools to use in tests. +POOL_SIZE = 10 diff --git a/src/python/grpcio_test/grpc_test/framework/common/test_control.py b/src/python/grpcio_test/grpc_test/framework/common/test_control.py new file mode 100644 index 00000000..8d6eba5c --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/common/test_control.py @@ -0,0 +1,95 @@ +# Copyright 2015, 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. + +"""Code for instructing systems under test to block or fail.""" + +import abc +import contextlib +import threading + + +class Defect(Exception): + """Simulates a programming defect raised into in a system under test. + + Use of a standard exception type is too easily misconstrued as an actual + defect in either the test infrastructure or the system under test. + """ + + +class Control(object): + """An object that accepts program control from a system under test. + + Systems under test passed a Control should call its control() method + frequently during execution. The control() method may block, raise an + exception, or do nothing, all according to the enclosing test's desire for + the system under test to simulate hanging, failing, or functioning. + """ + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def control(self): + """Potentially does anything.""" + raise NotImplementedError() + + +class PauseFailControl(Control): + """A Control that can be used to pause or fail code under control.""" + + def __init__(self): + self._condition = threading.Condition() + self._paused = False + self._fail = False + + def control(self): + with self._condition: + if self._fail: + raise Defect() + + while self._paused: + self._condition.wait() + + @contextlib.contextmanager + def pause(self): + """Pauses code under control while controlling code is in context.""" + with self._condition: + self._paused = True + yield + with self._condition: + self._paused = False + self._condition.notify_all() + + @contextlib.contextmanager + def fail(self): + """Fails code under control while controlling code is in context.""" + with self._condition: + self._fail = True + yield + with self._condition: + self._fail = False diff --git a/src/python/grpcio_test/grpc_test/framework/common/test_coverage.py b/src/python/grpcio_test/grpc_test/framework/common/test_coverage.py new file mode 100644 index 00000000..a7ed3582 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/common/test_coverage.py @@ -0,0 +1,116 @@ +# Copyright 2015, 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. + +"""Governs coverage for tests of RPCs throughout RPC Framework.""" + +import abc + +# This code is designed for use with the unittest module. +# pylint: disable=invalid-name + + +class Coverage(object): + """Specification of test coverage.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def testSuccessfulUnaryRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testSuccessfulUnaryRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testSuccessfulStreamRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testSuccessfulStreamRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testSequentialInvocations(self): + raise NotImplementedError() + + @abc.abstractmethod + def testParallelInvocations(self): + raise NotImplementedError() + + @abc.abstractmethod + def testWaitingForSomeButNotAllParallelInvocations(self): + raise NotImplementedError() + + @abc.abstractmethod + def testCancelledUnaryRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testCancelledUnaryRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testCancelledStreamRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testCancelledStreamRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testExpiredUnaryRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testExpiredUnaryRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testExpiredStreamRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testExpiredStreamRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testFailedUnaryRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testFailedUnaryRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testFailedStreamRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testFailedStreamRequestStreamResponse(self): + raise NotImplementedError() diff --git a/src/python/grpcio_test/grpc_test/framework/core/__init__.py b/src/python/grpcio_test/grpc_test/framework/core/__init__.py new file mode 100644 index 00000000..70865191 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/core/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, 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. + + diff --git a/src/python/grpcio_test/grpc_test/framework/core/_base_interface_test.py b/src/python/grpcio_test/grpc_test/framework/core/_base_interface_test.py new file mode 100644 index 00000000..8d72f131 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/core/_base_interface_test.py @@ -0,0 +1,96 @@ +# Copyright 2015, 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. + +"""Tests the RPC Framework Core's implementation of the Base interface.""" + +import logging +import random +import time +import unittest + +from grpc.framework.core import implementations +from grpc.framework.interfaces.base import utilities +from grpc_test.framework.common import test_constants +from grpc_test.framework.interfaces.base import test_cases +from grpc_test.framework.interfaces.base import test_interfaces + + +class _Implementation(test_interfaces.Implementation): + + def __init__(self): + self._invocation_initial_metadata = object() + self._service_initial_metadata = object() + self._invocation_terminal_metadata = object() + self._service_terminal_metadata = object() + + def instantiate(self, serializations, servicer): + invocation = implementations.invocation_end_link() + service = implementations.service_end_link( + servicer, test_constants.DEFAULT_TIMEOUT, + test_constants.MAXIMUM_TIMEOUT) + invocation.join_link(service) + service.join_link(invocation) + return invocation, service, None + + def destantiate(self, memo): + pass + + def invocation_initial_metadata(self): + return self._invocation_initial_metadata + + def service_initial_metadata(self): + return self._service_initial_metadata + + def invocation_completion(self): + return utilities.completion(self._invocation_terminal_metadata, None, None) + + def service_completion(self): + return utilities.completion(self._service_terminal_metadata, None, None) + + def metadata_transmitted(self, original_metadata, transmitted_metadata): + return transmitted_metadata is original_metadata + + def completion_transmitted(self, original_completion, transmitted_completion): + return ( + (original_completion.terminal_metadata is + transmitted_completion.terminal_metadata) and + original_completion.code is transmitted_completion.code and + original_completion.message is transmitted_completion.message + ) + + +def load_tests(loader, tests, pattern): + return unittest.TestSuite( + tests=tuple( + loader.loadTestsFromTestCase(test_case_class) + for test_case_class in test_cases.test_cases(_Implementation()))) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/framework/face/__init__.py b/src/python/grpcio_test/grpc_test/framework/face/__init__.py new file mode 100644 index 00000000..70865191 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, 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. + + diff --git a/src/python/grpcio_test/grpc_test/framework/face/_test_case.py b/src/python/grpcio_test/grpc_test/framework/face/_test_case.py new file mode 100644 index 00000000..486b6e63 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/_test_case.py @@ -0,0 +1,61 @@ +# Copyright 2015, 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. + +"""Common lifecycle code for in-memory-ticket-exchange Face-layer tests.""" + +from grpc.framework.face import implementations +from grpc.framework.foundation import logging_pool +from grpc_test.framework.face.testing import base_util +from grpc_test.framework.face.testing import test_case + +_TIMEOUT = 3 +_MAXIMUM_POOL_SIZE = 10 + + +class FaceTestCase(test_case.FaceTestCase): + """Provides abstract Face-layer tests an in-memory implementation.""" + + def set_up_implementation( + self, name, methods, method_implementations, + multi_method_implementation): + servicer_pool = logging_pool.pool(_MAXIMUM_POOL_SIZE) + stub_pool = logging_pool.pool(_MAXIMUM_POOL_SIZE) + + servicer = implementations.servicer( + servicer_pool, method_implementations, multi_method_implementation) + + linked_pair = base_util.linked_pair(servicer, _TIMEOUT) + stub = implementations.generic_stub(linked_pair.front, stub_pool) + return stub, (servicer_pool, stub_pool, linked_pair) + + def tear_down_implementation(self, memo): + servicer_pool, stub_pool, linked_pair = memo + linked_pair.shut_down() + stub_pool.shutdown(wait=True) + servicer_pool.shutdown(wait=True) diff --git a/src/python/grpcio_test/grpc_test/framework/face/blocking_invocation_inline_service_test.py b/src/python/grpcio_test/grpc_test/framework/face/blocking_invocation_inline_service_test.py new file mode 100644 index 00000000..86746664 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/blocking_invocation_inline_service_test.py @@ -0,0 +1,46 @@ +# Copyright 2015, 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. + +"""One of the tests of the Face layer of RPC Framework.""" + +import unittest + +from grpc_test.framework.face import _test_case +from grpc_test.framework.face.testing import blocking_invocation_inline_service_test_case as test_case + + +class BlockingInvocationInlineServiceTest( + _test_case.FaceTestCase, + test_case.BlockingInvocationInlineServiceTestCase, + unittest.TestCase): + pass + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/framework/face/event_invocation_synchronous_event_service_test.py b/src/python/grpcio_test/grpc_test/framework/face/event_invocation_synchronous_event_service_test.py new file mode 100644 index 00000000..dca373ef --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/event_invocation_synchronous_event_service_test.py @@ -0,0 +1,46 @@ +# Copyright 2015, 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. + +"""One of the tests of the Face layer of RPC Framework.""" + +import unittest + +from grpc_test.framework.face import _test_case +from grpc_test.framework.face.testing import event_invocation_synchronous_event_service_test_case as test_case + + +class EventInvocationSynchronousEventServiceTest( + _test_case.FaceTestCase, + test_case.EventInvocationSynchronousEventServiceTestCase, + unittest.TestCase): + pass + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/framework/face/future_invocation_asynchronous_event_service_test.py b/src/python/grpcio_test/grpc_test/framework/face/future_invocation_asynchronous_event_service_test.py new file mode 100644 index 00000000..99fdf181 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/future_invocation_asynchronous_event_service_test.py @@ -0,0 +1,46 @@ +# Copyright 2015, 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. + +"""One of the tests of the Face layer of RPC Framework.""" + +import unittest + +from grpc_test.framework.face import _test_case +from grpc_test.framework.face.testing import future_invocation_asynchronous_event_service_test_case as test_case + + +class FutureInvocationAsynchronousEventServiceTest( + _test_case.FaceTestCase, + test_case.FutureInvocationAsynchronousEventServiceTestCase, + unittest.TestCase): + pass + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/__init__.py b/src/python/grpcio_test/grpc_test/framework/face/testing/__init__.py new file mode 100644 index 00000000..70865191 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/testing/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, 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. + + diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/base_util.py b/src/python/grpcio_test/grpc_test/framework/face/testing/base_util.py new file mode 100644 index 00000000..1df1529b --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/testing/base_util.py @@ -0,0 +1,102 @@ +# Copyright 2015, 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. + +"""Utilities for creating Base-layer objects for use in Face-layer tests.""" + +import abc + +# interfaces is referenced from specification in this module. +from grpc.framework.base import util as _base_util +from grpc.framework.base import implementations +from grpc.framework.base import in_memory +from grpc.framework.base import interfaces # pylint: disable=unused-import +from grpc.framework.foundation import logging_pool + +_POOL_SIZE_LIMIT = 5 + +_MAXIMUM_TIMEOUT = 90 + + +class LinkedPair(object): + """A Front and Back that are linked to one another. + + Attributes: + front: An interfaces.Front. + back: An interfaces.Back. + """ + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def shut_down(self): + """Shuts down this object and releases its resources.""" + raise NotImplementedError() + + +class _LinkedPair(LinkedPair): + + def __init__(self, front, back, pools): + self.front = front + self.back = back + self._pools = pools + + def shut_down(self): + _base_util.wait_for_idle(self.front) + _base_util.wait_for_idle(self.back) + + for pool in self._pools: + pool.shutdown(wait=True) + + +def linked_pair(servicer, default_timeout): + """Creates a Server and Stub linked together for use.""" + link_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + front_work_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + front_transmission_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + front_utility_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + back_work_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + back_transmission_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + back_utility_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + pools = ( + link_pool, + front_work_pool, front_transmission_pool, front_utility_pool, + back_work_pool, back_transmission_pool, back_utility_pool) + + link = in_memory.Link(link_pool) + front = implementations.front_link( + front_work_pool, front_transmission_pool, front_utility_pool) + back = implementations.back_link( + servicer, back_work_pool, back_transmission_pool, back_utility_pool, + default_timeout, _MAXIMUM_TIMEOUT) + front.join_rear_link(link) + link.join_fore_link(front) + back.join_fore_link(link) + link.join_rear_link(back) + + return _LinkedPair(front, back, pools) diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/blocking_invocation_inline_service_test_case.py b/src/python/grpcio_test/grpc_test/framework/face/testing/blocking_invocation_inline_service_test_case.py new file mode 100644 index 00000000..251e1eb6 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/testing/blocking_invocation_inline_service_test_case.py @@ -0,0 +1,222 @@ +# Copyright 2015, 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. + +"""A test to verify an implementation of the Face layer of RPC Framework.""" + +# unittest is referenced from specification in this module. +import abc +import unittest # pylint: disable=unused-import + +from grpc.framework.face import exceptions +from grpc_test.framework.common import test_constants +from grpc_test.framework.face.testing import control +from grpc_test.framework.face.testing import coverage +from grpc_test.framework.face.testing import digest +from grpc_test.framework.face.testing import stock_service +from grpc_test.framework.face.testing import test_case + + +class BlockingInvocationInlineServiceTestCase( + test_case.FaceTestCase, coverage.BlockingCoverage): + """A test of the Face layer of RPC Framework. + + Concrete subclasses must also extend unittest.TestCase. + """ + __metaclass__ = abc.ABCMeta + + def setUp(self): + """See unittest.TestCase.setUp for full specification. + + Overriding implementations must call this implementation. + """ + self.control = control.PauseFailControl() + self.digest = digest.digest( + stock_service.STOCK_TEST_SERVICE, self.control, None) + + self.stub, self.memo = self.set_up_implementation( + self.digest.name, self.digest.methods, + self.digest.inline_method_implementations, None) + + def tearDown(self): + """See unittest.TestCase.tearDown for full specification. + + Overriding implementations must call this implementation. + """ + self.tear_down_implementation(self.memo) + + def testSuccessfulUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + response = self.stub.blocking_value_in_value_out( + name, request, test_constants.LONG_TIMEOUT) + + test_messages.verify(request, response, self) + + def testSuccessfulUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + response_iterator = self.stub.inline_value_in_stream_out( + name, request, test_constants.LONG_TIMEOUT) + responses = list(response_iterator) + + test_messages.verify(request, responses, self) + + def testSuccessfulStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + response = self.stub.blocking_stream_in_value_out( + name, iter(requests), test_constants.LONG_TIMEOUT) + + test_messages.verify(requests, response, self) + + def testSuccessfulStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + response_iterator = self.stub.inline_stream_in_stream_out( + name, iter(requests), test_constants.LONG_TIMEOUT) + responses = list(response_iterator) + + test_messages.verify(requests, responses, self) + + def testSequentialInvocations(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + first_request = test_messages.request() + second_request = test_messages.request() + + first_response = self.stub.blocking_value_in_value_out( + name, first_request, test_constants.SHORT_TIMEOUT) + + test_messages.verify(first_request, first_response, self) + + second_response = self.stub.blocking_value_in_value_out( + name, second_request, test_constants.SHORT_TIMEOUT) + + test_messages.verify(second_request, second_response, self) + + def testExpiredUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self.control.pause(), self.assertRaises( + exceptions.ExpirationError): + multi_callable = self.stub.unary_unary_multi_callable(name) + multi_callable(request, test_constants.SHORT_TIMEOUT) + + def testExpiredUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self.control.pause(), self.assertRaises( + exceptions.ExpirationError): + response_iterator = self.stub.inline_value_in_stream_out( + name, request, test_constants.SHORT_TIMEOUT) + list(response_iterator) + + def testExpiredStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self.control.pause(), self.assertRaises( + exceptions.ExpirationError): + multi_callable = self.stub.stream_unary_multi_callable(name) + multi_callable(iter(requests), test_constants.SHORT_TIMEOUT) + + def testExpiredStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self.control.pause(), self.assertRaises( + exceptions.ExpirationError): + response_iterator = self.stub.inline_stream_in_stream_out( + name, iter(requests), test_constants.SHORT_TIMEOUT) + list(response_iterator) + + def testFailedUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self.control.fail(), self.assertRaises(exceptions.ServicerError): + self.stub.blocking_value_in_value_out(name, request, + test_constants.SHORT_TIMEOUT) + + def testFailedUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self.control.fail(), self.assertRaises(exceptions.ServicerError): + response_iterator = self.stub.inline_value_in_stream_out( + name, request, test_constants.SHORT_TIMEOUT) + list(response_iterator) + + def testFailedStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self.control.fail(), self.assertRaises(exceptions.ServicerError): + self.stub.blocking_stream_in_value_out(name, iter(requests), + test_constants.SHORT_TIMEOUT) + + def testFailedStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self.control.fail(), self.assertRaises(exceptions.ServicerError): + response_iterator = self.stub.inline_stream_in_stream_out( + name, iter(requests), test_constants.SHORT_TIMEOUT) + list(response_iterator) diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/callback.py b/src/python/grpcio_test/grpc_test/framework/face/testing/callback.py new file mode 100644 index 00000000..d0e63c8c --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/testing/callback.py @@ -0,0 +1,94 @@ +# Copyright 2015, 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. + +"""A utility useful in tests of asynchronous, event-driven interfaces.""" + +import threading + +from grpc.framework.foundation import stream + + +class Callback(stream.Consumer): + """A utility object useful in tests of asynchronous code.""" + + def __init__(self): + self._condition = threading.Condition() + self._unary_response = None + self._streamed_responses = [] + self._completed = False + self._abortion = None + + def abort(self, abortion): + with self._condition: + self._abortion = abortion + self._condition.notify_all() + + def complete(self, unary_response): + with self._condition: + self._unary_response = unary_response + self._completed = True + self._condition.notify_all() + + def consume(self, streamed_response): + with self._condition: + self._streamed_responses.append(streamed_response) + + def terminate(self): + with self._condition: + self._completed = True + self._condition.notify_all() + + def consume_and_terminate(self, streamed_response): + with self._condition: + self._streamed_responses.append(streamed_response) + self._completed = True + self._condition.notify_all() + + def block_until_terminated(self): + with self._condition: + while self._abortion is None and not self._completed: + self._condition.wait() + + def response(self): + with self._condition: + if self._abortion is None: + return self._unary_response + else: + raise AssertionError('Aborted with abortion "%s"!' % self._abortion) + + def responses(self): + with self._condition: + if self._abortion is None: + return list(self._streamed_responses) + else: + raise AssertionError('Aborted with abortion "%s"!' % self._abortion) + + def abortion(self): + with self._condition: + return self._abortion diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/control.py b/src/python/grpcio_test/grpc_test/framework/face/testing/control.py new file mode 100644 index 00000000..3960c4e6 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/testing/control.py @@ -0,0 +1,87 @@ +# Copyright 2015, 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. + +"""Code for instructing systems under test to block or fail.""" + +import abc +import contextlib +import threading + + +class Control(object): + """An object that accepts program control from a system under test. + + Systems under test passed a Control should call its control() method + frequently during execution. The control() method may block, raise an + exception, or do nothing, all according to the enclosing test's desire for + the system under test to simulate hanging, failing, or functioning. + """ + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def control(self): + """Potentially does anything.""" + raise NotImplementedError() + + +class PauseFailControl(Control): + """A Control that can be used to pause or fail code under control.""" + + def __init__(self): + self._condition = threading.Condition() + self._paused = False + self._fail = False + + def control(self): + with self._condition: + if self._fail: + raise ValueError() + + while self._paused: + self._condition.wait() + + @contextlib.contextmanager + def pause(self): + """Pauses code under control while controlling code is in context.""" + with self._condition: + self._paused = True + yield + with self._condition: + self._paused = False + self._condition.notify_all() + + @contextlib.contextmanager + def fail(self): + """Fails code under control while controlling code is in context.""" + with self._condition: + self._fail = True + yield + with self._condition: + self._fail = False diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/coverage.py b/src/python/grpcio_test/grpc_test/framework/face/testing/coverage.py new file mode 100644 index 00000000..f3aca113 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/testing/coverage.py @@ -0,0 +1,123 @@ +# Copyright 2015, 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. + +"""Governs coverage for the tests of the Face layer of RPC Framework.""" + +import abc + +# These classes are only valid when inherited by unittest.TestCases. +# pylint: disable=invalid-name + + +class BlockingCoverage(object): + """Specification of test coverage for blocking behaviors.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def testSuccessfulUnaryRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testSuccessfulUnaryRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testSuccessfulStreamRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testSuccessfulStreamRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testSequentialInvocations(self): + raise NotImplementedError() + + @abc.abstractmethod + def testExpiredUnaryRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testExpiredUnaryRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testExpiredStreamRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testExpiredStreamRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testFailedUnaryRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testFailedUnaryRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testFailedStreamRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testFailedStreamRequestStreamResponse(self): + raise NotImplementedError() + + +class FullCoverage(BlockingCoverage): + """Specification of test coverage for non-blocking behaviors.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def testParallelInvocations(self): + raise NotImplementedError() + + @abc.abstractmethod + def testWaitingForSomeButNotAllParallelInvocations(self): + raise NotImplementedError() + + @abc.abstractmethod + def testCancelledUnaryRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testCancelledUnaryRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testCancelledStreamRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testCancelledStreamRequestStreamResponse(self): + raise NotImplementedError() diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/digest.py b/src/python/grpcio_test/grpc_test/framework/face/testing/digest.py new file mode 100644 index 00000000..54ff2177 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/testing/digest.py @@ -0,0 +1,450 @@ +# Copyright 2015, 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. + +"""Code for making a service.TestService more amenable to use in tests.""" + +import collections +import threading + +# testing_control, interfaces, and testing_service are referenced from +# specification in this module. +from grpc.framework.common import cardinality +from grpc.framework.common import style +from grpc.framework.face import exceptions +from grpc.framework.face import interfaces as face_interfaces +from grpc.framework.foundation import stream +from grpc.framework.foundation import stream_util +from grpc_test.framework.face.testing import control as testing_control # pylint: disable=unused-import +from grpc_test.framework.face.testing import interfaces # pylint: disable=unused-import +from grpc_test.framework.face.testing import service as testing_service # pylint: disable=unused-import + +_IDENTITY = lambda x: x + + +class TestServiceDigest( + collections.namedtuple( + 'TestServiceDigest', + ['name', + 'methods', + 'inline_method_implementations', + 'event_method_implementations', + 'multi_method_implementation', + 'unary_unary_messages_sequences', + 'unary_stream_messages_sequences', + 'stream_unary_messages_sequences', + 'stream_stream_messages_sequences'])): + """A transformation of a service.TestService. + + Attributes: + name: The RPC service name to be used in the test. + methods: A sequence of interfaces.Method objects describing the RPC + methods that will be called during the test. + inline_method_implementations: A dict from RPC method name to + face_interfaces.MethodImplementation object to be used in tests of + in-line calls to behaviors under test. + event_method_implementations: A dict from RPC method name to + face_interfaces.MethodImplementation object to be used in tests of + event-driven calls to behaviors under test. + multi_method_implementation: A face_interfaces.MultiMethodImplementation to + be used in tests of generic calls to behaviors under test. + unary_unary_messages_sequences: A dict from method name to sequence of + service.UnaryUnaryTestMessages objects to be used to test the method + with the given name. + unary_stream_messages_sequences: A dict from method name to sequence of + service.UnaryStreamTestMessages objects to be used to test the method + with the given name. + stream_unary_messages_sequences: A dict from method name to sequence of + service.StreamUnaryTestMessages objects to be used to test the method + with the given name. + stream_stream_messages_sequences: A dict from method name to sequence of + service.StreamStreamTestMessages objects to be used to test the + method with the given name. + serialization: A serial.Serialization object describing serialization + behaviors for all the RPC methods. + """ + + +class _BufferingConsumer(stream.Consumer): + """A trivial Consumer that dumps what it consumes in a user-mutable buffer.""" + + def __init__(self): + self.consumed = [] + self.terminated = False + + def consume(self, value): + self.consumed.append(value) + + def terminate(self): + self.terminated = True + + def consume_and_terminate(self, value): + self.consumed.append(value) + self.terminated = True + + +class _InlineUnaryUnaryMethod(face_interfaces.MethodImplementation): + + def __init__(self, unary_unary_test_method, control): + self._test_method = unary_unary_test_method + self._control = control + + self.cardinality = cardinality.Cardinality.UNARY_UNARY + self.style = style.Service.INLINE + + def unary_unary_inline(self, request, context): + response_list = [] + self._test_method.service( + request, response_list.append, context, self._control) + return response_list.pop(0) + + +class _EventUnaryUnaryMethod(face_interfaces.MethodImplementation): + + def __init__(self, unary_unary_test_method, control, pool): + self._test_method = unary_unary_test_method + self._control = control + self._pool = pool + + self.cardinality = cardinality.Cardinality.UNARY_UNARY + self.style = style.Service.EVENT + + def unary_unary_event(self, request, response_callback, context): + if self._pool is None: + self._test_method.service( + request, response_callback, context, self._control) + else: + self._pool.submit( + self._test_method.service, request, response_callback, context, + self._control) + + +class _InlineUnaryStreamMethod(face_interfaces.MethodImplementation): + + def __init__(self, unary_stream_test_method, control): + self._test_method = unary_stream_test_method + self._control = control + + self.cardinality = cardinality.Cardinality.UNARY_STREAM + self.style = style.Service.INLINE + + def unary_stream_inline(self, request, context): + response_consumer = _BufferingConsumer() + self._test_method.service( + request, response_consumer, context, self._control) + for response in response_consumer.consumed: + yield response + + +class _EventUnaryStreamMethod(face_interfaces.MethodImplementation): + + def __init__(self, unary_stream_test_method, control, pool): + self._test_method = unary_stream_test_method + self._control = control + self._pool = pool + + self.cardinality = cardinality.Cardinality.UNARY_STREAM + self.style = style.Service.EVENT + + def unary_stream_event(self, request, response_consumer, context): + if self._pool is None: + self._test_method.service( + request, response_consumer, context, self._control) + else: + self._pool.submit( + self._test_method.service, request, response_consumer, context, + self._control) + + +class _InlineStreamUnaryMethod(face_interfaces.MethodImplementation): + + def __init__(self, stream_unary_test_method, control): + self._test_method = stream_unary_test_method + self._control = control + + self.cardinality = cardinality.Cardinality.STREAM_UNARY + self.style = style.Service.INLINE + + def stream_unary_inline(self, request_iterator, context): + response_list = [] + request_consumer = self._test_method.service( + response_list.append, context, self._control) + for request in request_iterator: + request_consumer.consume(request) + request_consumer.terminate() + return response_list.pop(0) + + +class _EventStreamUnaryMethod(face_interfaces.MethodImplementation): + + def __init__(self, stream_unary_test_method, control, pool): + self._test_method = stream_unary_test_method + self._control = control + self._pool = pool + + self.cardinality = cardinality.Cardinality.STREAM_UNARY + self.style = style.Service.EVENT + + def stream_unary_event(self, response_callback, context): + request_consumer = self._test_method.service( + response_callback, context, self._control) + if self._pool is None: + return request_consumer + else: + return stream_util.ThreadSwitchingConsumer(request_consumer, self._pool) + + +class _InlineStreamStreamMethod(face_interfaces.MethodImplementation): + + def __init__(self, stream_stream_test_method, control): + self._test_method = stream_stream_test_method + self._control = control + + self.cardinality = cardinality.Cardinality.STREAM_STREAM + self.style = style.Service.INLINE + + def stream_stream_inline(self, request_iterator, context): + response_consumer = _BufferingConsumer() + request_consumer = self._test_method.service( + response_consumer, context, self._control) + + for request in request_iterator: + request_consumer.consume(request) + while response_consumer.consumed: + yield response_consumer.consumed.pop(0) + response_consumer.terminate() + + +class _EventStreamStreamMethod(face_interfaces.MethodImplementation): + + def __init__(self, stream_stream_test_method, control, pool): + self._test_method = stream_stream_test_method + self._control = control + self._pool = pool + + self.cardinality = cardinality.Cardinality.STREAM_STREAM + self.style = style.Service.EVENT + + def stream_stream_event(self, response_consumer, context): + request_consumer = self._test_method.service( + response_consumer, context, self._control) + if self._pool is None: + return request_consumer + else: + return stream_util.ThreadSwitchingConsumer(request_consumer, self._pool) + + +class _UnaryConsumer(stream.Consumer): + """A Consumer that only allows consumption of exactly one value.""" + + def __init__(self, action): + self._lock = threading.Lock() + self._action = action + self._consumed = False + self._terminated = False + + def consume(self, value): + with self._lock: + if self._consumed: + raise ValueError('Unary consumer already consumed!') + elif self._terminated: + raise ValueError('Unary consumer already terminated!') + else: + self._consumed = True + + self._action(value) + + def terminate(self): + with self._lock: + if not self._consumed: + raise ValueError('Unary consumer hasn\'t yet consumed!') + elif self._terminated: + raise ValueError('Unary consumer already terminated!') + else: + self._terminated = True + + def consume_and_terminate(self, value): + with self._lock: + if self._consumed: + raise ValueError('Unary consumer already consumed!') + elif self._terminated: + raise ValueError('Unary consumer already terminated!') + else: + self._consumed = True + self._terminated = True + + self._action(value) + + +class _UnaryUnaryAdaptation(object): + + def __init__(self, unary_unary_test_method): + self._method = unary_unary_test_method + + def service(self, response_consumer, context, control): + def action(request): + self._method.service( + request, response_consumer.consume_and_terminate, context, control) + return _UnaryConsumer(action) + + +class _UnaryStreamAdaptation(object): + + def __init__(self, unary_stream_test_method): + self._method = unary_stream_test_method + + def service(self, response_consumer, context, control): + def action(request): + self._method.service(request, response_consumer, context, control) + return _UnaryConsumer(action) + + +class _StreamUnaryAdaptation(object): + + def __init__(self, stream_unary_test_method): + self._method = stream_unary_test_method + + def service(self, response_consumer, context, control): + return self._method.service( + response_consumer.consume_and_terminate, context, control) + + +class _MultiMethodImplementation(face_interfaces.MultiMethodImplementation): + + def __init__(self, methods, control, pool): + self._methods = methods + self._control = control + self._pool = pool + + def service(self, name, response_consumer, context): + method = self._methods.get(name, None) + if method is None: + raise exceptions.NoSuchMethodError(name) + elif self._pool is None: + return method(response_consumer, context, self._control) + else: + request_consumer = method(response_consumer, context, self._control) + return stream_util.ThreadSwitchingConsumer(request_consumer, self._pool) + + +class _Assembly( + collections.namedtuple( + '_Assembly', + ['methods', 'inlines', 'events', 'adaptations', 'messages'])): + """An intermediate structure created when creating a TestServiceDigest.""" + + +def _assemble( + scenarios, names, inline_method_constructor, event_method_constructor, + adapter, control, pool): + """Creates an _Assembly from the given scenarios.""" + methods = [] + inlines = {} + events = {} + adaptations = {} + messages = {} + for name, scenario in scenarios.iteritems(): + if name in names: + raise ValueError('Repeated name "%s"!' % name) + + test_method = scenario[0] + inline_method = inline_method_constructor(test_method, control) + event_method = event_method_constructor(test_method, control, pool) + adaptation = adapter(test_method) + + methods.append(test_method) + inlines[name] = inline_method + events[name] = event_method + adaptations[name] = adaptation + messages[name] = scenario[1] + + return _Assembly(methods, inlines, events, adaptations, messages) + + +def digest(service, control, pool): + """Creates a TestServiceDigest from a TestService. + + Args: + service: A testing_service.TestService. + control: A testing_control.Control. + pool: If RPC methods should be serviced in a separate thread, a thread pool. + None if RPC methods should be serviced in the thread belonging to the + run-time that calls for their service. + + Returns: + A TestServiceDigest synthesized from the given service.TestService. + """ + names = set() + + unary_unary = _assemble( + service.unary_unary_scenarios(), names, _InlineUnaryUnaryMethod, + _EventUnaryUnaryMethod, _UnaryUnaryAdaptation, control, pool) + names.update(set(unary_unary.inlines)) + + unary_stream = _assemble( + service.unary_stream_scenarios(), names, _InlineUnaryStreamMethod, + _EventUnaryStreamMethod, _UnaryStreamAdaptation, control, pool) + names.update(set(unary_stream.inlines)) + + stream_unary = _assemble( + service.stream_unary_scenarios(), names, _InlineStreamUnaryMethod, + _EventStreamUnaryMethod, _StreamUnaryAdaptation, control, pool) + names.update(set(stream_unary.inlines)) + + stream_stream = _assemble( + service.stream_stream_scenarios(), names, _InlineStreamStreamMethod, + _EventStreamStreamMethod, _IDENTITY, control, pool) + names.update(set(stream_stream.inlines)) + + methods = list(unary_unary.methods) + methods.extend(unary_stream.methods) + methods.extend(stream_unary.methods) + methods.extend(stream_stream.methods) + adaptations = dict(unary_unary.adaptations) + adaptations.update(unary_stream.adaptations) + adaptations.update(stream_unary.adaptations) + adaptations.update(stream_stream.adaptations) + inlines = dict(unary_unary.inlines) + inlines.update(unary_stream.inlines) + inlines.update(stream_unary.inlines) + inlines.update(stream_stream.inlines) + events = dict(unary_unary.events) + events.update(unary_stream.events) + events.update(stream_unary.events) + events.update(stream_stream.events) + + return TestServiceDigest( + service.name(), + methods, + inlines, + events, + _MultiMethodImplementation(adaptations, control, pool), + unary_unary.messages, + unary_stream.messages, + stream_unary.messages, + stream_stream.messages) diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/event_invocation_synchronous_event_service_test_case.py b/src/python/grpcio_test/grpc_test/framework/face/testing/event_invocation_synchronous_event_service_test_case.py new file mode 100644 index 00000000..9df77678 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/testing/event_invocation_synchronous_event_service_test_case.py @@ -0,0 +1,376 @@ +# Copyright 2015, 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. + +"""A test to verify an implementation of the Face layer of RPC Framework.""" + +import abc +import unittest + +from grpc.framework.face import interfaces +from grpc_test.framework.common import test_constants +from grpc_test.framework.face.testing import callback as testing_callback +from grpc_test.framework.face.testing import control +from grpc_test.framework.face.testing import coverage +from grpc_test.framework.face.testing import digest +from grpc_test.framework.face.testing import stock_service +from grpc_test.framework.face.testing import test_case + + +class EventInvocationSynchronousEventServiceTestCase( + test_case.FaceTestCase, coverage.FullCoverage): + """A test of the Face layer of RPC Framework. + + Concrete subclasses must also extend unittest.TestCase. + """ + __metaclass__ = abc.ABCMeta + + def setUp(self): + """See unittest.TestCase.setUp for full specification. + + Overriding implementations must call this implementation. + """ + self.control = control.PauseFailControl() + self.digest = digest.digest( + stock_service.STOCK_TEST_SERVICE, self.control, None) + + self.stub, self.memo = self.set_up_implementation( + self.digest.name, self.digest.methods, + self.digest.event_method_implementations, None) + + def tearDown(self): + """See unittest.TestCase.tearDown for full specification. + + Overriding implementations must call this implementation. + """ + self.tear_down_implementation(self.memo) + + def testSuccessfulUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + callback = testing_callback.Callback() + + self.stub.event_value_in_value_out( + name, request, callback.complete, callback.abort, + test_constants.SHORT_TIMEOUT) + callback.block_until_terminated() + response = callback.response() + + test_messages.verify(request, response, self) + + def testSuccessfulUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + callback = testing_callback.Callback() + + self.stub.event_value_in_stream_out( + name, request, callback, callback.abort, + test_constants.SHORT_TIMEOUT) + callback.block_until_terminated() + responses = callback.responses() + + test_messages.verify(request, responses, self) + + def testSuccessfulStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + callback = testing_callback.Callback() + + unused_call, request_consumer = self.stub.event_stream_in_value_out( + name, callback.complete, callback.abort, + test_constants.SHORT_TIMEOUT) + for request in requests: + request_consumer.consume(request) + request_consumer.terminate() + callback.block_until_terminated() + response = callback.response() + + test_messages.verify(requests, response, self) + + def testSuccessfulStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + callback = testing_callback.Callback() + + unused_call, request_consumer = self.stub.event_stream_in_stream_out( + name, callback, callback.abort, test_constants.SHORT_TIMEOUT) + for request in requests: + request_consumer.consume(request) + request_consumer.terminate() + callback.block_until_terminated() + responses = callback.responses() + + test_messages.verify(requests, responses, self) + + def testSequentialInvocations(self): + # pylint: disable=cell-var-from-loop + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + first_request = test_messages.request() + second_request = test_messages.request() + first_callback = testing_callback.Callback() + second_callback = testing_callback.Callback() + + def make_second_invocation(first_response): + first_callback.complete(first_response) + self.stub.event_value_in_value_out( + name, second_request, second_callback.complete, + second_callback.abort, test_constants.SHORT_TIMEOUT) + + self.stub.event_value_in_value_out( + name, first_request, make_second_invocation, first_callback.abort, + test_constants.SHORT_TIMEOUT) + second_callback.block_until_terminated() + + first_response = first_callback.response() + second_response = second_callback.response() + test_messages.verify(first_request, first_response, self) + test_messages.verify(second_request, second_response, self) + + def testExpiredUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + callback = testing_callback.Callback() + + with self.control.pause(): + self.stub.event_value_in_value_out( + name, request, callback.complete, callback.abort, + test_constants.SHORT_TIMEOUT) + callback.block_until_terminated() + + self.assertEqual(interfaces.Abortion.EXPIRED, callback.abortion()) + + def testExpiredUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + callback = testing_callback.Callback() + + with self.control.pause(): + self.stub.event_value_in_stream_out( + name, request, callback, callback.abort, + test_constants.SHORT_TIMEOUT) + callback.block_until_terminated() + + self.assertEqual(interfaces.Abortion.EXPIRED, callback.abortion()) + + def testExpiredStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for unused_test_messages in test_messages_sequence: + callback = testing_callback.Callback() + + self.stub.event_stream_in_value_out( + name, callback.complete, callback.abort, + test_constants.SHORT_TIMEOUT) + callback.block_until_terminated() + + self.assertEqual(interfaces.Abortion.EXPIRED, callback.abortion()) + + def testExpiredStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + callback = testing_callback.Callback() + + unused_call, request_consumer = self.stub.event_stream_in_stream_out( + name, callback, callback.abort, test_constants.SHORT_TIMEOUT) + for request in requests: + request_consumer.consume(request) + callback.block_until_terminated() + + self.assertEqual(interfaces.Abortion.EXPIRED, callback.abortion()) + + def testFailedUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + callback = testing_callback.Callback() + + with self.control.fail(): + self.stub.event_value_in_value_out( + name, request, callback.complete, callback.abort, + test_constants.SHORT_TIMEOUT) + callback.block_until_terminated() + + self.assertEqual(interfaces.Abortion.SERVICER_FAILURE, + callback.abortion()) + + def testFailedUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + callback = testing_callback.Callback() + + with self.control.fail(): + self.stub.event_value_in_stream_out( + name, request, callback, callback.abort, + test_constants.SHORT_TIMEOUT) + callback.block_until_terminated() + + self.assertEqual(interfaces.Abortion.SERVICER_FAILURE, + callback.abortion()) + + def testFailedStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + callback = testing_callback.Callback() + + with self.control.fail(): + unused_call, request_consumer = self.stub.event_stream_in_value_out( + name, callback.complete, callback.abort, + test_constants.SHORT_TIMEOUT) + for request in requests: + request_consumer.consume(request) + request_consumer.terminate() + callback.block_until_terminated() + + self.assertEqual(interfaces.Abortion.SERVICER_FAILURE, + callback.abortion()) + + def testFailedStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + callback = testing_callback.Callback() + + with self.control.fail(): + unused_call, request_consumer = self.stub.event_stream_in_stream_out( + name, callback, callback.abort, test_constants.SHORT_TIMEOUT) + for request in requests: + request_consumer.consume(request) + request_consumer.terminate() + callback.block_until_terminated() + + self.assertEqual(interfaces.Abortion.SERVICER_FAILURE, callback.abortion()) + + def testParallelInvocations(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + first_request = test_messages.request() + first_callback = testing_callback.Callback() + second_request = test_messages.request() + second_callback = testing_callback.Callback() + + self.stub.event_value_in_value_out( + name, first_request, first_callback.complete, first_callback.abort, + test_constants.SHORT_TIMEOUT) + self.stub.event_value_in_value_out( + name, second_request, second_callback.complete, + second_callback.abort, test_constants.SHORT_TIMEOUT) + first_callback.block_until_terminated() + second_callback.block_until_terminated() + + first_response = first_callback.response() + second_response = second_callback.response() + test_messages.verify(first_request, first_response, self) + test_messages.verify(second_request, second_response, self) + + @unittest.skip('TODO(nathaniel): implement.') + def testWaitingForSomeButNotAllParallelInvocations(self): + raise NotImplementedError() + + def testCancelledUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + callback = testing_callback.Callback() + + with self.control.pause(): + call = self.stub.event_value_in_value_out( + name, request, callback.complete, callback.abort, + test_constants.SHORT_TIMEOUT) + call.cancel() + callback.block_until_terminated() + + self.assertEqual(interfaces.Abortion.CANCELLED, callback.abortion()) + + def testCancelledUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + callback = testing_callback.Callback() + + call = self.stub.event_value_in_stream_out( + name, request, callback, callback.abort, + test_constants.SHORT_TIMEOUT) + call.cancel() + callback.block_until_terminated() + + self.assertEqual(interfaces.Abortion.CANCELLED, callback.abortion()) + + def testCancelledStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + callback = testing_callback.Callback() + + call, request_consumer = self.stub.event_stream_in_value_out( + name, callback.complete, callback.abort, + test_constants.SHORT_TIMEOUT) + for request in requests: + request_consumer.consume(request) + call.cancel() + callback.block_until_terminated() + + self.assertEqual(interfaces.Abortion.CANCELLED, callback.abortion()) + + def testCancelledStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for unused_test_messages in test_messages_sequence: + callback = testing_callback.Callback() + + call, unused_request_consumer = self.stub.event_stream_in_stream_out( + name, callback, callback.abort, test_constants.SHORT_TIMEOUT) + call.cancel() + callback.block_until_terminated() + + self.assertEqual(interfaces.Abortion.CANCELLED, callback.abortion()) diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/future_invocation_asynchronous_event_service_test_case.py b/src/python/grpcio_test/grpc_test/framework/face/testing/future_invocation_asynchronous_event_service_test_case.py new file mode 100644 index 00000000..70d86a04 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/testing/future_invocation_asynchronous_event_service_test_case.py @@ -0,0 +1,379 @@ +# Copyright 2015, 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. + +"""A test to verify an implementation of the Face layer of RPC Framework.""" + +import abc +import contextlib +import threading +import unittest + +from grpc.framework.face import exceptions +from grpc.framework.foundation import future +from grpc.framework.foundation import logging_pool +from grpc_test.framework.common import test_constants +from grpc_test.framework.face.testing import control +from grpc_test.framework.face.testing import coverage +from grpc_test.framework.face.testing import digest +from grpc_test.framework.face.testing import stock_service +from grpc_test.framework.face.testing import test_case + +_MAXIMUM_POOL_SIZE = 10 + + +class _PauseableIterator(object): + + def __init__(self, upstream): + self._upstream = upstream + self._condition = threading.Condition() + self._paused = False + + @contextlib.contextmanager + def pause(self): + with self._condition: + self._paused = True + yield + with self._condition: + self._paused = False + self._condition.notify_all() + + def __iter__(self): + return self + + def next(self): + with self._condition: + while self._paused: + self._condition.wait() + return next(self._upstream) + + +class FutureInvocationAsynchronousEventServiceTestCase( + test_case.FaceTestCase, coverage.FullCoverage): + """A test of the Face layer of RPC Framework. + + Concrete subclasses must also extend unittest.TestCase. + """ + __metaclass__ = abc.ABCMeta + + def setUp(self): + """See unittest.TestCase.setUp for full specification. + + Overriding implementations must call this implementation. + """ + self.control = control.PauseFailControl() + self.digest_pool = logging_pool.pool(_MAXIMUM_POOL_SIZE) + self.digest = digest.digest( + stock_service.STOCK_TEST_SERVICE, self.control, self.digest_pool) + + self.stub, self.memo = self.set_up_implementation( + self.digest.name, self.digest.methods, + self.digest.event_method_implementations, None) + + def tearDown(self): + """See unittest.TestCase.tearDown for full specification. + + Overriding implementations must call this implementation. + """ + self.tear_down_implementation(self.memo) + self.digest_pool.shutdown(wait=True) + + def testSuccessfulUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + response_future = self.stub.future_value_in_value_out( + name, request, test_constants.SHORT_TIMEOUT) + response = response_future.result() + + test_messages.verify(request, response, self) + + def testSuccessfulUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + response_iterator = self.stub.inline_value_in_stream_out( + name, request, test_constants.SHORT_TIMEOUT) + responses = list(response_iterator) + + test_messages.verify(request, responses, self) + + def testSuccessfulStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + request_iterator = _PauseableIterator(iter(requests)) + + # Use of a paused iterator of requests allows us to test that control is + # returned to calling code before the iterator yields any requests. + with request_iterator.pause(): + response_future = self.stub.future_stream_in_value_out( + name, request_iterator, test_constants.SHORT_TIMEOUT) + response = response_future.result() + + test_messages.verify(requests, response, self) + + def testSuccessfulStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + request_iterator = _PauseableIterator(iter(requests)) + + # Use of a paused iterator of requests allows us to test that control is + # returned to calling code before the iterator yields any requests. + with request_iterator.pause(): + response_iterator = self.stub.inline_stream_in_stream_out( + name, request_iterator, test_constants.SHORT_TIMEOUT) + responses = list(response_iterator) + + test_messages.verify(requests, responses, self) + + def testSequentialInvocations(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + first_request = test_messages.request() + second_request = test_messages.request() + + first_response_future = self.stub.future_value_in_value_out( + name, first_request, test_constants.SHORT_TIMEOUT) + first_response = first_response_future.result() + + test_messages.verify(first_request, first_response, self) + + second_response_future = self.stub.future_value_in_value_out( + name, second_request, test_constants.SHORT_TIMEOUT) + second_response = second_response_future.result() + + test_messages.verify(second_request, second_response, self) + + def testExpiredUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self.control.pause(): + multi_callable = self.stub.unary_unary_multi_callable(name) + response_future = multi_callable.future(request, + test_constants.SHORT_TIMEOUT) + self.assertIsInstance( + response_future.exception(), exceptions.ExpirationError) + with self.assertRaises(exceptions.ExpirationError): + response_future.result() + + def testExpiredUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self.control.pause(): + response_iterator = self.stub.inline_value_in_stream_out( + name, request, test_constants.SHORT_TIMEOUT) + with self.assertRaises(exceptions.ExpirationError): + list(response_iterator) + + def testExpiredStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self.control.pause(): + multi_callable = self.stub.stream_unary_multi_callable(name) + response_future = multi_callable.future(iter(requests), + test_constants.SHORT_TIMEOUT) + self.assertIsInstance( + response_future.exception(), exceptions.ExpirationError) + with self.assertRaises(exceptions.ExpirationError): + response_future.result() + + def testExpiredStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self.control.pause(): + response_iterator = self.stub.inline_stream_in_stream_out( + name, iter(requests), test_constants.SHORT_TIMEOUT) + with self.assertRaises(exceptions.ExpirationError): + list(response_iterator) + + def testFailedUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self.control.fail(): + response_future = self.stub.future_value_in_value_out( + name, request, test_constants.SHORT_TIMEOUT) + + # Because the servicer fails outside of the thread from which the + # servicer-side runtime called into it its failure is + # indistinguishable from simply not having called its + # response_callback before the expiration of the RPC. + self.assertIsInstance( + response_future.exception(), exceptions.ExpirationError) + with self.assertRaises(exceptions.ExpirationError): + response_future.result() + + def testFailedUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + # Because the servicer fails outside of the thread from which the + # servicer-side runtime called into it its failure is indistinguishable + # from simply not having called its response_consumer before the + # expiration of the RPC. + with self.control.fail(), self.assertRaises(exceptions.ExpirationError): + response_iterator = self.stub.inline_value_in_stream_out( + name, request, test_constants.SHORT_TIMEOUT) + list(response_iterator) + + def testFailedStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self.control.fail(): + response_future = self.stub.future_stream_in_value_out( + name, iter(requests), test_constants.SHORT_TIMEOUT) + + # Because the servicer fails outside of the thread from which the + # servicer-side runtime called into it its failure is + # indistinguishable from simply not having called its + # response_callback before the expiration of the RPC. + self.assertIsInstance( + response_future.exception(), exceptions.ExpirationError) + with self.assertRaises(exceptions.ExpirationError): + response_future.result() + + def testFailedStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + # Because the servicer fails outside of the thread from which the + # servicer-side runtime called into it its failure is indistinguishable + # from simply not having called its response_consumer before the + # expiration of the RPC. + with self.control.fail(), self.assertRaises(exceptions.ExpirationError): + response_iterator = self.stub.inline_stream_in_stream_out( + name, iter(requests), test_constants.SHORT_TIMEOUT) + list(response_iterator) + + def testParallelInvocations(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + first_request = test_messages.request() + second_request = test_messages.request() + + # TODO(bug 2039): use LONG_TIMEOUT instead + first_response_future = self.stub.future_value_in_value_out( + name, first_request, test_constants.SHORT_TIMEOUT) + second_response_future = self.stub.future_value_in_value_out( + name, second_request, test_constants.SHORT_TIMEOUT) + first_response = first_response_future.result() + second_response = second_response_future.result() + + test_messages.verify(first_request, first_response, self) + test_messages.verify(second_request, second_response, self) + + @unittest.skip('TODO(nathaniel): implement.') + def testWaitingForSomeButNotAllParallelInvocations(self): + raise NotImplementedError() + + def testCancelledUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self.control.pause(): + response_future = self.stub.future_value_in_value_out( + name, request, test_constants.SHORT_TIMEOUT) + cancel_method_return_value = response_future.cancel() + + self.assertFalse(cancel_method_return_value) + self.assertTrue(response_future.cancelled()) + + def testCancelledUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self.control.pause(): + response_iterator = self.stub.inline_value_in_stream_out( + name, request, test_constants.SHORT_TIMEOUT) + response_iterator.cancel() + + with self.assertRaises(future.CancelledError): + next(response_iterator) + + def testCancelledStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self.control.pause(): + response_future = self.stub.future_stream_in_value_out( + name, iter(requests), test_constants.SHORT_TIMEOUT) + cancel_method_return_value = response_future.cancel() + + self.assertFalse(cancel_method_return_value) + self.assertTrue(response_future.cancelled()) + + def testCancelledStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self.control.pause(): + response_iterator = self.stub.inline_stream_in_stream_out( + name, iter(requests), test_constants.SHORT_TIMEOUT) + response_iterator.cancel() + + with self.assertRaises(future.CancelledError): + next(response_iterator) diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/interfaces.py b/src/python/grpcio_test/grpc_test/framework/face/testing/interfaces.py new file mode 100644 index 00000000..5932dabf --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/testing/interfaces.py @@ -0,0 +1,117 @@ +# Copyright 2015, 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. + +"""Interfaces implemented by data sets used in Face-layer tests.""" + +import abc + +# cardinality is referenced from specification in this module. +from grpc.framework.common import cardinality # pylint: disable=unused-import + + +class Method(object): + """An RPC method to be used in tests of RPC implementations.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def name(self): + """Identify the name of the method. + + Returns: + The name of the method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def cardinality(self): + """Identify the cardinality of the method. + + Returns: + A cardinality.Cardinality value describing the streaming semantics of the + method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def request_class(self): + """Identify the class used for the method's request objects. + + Returns: + The class object of the class to which the method's request objects + belong. + """ + raise NotImplementedError() + + @abc.abstractmethod + def response_class(self): + """Identify the class used for the method's response objects. + + Returns: + The class object of the class to which the method's response objects + belong. + """ + raise NotImplementedError() + + @abc.abstractmethod + def serialize_request(self, request): + """Serialize the given request object. + + Args: + request: A request object appropriate for this method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def deserialize_request(self, serialized_request): + """Synthesize a request object from a given bytestring. + + Args: + serialized_request: A bytestring deserializable into a request object + appropriate for this method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def serialize_response(self, response): + """Serialize the given response object. + + Args: + response: A response object appropriate for this method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def deserialize_response(self, serialized_response): + """Synthesize a response object from a given bytestring. + + Args: + serialized_response: A bytestring deserializable into a response object + appropriate for this method. + """ + raise NotImplementedError() diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/serial.py b/src/python/grpcio_test/grpc_test/framework/face/testing/serial.py new file mode 100644 index 00000000..47fc5822 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/testing/serial.py @@ -0,0 +1,70 @@ +# Copyright 2015, 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. + +"""Utility for serialization in the context of test RPC services.""" + +import collections + + +class Serialization( + collections.namedtuple( + '_Serialization', + ['request_serializers', + 'request_deserializers', + 'response_serializers', + 'response_deserializers'])): + """An aggregation of serialization behaviors for an RPC service. + + Attributes: + request_serializers: A dict from method name to request object serializer + behavior. + request_deserializers: A dict from method name to request object + deserializer behavior. + response_serializers: A dict from method name to response object serializer + behavior. + response_deserializers: A dict from method name to response object + deserializer behavior. + """ + + +def serialization(methods): + """Creates a Serialization from a sequences of interfaces.Method objects.""" + request_serializers = {} + request_deserializers = {} + response_serializers = {} + response_deserializers = {} + for method in methods: + name = method.name() + request_serializers[name] = method.serialize_request + request_deserializers[name] = method.deserialize_request + response_serializers[name] = method.serialize_response + response_deserializers[name] = method.deserialize_response + return Serialization( + request_serializers, request_deserializers, response_serializers, + response_deserializers) diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/service.py b/src/python/grpcio_test/grpc_test/framework/face/testing/service.py new file mode 100644 index 00000000..ee9d6a3d --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/testing/service.py @@ -0,0 +1,337 @@ +# Copyright 2015, 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. + +"""Private interfaces implemented by data sets used in Face-layer tests.""" + +import abc + +# interfaces is referenced from specification in this module. +from grpc.framework.face import interfaces as face_interfaces # pylint: disable=unused-import +from grpc_test.framework.face.testing import interfaces + + +class UnaryUnaryTestMethodImplementation(interfaces.Method): + """A controllable implementation of a unary-unary RPC method.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def service(self, request, response_callback, context, control): + """Services an RPC that accepts one message and produces one message. + + Args: + request: The single request message for the RPC. + response_callback: A callback to be called to accept the response message + of the RPC. + context: An face_interfaces.RpcContext object. + control: A test_control.Control to control execution of this method. + + Raises: + abandonment.Abandoned: May or may not be raised when the RPC has been + aborted. + """ + raise NotImplementedError() + + +class UnaryUnaryTestMessages(object): + """A type for unary-request-unary-response message pairings.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def request(self): + """Affords a request message. + + Implementations of this method should return a different message with each + call so that multiple test executions of the test method may be made with + different inputs. + + Returns: + A request message. + """ + raise NotImplementedError() + + @abc.abstractmethod + def verify(self, request, response, test_case): + """Verifies that the computed response matches the given request. + + Args: + request: A request message. + response: A response message. + test_case: A unittest.TestCase object affording useful assertion methods. + + Raises: + AssertionError: If the request and response do not match, indicating that + there was some problem executing the RPC under test. + """ + raise NotImplementedError() + + +class UnaryStreamTestMethodImplementation(interfaces.Method): + """A controllable implementation of a unary-stream RPC method.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def service(self, request, response_consumer, context, control): + """Services an RPC that takes one message and produces a stream of messages. + + Args: + request: The single request message for the RPC. + response_consumer: A stream.Consumer to be called to accept the response + messages of the RPC. + context: A face_interfaces.RpcContext object. + control: A test_control.Control to control execution of this method. + + Raises: + abandonment.Abandoned: May or may not be raised when the RPC has been + aborted. + """ + raise NotImplementedError() + + +class UnaryStreamTestMessages(object): + """A type for unary-request-stream-response message pairings.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def request(self): + """Affords a request message. + + Implementations of this method should return a different message with each + call so that multiple test executions of the test method may be made with + different inputs. + + Returns: + A request message. + """ + raise NotImplementedError() + + @abc.abstractmethod + def verify(self, request, responses, test_case): + """Verifies that the computed responses match the given request. + + Args: + request: A request message. + responses: A sequence of response messages. + test_case: A unittest.TestCase object affording useful assertion methods. + + Raises: + AssertionError: If the request and responses do not match, indicating that + there was some problem executing the RPC under test. + """ + raise NotImplementedError() + + +class StreamUnaryTestMethodImplementation(interfaces.Method): + """A controllable implementation of a stream-unary RPC method.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def service(self, response_callback, context, control): + """Services an RPC that takes a stream of messages and produces one message. + + Args: + response_callback: A callback to be called to accept the response message + of the RPC. + context: A face_interfaces.RpcContext object. + control: A test_control.Control to control execution of this method. + + Returns: + A stream.Consumer with which to accept the request messages of the RPC. + The consumer returned from this method may or may not be invoked to + completion: in the case of RPC abortion, RPC Framework will simply stop + passing messages to this object. Implementations must not assume that + this object will be called to completion of the request stream or even + called at all. + + Raises: + abandonment.Abandoned: May or may not be raised when the RPC has been + aborted. + """ + raise NotImplementedError() + + +class StreamUnaryTestMessages(object): + """A type for stream-request-unary-response message pairings.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def requests(self): + """Affords a sequence of request messages. + + Implementations of this method should return a different sequences with each + call so that multiple test executions of the test method may be made with + different inputs. + + Returns: + A sequence of request messages. + """ + raise NotImplementedError() + + @abc.abstractmethod + def verify(self, requests, response, test_case): + """Verifies that the computed response matches the given requests. + + Args: + requests: A sequence of request messages. + response: A response message. + test_case: A unittest.TestCase object affording useful assertion methods. + + Raises: + AssertionError: If the requests and response do not match, indicating that + there was some problem executing the RPC under test. + """ + raise NotImplementedError() + + +class StreamStreamTestMethodImplementation(interfaces.Method): + """A controllable implementation of a stream-stream RPC method.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def service(self, response_consumer, context, control): + """Services an RPC that accepts and produces streams of messages. + + Args: + response_consumer: A stream.Consumer to be called to accept the response + messages of the RPC. + context: A face_interfaces.RpcContext object. + control: A test_control.Control to control execution of this method. + + Returns: + A stream.Consumer with which to accept the request messages of the RPC. + The consumer returned from this method may or may not be invoked to + completion: in the case of RPC abortion, RPC Framework will simply stop + passing messages to this object. Implementations must not assume that + this object will be called to completion of the request stream or even + called at all. + + Raises: + abandonment.Abandoned: May or may not be raised when the RPC has been + aborted. + """ + raise NotImplementedError() + + +class StreamStreamTestMessages(object): + """A type for stream-request-stream-response message pairings.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def requests(self): + """Affords a sequence of request messages. + + Implementations of this method should return a different sequences with each + call so that multiple test executions of the test method may be made with + different inputs. + + Returns: + A sequence of request messages. + """ + raise NotImplementedError() + + @abc.abstractmethod + def verify(self, requests, responses, test_case): + """Verifies that the computed response matches the given requests. + + Args: + requests: A sequence of request messages. + responses: A sequence of response messages. + test_case: A unittest.TestCase object affording useful assertion methods. + + Raises: + AssertionError: If the requests and responses do not match, indicating + that there was some problem executing the RPC under test. + """ + raise NotImplementedError() + + +class TestService(object): + """A specification of implemented RPC methods to use in tests.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def name(self): + """Identifies the RPC service name used during the test. + + Returns: + The RPC service name to be used for the test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def unary_unary_scenarios(self): + """Affords unary-request-unary-response test methods and their messages. + + Returns: + A dict from method name to pair. The first element of the pair + is a UnaryUnaryTestMethodImplementation object and the second element + is a sequence of UnaryUnaryTestMethodMessages objects. + """ + raise NotImplementedError() + + @abc.abstractmethod + def unary_stream_scenarios(self): + """Affords unary-request-stream-response test methods and their messages. + + Returns: + A dict from method name to pair. The first element of the pair is a + UnaryStreamTestMethodImplementation object and the second element is a + sequence of UnaryStreamTestMethodMessages objects. + """ + raise NotImplementedError() + + @abc.abstractmethod + def stream_unary_scenarios(self): + """Affords stream-request-unary-response test methods and their messages. + + Returns: + A dict from method name to pair. The first element of the pair is a + StreamUnaryTestMethodImplementation object and the second element is a + sequence of StreamUnaryTestMethodMessages objects. + """ + raise NotImplementedError() + + @abc.abstractmethod + def stream_stream_scenarios(self): + """Affords stream-request-stream-response test methods and their messages. + + Returns: + A dict from method name to pair. The first element of the pair is a + StreamStreamTestMethodImplementation object and the second element is a + sequence of StreamStreamTestMethodMessages objects. + """ + raise NotImplementedError() diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/stock_service.py b/src/python/grpcio_test/grpc_test/framework/face/testing/stock_service.py new file mode 100644 index 00000000..0f83ca4d --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/testing/stock_service.py @@ -0,0 +1,374 @@ +# Copyright 2015, 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. + +"""Examples of Python implementations of the stock.proto Stock service.""" + +from grpc.framework.common import cardinality +from grpc.framework.foundation import abandonment +from grpc.framework.foundation import stream +from grpc.framework.foundation import stream_util +from grpc_test.framework.face.testing import service +from grpc_test._junkdrawer import stock_pb2 + +SYMBOL_FORMAT = 'test symbol:%03d' +STREAM_LENGTH = 400 + +# A test-appropriate security-pricing function. :-P +_price = lambda symbol_name: float(hash(symbol_name) % 4096) + + +def _get_last_trade_price(stock_request, stock_reply_callback, control, active): + """A unary-request, unary-response test method.""" + control.control() + if active(): + stock_reply_callback( + stock_pb2.StockReply( + symbol=stock_request.symbol, price=_price(stock_request.symbol))) + else: + raise abandonment.Abandoned() + + +def _get_last_trade_price_multiple(stock_reply_consumer, control, active): + """A stream-request, stream-response test method.""" + def stock_reply_for_stock_request(stock_request): + control.control() + if active(): + return stock_pb2.StockReply( + symbol=stock_request.symbol, price=_price(stock_request.symbol)) + else: + raise abandonment.Abandoned() + return stream_util.TransformingConsumer( + stock_reply_for_stock_request, stock_reply_consumer) + + +def _watch_future_trades(stock_request, stock_reply_consumer, control, active): + """A unary-request, stream-response test method.""" + base_price = _price(stock_request.symbol) + for index in range(stock_request.num_trades_to_watch): + control.control() + if active(): + stock_reply_consumer.consume( + stock_pb2.StockReply( + symbol=stock_request.symbol, price=base_price + index)) + else: + raise abandonment.Abandoned() + stock_reply_consumer.terminate() + + +def _get_highest_trade_price(stock_reply_callback, control, active): + """A stream-request, unary-response test method.""" + + class StockRequestConsumer(stream.Consumer): + """Keeps an ongoing record of the most valuable symbol yet consumed.""" + + def __init__(self): + self._symbol = None + self._price = None + + def consume(self, stock_request): + control.control() + if active(): + if self._price is None: + self._symbol = stock_request.symbol + self._price = _price(stock_request.symbol) + else: + candidate_price = _price(stock_request.symbol) + if self._price < candidate_price: + self._symbol = stock_request.symbol + self._price = candidate_price + + def terminate(self): + control.control() + if active(): + if self._symbol is None: + raise ValueError() + else: + stock_reply_callback( + stock_pb2.StockReply(symbol=self._symbol, price=self._price)) + self._symbol = None + self._price = None + + def consume_and_terminate(self, stock_request): + control.control() + if active(): + if self._price is None: + stock_reply_callback( + stock_pb2.StockReply( + symbol=stock_request.symbol, + price=_price(stock_request.symbol))) + else: + candidate_price = _price(stock_request.symbol) + if self._price < candidate_price: + stock_reply_callback( + stock_pb2.StockReply( + symbol=stock_request.symbol, price=candidate_price)) + else: + stock_reply_callback( + stock_pb2.StockReply( + symbol=self._symbol, price=self._price)) + + self._symbol = None + self._price = None + + return StockRequestConsumer() + + +class GetLastTradePrice(service.UnaryUnaryTestMethodImplementation): + """GetLastTradePrice for use in tests.""" + + def name(self): + return 'GetLastTradePrice' + + def cardinality(self): + return cardinality.Cardinality.UNARY_UNARY + + def request_class(self): + return stock_pb2.StockRequest + + def response_class(self): + return stock_pb2.StockReply + + def serialize_request(self, request): + return request.SerializeToString() + + def deserialize_request(self, serialized_request): + return stock_pb2.StockRequest.FromString(serialized_request) + + def serialize_response(self, response): + return response.SerializeToString() + + def deserialize_response(self, serialized_response): + return stock_pb2.StockReply.FromString(serialized_response) + + def service(self, request, response_callback, context, control): + _get_last_trade_price( + request, response_callback, control, context.is_active) + + +class GetLastTradePriceMessages(service.UnaryUnaryTestMessages): + + def __init__(self): + self._index = 0 + + def request(self): + symbol = SYMBOL_FORMAT % self._index + self._index += 1 + return stock_pb2.StockRequest(symbol=symbol) + + def verify(self, request, response, test_case): + test_case.assertEqual(request.symbol, response.symbol) + test_case.assertEqual(_price(request.symbol), response.price) + + +class GetLastTradePriceMultiple(service.StreamStreamTestMethodImplementation): + """GetLastTradePriceMultiple for use in tests.""" + + def name(self): + return 'GetLastTradePriceMultiple' + + def cardinality(self): + return cardinality.Cardinality.STREAM_STREAM + + def request_class(self): + return stock_pb2.StockRequest + + def response_class(self): + return stock_pb2.StockReply + + def serialize_request(self, request): + return request.SerializeToString() + + def deserialize_request(self, serialized_request): + return stock_pb2.StockRequest.FromString(serialized_request) + + def serialize_response(self, response): + return response.SerializeToString() + + def deserialize_response(self, serialized_response): + return stock_pb2.StockReply.FromString(serialized_response) + + def service(self, response_consumer, context, control): + return _get_last_trade_price_multiple( + response_consumer, control, context.is_active) + + +class GetLastTradePriceMultipleMessages(service.StreamStreamTestMessages): + """Pairs of message streams for use with GetLastTradePriceMultiple.""" + + def __init__(self): + self._index = 0 + + def requests(self): + base_index = self._index + self._index += 1 + return [ + stock_pb2.StockRequest(symbol=SYMBOL_FORMAT % (base_index + index)) + for index in range(STREAM_LENGTH)] + + def verify(self, requests, responses, test_case): + test_case.assertEqual(len(requests), len(responses)) + for stock_request, stock_reply in zip(requests, responses): + test_case.assertEqual(stock_request.symbol, stock_reply.symbol) + test_case.assertEqual(_price(stock_request.symbol), stock_reply.price) + + +class WatchFutureTrades(service.UnaryStreamTestMethodImplementation): + """WatchFutureTrades for use in tests.""" + + def name(self): + return 'WatchFutureTrades' + + def cardinality(self): + return cardinality.Cardinality.UNARY_STREAM + + def request_class(self): + return stock_pb2.StockRequest + + def response_class(self): + return stock_pb2.StockReply + + def serialize_request(self, request): + return request.SerializeToString() + + def deserialize_request(self, serialized_request): + return stock_pb2.StockRequest.FromString(serialized_request) + + def serialize_response(self, response): + return response.SerializeToString() + + def deserialize_response(self, serialized_response): + return stock_pb2.StockReply.FromString(serialized_response) + + def service(self, request, response_consumer, context, control): + _watch_future_trades(request, response_consumer, control, context.is_active) + + +class WatchFutureTradesMessages(service.UnaryStreamTestMessages): + """Pairs of a single request message and a sequence of response messages.""" + + def __init__(self): + self._index = 0 + + def request(self): + symbol = SYMBOL_FORMAT % self._index + self._index += 1 + return stock_pb2.StockRequest( + symbol=symbol, num_trades_to_watch=STREAM_LENGTH) + + def verify(self, request, responses, test_case): + test_case.assertEqual(STREAM_LENGTH, len(responses)) + base_price = _price(request.symbol) + for index, response in enumerate(responses): + test_case.assertEqual(base_price + index, response.price) + + +class GetHighestTradePrice(service.StreamUnaryTestMethodImplementation): + """GetHighestTradePrice for use in tests.""" + + def name(self): + return 'GetHighestTradePrice' + + def cardinality(self): + return cardinality.Cardinality.STREAM_UNARY + + def request_class(self): + return stock_pb2.StockRequest + + def response_class(self): + return stock_pb2.StockReply + + def serialize_request(self, request): + return request.SerializeToString() + + def deserialize_request(self, serialized_request): + return stock_pb2.StockRequest.FromString(serialized_request) + + def serialize_response(self, response): + return response.SerializeToString() + + def deserialize_response(self, serialized_response): + return stock_pb2.StockReply.FromString(serialized_response) + + def service(self, response_callback, context, control): + return _get_highest_trade_price( + response_callback, control, context.is_active) + + +class GetHighestTradePriceMessages(service.StreamUnaryTestMessages): + + def requests(self): + return [ + stock_pb2.StockRequest(symbol=SYMBOL_FORMAT % index) + for index in range(STREAM_LENGTH)] + + def verify(self, requests, response, test_case): + price = None + symbol = None + for stock_request in requests: + current_symbol = stock_request.symbol + current_price = _price(current_symbol) + if price is None or price < current_price: + price = current_price + symbol = current_symbol + test_case.assertEqual(price, response.price) + test_case.assertEqual(symbol, response.symbol) + + +class StockTestService(service.TestService): + """A corpus of test data with one method of each RPC cardinality.""" + + def name(self): + return 'Stock' + + def unary_unary_scenarios(self): + return { + 'GetLastTradePrice': ( + GetLastTradePrice(), [GetLastTradePriceMessages()]), + } + + def unary_stream_scenarios(self): + return { + 'WatchFutureTrades': ( + WatchFutureTrades(), [WatchFutureTradesMessages()]), + } + + def stream_unary_scenarios(self): + return { + 'GetHighestTradePrice': ( + GetHighestTradePrice(), [GetHighestTradePriceMessages()]) + } + + def stream_stream_scenarios(self): + return { + 'GetLastTradePriceMultiple': ( + GetLastTradePriceMultiple(), [GetLastTradePriceMultipleMessages()]), + } + + +STOCK_TEST_SERVICE = StockTestService() diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/test_case.py b/src/python/grpcio_test/grpc_test/framework/face/testing/test_case.py new file mode 100644 index 00000000..858d5cf7 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/testing/test_case.py @@ -0,0 +1,80 @@ +# Copyright 2015, 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. + +"""Tools for creating tests of implementations of the Face layer.""" + +import abc + +# face_interfaces and interfaces are referenced in specification in this module. +from grpc.framework.face import interfaces as face_interfaces # pylint: disable=unused-import +from grpc_test.framework.face.testing import interfaces # pylint: disable=unused-import + + +class FaceTestCase(object): + """Describes a test of the Face Layer of RPC Framework. + + Concrete subclasses must also inherit from unittest.TestCase and from at least + one class that defines test methods. + """ + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def set_up_implementation( + self, name, methods, method_implementations, + multi_method_implementation): + """Instantiates the Face Layer implementation under test. + + Args: + name: The service name to be used in the test. + methods: A sequence of interfaces.Method objects describing the RPC + methods that will be called during the test. + method_implementations: A dictionary from string RPC method name to + face_interfaces.MethodImplementation object specifying + implementation of an RPC method. + multi_method_implementation: An face_interfaces.MultiMethodImplementation + or None. + + Returns: + A sequence of length two the first element of which is a + face_interfaces.GenericStub (backed by the given method + implementations), and the second element of which is an arbitrary memo + object to be kept and passed to tearDownImplementation at the conclusion + of the test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def tear_down_implementation(self, memo): + """Destroys the Face layer implementation under test. + + Args: + memo: The object from the second position of the return value of + set_up_implementation. + """ + raise NotImplementedError() diff --git a/src/python/grpcio_test/grpc_test/framework/foundation/__init__.py b/src/python/grpcio_test/grpc_test/framework/foundation/__init__.py new file mode 100644 index 00000000..70865191 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/foundation/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, 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. + + diff --git a/src/python/grpcio_test/grpc_test/framework/foundation/_later_test.py b/src/python/grpcio_test/grpc_test/framework/foundation/_later_test.py new file mode 100644 index 00000000..6c2459e1 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/foundation/_later_test.py @@ -0,0 +1,151 @@ +# Copyright 2015, 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. + +"""Tests of the later module.""" + +import threading +import time +import unittest + +from grpc.framework.foundation import later + +TICK = 0.1 + + +class LaterTest(unittest.TestCase): + + def test_simple_delay(self): + lock = threading.Lock() + cell = [0] + return_value = object() + + def computation(): + with lock: + cell[0] += 1 + return return_value + computation_future = later.later(TICK * 2, computation) + + self.assertFalse(computation_future.done()) + self.assertFalse(computation_future.cancelled()) + time.sleep(TICK) + self.assertFalse(computation_future.done()) + self.assertFalse(computation_future.cancelled()) + with lock: + self.assertEqual(0, cell[0]) + time.sleep(TICK * 2) + self.assertTrue(computation_future.done()) + self.assertFalse(computation_future.cancelled()) + with lock: + self.assertEqual(1, cell[0]) + self.assertEqual(return_value, computation_future.result()) + + def test_callback(self): + lock = threading.Lock() + cell = [0] + callback_called = [False] + future_passed_to_callback = [None] + def computation(): + with lock: + cell[0] += 1 + computation_future = later.later(TICK * 2, computation) + def callback(outcome): + with lock: + callback_called[0] = True + future_passed_to_callback[0] = outcome + computation_future.add_done_callback(callback) + time.sleep(TICK) + with lock: + self.assertFalse(callback_called[0]) + time.sleep(TICK * 2) + with lock: + self.assertTrue(callback_called[0]) + self.assertTrue(future_passed_to_callback[0].done()) + + callback_called[0] = False + future_passed_to_callback[0] = None + + computation_future.add_done_callback(callback) + with lock: + self.assertTrue(callback_called[0]) + self.assertTrue(future_passed_to_callback[0].done()) + + def test_cancel(self): + lock = threading.Lock() + cell = [0] + callback_called = [False] + future_passed_to_callback = [None] + def computation(): + with lock: + cell[0] += 1 + computation_future = later.later(TICK * 2, computation) + def callback(outcome): + with lock: + callback_called[0] = True + future_passed_to_callback[0] = outcome + computation_future.add_done_callback(callback) + time.sleep(TICK) + with lock: + self.assertFalse(callback_called[0]) + computation_future.cancel() + self.assertTrue(computation_future.cancelled()) + self.assertFalse(computation_future.running()) + self.assertTrue(computation_future.done()) + with lock: + self.assertTrue(callback_called[0]) + self.assertTrue(future_passed_to_callback[0].cancelled()) + + def test_result(self): + lock = threading.Lock() + cell = [0] + callback_called = [False] + future_passed_to_callback_cell = [None] + return_value = object() + + def computation(): + with lock: + cell[0] += 1 + return return_value + computation_future = later.later(TICK * 2, computation) + + def callback(future_passed_to_callback): + with lock: + callback_called[0] = True + future_passed_to_callback_cell[0] = future_passed_to_callback + computation_future.add_done_callback(callback) + returned_value = computation_future.result() + self.assertEqual(return_value, returned_value) + + # The callback may not yet have been called! Sleep a tick. + time.sleep(TICK) + with lock: + self.assertTrue(callback_called[0]) + self.assertEqual(return_value, future_passed_to_callback_cell[0].result()) + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/framework/foundation/_logging_pool_test.py b/src/python/grpcio_test/grpc_test/framework/foundation/_logging_pool_test.py new file mode 100644 index 00000000..452802da --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/foundation/_logging_pool_test.py @@ -0,0 +1,64 @@ +# Copyright 2015, 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. + +"""Tests for grpc.framework.foundation.logging_pool.""" + +import unittest + +from grpc.framework.foundation import logging_pool + +_POOL_SIZE = 16 + + +class LoggingPoolTest(unittest.TestCase): + + def testUpAndDown(self): + pool = logging_pool.pool(_POOL_SIZE) + pool.shutdown(wait=True) + + with logging_pool.pool(_POOL_SIZE) as pool: + self.assertIsNotNone(pool) + + def testTaskExecuted(self): + test_list = [] + + with logging_pool.pool(_POOL_SIZE) as pool: + pool.submit(lambda: test_list.append(object())).result() + + self.assertTrue(test_list) + + def testException(self): + with logging_pool.pool(_POOL_SIZE) as pool: + raised_exception = pool.submit(lambda: 1/0).exception() + + self.assertIsNotNone(raised_exception) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/framework/foundation/stream_testing.py b/src/python/grpcio_test/grpc_test/framework/foundation/stream_testing.py new file mode 100644 index 00000000..098a53d5 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/foundation/stream_testing.py @@ -0,0 +1,73 @@ +# Copyright 2015, 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. + +"""Utilities for testing stream-related code.""" + +from grpc.framework.foundation import stream + + +class TestConsumer(stream.Consumer): + """A stream.Consumer instrumented for testing. + + Attributes: + calls: A sequence of value-termination pairs describing the history of calls + made on this object. + """ + + def __init__(self): + self.calls = [] + + def consume(self, value): + """See stream.Consumer.consume for specification.""" + self.calls.append((value, False)) + + def terminate(self): + """See stream.Consumer.terminate for specification.""" + self.calls.append((None, True)) + + def consume_and_terminate(self, value): + """See stream.Consumer.consume_and_terminate for specification.""" + self.calls.append((value, True)) + + def is_legal(self): + """Reports whether or not a legal sequence of calls has been made.""" + terminated = False + for value, terminal in self.calls: + if terminated: + return False + elif terminal: + terminated = True + elif value is None: + return False + else: # pylint: disable=useless-else-on-loop + return True + + def values(self): + """Returns the sequence of values that have been passed to this Consumer.""" + return [value for value, _ in self.calls if value] diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/__init__.py b/src/python/grpcio_test/grpc_test/framework/interfaces/__init__.py new file mode 100644 index 00000000..70865191 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/interfaces/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, 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. + + diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/base/__init__.py b/src/python/grpcio_test/grpc_test/framework/interfaces/base/__init__.py new file mode 100644 index 00000000..70865191 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/interfaces/base/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, 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. + + diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/base/_control.py b/src/python/grpcio_test/grpc_test/framework/interfaces/base/_control.py new file mode 100644 index 00000000..46a01876 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/interfaces/base/_control.py @@ -0,0 +1,568 @@ +# Copyright 2015, 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. + +"""Part of the tests of the base interface of RPC Framework.""" + +import abc +import collections +import enum +import random # pylint: disable=unused-import +import threading +import time + +from grpc.framework.interfaces.base import base +from grpc_test.framework.common import test_constants +from grpc_test.framework.interfaces.base import _sequence +from grpc_test.framework.interfaces.base import _state +from grpc_test.framework.interfaces.base import test_interfaces # pylint: disable=unused-import + +_GROUP = 'base test cases test group' +_METHOD = 'base test cases test method' + +_PAYLOAD_RANDOM_SECTION_MAXIMUM_SIZE = test_constants.PAYLOAD_SIZE / 20 +_MINIMUM_PAYLOAD_SIZE = test_constants.PAYLOAD_SIZE / 600 + + +def _create_payload(randomness): + length = randomness.randint( + _MINIMUM_PAYLOAD_SIZE, test_constants.PAYLOAD_SIZE) + random_section_length = randomness.randint( + 0, min(_PAYLOAD_RANDOM_SECTION_MAXIMUM_SIZE, length)) + random_section = bytes( + bytearray( + randomness.getrandbits(8) for _ in range(random_section_length))) + sevens_section = '\x07' * (length - random_section_length) + return b''.join(randomness.sample((random_section, sevens_section), 2)) + + +def _anything_in_flight(state): + return ( + state.invocation_initial_metadata_in_flight is not None or + state.invocation_payloads_in_flight or + state.invocation_completion_in_flight is not None or + state.service_initial_metadata_in_flight is not None or + state.service_payloads_in_flight or + state.service_completion_in_flight is not None or + 0 < state.invocation_allowance_in_flight or + 0 < state.service_allowance_in_flight + ) + + +def _verify_service_advance_and_update_state( + initial_metadata, payload, completion, allowance, state, implementation): + if initial_metadata is not None: + if state.invocation_initial_metadata_received: + return 'Later invocation initial metadata received: %s' % ( + initial_metadata,) + if state.invocation_payloads_received: + return 'Invocation initial metadata received after payloads: %s' % ( + state.invocation_payloads_received) + if state.invocation_completion_received: + return 'Invocation initial metadata received after invocation completion!' + if not implementation.metadata_transmitted( + state.invocation_initial_metadata_in_flight, initial_metadata): + return 'Invocation initial metadata maltransmitted: %s, %s' % ( + state.invocation_initial_metadata_in_flight, initial_metadata) + else: + state.invocation_initial_metadata_in_flight = None + state.invocation_initial_metadata_received = True + + if payload is not None: + if state.invocation_completion_received: + return 'Invocation payload received after invocation completion!' + elif not state.invocation_payloads_in_flight: + return 'Invocation payload "%s" received but not in flight!' % (payload,) + elif state.invocation_payloads_in_flight[0] != payload: + return 'Invocation payload mismatch: %s, %s' % ( + state.invocation_payloads_in_flight[0], payload) + elif state.service_side_invocation_allowance < 1: + return 'Disallowed invocation payload!' + else: + state.invocation_payloads_in_flight.pop(0) + state.invocation_payloads_received += 1 + state.service_side_invocation_allowance -= 1 + + if completion is not None: + if state.invocation_completion_received: + return 'Later invocation completion received: %s' % (completion,) + elif not implementation.completion_transmitted( + state.invocation_completion_in_flight, completion): + return 'Invocation completion maltransmitted: %s, %s' % ( + state.invocation_completion_in_flight, completion) + else: + state.invocation_completion_in_flight = None + state.invocation_completion_received = True + + if allowance is not None: + if allowance <= 0: + return 'Illegal allowance value: %s' % (allowance,) + else: + state.service_allowance_in_flight -= allowance + state.service_side_service_allowance += allowance + + +def _verify_invocation_advance_and_update_state( + initial_metadata, payload, completion, allowance, state, implementation): + if initial_metadata is not None: + if state.service_initial_metadata_received: + return 'Later service initial metadata received: %s' % (initial_metadata,) + if state.service_payloads_received: + return 'Service initial metadata received after service payloads: %s' % ( + state.service_payloads_received) + if state.service_completion_received: + return 'Service initial metadata received after service completion!' + if not implementation.metadata_transmitted( + state.service_initial_metadata_in_flight, initial_metadata): + return 'Service initial metadata maltransmitted: %s, %s' % ( + state.service_initial_metadata_in_flight, initial_metadata) + else: + state.service_initial_metadata_in_flight = None + state.service_initial_metadata_received = True + + if payload is not None: + if state.service_completion_received: + return 'Service payload received after service completion!' + elif not state.service_payloads_in_flight: + return 'Service payload "%s" received but not in flight!' % (payload,) + elif state.service_payloads_in_flight[0] != payload: + return 'Service payload mismatch: %s, %s' % ( + state.invocation_payloads_in_flight[0], payload) + elif state.invocation_side_service_allowance < 1: + return 'Disallowed service payload!' + else: + state.service_payloads_in_flight.pop(0) + state.service_payloads_received += 1 + state.invocation_side_service_allowance -= 1 + + if completion is not None: + if state.service_completion_received: + return 'Later service completion received: %s' % (completion,) + elif not implementation.completion_transmitted( + state.service_completion_in_flight, completion): + return 'Service completion maltransmitted: %s, %s' % ( + state.service_completion_in_flight, completion) + else: + state.service_completion_in_flight = None + state.service_completion_received = True + + if allowance is not None: + if allowance <= 0: + return 'Illegal allowance value: %s' % (allowance,) + else: + state.invocation_allowance_in_flight -= allowance + state.invocation_side_service_allowance += allowance + + +class Invocation( + collections.namedtuple( + 'Invocation', + ('group', 'method', 'subscription_kind', 'timeout', 'initial_metadata', + 'payload', 'completion',))): + """A description of operation invocation. + + Attributes: + group: The group identifier for the operation. + method: The method identifier for the operation. + subscription_kind: A base.Subscription.Kind value describing the kind of + subscription to use for the operation. + timeout: A duration in seconds to pass as the timeout value for the + operation. + initial_metadata: An object to pass as the initial metadata for the + operation or None. + payload: An object to pass as a payload value for the operation or None. + completion: An object to pass as a completion value for the operation or + None. + """ + + +class OnAdvance( + collections.namedtuple( + 'OnAdvance', + ('kind', 'initial_metadata', 'payload', 'completion', 'allowance'))): + """Describes action to be taken in a test in response to an advance call. + + Attributes: + kind: A Kind value describing the overall kind of response. + initial_metadata: An initial metadata value to pass to a call of the advance + method of the operator under test. Only valid if kind is Kind.ADVANCE and + may be None. + payload: A payload value to pass to a call of the advance method of the + operator under test. Only valid if kind is Kind.ADVANCE and may be None. + completion: A base.Completion value to pass to a call of the advance method + of the operator under test. Only valid if kind is Kind.ADVANCE and may be + None. + allowance: An allowance value to pass to a call of the advance method of the + operator under test. Only valid if kind is Kind.ADVANCE and may be None. + """ + + @enum.unique + class Kind(enum.Enum): + ADVANCE = 'advance' + DEFECT = 'defect' + IDLE = 'idle' + + +_DEFECT_ON_ADVANCE = OnAdvance(OnAdvance.Kind.DEFECT, None, None, None, None) +_IDLE_ON_ADVANCE = OnAdvance(OnAdvance.Kind.IDLE, None, None, None, None) + + +class Instruction( + collections.namedtuple( + 'Instruction', + ('kind', 'advance_args', 'advance_kwargs', 'conclude_success', + 'conclude_message', 'conclude_invocation_outcome_kind', + 'conclude_service_outcome_kind',))): + """""" + + @enum.unique + class Kind(enum.Enum): + ADVANCE = 'ADVANCE' + CANCEL = 'CANCEL' + CONCLUDE = 'CONCLUDE' + + +class Controller(object): + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def failed(self, message): + """""" + raise NotImplementedError() + + @abc.abstractmethod + def serialize_request(self, request): + """""" + raise NotImplementedError() + + @abc.abstractmethod + def deserialize_request(self, serialized_request): + """""" + raise NotImplementedError() + + @abc.abstractmethod + def serialize_response(self, response): + """""" + raise NotImplementedError() + + @abc.abstractmethod + def deserialize_response(self, serialized_response): + """""" + raise NotImplementedError() + + @abc.abstractmethod + def invocation(self): + """""" + raise NotImplementedError() + + @abc.abstractmethod + def poll(self): + """""" + raise NotImplementedError() + + @abc.abstractmethod + def on_service_advance( + self, initial_metadata, payload, completion, allowance): + """""" + raise NotImplementedError() + + @abc.abstractmethod + def on_invocation_advance( + self, initial_metadata, payload, completion, allowance): + """""" + raise NotImplementedError() + + @abc.abstractmethod + def service_on_termination(self, outcome): + """""" + raise NotImplementedError() + + @abc.abstractmethod + def invocation_on_termination(self, outcome): + """""" + raise NotImplementedError() + + +class ControllerCreator(object): + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def name(self): + """""" + raise NotImplementedError() + + @abc.abstractmethod + def controller(self, implementation, randomness): + """""" + raise NotImplementedError() + + +class _Remainder( + collections.namedtuple( + '_Remainder', + ('invocation_payloads', 'service_payloads', 'invocation_completion', + 'service_completion',))): + """Describes work remaining to be done in a portion of a test. + + Attributes: + invocation_payloads: The number of payloads to be sent from the invocation + side of the operation to the service side of the operation. + service_payloads: The number of payloads to be sent from the service side of + the operation to the invocation side of the operation. + invocation_completion: Whether or not completion from the invocation side of + the operation should be indicated and has yet to be indicated. + service_completion: Whether or not completion from the service side of the + operation should be indicated and has yet to be indicated. + """ + + +class _SequenceController(Controller): + + def __init__(self, sequence, implementation, randomness): + """Constructor. + + Args: + sequence: A _sequence.Sequence describing the steps to be taken in the + test at a relatively high level. + implementation: A test_interfaces.Implementation encapsulating the + base interface implementation that is the system under test. + randomness: A random.Random instance for use in the test. + """ + self._condition = threading.Condition() + self._sequence = sequence + self._implementation = implementation + self._randomness = randomness + + self._until = None + self._remaining_elements = None + self._poll_next = None + self._message = None + + self._state = _state.OperationState() + self._todo = None + + # called with self._condition + def _failed(self, message): + self._message = message + self._condition.notify_all() + + def _passed(self, invocation_outcome, service_outcome): + self._poll_next = Instruction( + Instruction.Kind.CONCLUDE, None, None, True, None, invocation_outcome, + service_outcome) + self._condition.notify_all() + + def failed(self, message): + with self._condition: + self._failed(message) + + def serialize_request(self, request): + return request + request + + def deserialize_request(self, serialized_request): + return serialized_request[:len(serialized_request) / 2] + + def serialize_response(self, response): + return response * 3 + + def deserialize_response(self, serialized_response): + return serialized_response[2 * len(serialized_response) / 3:] + + def invocation(self): + with self._condition: + self._until = time.time() + self._sequence.maximum_duration + self._remaining_elements = list(self._sequence.elements) + if self._sequence.invocation.initial_metadata: + initial_metadata = self._implementation.invocation_initial_metadata() + self._state.invocation_initial_metadata_in_flight = initial_metadata + else: + initial_metadata = None + if self._sequence.invocation.payload: + payload = _create_payload(self._randomness) + self._state.invocation_payloads_in_flight.append(payload) + else: + payload = None + if self._sequence.invocation.complete: + completion = self._implementation.invocation_completion() + self._state.invocation_completion_in_flight = completion + else: + completion = None + return Invocation( + _GROUP, _METHOD, base.Subscription.Kind.FULL, + self._sequence.invocation.timeout, initial_metadata, payload, + completion) + + def poll(self): + with self._condition: + while True: + if self._message is not None: + return Instruction( + Instruction.Kind.CONCLUDE, None, None, False, self._message, None, + None) + elif self._poll_next: + poll_next = self._poll_next + self._poll_next = None + return poll_next + elif self._until < time.time(): + return Instruction( + Instruction.Kind.CONCLUDE, None, None, False, + 'overran allotted time!', None, None) + else: + self._condition.wait(timeout=self._until-time.time()) + + def on_service_advance( + self, initial_metadata, payload, completion, allowance): + with self._condition: + message = _verify_service_advance_and_update_state( + initial_metadata, payload, completion, allowance, self._state, + self._implementation) + if message is not None: + self._failed(message) + if self._todo is not None: + raise ValueError('TODO!!!') + elif _anything_in_flight(self._state): + return _IDLE_ON_ADVANCE + elif self._remaining_elements: + element = self._remaining_elements.pop(0) + if element.kind is _sequence.Element.Kind.SERVICE_TRANSMISSION: + if element.transmission.initial_metadata: + initial_metadata = self._implementation.service_initial_metadata() + self._state.service_initial_metadata_in_flight = initial_metadata + else: + initial_metadata = None + if element.transmission.payload: + payload = _create_payload(self._randomness) + self._state.service_payloads_in_flight.append(payload) + self._state.service_side_service_allowance -= 1 + else: + payload = None + if element.transmission.complete: + completion = self._implementation.service_completion() + self._state.service_completion_in_flight = completion + else: + completion = None + if (not self._state.invocation_completion_received and + 0 <= self._state.service_side_invocation_allowance): + allowance = 1 + self._state.service_side_invocation_allowance += 1 + self._state.invocation_allowance_in_flight += 1 + else: + allowance = None + return OnAdvance( + OnAdvance.Kind.ADVANCE, initial_metadata, payload, completion, + allowance) + else: + raise ValueError('TODO!!!') + else: + return _IDLE_ON_ADVANCE + + def on_invocation_advance( + self, initial_metadata, payload, completion, allowance): + with self._condition: + message = _verify_invocation_advance_and_update_state( + initial_metadata, payload, completion, allowance, self._state, + self._implementation) + if message is not None: + self._failed(message) + if self._todo is not None: + raise ValueError('TODO!!!') + elif _anything_in_flight(self._state): + return _IDLE_ON_ADVANCE + elif self._remaining_elements: + element = self._remaining_elements.pop(0) + if element.kind is _sequence.Element.Kind.INVOCATION_TRANSMISSION: + if element.transmission.initial_metadata: + initial_metadata = self._implementation.invocation_initial_metadata() + self._state.invocation_initial_metadata_in_fight = initial_metadata + else: + initial_metadata = None + if element.transmission.payload: + payload = _create_payload(self._randomness) + self._state.invocation_payloads_in_flight.append(payload) + self._state.invocation_side_invocation_allowance -= 1 + else: + payload = None + if element.transmission.complete: + completion = self._implementation.invocation_completion() + self._state.invocation_completion_in_flight = completion + else: + completion = None + if (not self._state.service_completion_received and + 0 <= self._state.invocation_side_service_allowance): + allowance = 1 + self._state.invocation_side_service_allowance += 1 + self._state.service_allowance_in_flight += 1 + else: + allowance = None + return OnAdvance( + OnAdvance.Kind.ADVANCE, initial_metadata, payload, completion, + allowance) + else: + raise ValueError('TODO!!!') + else: + return _IDLE_ON_ADVANCE + + def service_on_termination(self, outcome): + with self._condition: + self._state.service_side_outcome = outcome + if self._todo is not None or self._remaining_elements: + self._failed('Premature service-side outcome %s!' % (outcome,)) + elif outcome.kind is not self._sequence.outcome_kinds.service: + self._failed( + 'Incorrect service-side outcome kind: %s should have been %s' % ( + outcome.kind, self._sequence.outcome_kinds.service)) + elif self._state.invocation_side_outcome is not None: + self._passed(self._state.invocation_side_outcome.kind, outcome.kind) + + def invocation_on_termination(self, outcome): + with self._condition: + self._state.invocation_side_outcome = outcome + if self._todo is not None or self._remaining_elements: + self._failed('Premature invocation-side outcome %s!' % (outcome,)) + elif outcome.kind is not self._sequence.outcome_kinds.invocation: + self._failed( + 'Incorrect invocation-side outcome kind: %s should have been %s' % ( + outcome.kind, self._sequence.outcome_kinds.invocation)) + elif self._state.service_side_outcome is not None: + self._passed(outcome.kind, self._state.service_side_outcome.kind) + + +class _SequenceControllerCreator(ControllerCreator): + + def __init__(self, sequence): + self._sequence = sequence + + def name(self): + return self._sequence.name + + def controller(self, implementation, randomness): + return _SequenceController(self._sequence, implementation, randomness) + + +CONTROLLER_CREATORS = tuple( + _SequenceControllerCreator(sequence) for sequence in _sequence.SEQUENCES) diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/base/_sequence.py b/src/python/grpcio_test/grpc_test/framework/interfaces/base/_sequence.py new file mode 100644 index 00000000..f547d916 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/interfaces/base/_sequence.py @@ -0,0 +1,171 @@ +# Copyright 2015, 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. + +"""Part of the tests of the base interface of RPC Framework.""" + +import collections +import enum + +from grpc.framework.interfaces.base import base +from grpc_test.framework.common import test_constants + + +class Invocation( + collections.namedtuple( + 'Invocation', ('timeout', 'initial_metadata', 'payload', 'complete',))): + """A recipe for operation invocation. + + Attributes: + timeout: A duration in seconds to pass to the system under test as the + operation's timeout value. + initial_metadata: A boolean indicating whether or not to pass initial + metadata when invoking the operation. + payload: A boolean indicating whether or not to pass a payload when + invoking the operation. + complete: A boolean indicating whether or not to indicate completion of + transmissions from the invoking side of the operation when invoking the + operation. + """ + + +class Transmission( + collections.namedtuple( + 'Transmission', ('initial_metadata', 'payload', 'complete',))): + """A recipe for a single transmission in an operation. + + Attributes: + initial_metadata: A boolean indicating whether or not to pass initial + metadata as part of the transmission. + payload: A boolean indicating whether or not to pass a payload as part of + the transmission. + complete: A boolean indicating whether or not to indicate completion of + transmission from the transmitting side of the operation as part of the + transmission. + """ + + +class Intertransmission( + collections.namedtuple('Intertransmission', ('invocation', 'service',))): + """A recipe for multiple transmissions in an operation. + + Attributes: + invocation: An integer describing the number of payloads to send from the + invocation side of the operation to the service side. + service: An integer describing the number of payloads to send from the + service side of the operation to the invocation side. + """ + + +class Element(collections.namedtuple('Element', ('kind', 'transmission',))): + """A sum type for steps to perform when testing an operation. + + Attributes: + kind: A Kind value describing the kind of step to perform in the test. + transmission: Only valid for kinds Kind.INVOCATION_TRANSMISSION and + Kind.SERVICE_TRANSMISSION, a Transmission value describing the details of + the transmission to be made. + """ + + @enum.unique + class Kind(enum.Enum): + INVOCATION_TRANSMISSION = 'invocation transmission' + SERVICE_TRANSMISSION = 'service transmission' + INTERTRANSMISSION = 'intertransmission' + INVOCATION_CANCEL = 'invocation cancel' + SERVICE_CANCEL = 'service cancel' + INVOCATION_FAILURE = 'invocation failure' + SERVICE_FAILURE = 'service failure' + + +class OutcomeKinds( + collections.namedtuple('Outcome', ('invocation', 'service',))): + """A description of the expected outcome of an operation test. + + Attributes: + invocation: The base.Outcome.Kind value expected on the invocation side of + the operation. + service: The base.Outcome.Kind value expected on the service side of the + operation. + """ + + +class Sequence( + collections.namedtuple( + 'Sequence', + ('name', 'maximum_duration', 'invocation', 'elements', + 'outcome_kinds',))): + """Describes at a high level steps to perform in a test. + + Attributes: + name: The string name of the sequence. + maximum_duration: A length of time in seconds to allow for the test before + declaring it to have failed. + invocation: An Invocation value describing how to invoke the operation + under test. + elements: A sequence of Element values describing at coarse granularity + actions to take during the operation under test. + outcome_kinds: An OutcomeKinds value describing the expected outcome kinds + of the test. + """ + +_EASY = Sequence( + 'Easy', + test_constants.TIME_ALLOWANCE, + Invocation(test_constants.LONG_TIMEOUT, True, True, True), + ( + Element( + Element.Kind.SERVICE_TRANSMISSION, Transmission(True, True, True)), + ), + OutcomeKinds(base.Outcome.Kind.COMPLETED, base.Outcome.Kind.COMPLETED)) + +_PEASY = Sequence( + 'Peasy', + test_constants.TIME_ALLOWANCE, + Invocation(test_constants.LONG_TIMEOUT, True, True, False), + ( + Element( + Element.Kind.SERVICE_TRANSMISSION, Transmission(True, True, False)), + Element( + Element.Kind.INVOCATION_TRANSMISSION, + Transmission(False, True, True)), + Element( + Element.Kind.SERVICE_TRANSMISSION, Transmission(False, True, True)), + ), + OutcomeKinds(base.Outcome.Kind.COMPLETED, base.Outcome.Kind.COMPLETED)) + + +# TODO(issue 2959): Finish this test suite. This tuple of sequences should +# contain at least the values in the Cartesian product of (half-duplex, +# full-duplex) * (zero payloads, one payload, test_constants.STREAM_LENGTH +# payloads) * (completion, cancellation, expiration, programming defect in +# servicer code). +SEQUENCES = ( + _EASY, + _PEASY, +) diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/base/_state.py b/src/python/grpcio_test/grpc_test/framework/interfaces/base/_state.py new file mode 100644 index 00000000..21cf33ae --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/interfaces/base/_state.py @@ -0,0 +1,55 @@ +# Copyright 2015, 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. + +"""Part of the tests of the base interface of RPC Framework.""" + + +class OperationState(object): + + def __init__(self): + self.invocation_initial_metadata_in_flight = None + self.invocation_initial_metadata_received = False + self.invocation_payloads_in_flight = [] + self.invocation_payloads_received = 0 + self.invocation_completion_in_flight = None + self.invocation_completion_received = False + self.service_initial_metadata_in_flight = None + self.service_initial_metadata_received = False + self.service_payloads_in_flight = [] + self.service_payloads_received = 0 + self.service_completion_in_flight = None + self.service_completion_received = False + self.invocation_side_invocation_allowance = 1 + self.invocation_side_service_allowance = 1 + self.service_side_invocation_allowance = 1 + self.service_side_service_allowance = 1 + self.invocation_allowance_in_flight = 0 + self.service_allowance_in_flight = 0 + self.invocation_side_outcome = None + self.service_side_outcome = None diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/base/test_cases.py b/src/python/grpcio_test/grpc_test/framework/interfaces/base/test_cases.py new file mode 100644 index 00000000..ddda1018 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/interfaces/base/test_cases.py @@ -0,0 +1,276 @@ +# Copyright 2015, 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. + +"""Tests of the base interface of RPC Framework.""" + +import logging +import random +import threading +import time +import unittest + +from grpc.framework.foundation import logging_pool +from grpc.framework.interfaces.base import base +from grpc.framework.interfaces.base import utilities +from grpc_test.framework.common import test_constants +from grpc_test.framework.interfaces.base import _control +from grpc_test.framework.interfaces.base import test_interfaces + +_SYNCHRONICITY_VARIATION = (('Sync', False), ('Async', True)) + +_EMPTY_OUTCOME_KIND_DICT = { + outcome_kind: 0 for outcome_kind in base.Outcome.Kind} + + +class _Serialization(test_interfaces.Serialization): + + def serialize_request(self, request): + return request + request + + def deserialize_request(self, serialized_request): + return serialized_request[:len(serialized_request) / 2] + + def serialize_response(self, response): + return response * 3 + + def deserialize_response(self, serialized_response): + return serialized_response[2 * len(serialized_response) / 3:] + + +def _advance(quadruples, operator, controller): + try: + for quadruple in quadruples: + operator.advance( + initial_metadata=quadruple[0], payload=quadruple[1], + completion=quadruple[2], allowance=quadruple[3]) + except Exception as e: # pylint: disable=broad-except + controller.failed('Exception on advance: %e' % e) + + +class _Operator(base.Operator): + + def __init__(self, controller, on_advance, pool, operator_under_test): + self._condition = threading.Condition() + self._controller = controller + self._on_advance = on_advance + self._pool = pool + self._operator_under_test = operator_under_test + self._pending_advances = [] + + def set_operator_under_test(self, operator_under_test): + with self._condition: + self._operator_under_test = operator_under_test + pent_advances = self._pending_advances + self._pending_advances = [] + pool = self._pool + controller = self._controller + + if pool is None: + _advance(pent_advances, operator_under_test, controller) + else: + pool.submit(_advance, pent_advances, operator_under_test, controller) + + def advance( + self, initial_metadata=None, payload=None, completion=None, + allowance=None): + on_advance = self._on_advance( + initial_metadata, payload, completion, allowance) + if on_advance.kind is _control.OnAdvance.Kind.ADVANCE: + with self._condition: + pool = self._pool + operator_under_test = self._operator_under_test + controller = self._controller + + quadruple = ( + on_advance.initial_metadata, on_advance.payload, + on_advance.completion, on_advance.allowance) + if pool is None: + _advance((quadruple,), operator_under_test, controller) + else: + pool.submit(_advance, (quadruple,), operator_under_test, controller) + elif on_advance.kind is _control.OnAdvance.Kind.DEFECT: + raise ValueError( + 'Deliberately raised exception from Operator.advance (in a test)!') + + +class _ProtocolReceiver(base.ProtocolReceiver): + + def __init__(self): + self._condition = threading.Condition() + self._contexts = [] + + def context(self, protocol_context): + with self._condition: + self._contexts.append(protocol_context) + + +class _Servicer(base.Servicer): + """A base.Servicer with instrumented for testing.""" + + def __init__(self, group, method, controllers, pool): + self._condition = threading.Condition() + self._group = group + self._method = method + self._pool = pool + self._controllers = list(controllers) + + def service(self, group, method, context, output_operator): + with self._condition: + controller = self._controllers.pop(0) + if group != self._group or method != self._method: + controller.fail( + '%s != %s or %s != %s' % (group, self._group, method, self._method)) + raise base.NoSuchMethodError(None, None) + else: + operator = _Operator( + controller, controller.on_service_advance, self._pool, + output_operator) + outcome = context.add_termination_callback( + controller.service_on_termination) + if outcome is not None: + controller.service_on_termination(outcome) + return utilities.full_subscription(operator, _ProtocolReceiver()) + + +class _OperationTest(unittest.TestCase): + + def setUp(self): + if self._synchronicity_variation: + self._pool = logging_pool.pool(test_constants.POOL_SIZE) + else: + self._pool = None + self._controller = self._controller_creator.controller( + self._implementation, self._randomness) + + def tearDown(self): + if self._synchronicity_variation: + self._pool.shutdown(wait=True) + else: + self._pool = None + + def test_operation(self): + invocation = self._controller.invocation() + if invocation.subscription_kind is base.Subscription.Kind.FULL: + test_operator = _Operator( + self._controller, self._controller.on_invocation_advance, + self._pool, None) + subscription = utilities.full_subscription( + test_operator, _ProtocolReceiver()) + else: + # TODO(nathaniel): support and test other subscription kinds. + self.fail('Non-full subscriptions not yet supported!') + + servicer = _Servicer( + invocation.group, invocation.method, (self._controller,), self._pool) + + invocation_end, service_end, memo = self._implementation.instantiate( + {(invocation.group, invocation.method): _Serialization()}, servicer) + + try: + invocation_end.start() + service_end.start() + operation_context, operator_under_test = invocation_end.operate( + invocation.group, invocation.method, subscription, invocation.timeout, + initial_metadata=invocation.initial_metadata, payload=invocation.payload, + completion=invocation.completion) + test_operator.set_operator_under_test(operator_under_test) + outcome = operation_context.add_termination_callback( + self._controller.invocation_on_termination) + if outcome is not None: + self._controller.invocation_on_termination(outcome) + except Exception as e: # pylint: disable=broad-except + self._controller.failed('Exception on invocation: %s' % e) + self.fail(e) + + while True: + instruction = self._controller.poll() + if instruction.kind is _control.Instruction.Kind.ADVANCE: + try: + test_operator.advance( + *instruction.advance_args, **instruction.advance_kwargs) + except Exception as e: # pylint: disable=broad-except + self._controller.failed('Exception on instructed advance: %s' % e) + elif instruction.kind is _control.Instruction.Kind.CANCEL: + try: + operation_context.cancel() + except Exception as e: # pylint: disable=broad-except + self._controller.failed('Exception on cancel: %s' % e) + elif instruction.kind is _control.Instruction.Kind.CONCLUDE: + break + + invocation_stop_event = invocation_end.stop(0) + service_stop_event = service_end.stop(0) + invocation_stop_event.wait() + service_stop_event.wait() + invocation_stats = invocation_end.operation_stats() + service_stats = service_end.operation_stats() + + self._implementation.destantiate(memo) + + self.assertTrue( + instruction.conclude_success, msg=instruction.conclude_message) + + expected_invocation_stats = dict(_EMPTY_OUTCOME_KIND_DICT) + expected_invocation_stats[ + instruction.conclude_invocation_outcome_kind] += 1 + self.assertDictEqual(expected_invocation_stats, invocation_stats) + expected_service_stats = dict(_EMPTY_OUTCOME_KIND_DICT) + expected_service_stats[instruction.conclude_service_outcome_kind] += 1 + self.assertDictEqual(expected_service_stats, service_stats) + + +def test_cases(implementation): + """Creates unittest.TestCase classes for a given Base implementation. + + Args: + implementation: A test_interfaces.Implementation specifying creation and + destruction of the Base implementation under test. + + Returns: + A sequence of subclasses of unittest.TestCase defining tests of the + specified Base layer implementation. + """ + random_seed = hash(time.time()) + logging.warning('Random seed for this execution: %s', random_seed) + randomness = random.Random(x=random_seed) + + test_case_classes = [] + for synchronicity_variation in _SYNCHRONICITY_VARIATION: + for controller_creator in _control.CONTROLLER_CREATORS: + name = ''.join( + (synchronicity_variation[0], controller_creator.name(), 'Test',)) + test_case_classes.append( + type(name, (_OperationTest,), + {'_implementation': implementation, + '_randomness': randomness, + '_synchronicity_variation': synchronicity_variation[1], + '_controller_creator': controller_creator, + })) + + return test_case_classes diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/base/test_interfaces.py b/src/python/grpcio_test/grpc_test/framework/interfaces/base/test_interfaces.py new file mode 100644 index 00000000..02426ab8 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/interfaces/base/test_interfaces.py @@ -0,0 +1,186 @@ +# Copyright 2015, 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. + +"""Interfaces used in tests of implementations of the Base layer.""" + +import abc + +from grpc.framework.interfaces.base import base # pylint: disable=unused-import + + +class Serialization(object): + """Specifies serialization and deserialization of test payloads.""" + __metaclass__ = abc.ABCMeta + + def serialize_request(self, request): + """Serializes a request value used in a test. + + Args: + request: A request value created by a test. + + Returns: + A bytestring that is the serialization of the given request. + """ + raise NotImplementedError() + + def deserialize_request(self, serialized_request): + """Deserializes a request value used in a test. + + Args: + serialized_request: A bytestring that is the serialization of some request + used in a test. + + Returns: + The request value encoded by the given bytestring. + """ + raise NotImplementedError() + + def serialize_response(self, response): + """Serializes a response value used in a test. + + Args: + response: A response value created by a test. + + Returns: + A bytestring that is the serialization of the given response. + """ + raise NotImplementedError() + + def deserialize_response(self, serialized_response): + """Deserializes a response value used in a test. + + Args: + serialized_response: A bytestring that is the serialization of some + response used in a test. + + Returns: + The response value encoded by the given bytestring. + """ + raise NotImplementedError() + + +class Implementation(object): + """Specifies an implementation of the Base layer.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def instantiate(self, serializations, servicer): + """Instantiates the Base layer implementation to be used in a test. + + Args: + serializations: A dict from group-method pair to Serialization object + specifying how to serialize and deserialize payload values used in the + test. + servicer: A base.Servicer object to be called to service RPCs made during + the test. + + Returns: + A sequence of length three the first element of which is a + base.End to be used to invoke RPCs, the second element of which is a + base.End to be used to service invoked RPCs, and the third element of + which is an arbitrary memo object to be kept and passed to destantiate + at the conclusion of the test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def destantiate(self, memo): + """Destroys the Base layer implementation under test. + + Args: + memo: The object from the third position of the return value of a call to + instantiate. + """ + raise NotImplementedError() + + @abc.abstractmethod + def invocation_initial_metadata(self): + """Provides an operation's invocation-side initial metadata. + + Returns: + A value to use for an operation's invocation-side initial metadata, or + None. + """ + raise NotImplementedError() + + @abc.abstractmethod + def service_initial_metadata(self): + """Provices an operation's service-side initial metadata. + + Returns: + A value to use for an operation's service-side initial metadata, or + None. + """ + raise NotImplementedError() + + @abc.abstractmethod + def invocation_completion(self): + """Provides an operation's invocation-side completion. + + Returns: + A base.Completion to use for an operation's invocation-side completion. + """ + raise NotImplementedError() + + @abc.abstractmethod + def service_completion(self): + """Provides an operation's service-side completion. + + Returns: + A base.Completion to use for an operation's service-side completion. + """ + raise NotImplementedError() + + @abc.abstractmethod + def metadata_transmitted(self, original_metadata, transmitted_metadata): + """Identifies whether or not metadata was properly transmitted. + + Args: + original_metadata: A metadata value passed to the system under test. + transmitted_metadata: The same metadata value after having been + transmitted through the system under test. + + Returns: + Whether or not the metadata was properly transmitted. + """ + raise NotImplementedError() + + @abc.abstractmethod + def completion_transmitted(self, original_completion, transmitted_completion): + """Identifies whether or not a base.Completion was properly transmitted. + + Args: + original_completion: A base.Completion passed to the system under test. + transmitted_completion: The same completion value after having been + transmitted through the system under test. + + Returns: + Whether or not the completion was properly transmitted. + """ + raise NotImplementedError() diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/face/_3069_test_constant.py b/src/python/grpcio_test/grpc_test/framework/interfaces/face/_3069_test_constant.py new file mode 100644 index 00000000..363d9ce8 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/interfaces/face/_3069_test_constant.py @@ -0,0 +1,37 @@ +# Copyright 2015, 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. + +"""A test constant working around issue 3069.""" + +# test_constants is referenced from specification in this module. +from grpc_test.framework.common import test_constants # pylint: disable=unused-import + +# TODO(issue 3069): Replace uses of this constant with +# test_constants.SHORT_TIMEOUT. +REALLY_SHORT_TIMEOUT = 0.1 diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/face/__init__.py b/src/python/grpcio_test/grpc_test/framework/interfaces/face/__init__.py new file mode 100644 index 00000000..70865191 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/interfaces/face/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, 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. + + diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/face/_blocking_invocation_inline_service.py b/src/python/grpcio_test/grpc_test/framework/interfaces/face/_blocking_invocation_inline_service.py new file mode 100644 index 00000000..2d2a0819 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/interfaces/face/_blocking_invocation_inline_service.py @@ -0,0 +1,252 @@ +# Copyright 2015, 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. + +"""Test code for the Face layer of RPC Framework.""" + +import abc +import unittest + +# test_interfaces is referenced from specification in this module. +from grpc.framework.interfaces.face import face +from grpc_test.framework.common import test_constants +from grpc_test.framework.common import test_control +from grpc_test.framework.common import test_coverage +from grpc_test.framework.interfaces.face import _3069_test_constant +from grpc_test.framework.interfaces.face import _digest +from grpc_test.framework.interfaces.face import _stock_service +from grpc_test.framework.interfaces.face import test_interfaces # pylint: disable=unused-import + + +class TestCase(test_coverage.Coverage, unittest.TestCase): + """A test of the Face layer of RPC Framework. + + Concrete subclasses must have an "implementation" attribute of type + test_interfaces.Implementation and an "invoker_constructor" attribute of type + _invocation.InvokerConstructor. + """ + __metaclass__ = abc.ABCMeta + + NAME = 'BlockingInvocationInlineServiceTest' + + def setUp(self): + """See unittest.TestCase.setUp for full specification. + + Overriding implementations must call this implementation. + """ + self._control = test_control.PauseFailControl() + self._digest = _digest.digest( + _stock_service.STOCK_TEST_SERVICE, self._control, None) + + generic_stub, dynamic_stubs, self._memo = self.implementation.instantiate( + self._digest.methods, self._digest.inline_method_implementations, None) + self._invoker = self.invoker_constructor.construct_invoker( + generic_stub, dynamic_stubs, self._digest.methods) + + def tearDown(self): + """See unittest.TestCase.tearDown for full specification. + + Overriding implementations must call this implementation. + """ + self._invoker = None + self.implementation.destantiate(self._memo) + + def testSuccessfulUnaryRequestUnaryResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + response, call = self._invoker.blocking(group, method)( + request, test_constants.LONG_TIMEOUT, with_call=True) + + test_messages.verify(request, response, self) + + def testSuccessfulUnaryRequestStreamResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + response_iterator = self._invoker.blocking(group, method)( + request, test_constants.LONG_TIMEOUT) + responses = list(response_iterator) + + test_messages.verify(request, responses, self) + + def testSuccessfulStreamRequestUnaryResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + response, call = self._invoker.blocking(group, method)( + iter(requests), test_constants.LONG_TIMEOUT, with_call=True) + + test_messages.verify(requests, response, self) + + def testSuccessfulStreamRequestStreamResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + response_iterator = self._invoker.blocking(group, method)( + iter(requests), test_constants.LONG_TIMEOUT) + responses = list(response_iterator) + + test_messages.verify(requests, responses, self) + + def testSequentialInvocations(self): + for (group, method), test_messages_sequence in ( + self._digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + first_request = test_messages.request() + second_request = test_messages.request() + + first_response = self._invoker.blocking(group, method)( + first_request, test_constants.LONG_TIMEOUT) + + test_messages.verify(first_request, first_response, self) + + second_response = self._invoker.blocking(group, method)( + second_request, test_constants.LONG_TIMEOUT) + + test_messages.verify(second_request, second_response, self) + + @unittest.skip('Parallel invocations impossible with blocking control flow!') + def testParallelInvocations(self): + raise NotImplementedError() + + @unittest.skip('Parallel invocations impossible with blocking control flow!') + def testWaitingForSomeButNotAllParallelInvocations(self): + raise NotImplementedError() + + @unittest.skip('Cancellation impossible with blocking control flow!') + def testCancelledUnaryRequestUnaryResponse(self): + raise NotImplementedError() + + @unittest.skip('Cancellation impossible with blocking control flow!') + def testCancelledUnaryRequestStreamResponse(self): + raise NotImplementedError() + + @unittest.skip('Cancellation impossible with blocking control flow!') + def testCancelledStreamRequestUnaryResponse(self): + raise NotImplementedError() + + @unittest.skip('Cancellation impossible with blocking control flow!') + def testCancelledStreamRequestStreamResponse(self): + raise NotImplementedError() + + def testExpiredUnaryRequestUnaryResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self._control.pause(), self.assertRaises( + face.ExpirationError): + self._invoker.blocking(group, method)( + request, _3069_test_constant.REALLY_SHORT_TIMEOUT) + + def testExpiredUnaryRequestStreamResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self._control.pause(), self.assertRaises( + face.ExpirationError): + response_iterator = self._invoker.blocking(group, method)( + request, _3069_test_constant.REALLY_SHORT_TIMEOUT) + list(response_iterator) + + def testExpiredStreamRequestUnaryResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self._control.pause(), self.assertRaises( + face.ExpirationError): + self._invoker.blocking(group, method)( + iter(requests), _3069_test_constant.REALLY_SHORT_TIMEOUT) + + def testExpiredStreamRequestStreamResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self._control.pause(), self.assertRaises( + face.ExpirationError): + response_iterator = self._invoker.blocking(group, method)( + iter(requests), _3069_test_constant.REALLY_SHORT_TIMEOUT) + list(response_iterator) + + def testFailedUnaryRequestUnaryResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self._control.fail(), self.assertRaises(face.RemoteError): + self._invoker.blocking(group, method)( + request, test_constants.LONG_TIMEOUT) + + def testFailedUnaryRequestStreamResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self._control.fail(), self.assertRaises(face.RemoteError): + response_iterator = self._invoker.blocking(group, method)( + request, test_constants.LONG_TIMEOUT) + list(response_iterator) + + def testFailedStreamRequestUnaryResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self._control.fail(), self.assertRaises(face.RemoteError): + self._invoker.blocking(group, method)( + iter(requests), test_constants.LONG_TIMEOUT) + + def testFailedStreamRequestStreamResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self._control.fail(), self.assertRaises(face.RemoteError): + response_iterator = self._invoker.blocking(group, method)( + iter(requests), test_constants.LONG_TIMEOUT) + list(response_iterator) diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/face/_digest.py b/src/python/grpcio_test/grpc_test/framework/interfaces/face/_digest.py new file mode 100644 index 00000000..da56ed7b --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/interfaces/face/_digest.py @@ -0,0 +1,444 @@ +# Copyright 2015, 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. + +"""Code for making a service.TestService more amenable to use in tests.""" + +import collections +import threading + +# test_control, _service, and test_interfaces are referenced from specification +# in this module. +from grpc.framework.common import cardinality +from grpc.framework.common import style +from grpc.framework.foundation import stream +from grpc.framework.foundation import stream_util +from grpc.framework.interfaces.face import face +from grpc_test.framework.common import test_control # pylint: disable=unused-import +from grpc_test.framework.interfaces.face import _service # pylint: disable=unused-import +from grpc_test.framework.interfaces.face import test_interfaces # pylint: disable=unused-import + +_IDENTITY = lambda x: x + + +class TestServiceDigest( + collections.namedtuple( + 'TestServiceDigest', + ('methods', + 'inline_method_implementations', + 'event_method_implementations', + 'multi_method_implementation', + 'unary_unary_messages_sequences', + 'unary_stream_messages_sequences', + 'stream_unary_messages_sequences', + 'stream_stream_messages_sequences',))): + """A transformation of a service.TestService. + + Attributes: + methods: A dict from method group-name pair to test_interfaces.Method object + describing the RPC methods that may be called during the test. + inline_method_implementations: A dict from method group-name pair to + face.MethodImplementation object to be used in tests of in-line calls to + behaviors under test. + event_method_implementations: A dict from method group-name pair to + face.MethodImplementation object to be used in tests of event-driven calls + to behaviors under test. + multi_method_implementation: A face.MultiMethodImplementation to be used in + tests of generic calls to behaviors under test. + unary_unary_messages_sequences: A dict from method group-name pair to + sequence of service.UnaryUnaryTestMessages objects to be used to test the + identified method. + unary_stream_messages_sequences: A dict from method group-name pair to + sequence of service.UnaryStreamTestMessages objects to be used to test the + identified method. + stream_unary_messages_sequences: A dict from method group-name pair to + sequence of service.StreamUnaryTestMessages objects to be used to test the + identified method. + stream_stream_messages_sequences: A dict from method group-name pair to + sequence of service.StreamStreamTestMessages objects to be used to test + the identified method. + """ + + +class _BufferingConsumer(stream.Consumer): + """A trivial Consumer that dumps what it consumes in a user-mutable buffer.""" + + def __init__(self): + self.consumed = [] + self.terminated = False + + def consume(self, value): + self.consumed.append(value) + + def terminate(self): + self.terminated = True + + def consume_and_terminate(self, value): + self.consumed.append(value) + self.terminated = True + + +class _InlineUnaryUnaryMethod(face.MethodImplementation): + + def __init__(self, unary_unary_test_method, control): + self._test_method = unary_unary_test_method + self._control = control + + self.cardinality = cardinality.Cardinality.UNARY_UNARY + self.style = style.Service.INLINE + + def unary_unary_inline(self, request, context): + response_list = [] + self._test_method.service( + request, response_list.append, context, self._control) + return response_list.pop(0) + + +class _EventUnaryUnaryMethod(face.MethodImplementation): + + def __init__(self, unary_unary_test_method, control, pool): + self._test_method = unary_unary_test_method + self._control = control + self._pool = pool + + self.cardinality = cardinality.Cardinality.UNARY_UNARY + self.style = style.Service.EVENT + + def unary_unary_event(self, request, response_callback, context): + if self._pool is None: + self._test_method.service( + request, response_callback, context, self._control) + else: + self._pool.submit( + self._test_method.service, request, response_callback, context, + self._control) + + +class _InlineUnaryStreamMethod(face.MethodImplementation): + + def __init__(self, unary_stream_test_method, control): + self._test_method = unary_stream_test_method + self._control = control + + self.cardinality = cardinality.Cardinality.UNARY_STREAM + self.style = style.Service.INLINE + + def unary_stream_inline(self, request, context): + response_consumer = _BufferingConsumer() + self._test_method.service( + request, response_consumer, context, self._control) + for response in response_consumer.consumed: + yield response + + +class _EventUnaryStreamMethod(face.MethodImplementation): + + def __init__(self, unary_stream_test_method, control, pool): + self._test_method = unary_stream_test_method + self._control = control + self._pool = pool + + self.cardinality = cardinality.Cardinality.UNARY_STREAM + self.style = style.Service.EVENT + + def unary_stream_event(self, request, response_consumer, context): + if self._pool is None: + self._test_method.service( + request, response_consumer, context, self._control) + else: + self._pool.submit( + self._test_method.service, request, response_consumer, context, + self._control) + + +class _InlineStreamUnaryMethod(face.MethodImplementation): + + def __init__(self, stream_unary_test_method, control): + self._test_method = stream_unary_test_method + self._control = control + + self.cardinality = cardinality.Cardinality.STREAM_UNARY + self.style = style.Service.INLINE + + def stream_unary_inline(self, request_iterator, context): + response_list = [] + request_consumer = self._test_method.service( + response_list.append, context, self._control) + for request in request_iterator: + request_consumer.consume(request) + request_consumer.terminate() + return response_list.pop(0) + + +class _EventStreamUnaryMethod(face.MethodImplementation): + + def __init__(self, stream_unary_test_method, control, pool): + self._test_method = stream_unary_test_method + self._control = control + self._pool = pool + + self.cardinality = cardinality.Cardinality.STREAM_UNARY + self.style = style.Service.EVENT + + def stream_unary_event(self, response_callback, context): + request_consumer = self._test_method.service( + response_callback, context, self._control) + if self._pool is None: + return request_consumer + else: + return stream_util.ThreadSwitchingConsumer(request_consumer, self._pool) + + +class _InlineStreamStreamMethod(face.MethodImplementation): + + def __init__(self, stream_stream_test_method, control): + self._test_method = stream_stream_test_method + self._control = control + + self.cardinality = cardinality.Cardinality.STREAM_STREAM + self.style = style.Service.INLINE + + def stream_stream_inline(self, request_iterator, context): + response_consumer = _BufferingConsumer() + request_consumer = self._test_method.service( + response_consumer, context, self._control) + + for request in request_iterator: + request_consumer.consume(request) + while response_consumer.consumed: + yield response_consumer.consumed.pop(0) + response_consumer.terminate() + + +class _EventStreamStreamMethod(face.MethodImplementation): + + def __init__(self, stream_stream_test_method, control, pool): + self._test_method = stream_stream_test_method + self._control = control + self._pool = pool + + self.cardinality = cardinality.Cardinality.STREAM_STREAM + self.style = style.Service.EVENT + + def stream_stream_event(self, response_consumer, context): + request_consumer = self._test_method.service( + response_consumer, context, self._control) + if self._pool is None: + return request_consumer + else: + return stream_util.ThreadSwitchingConsumer(request_consumer, self._pool) + + +class _UnaryConsumer(stream.Consumer): + """A Consumer that only allows consumption of exactly one value.""" + + def __init__(self, action): + self._lock = threading.Lock() + self._action = action + self._consumed = False + self._terminated = False + + def consume(self, value): + with self._lock: + if self._consumed: + raise ValueError('Unary consumer already consumed!') + elif self._terminated: + raise ValueError('Unary consumer already terminated!') + else: + self._consumed = True + + self._action(value) + + def terminate(self): + with self._lock: + if not self._consumed: + raise ValueError('Unary consumer hasn\'t yet consumed!') + elif self._terminated: + raise ValueError('Unary consumer already terminated!') + else: + self._terminated = True + + def consume_and_terminate(self, value): + with self._lock: + if self._consumed: + raise ValueError('Unary consumer already consumed!') + elif self._terminated: + raise ValueError('Unary consumer already terminated!') + else: + self._consumed = True + self._terminated = True + + self._action(value) + + +class _UnaryUnaryAdaptation(object): + + def __init__(self, unary_unary_test_method): + self._method = unary_unary_test_method + + def service(self, response_consumer, context, control): + def action(request): + self._method.service( + request, response_consumer.consume_and_terminate, context, control) + return _UnaryConsumer(action) + + +class _UnaryStreamAdaptation(object): + + def __init__(self, unary_stream_test_method): + self._method = unary_stream_test_method + + def service(self, response_consumer, context, control): + def action(request): + self._method.service(request, response_consumer, context, control) + return _UnaryConsumer(action) + + +class _StreamUnaryAdaptation(object): + + def __init__(self, stream_unary_test_method): + self._method = stream_unary_test_method + + def service(self, response_consumer, context, control): + return self._method.service( + response_consumer.consume_and_terminate, context, control) + + +class _MultiMethodImplementation(face.MultiMethodImplementation): + + def __init__(self, methods, control, pool): + self._methods = methods + self._control = control + self._pool = pool + + def service(self, group, name, response_consumer, context): + method = self._methods.get(group, name, None) + if method is None: + raise face.NoSuchMethodError(group, name) + elif self._pool is None: + return method(response_consumer, context, self._control) + else: + request_consumer = method(response_consumer, context, self._control) + return stream_util.ThreadSwitchingConsumer(request_consumer, self._pool) + + +class _Assembly( + collections.namedtuple( + '_Assembly', + ['methods', 'inlines', 'events', 'adaptations', 'messages'])): + """An intermediate structure created when creating a TestServiceDigest.""" + + +def _assemble( + scenarios, identifiers, inline_method_constructor, event_method_constructor, + adapter, control, pool): + """Creates an _Assembly from the given scenarios.""" + methods = {} + inlines = {} + events = {} + adaptations = {} + messages = {} + for identifier, scenario in scenarios.iteritems(): + if identifier in identifiers: + raise ValueError('Repeated identifier "(%s, %s)"!' % identifier) + + test_method = scenario[0] + inline_method = inline_method_constructor(test_method, control) + event_method = event_method_constructor(test_method, control, pool) + adaptation = adapter(test_method) + + methods[identifier] = test_method + inlines[identifier] = inline_method + events[identifier] = event_method + adaptations[identifier] = adaptation + messages[identifier] = scenario[1] + + return _Assembly(methods, inlines, events, adaptations, messages) + + +def digest(service, control, pool): + """Creates a TestServiceDigest from a TestService. + + Args: + service: A _service.TestService. + control: A test_control.Control. + pool: If RPC methods should be serviced in a separate thread, a thread pool. + None if RPC methods should be serviced in the thread belonging to the + run-time that calls for their service. + + Returns: + A TestServiceDigest synthesized from the given service.TestService. + """ + identifiers = set() + + unary_unary = _assemble( + service.unary_unary_scenarios(), identifiers, _InlineUnaryUnaryMethod, + _EventUnaryUnaryMethod, _UnaryUnaryAdaptation, control, pool) + identifiers.update(unary_unary.inlines) + + unary_stream = _assemble( + service.unary_stream_scenarios(), identifiers, _InlineUnaryStreamMethod, + _EventUnaryStreamMethod, _UnaryStreamAdaptation, control, pool) + identifiers.update(unary_stream.inlines) + + stream_unary = _assemble( + service.stream_unary_scenarios(), identifiers, _InlineStreamUnaryMethod, + _EventStreamUnaryMethod, _StreamUnaryAdaptation, control, pool) + identifiers.update(stream_unary.inlines) + + stream_stream = _assemble( + service.stream_stream_scenarios(), identifiers, _InlineStreamStreamMethod, + _EventStreamStreamMethod, _IDENTITY, control, pool) + identifiers.update(stream_stream.inlines) + + methods = dict(unary_unary.methods) + methods.update(unary_stream.methods) + methods.update(stream_unary.methods) + methods.update(stream_stream.methods) + adaptations = dict(unary_unary.adaptations) + adaptations.update(unary_stream.adaptations) + adaptations.update(stream_unary.adaptations) + adaptations.update(stream_stream.adaptations) + inlines = dict(unary_unary.inlines) + inlines.update(unary_stream.inlines) + inlines.update(stream_unary.inlines) + inlines.update(stream_stream.inlines) + events = dict(unary_unary.events) + events.update(unary_stream.events) + events.update(stream_unary.events) + events.update(stream_stream.events) + + return TestServiceDigest( + methods, + inlines, + events, + _MultiMethodImplementation(adaptations, control, pool), + unary_unary.messages, + unary_stream.messages, + stream_unary.messages, + stream_stream.messages) diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/face/_event_invocation_synchronous_event_service.py b/src/python/grpcio_test/grpc_test/framework/interfaces/face/_event_invocation_synchronous_event_service.py new file mode 100644 index 00000000..7cb273bf --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/interfaces/face/_event_invocation_synchronous_event_service.py @@ -0,0 +1,381 @@ +# Copyright 2015, 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. + +"""Test code for the Face layer of RPC Framework.""" + +import abc +import unittest + +# test_interfaces is referenced from specification in this module. +from grpc.framework.interfaces.face import face +from grpc_test.framework.common import test_constants +from grpc_test.framework.common import test_control +from grpc_test.framework.common import test_coverage +from grpc_test.framework.interfaces.face import _3069_test_constant +from grpc_test.framework.interfaces.face import _digest +from grpc_test.framework.interfaces.face import _receiver +from grpc_test.framework.interfaces.face import _stock_service +from grpc_test.framework.interfaces.face import test_interfaces # pylint: disable=unused-import + + +class TestCase(test_coverage.Coverage, unittest.TestCase): + """A test of the Face layer of RPC Framework. + + Concrete subclasses must have an "implementation" attribute of type + test_interfaces.Implementation and an "invoker_constructor" attribute of type + _invocation.InvokerConstructor. + """ + __metaclass__ = abc.ABCMeta + + NAME = 'EventInvocationSynchronousEventServiceTest' + + def setUp(self): + """See unittest.TestCase.setUp for full specification. + + Overriding implementations must call this implementation. + """ + self._control = test_control.PauseFailControl() + self._digest = _digest.digest( + _stock_service.STOCK_TEST_SERVICE, self._control, None) + + generic_stub, dynamic_stubs, self._memo = self.implementation.instantiate( + self._digest.methods, self._digest.event_method_implementations, None) + self._invoker = self.invoker_constructor.construct_invoker( + generic_stub, dynamic_stubs, self._digest.methods) + + def tearDown(self): + """See unittest.TestCase.tearDown for full specification. + + Overriding implementations must call this implementation. + """ + self._invoker = None + self.implementation.destantiate(self._memo) + + def testSuccessfulUnaryRequestUnaryResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + receiver = _receiver.Receiver() + + self._invoker.event(group, method)( + request, receiver, receiver.abort, test_constants.LONG_TIMEOUT) + receiver.block_until_terminated() + response = receiver.unary_response() + + test_messages.verify(request, response, self) + + def testSuccessfulUnaryRequestStreamResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + receiver = _receiver.Receiver() + + self._invoker.event(group, method)( + request, receiver, receiver.abort, test_constants.LONG_TIMEOUT) + receiver.block_until_terminated() + responses = receiver.stream_responses() + + test_messages.verify(request, responses, self) + + def testSuccessfulStreamRequestUnaryResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + receiver = _receiver.Receiver() + + call_consumer = self._invoker.event(group, method)( + receiver, receiver.abort, test_constants.LONG_TIMEOUT) + for request in requests: + call_consumer.consume(request) + call_consumer.terminate() + receiver.block_until_terminated() + response = receiver.unary_response() + + test_messages.verify(requests, response, self) + + def testSuccessfulStreamRequestStreamResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + receiver = _receiver.Receiver() + + call_consumer = self._invoker.event(group, method)( + receiver, receiver.abort, test_constants.LONG_TIMEOUT) + for request in requests: + call_consumer.consume(request) + call_consumer.terminate() + receiver.block_until_terminated() + responses = receiver.stream_responses() + + test_messages.verify(requests, responses, self) + + def testSequentialInvocations(self): + # pylint: disable=cell-var-from-loop + for (group, method), test_messages_sequence in ( + self._digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + first_request = test_messages.request() + second_request = test_messages.request() + second_receiver = _receiver.Receiver() + + def make_second_invocation(): + self._invoker.event(group, method)( + second_request, second_receiver, second_receiver.abort, + test_constants.LONG_TIMEOUT) + + class FirstReceiver(_receiver.Receiver): + + def complete(self, terminal_metadata, code, details): + super(FirstReceiver, self).complete( + terminal_metadata, code, details) + make_second_invocation() + + first_receiver = FirstReceiver() + + self._invoker.event(group, method)( + first_request, first_receiver, first_receiver.abort, + test_constants.LONG_TIMEOUT) + second_receiver.block_until_terminated() + + first_response = first_receiver.unary_response() + second_response = second_receiver.unary_response() + test_messages.verify(first_request, first_response, self) + test_messages.verify(second_request, second_response, self) + + def testParallelInvocations(self): + for (group, method), test_messages_sequence in ( + self._digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + first_request = test_messages.request() + first_receiver = _receiver.Receiver() + second_request = test_messages.request() + second_receiver = _receiver.Receiver() + + self._invoker.event(group, method)( + first_request, first_receiver, first_receiver.abort, + test_constants.LONG_TIMEOUT) + self._invoker.event(group, method)( + second_request, second_receiver, second_receiver.abort, + test_constants.LONG_TIMEOUT) + first_receiver.block_until_terminated() + second_receiver.block_until_terminated() + + first_response = first_receiver.unary_response() + second_response = second_receiver.unary_response() + test_messages.verify(first_request, first_response, self) + test_messages.verify(second_request, second_response, self) + + @unittest.skip('TODO(nathaniel): implement.') + def testWaitingForSomeButNotAllParallelInvocations(self): + raise NotImplementedError() + + def testCancelledUnaryRequestUnaryResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + receiver = _receiver.Receiver() + + with self._control.pause(): + call = self._invoker.event(group, method)( + request, receiver, receiver.abort, test_constants.LONG_TIMEOUT) + call.cancel() + receiver.block_until_terminated() + + self.assertIs(face.Abortion.Kind.CANCELLED, receiver.abortion().kind) + + def testCancelledUnaryRequestStreamResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + receiver = _receiver.Receiver() + + call = self._invoker.event(group, method)( + request, receiver, receiver.abort, test_constants.LONG_TIMEOUT) + call.cancel() + receiver.block_until_terminated() + + self.assertIs(face.Abortion.Kind.CANCELLED, receiver.abortion().kind) + + def testCancelledStreamRequestUnaryResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + receiver = _receiver.Receiver() + + call_consumer = self._invoker.event(group, method)( + receiver, receiver.abort, test_constants.LONG_TIMEOUT) + for request in requests: + call_consumer.consume(request) + call_consumer.cancel() + receiver.block_until_terminated() + + self.assertIs(face.Abortion.Kind.CANCELLED, receiver.abortion().kind) + + def testCancelledStreamRequestStreamResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.stream_stream_messages_sequences.iteritems()): + for unused_test_messages in test_messages_sequence: + receiver = _receiver.Receiver() + + call_consumer = self._invoker.event(group, method)( + receiver, receiver.abort, test_constants.LONG_TIMEOUT) + call_consumer.cancel() + receiver.block_until_terminated() + + self.assertIs(face.Abortion.Kind.CANCELLED, receiver.abortion().kind) + + def testExpiredUnaryRequestUnaryResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + receiver = _receiver.Receiver() + + with self._control.pause(): + self._invoker.event(group, method)( + request, receiver, receiver.abort, + _3069_test_constant.REALLY_SHORT_TIMEOUT) + receiver.block_until_terminated() + + self.assertIs(face.Abortion.Kind.EXPIRED, receiver.abortion().kind) + + def testExpiredUnaryRequestStreamResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + receiver = _receiver.Receiver() + + with self._control.pause(): + self._invoker.event(group, method)( + request, receiver, receiver.abort, + _3069_test_constant.REALLY_SHORT_TIMEOUT) + receiver.block_until_terminated() + + self.assertIs(face.Abortion.Kind.EXPIRED, receiver.abortion().kind) + + def testExpiredStreamRequestUnaryResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.stream_unary_messages_sequences.iteritems()): + for unused_test_messages in test_messages_sequence: + receiver = _receiver.Receiver() + + self._invoker.event(group, method)( + receiver, receiver.abort, _3069_test_constant.REALLY_SHORT_TIMEOUT) + receiver.block_until_terminated() + + self.assertIs(face.Abortion.Kind.EXPIRED, receiver.abortion().kind) + + def testExpiredStreamRequestStreamResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + receiver = _receiver.Receiver() + + call_consumer = self._invoker.event(group, method)( + receiver, receiver.abort, _3069_test_constant.REALLY_SHORT_TIMEOUT) + for request in requests: + call_consumer.consume(request) + receiver.block_until_terminated() + + self.assertIs(face.Abortion.Kind.EXPIRED, receiver.abortion().kind) + + def testFailedUnaryRequestUnaryResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + receiver = _receiver.Receiver() + + with self._control.fail(): + self._invoker.event(group, method)( + request, receiver, receiver.abort, test_constants.LONG_TIMEOUT) + receiver.block_until_terminated() + + self.assertIs( + face.Abortion.Kind.REMOTE_FAILURE, receiver.abortion().kind) + + def testFailedUnaryRequestStreamResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + receiver = _receiver.Receiver() + + with self._control.fail(): + self._invoker.event(group, method)( + request, receiver, receiver.abort, test_constants.LONG_TIMEOUT) + receiver.block_until_terminated() + + self.assertIs( + face.Abortion.Kind.REMOTE_FAILURE, receiver.abortion().kind) + + def testFailedStreamRequestUnaryResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + receiver = _receiver.Receiver() + + with self._control.fail(): + call_consumer = self._invoker.event(group, method)( + receiver, receiver.abort, test_constants.LONG_TIMEOUT) + for request in requests: + call_consumer.consume(request) + call_consumer.terminate() + receiver.block_until_terminated() + + self.assertIs( + face.Abortion.Kind.REMOTE_FAILURE, receiver.abortion().kind) + + def testFailedStreamRequestStreamResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + receiver = _receiver.Receiver() + + with self._control.fail(): + call_consumer = self._invoker.event(group, method)( + receiver, receiver.abort, test_constants.LONG_TIMEOUT) + for request in requests: + call_consumer.consume(request) + call_consumer.terminate() + receiver.block_until_terminated() + + self.assertIs( + face.Abortion.Kind.REMOTE_FAILURE, receiver.abortion().kind) diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/face/_future_invocation_asynchronous_event_service.py b/src/python/grpcio_test/grpc_test/framework/interfaces/face/_future_invocation_asynchronous_event_service.py new file mode 100644 index 00000000..30327369 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/interfaces/face/_future_invocation_asynchronous_event_service.py @@ -0,0 +1,435 @@ +# Copyright 2015, 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. + +"""Test code for the Face layer of RPC Framework.""" + +import abc +import contextlib +import threading +import unittest + +# test_interfaces is referenced from specification in this module. +from grpc.framework.foundation import logging_pool +from grpc.framework.interfaces.face import face +from grpc_test.framework.common import test_constants +from grpc_test.framework.common import test_control +from grpc_test.framework.common import test_coverage +from grpc_test.framework.interfaces.face import _3069_test_constant +from grpc_test.framework.interfaces.face import _digest +from grpc_test.framework.interfaces.face import _stock_service +from grpc_test.framework.interfaces.face import test_interfaces # pylint: disable=unused-import + + +class _PauseableIterator(object): + + def __init__(self, upstream): + self._upstream = upstream + self._condition = threading.Condition() + self._paused = False + + @contextlib.contextmanager + def pause(self): + with self._condition: + self._paused = True + yield + with self._condition: + self._paused = False + self._condition.notify_all() + + def __iter__(self): + return self + + def next(self): + with self._condition: + while self._paused: + self._condition.wait() + return next(self._upstream) + + +class _Callback(object): + + def __init__(self): + self._condition = threading.Condition() + self._called = False + self._passed_future = None + self._passed_other_stuff = None + + def __call__(self, *args, **kwargs): + with self._condition: + self._called = True + if args: + self._passed_future = args[0] + if 1 < len(args) or kwargs: + self._passed_other_stuff = tuple(args[1:]), dict(kwargs) + self._condition.notify_all() + + def future(self): + with self._condition: + while True: + if self._passed_other_stuff is not None: + raise ValueError( + 'Test callback passed unexpected values: %s', + self._passed_other_stuff) + elif self._called: + return self._passed_future + else: + self._condition.wait() + + +class TestCase(test_coverage.Coverage, unittest.TestCase): + """A test of the Face layer of RPC Framework. + + Concrete subclasses must have an "implementation" attribute of type + test_interfaces.Implementation and an "invoker_constructor" attribute of type + _invocation.InvokerConstructor. + """ + __metaclass__ = abc.ABCMeta + + NAME = 'FutureInvocationAsynchronousEventServiceTest' + + def setUp(self): + """See unittest.TestCase.setUp for full specification. + + Overriding implementations must call this implementation. + """ + self._control = test_control.PauseFailControl() + self._digest_pool = logging_pool.pool(test_constants.POOL_SIZE) + self._digest = _digest.digest( + _stock_service.STOCK_TEST_SERVICE, self._control, self._digest_pool) + + generic_stub, dynamic_stubs, self._memo = self.implementation.instantiate( + self._digest.methods, self._digest.event_method_implementations, None) + self._invoker = self.invoker_constructor.construct_invoker( + generic_stub, dynamic_stubs, self._digest.methods) + + def tearDown(self): + """See unittest.TestCase.tearDown for full specification. + + Overriding implementations must call this implementation. + """ + self._invoker = None + self.implementation.destantiate(self._memo) + self._digest_pool.shutdown(wait=True) + + def testSuccessfulUnaryRequestUnaryResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + callback = _Callback() + + response_future = self._invoker.future(group, method)( + request, test_constants.LONG_TIMEOUT) + response_future.add_done_callback(callback) + response = response_future.result() + + test_messages.verify(request, response, self) + self.assertIs(callback.future(), response_future) + + def testSuccessfulUnaryRequestStreamResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + response_iterator = self._invoker.future(group, method)( + request, test_constants.LONG_TIMEOUT) + responses = list(response_iterator) + + test_messages.verify(request, responses, self) + + def testSuccessfulStreamRequestUnaryResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + request_iterator = _PauseableIterator(iter(requests)) + callback = _Callback() + + # Use of a paused iterator of requests allows us to test that control is + # returned to calling code before the iterator yields any requests. + with request_iterator.pause(): + response_future = self._invoker.future(group, method)( + request_iterator, test_constants.LONG_TIMEOUT) + response_future.add_done_callback(callback) + future_passed_to_callback = callback.future() + response = future_passed_to_callback.result() + + test_messages.verify(requests, response, self) + self.assertIs(future_passed_to_callback, response_future) + + def testSuccessfulStreamRequestStreamResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + request_iterator = _PauseableIterator(iter(requests)) + + # Use of a paused iterator of requests allows us to test that control is + # returned to calling code before the iterator yields any requests. + with request_iterator.pause(): + response_iterator = self._invoker.future(group, method)( + request_iterator, test_constants.LONG_TIMEOUT) + responses = list(response_iterator) + + test_messages.verify(requests, responses, self) + + def testSequentialInvocations(self): + for (group, method), test_messages_sequence in ( + self._digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + first_request = test_messages.request() + second_request = test_messages.request() + + first_response_future = self._invoker.future(group, method)( + first_request, test_constants.LONG_TIMEOUT) + first_response = first_response_future.result() + + test_messages.verify(first_request, first_response, self) + + second_response_future = self._invoker.future(group, method)( + second_request, test_constants.LONG_TIMEOUT) + second_response = second_response_future.result() + + test_messages.verify(second_request, second_response, self) + + def testParallelInvocations(self): + for (group, method), test_messages_sequence in ( + self._digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + first_request = test_messages.request() + second_request = test_messages.request() + + first_response_future = self._invoker.future(group, method)( + first_request, test_constants.LONG_TIMEOUT) + second_response_future = self._invoker.future(group, method)( + second_request, test_constants.LONG_TIMEOUT) + first_response = first_response_future.result() + second_response = second_response_future.result() + + test_messages.verify(first_request, first_response, self) + test_messages.verify(second_request, second_response, self) + + @unittest.skip('TODO(nathaniel): implement.') + def testWaitingForSomeButNotAllParallelInvocations(self): + raise NotImplementedError() + + def testCancelledUnaryRequestUnaryResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + callback = _Callback() + + with self._control.pause(): + response_future = self._invoker.future(group, method)( + request, test_constants.LONG_TIMEOUT) + response_future.add_done_callback(callback) + cancel_method_return_value = response_future.cancel() + + self.assertIs(callback.future(), response_future) + self.assertFalse(cancel_method_return_value) + self.assertTrue(response_future.cancelled()) + + def testCancelledUnaryRequestStreamResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self._control.pause(): + response_iterator = self._invoker.future(group, method)( + request, test_constants.LONG_TIMEOUT) + response_iterator.cancel() + + with self.assertRaises(face.CancellationError): + next(response_iterator) + + def testCancelledStreamRequestUnaryResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + callback = _Callback() + + with self._control.pause(): + response_future = self._invoker.future(group, method)( + iter(requests), test_constants.LONG_TIMEOUT) + response_future.add_done_callback(callback) + cancel_method_return_value = response_future.cancel() + + self.assertIs(callback.future(), response_future) + self.assertFalse(cancel_method_return_value) + self.assertTrue(response_future.cancelled()) + + def testCancelledStreamRequestStreamResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self._control.pause(): + response_iterator = self._invoker.future(group, method)( + iter(requests), test_constants.LONG_TIMEOUT) + response_iterator.cancel() + + with self.assertRaises(face.CancellationError): + next(response_iterator) + + def testExpiredUnaryRequestUnaryResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + callback = _Callback() + + with self._control.pause(): + response_future = self._invoker.future( + group, method)(request, _3069_test_constant.REALLY_SHORT_TIMEOUT) + response_future.add_done_callback(callback) + self.assertIs(callback.future(), response_future) + self.assertIsInstance( + response_future.exception(), face.ExpirationError) + with self.assertRaises(face.ExpirationError): + response_future.result() + + def testExpiredUnaryRequestStreamResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self._control.pause(): + response_iterator = self._invoker.future(group, method)( + request, _3069_test_constant.REALLY_SHORT_TIMEOUT) + with self.assertRaises(face.ExpirationError): + list(response_iterator) + + def testExpiredStreamRequestUnaryResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + callback = _Callback() + + with self._control.pause(): + response_future = self._invoker.future(group, method)( + iter(requests), _3069_test_constant.REALLY_SHORT_TIMEOUT) + response_future.add_done_callback(callback) + self.assertIs(callback.future(), response_future) + self.assertIsInstance( + response_future.exception(), face.ExpirationError) + with self.assertRaises(face.ExpirationError): + response_future.result() + + def testExpiredStreamRequestStreamResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self._control.pause(): + response_iterator = self._invoker.future(group, method)( + iter(requests), _3069_test_constant.REALLY_SHORT_TIMEOUT) + with self.assertRaises(face.ExpirationError): + list(response_iterator) + + def testFailedUnaryRequestUnaryResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + callback = _Callback() + + with self._control.fail(): + response_future = self._invoker.future(group, method)( + request, _3069_test_constant.REALLY_SHORT_TIMEOUT) + response_future.add_done_callback(callback) + + self.assertIs(callback.future(), response_future) + # Because the servicer fails outside of the thread from which the + # servicer-side runtime called into it its failure is + # indistinguishable from simply not having called its + # response_callback before the expiration of the RPC. + self.assertIsInstance( + response_future.exception(), face.ExpirationError) + with self.assertRaises(face.ExpirationError): + response_future.result() + + def testFailedUnaryRequestStreamResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + # Because the servicer fails outside of the thread from which the + # servicer-side runtime called into it its failure is indistinguishable + # from simply not having called its response_consumer before the + # expiration of the RPC. + with self._control.fail(), self.assertRaises(face.ExpirationError): + response_iterator = self._invoker.future(group, method)( + request, _3069_test_constant.REALLY_SHORT_TIMEOUT) + list(response_iterator) + + def testFailedStreamRequestUnaryResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + callback = _Callback() + + with self._control.fail(): + response_future = self._invoker.future(group, method)( + iter(requests), _3069_test_constant.REALLY_SHORT_TIMEOUT) + response_future.add_done_callback(callback) + + self.assertIs(callback.future(), response_future) + # Because the servicer fails outside of the thread from which the + # servicer-side runtime called into it its failure is + # indistinguishable from simply not having called its + # response_callback before the expiration of the RPC. + self.assertIsInstance( + response_future.exception(), face.ExpirationError) + with self.assertRaises(face.ExpirationError): + response_future.result() + + def testFailedStreamRequestStreamResponse(self): + for (group, method), test_messages_sequence in ( + self._digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + # Because the servicer fails outside of the thread from which the + # servicer-side runtime called into it its failure is indistinguishable + # from simply not having called its response_consumer before the + # expiration of the RPC. + with self._control.fail(), self.assertRaises(face.ExpirationError): + response_iterator = self._invoker.future(group, method)( + iter(requests), _3069_test_constant.REALLY_SHORT_TIMEOUT) + list(response_iterator) diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/face/_invocation.py b/src/python/grpcio_test/grpc_test/framework/interfaces/face/_invocation.py new file mode 100644 index 00000000..448e845a --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/interfaces/face/_invocation.py @@ -0,0 +1,213 @@ +# Copyright 2015, 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. + +"""Coverage across the Face layer's generic-to-dynamic range for invocation.""" + +import abc + +from grpc.framework.common import cardinality + +_CARDINALITY_TO_GENERIC_BLOCKING_BEHAVIOR = { + cardinality.Cardinality.UNARY_UNARY: 'blocking_unary_unary', + cardinality.Cardinality.UNARY_STREAM: 'inline_unary_stream', + cardinality.Cardinality.STREAM_UNARY: 'blocking_stream_unary', + cardinality.Cardinality.STREAM_STREAM: 'inline_stream_stream', +} + +_CARDINALITY_TO_GENERIC_FUTURE_BEHAVIOR = { + cardinality.Cardinality.UNARY_UNARY: 'future_unary_unary', + cardinality.Cardinality.UNARY_STREAM: 'inline_unary_stream', + cardinality.Cardinality.STREAM_UNARY: 'future_stream_unary', + cardinality.Cardinality.STREAM_STREAM: 'inline_stream_stream', +} + +_CARDINALITY_TO_GENERIC_EVENT_BEHAVIOR = { + cardinality.Cardinality.UNARY_UNARY: 'event_unary_unary', + cardinality.Cardinality.UNARY_STREAM: 'event_unary_stream', + cardinality.Cardinality.STREAM_UNARY: 'event_stream_unary', + cardinality.Cardinality.STREAM_STREAM: 'event_stream_stream', +} + +_CARDINALITY_TO_MULTI_CALLABLE_ATTRIBUTE = { + cardinality.Cardinality.UNARY_UNARY: 'unary_unary', + cardinality.Cardinality.UNARY_STREAM: 'unary_stream', + cardinality.Cardinality.STREAM_UNARY: 'stream_unary', + cardinality.Cardinality.STREAM_STREAM: 'stream_stream', +} + + +class Invoker(object): + """A type used to invoke test RPCs.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def blocking(self, group, name): + """Invokes an RPC with blocking control flow.""" + raise NotImplementedError() + + @abc.abstractmethod + def future(self, group, name): + """Invokes an RPC with future control flow.""" + raise NotImplementedError() + + @abc.abstractmethod + def event(self, group, name): + """Invokes an RPC with event control flow.""" + raise NotImplementedError() + + +class InvokerConstructor(object): + """A type used to create Invokers.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def name(self): + """Specifies the name of the Invoker constructed by this object.""" + raise NotImplementedError() + + @abc.abstractmethod + def construct_invoker(self, generic_stub, dynamic_stubs, methods): + """Constructs an Invoker for the given stubs and methods.""" + raise NotImplementedError() + + +class _GenericInvoker(Invoker): + + def __init__(self, generic_stub, methods): + self._stub = generic_stub + self._methods = methods + + def _behavior(self, group, name, cardinality_to_generic_method): + method_cardinality = self._methods[group, name].cardinality() + behavior = getattr( + self._stub, cardinality_to_generic_method[method_cardinality]) + return lambda *args, **kwargs: behavior(group, name, *args, **kwargs) + + def blocking(self, group, name): + return self._behavior( + group, name, _CARDINALITY_TO_GENERIC_BLOCKING_BEHAVIOR) + + def future(self, group, name): + return self._behavior(group, name, _CARDINALITY_TO_GENERIC_FUTURE_BEHAVIOR) + + def event(self, group, name): + return self._behavior(group, name, _CARDINALITY_TO_GENERIC_EVENT_BEHAVIOR) + + +class _GenericInvokerConstructor(InvokerConstructor): + + def name(self): + return 'GenericInvoker' + + def construct_invoker(self, generic_stub, dynamic_stub, methods): + return _GenericInvoker(generic_stub, methods) + + +class _MultiCallableInvoker(Invoker): + + def __init__(self, generic_stub, methods): + self._stub = generic_stub + self._methods = methods + + def _multi_callable(self, group, name): + method_cardinality = self._methods[group, name].cardinality() + behavior = getattr( + self._stub, + _CARDINALITY_TO_MULTI_CALLABLE_ATTRIBUTE[method_cardinality]) + return behavior(group, name) + + def blocking(self, group, name): + return self._multi_callable(group, name) + + def future(self, group, name): + method_cardinality = self._methods[group, name].cardinality() + behavior = getattr( + self._stub, + _CARDINALITY_TO_MULTI_CALLABLE_ATTRIBUTE[method_cardinality]) + if method_cardinality in ( + cardinality.Cardinality.UNARY_UNARY, + cardinality.Cardinality.STREAM_UNARY): + return behavior(group, name).future + else: + return behavior(group, name) + + def event(self, group, name): + return self._multi_callable(group, name).event + + +class _MultiCallableInvokerConstructor(InvokerConstructor): + + def name(self): + return 'MultiCallableInvoker' + + def construct_invoker(self, generic_stub, dynamic_stub, methods): + return _MultiCallableInvoker(generic_stub, methods) + + +class _DynamicInvoker(Invoker): + + def __init__(self, dynamic_stubs, methods): + self._stubs = dynamic_stubs + self._methods = methods + + def blocking(self, group, name): + return getattr(self._stubs[group], name) + + def future(self, group, name): + if self._methods[group, name].cardinality() in ( + cardinality.Cardinality.UNARY_UNARY, + cardinality.Cardinality.STREAM_UNARY): + return getattr(self._stubs[group], name).future + else: + return getattr(self._stubs[group], name) + + def event(self, group, name): + return getattr(self._stubs[group], name).event + + +class _DynamicInvokerConstructor(InvokerConstructor): + + def name(self): + return 'DynamicInvoker' + + def construct_invoker(self, generic_stub, dynamic_stubs, methods): + return _DynamicInvoker(dynamic_stubs, methods) + + +def invoker_constructors(): + """Creates a sequence of InvokerConstructors to use in tests of RPCs. + + Returns: + A sequence of InvokerConstructors. + """ + return ( + _GenericInvokerConstructor(), + _MultiCallableInvokerConstructor(), + _DynamicInvokerConstructor(), + ) diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/face/_receiver.py b/src/python/grpcio_test/grpc_test/framework/interfaces/face/_receiver.py new file mode 100644 index 00000000..2e444ff0 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/interfaces/face/_receiver.py @@ -0,0 +1,95 @@ +# Copyright 2015, 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. + +"""A utility useful in tests of asynchronous, event-driven interfaces.""" + +import threading + +from grpc.framework.interfaces.face import face + + +class Receiver(face.ResponseReceiver): + """A utility object useful in tests of asynchronous code.""" + + def __init__(self): + self._condition = threading.Condition() + self._initial_metadata = None + self._responses = [] + self._terminal_metadata = None + self._code = None + self._details = None + self._completed = False + self._abortion = None + + def abort(self, abortion): + with self._condition: + self._abortion = abortion + self._condition.notify_all() + + def initial_metadata(self, initial_metadata): + with self._condition: + self._initial_metadata = initial_metadata + + def response(self, response): + with self._condition: + self._responses.append(response) + + def complete(self, terminal_metadata, code, details): + with self._condition: + self._terminal_metadata = terminal_metadata + self._code = code + self._details = details + self._completed = True + self._condition.notify_all() + + def block_until_terminated(self): + with self._condition: + while self._abortion is None and not self._completed: + self._condition.wait() + + def unary_response(self): + with self._condition: + if self._abortion is not None: + raise AssertionError('Aborted with abortion "%s"!' % self._abortion) + elif len(self._responses) != 1: + raise AssertionError( + '%d responses received, not exactly one!', len(self._responses)) + else: + return self._responses[0] + + def stream_responses(self): + with self._condition: + if self._abortion is None: + return list(self._responses) + else: + raise AssertionError('Aborted with abortion "%s"!' % self._abortion) + + def abortion(self): + with self._condition: + return self._abortion diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/face/_service.py b/src/python/grpcio_test/grpc_test/framework/interfaces/face/_service.py new file mode 100644 index 00000000..e25b8a03 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/interfaces/face/_service.py @@ -0,0 +1,332 @@ +# Copyright 2015, 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. + +"""Private interfaces implemented by data sets used in Face-layer tests.""" + +import abc + +# face is referenced from specification in this module. +from grpc.framework.interfaces.face import face # pylint: disable=unused-import +from grpc_test.framework.interfaces.face import test_interfaces + + +class UnaryUnaryTestMethodImplementation(test_interfaces.Method): + """A controllable implementation of a unary-unary method.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def service(self, request, response_callback, context, control): + """Services an RPC that accepts one message and produces one message. + + Args: + request: The single request message for the RPC. + response_callback: A callback to be called to accept the response message + of the RPC. + context: An face.ServicerContext object. + control: A test_control.Control to control execution of this method. + + Raises: + abandonment.Abandoned: May or may not be raised when the RPC has been + aborted. + """ + raise NotImplementedError() + + +class UnaryUnaryTestMessages(object): + """A type for unary-request-unary-response message pairings.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def request(self): + """Affords a request message. + + Implementations of this method should return a different message with each + call so that multiple test executions of the test method may be made with + different inputs. + + Returns: + A request message. + """ + raise NotImplementedError() + + @abc.abstractmethod + def verify(self, request, response, test_case): + """Verifies that the computed response matches the given request. + + Args: + request: A request message. + response: A response message. + test_case: A unittest.TestCase object affording useful assertion methods. + + Raises: + AssertionError: If the request and response do not match, indicating that + there was some problem executing the RPC under test. + """ + raise NotImplementedError() + + +class UnaryStreamTestMethodImplementation(test_interfaces.Method): + """A controllable implementation of a unary-stream method.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def service(self, request, response_consumer, context, control): + """Services an RPC that takes one message and produces a stream of messages. + + Args: + request: The single request message for the RPC. + response_consumer: A stream.Consumer to be called to accept the response + messages of the RPC. + context: A face.ServicerContext object. + control: A test_control.Control to control execution of this method. + + Raises: + abandonment.Abandoned: May or may not be raised when the RPC has been + aborted. + """ + raise NotImplementedError() + + +class UnaryStreamTestMessages(object): + """A type for unary-request-stream-response message pairings.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def request(self): + """Affords a request message. + + Implementations of this method should return a different message with each + call so that multiple test executions of the test method may be made with + different inputs. + + Returns: + A request message. + """ + raise NotImplementedError() + + @abc.abstractmethod + def verify(self, request, responses, test_case): + """Verifies that the computed responses match the given request. + + Args: + request: A request message. + responses: A sequence of response messages. + test_case: A unittest.TestCase object affording useful assertion methods. + + Raises: + AssertionError: If the request and responses do not match, indicating that + there was some problem executing the RPC under test. + """ + raise NotImplementedError() + + +class StreamUnaryTestMethodImplementation(test_interfaces.Method): + """A controllable implementation of a stream-unary method.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def service(self, response_callback, context, control): + """Services an RPC that takes a stream of messages and produces one message. + + Args: + response_callback: A callback to be called to accept the response message + of the RPC. + context: A face.ServicerContext object. + control: A test_control.Control to control execution of this method. + + Returns: + A stream.Consumer with which to accept the request messages of the RPC. + The consumer returned from this method may or may not be invoked to + completion: in the case of RPC abortion, RPC Framework will simply stop + passing messages to this object. Implementations must not assume that + this object will be called to completion of the request stream or even + called at all. + + Raises: + abandonment.Abandoned: May or may not be raised when the RPC has been + aborted. + """ + raise NotImplementedError() + + +class StreamUnaryTestMessages(object): + """A type for stream-request-unary-response message pairings.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def requests(self): + """Affords a sequence of request messages. + + Implementations of this method should return a different sequences with each + call so that multiple test executions of the test method may be made with + different inputs. + + Returns: + A sequence of request messages. + """ + raise NotImplementedError() + + @abc.abstractmethod + def verify(self, requests, response, test_case): + """Verifies that the computed response matches the given requests. + + Args: + requests: A sequence of request messages. + response: A response message. + test_case: A unittest.TestCase object affording useful assertion methods. + + Raises: + AssertionError: If the requests and response do not match, indicating that + there was some problem executing the RPC under test. + """ + raise NotImplementedError() + + +class StreamStreamTestMethodImplementation(test_interfaces.Method): + """A controllable implementation of a stream-stream method.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def service(self, response_consumer, context, control): + """Services an RPC that accepts and produces streams of messages. + + Args: + response_consumer: A stream.Consumer to be called to accept the response + messages of the RPC. + context: A face.ServicerContext object. + control: A test_control.Control to control execution of this method. + + Returns: + A stream.Consumer with which to accept the request messages of the RPC. + The consumer returned from this method may or may not be invoked to + completion: in the case of RPC abortion, RPC Framework will simply stop + passing messages to this object. Implementations must not assume that + this object will be called to completion of the request stream or even + called at all. + + Raises: + abandonment.Abandoned: May or may not be raised when the RPC has been + aborted. + """ + raise NotImplementedError() + + +class StreamStreamTestMessages(object): + """A type for stream-request-stream-response message pairings.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def requests(self): + """Affords a sequence of request messages. + + Implementations of this method should return a different sequences with each + call so that multiple test executions of the test method may be made with + different inputs. + + Returns: + A sequence of request messages. + """ + raise NotImplementedError() + + @abc.abstractmethod + def verify(self, requests, responses, test_case): + """Verifies that the computed response matches the given requests. + + Args: + requests: A sequence of request messages. + responses: A sequence of response messages. + test_case: A unittest.TestCase object affording useful assertion methods. + + Raises: + AssertionError: If the requests and responses do not match, indicating + that there was some problem executing the RPC under test. + """ + raise NotImplementedError() + + +class TestService(object): + """A specification of implemented methods to use in tests.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def unary_unary_scenarios(self): + """Affords unary-request-unary-response test methods and their messages. + + Returns: + A dict from method group-name pair to implementation/messages pair. The + first element of the pair is a UnaryUnaryTestMethodImplementation object + and the second element is a sequence of UnaryUnaryTestMethodMessages + objects. + """ + raise NotImplementedError() + + @abc.abstractmethod + def unary_stream_scenarios(self): + """Affords unary-request-stream-response test methods and their messages. + + Returns: + A dict from method group-name pair to implementation/messages pair. The + first element of the pair is a UnaryStreamTestMethodImplementation + object and the second element is a sequence of + UnaryStreamTestMethodMessages objects. + """ + raise NotImplementedError() + + @abc.abstractmethod + def stream_unary_scenarios(self): + """Affords stream-request-unary-response test methods and their messages. + + Returns: + A dict from method group-name pair to implementation/messages pair. The + first element of the pair is a StreamUnaryTestMethodImplementation + object and the second element is a sequence of + StreamUnaryTestMethodMessages objects. + """ + raise NotImplementedError() + + @abc.abstractmethod + def stream_stream_scenarios(self): + """Affords stream-request-stream-response test methods and their messages. + + Returns: + A dict from method group-name pair to implementation/messages pair. The + first element of the pair is a StreamStreamTestMethodImplementation + object and the second element is a sequence of + StreamStreamTestMethodMessages objects. + """ + raise NotImplementedError() diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/face/_stock_service.py b/src/python/grpcio_test/grpc_test/framework/interfaces/face/_stock_service.py new file mode 100644 index 00000000..808e2c4e --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/interfaces/face/_stock_service.py @@ -0,0 +1,396 @@ +# Copyright 2015, 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. + +"""Examples of Python implementations of the stock.proto Stock service.""" + +from grpc.framework.common import cardinality +from grpc.framework.foundation import abandonment +from grpc.framework.foundation import stream +from grpc_test.framework.common import test_constants +from grpc_test.framework.interfaces.face import _service +from grpc_test._junkdrawer import stock_pb2 + +_STOCK_GROUP_NAME = 'Stock' +_SYMBOL_FORMAT = 'test symbol:%03d' + +# A test-appropriate security-pricing function. :-P +_price = lambda symbol_name: float(hash(symbol_name) % 4096) + + +def _get_last_trade_price(stock_request, stock_reply_callback, control, active): + """A unary-request, unary-response test method.""" + control.control() + if active(): + stock_reply_callback( + stock_pb2.StockReply( + symbol=stock_request.symbol, price=_price(stock_request.symbol))) + else: + raise abandonment.Abandoned() + + +def _get_last_trade_price_multiple(stock_reply_consumer, control, active): + """A stream-request, stream-response test method.""" + def stock_reply_for_stock_request(stock_request): + control.control() + if active(): + return stock_pb2.StockReply( + symbol=stock_request.symbol, price=_price(stock_request.symbol)) + else: + raise abandonment.Abandoned() + + class StockRequestConsumer(stream.Consumer): + + def consume(self, stock_request): + stock_reply_consumer.consume(stock_reply_for_stock_request(stock_request)) + + def terminate(self): + control.control() + stock_reply_consumer.terminate() + + def consume_and_terminate(self, stock_request): + stock_reply_consumer.consume_and_terminate( + stock_reply_for_stock_request(stock_request)) + + return StockRequestConsumer() + + +def _watch_future_trades(stock_request, stock_reply_consumer, control, active): + """A unary-request, stream-response test method.""" + base_price = _price(stock_request.symbol) + for index in range(stock_request.num_trades_to_watch): + control.control() + if active(): + stock_reply_consumer.consume( + stock_pb2.StockReply( + symbol=stock_request.symbol, price=base_price + index)) + else: + raise abandonment.Abandoned() + stock_reply_consumer.terminate() + + +def _get_highest_trade_price(stock_reply_callback, control, active): + """A stream-request, unary-response test method.""" + + class StockRequestConsumer(stream.Consumer): + """Keeps an ongoing record of the most valuable symbol yet consumed.""" + + def __init__(self): + self._symbol = None + self._price = None + + def consume(self, stock_request): + control.control() + if active(): + if self._price is None: + self._symbol = stock_request.symbol + self._price = _price(stock_request.symbol) + else: + candidate_price = _price(stock_request.symbol) + if self._price < candidate_price: + self._symbol = stock_request.symbol + self._price = candidate_price + + def terminate(self): + control.control() + if active(): + if self._symbol is None: + raise ValueError() + else: + stock_reply_callback( + stock_pb2.StockReply(symbol=self._symbol, price=self._price)) + self._symbol = None + self._price = None + + def consume_and_terminate(self, stock_request): + control.control() + if active(): + if self._price is None: + stock_reply_callback( + stock_pb2.StockReply( + symbol=stock_request.symbol, + price=_price(stock_request.symbol))) + else: + candidate_price = _price(stock_request.symbol) + if self._price < candidate_price: + stock_reply_callback( + stock_pb2.StockReply( + symbol=stock_request.symbol, price=candidate_price)) + else: + stock_reply_callback( + stock_pb2.StockReply( + symbol=self._symbol, price=self._price)) + + self._symbol = None + self._price = None + + return StockRequestConsumer() + + +class GetLastTradePrice(_service.UnaryUnaryTestMethodImplementation): + """GetLastTradePrice for use in tests.""" + + def group(self): + return _STOCK_GROUP_NAME + + def name(self): + return 'GetLastTradePrice' + + def cardinality(self): + return cardinality.Cardinality.UNARY_UNARY + + def request_class(self): + return stock_pb2.StockRequest + + def response_class(self): + return stock_pb2.StockReply + + def serialize_request(self, request): + return request.SerializeToString() + + def deserialize_request(self, serialized_request): + return stock_pb2.StockRequest.FromString(serialized_request) + + def serialize_response(self, response): + return response.SerializeToString() + + def deserialize_response(self, serialized_response): + return stock_pb2.StockReply.FromString(serialized_response) + + def service(self, request, response_callback, context, control): + _get_last_trade_price( + request, response_callback, control, context.is_active) + + +class GetLastTradePriceMessages(_service.UnaryUnaryTestMessages): + + def __init__(self): + self._index = 0 + + def request(self): + symbol = _SYMBOL_FORMAT % self._index + self._index += 1 + return stock_pb2.StockRequest(symbol=symbol) + + def verify(self, request, response, test_case): + test_case.assertEqual(request.symbol, response.symbol) + test_case.assertEqual(_price(request.symbol), response.price) + + +class GetLastTradePriceMultiple(_service.StreamStreamTestMethodImplementation): + """GetLastTradePriceMultiple for use in tests.""" + + def group(self): + return _STOCK_GROUP_NAME + + def name(self): + return 'GetLastTradePriceMultiple' + + def cardinality(self): + return cardinality.Cardinality.STREAM_STREAM + + def request_class(self): + return stock_pb2.StockRequest + + def response_class(self): + return stock_pb2.StockReply + + def serialize_request(self, request): + return request.SerializeToString() + + def deserialize_request(self, serialized_request): + return stock_pb2.StockRequest.FromString(serialized_request) + + def serialize_response(self, response): + return response.SerializeToString() + + def deserialize_response(self, serialized_response): + return stock_pb2.StockReply.FromString(serialized_response) + + def service(self, response_consumer, context, control): + return _get_last_trade_price_multiple( + response_consumer, control, context.is_active) + + +class GetLastTradePriceMultipleMessages(_service.StreamStreamTestMessages): + """Pairs of message streams for use with GetLastTradePriceMultiple.""" + + def __init__(self): + self._index = 0 + + def requests(self): + base_index = self._index + self._index += 1 + return [ + stock_pb2.StockRequest(symbol=_SYMBOL_FORMAT % (base_index + index)) + for index in range(test_constants.STREAM_LENGTH)] + + def verify(self, requests, responses, test_case): + test_case.assertEqual(len(requests), len(responses)) + for stock_request, stock_reply in zip(requests, responses): + test_case.assertEqual(stock_request.symbol, stock_reply.symbol) + test_case.assertEqual(_price(stock_request.symbol), stock_reply.price) + + +class WatchFutureTrades(_service.UnaryStreamTestMethodImplementation): + """WatchFutureTrades for use in tests.""" + + def group(self): + return _STOCK_GROUP_NAME + + def name(self): + return 'WatchFutureTrades' + + def cardinality(self): + return cardinality.Cardinality.UNARY_STREAM + + def request_class(self): + return stock_pb2.StockRequest + + def response_class(self): + return stock_pb2.StockReply + + def serialize_request(self, request): + return request.SerializeToString() + + def deserialize_request(self, serialized_request): + return stock_pb2.StockRequest.FromString(serialized_request) + + def serialize_response(self, response): + return response.SerializeToString() + + def deserialize_response(self, serialized_response): + return stock_pb2.StockReply.FromString(serialized_response) + + def service(self, request, response_consumer, context, control): + _watch_future_trades(request, response_consumer, control, context.is_active) + + +class WatchFutureTradesMessages(_service.UnaryStreamTestMessages): + """Pairs of a single request message and a sequence of response messages.""" + + def __init__(self): + self._index = 0 + + def request(self): + symbol = _SYMBOL_FORMAT % self._index + self._index += 1 + return stock_pb2.StockRequest( + symbol=symbol, num_trades_to_watch=test_constants.STREAM_LENGTH) + + def verify(self, request, responses, test_case): + test_case.assertEqual(test_constants.STREAM_LENGTH, len(responses)) + base_price = _price(request.symbol) + for index, response in enumerate(responses): + test_case.assertEqual(base_price + index, response.price) + + +class GetHighestTradePrice(_service.StreamUnaryTestMethodImplementation): + """GetHighestTradePrice for use in tests.""" + + def group(self): + return _STOCK_GROUP_NAME + + def name(self): + return 'GetHighestTradePrice' + + def cardinality(self): + return cardinality.Cardinality.STREAM_UNARY + + def request_class(self): + return stock_pb2.StockRequest + + def response_class(self): + return stock_pb2.StockReply + + def serialize_request(self, request): + return request.SerializeToString() + + def deserialize_request(self, serialized_request): + return stock_pb2.StockRequest.FromString(serialized_request) + + def serialize_response(self, response): + return response.SerializeToString() + + def deserialize_response(self, serialized_response): + return stock_pb2.StockReply.FromString(serialized_response) + + def service(self, response_callback, context, control): + return _get_highest_trade_price( + response_callback, control, context.is_active) + + +class GetHighestTradePriceMessages(_service.StreamUnaryTestMessages): + + def requests(self): + return [ + stock_pb2.StockRequest(symbol=_SYMBOL_FORMAT % index) + for index in range(test_constants.STREAM_LENGTH)] + + def verify(self, requests, response, test_case): + price = None + symbol = None + for stock_request in requests: + current_symbol = stock_request.symbol + current_price = _price(current_symbol) + if price is None or price < current_price: + price = current_price + symbol = current_symbol + test_case.assertEqual(price, response.price) + test_case.assertEqual(symbol, response.symbol) + + +class StockTestService(_service.TestService): + """A corpus of test data with one method of each RPC cardinality.""" + + def unary_unary_scenarios(self): + return { + (_STOCK_GROUP_NAME, 'GetLastTradePrice'): ( + GetLastTradePrice(), [GetLastTradePriceMessages()]), + } + + def unary_stream_scenarios(self): + return { + (_STOCK_GROUP_NAME, 'WatchFutureTrades'): ( + WatchFutureTrades(), [WatchFutureTradesMessages()]), + } + + def stream_unary_scenarios(self): + return { + (_STOCK_GROUP_NAME, 'GetHighestTradePrice'): ( + GetHighestTradePrice(), [GetHighestTradePriceMessages()]) + } + + def stream_stream_scenarios(self): + return { + (_STOCK_GROUP_NAME, 'GetLastTradePriceMultiple'): ( + GetLastTradePriceMultiple(), [GetLastTradePriceMultipleMessages()]), + } + + +STOCK_TEST_SERVICE = StockTestService() diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/face/test_cases.py b/src/python/grpcio_test/grpc_test/framework/interfaces/face/test_cases.py new file mode 100644 index 00000000..ca623662 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/interfaces/face/test_cases.py @@ -0,0 +1,67 @@ +# Copyright 2015, 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. + +"""Tools for creating tests of implementations of the Face layer.""" + +# unittest is referenced from specification in this module. +import unittest # pylint: disable=unused-import + +# test_interfaces is referenced from specification in this module. +from grpc_test.framework.interfaces.face import _blocking_invocation_inline_service +from grpc_test.framework.interfaces.face import _event_invocation_synchronous_event_service +from grpc_test.framework.interfaces.face import _future_invocation_asynchronous_event_service +from grpc_test.framework.interfaces.face import _invocation +from grpc_test.framework.interfaces.face import test_interfaces # pylint: disable=unused-import + +_TEST_CASE_SUPERCLASSES = ( + _blocking_invocation_inline_service.TestCase, + _event_invocation_synchronous_event_service.TestCase, + _future_invocation_asynchronous_event_service.TestCase, +) + + +def test_cases(implementation): + """Creates unittest.TestCase classes for a given Face layer implementation. + + Args: + implementation: A test_interfaces.Implementation specifying creation and + destruction of a given Face layer implementation. + + Returns: + A sequence of subclasses of unittest.TestCase defining tests of the + specified Face layer implementation. + """ + test_case_classes = [] + for invoker_constructor in _invocation.invoker_constructors(): + for super_class in _TEST_CASE_SUPERCLASSES: + test_case_classes.append( + type(invoker_constructor.name() + super_class.NAME, (super_class,), + {'implementation': implementation, + 'invoker_constructor': invoker_constructor})) + return test_case_classes diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/face/test_interfaces.py b/src/python/grpcio_test/grpc_test/framework/interfaces/face/test_interfaces.py new file mode 100644 index 00000000..b2b5c10f --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/interfaces/face/test_interfaces.py @@ -0,0 +1,229 @@ +# Copyright 2015, 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. + +"""Interfaces used in tests of implementations of the Face layer.""" + +import abc + +from grpc.framework.common import cardinality # pylint: disable=unused-import +from grpc.framework.interfaces.face import face # pylint: disable=unused-import + + +class Method(object): + """Specifies a method to be used in tests.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def group(self): + """Identify the group of the method. + + Returns: + The group of the method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def name(self): + """Identify the name of the method. + + Returns: + The name of the method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def cardinality(self): + """Identify the cardinality of the method. + + Returns: + A cardinality.Cardinality value describing the streaming semantics of the + method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def request_class(self): + """Identify the class used for the method's request objects. + + Returns: + The class object of the class to which the method's request objects + belong. + """ + raise NotImplementedError() + + @abc.abstractmethod + def response_class(self): + """Identify the class used for the method's response objects. + + Returns: + The class object of the class to which the method's response objects + belong. + """ + raise NotImplementedError() + + @abc.abstractmethod + def serialize_request(self, request): + """Serialize the given request object. + + Args: + request: A request object appropriate for this method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def deserialize_request(self, serialized_request): + """Synthesize a request object from a given bytestring. + + Args: + serialized_request: A bytestring deserializable into a request object + appropriate for this method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def serialize_response(self, response): + """Serialize the given response object. + + Args: + response: A response object appropriate for this method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def deserialize_response(self, serialized_response): + """Synthesize a response object from a given bytestring. + + Args: + serialized_response: A bytestring deserializable into a response object + appropriate for this method. + """ + raise NotImplementedError() + + +class Implementation(object): + """Specifies an implementation of the Face layer.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def instantiate( + self, methods, method_implementations, + multi_method_implementation): + """Instantiates the Face layer implementation to be used in a test. + + Args: + methods: A sequence of Method objects describing the methods available to + be called during the test. + method_implementations: A dictionary from group-name pair to + face.MethodImplementation object specifying implementation of a method. + multi_method_implementation: A face.MultiMethodImplementation or None. + + Returns: + A sequence of length three the first element of which is a + face.GenericStub, the second element of which is dictionary from groups + to face.DynamicStubs affording invocation of the group's methods, and + the third element of which is an arbitrary memo object to be kept and + passed to destantiate at the conclusion of the test. The returned stubs + must be backed by the provided implementations. + """ + raise NotImplementedError() + + @abc.abstractmethod + def destantiate(self, memo): + """Destroys the Face layer implementation under test. + + Args: + memo: The object from the third position of the return value of a call to + instantiate. + """ + raise NotImplementedError() + + @abc.abstractmethod + def invocation_metadata(self): + """Provides the metadata to be used when invoking a test RPC. + + Returns: + An object to use as the supplied-at-invocation-time metadata in a test + RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def initial_metadata(self): + """Provides the metadata for use as a test RPC's first servicer metadata. + + Returns: + An object to use as the from-the-servicer-before-responses metadata in a + test RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def terminal_metadata(self): + """Provides the metadata for use as a test RPC's second servicer metadata. + + Returns: + An object to use as the from-the-servicer-after-all-responses metadata in + a test RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def code(self): + """Provides the value for use as a test RPC's code. + + Returns: + An object to use as the from-the-servicer code in a test RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def details(self): + """Provides the value for use as a test RPC's details. + + Returns: + An object to use as the from-the-servicer details in a test RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def metadata_transmitted(self, original_metadata, transmitted_metadata): + """Identifies whether or not metadata was properly transmitted. + + Args: + original_metadata: A metadata value passed to the Face interface + implementation under test. + transmitted_metadata: The same metadata value after having been + transmitted via an RPC performed by the Face interface implementation + under test. + + Returns: + Whether or not the metadata was properly transmitted by the Face interface + implementation under test. + """ + raise NotImplementedError() diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/links/__init__.py b/src/python/grpcio_test/grpc_test/framework/interfaces/links/__init__.py new file mode 100644 index 00000000..70865191 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/interfaces/links/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2015, 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. + + diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/links/test_cases.py b/src/python/grpcio_test/grpc_test/framework/interfaces/links/test_cases.py new file mode 100644 index 00000000..ecf49d9c --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/interfaces/links/test_cases.py @@ -0,0 +1,326 @@ +# Copyright 2015, 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. + +"""Tests of the links interface of RPC Framework.""" + +# unittest is referenced from specification in this module. +import abc +import unittest # pylint: disable=unused-import + +from grpc.framework.interfaces.links import links +from grpc_test.framework.common import test_constants +from grpc_test.framework.interfaces.links import test_utilities + + +def at_least_n_payloads_received_predicate(n): + def predicate(ticket_sequence): + payload_count = 0 + for ticket in ticket_sequence: + if ticket.payload is not None: + payload_count += 1 + if n <= payload_count: + return True + else: + return False + return predicate + + +def terminated(ticket_sequence): + return ticket_sequence and ticket_sequence[-1].termination is not None + +_TRANSMISSION_GROUP = 'test.Group' +_TRANSMISSION_METHOD = 'TestMethod' + + +class TransmissionTest(object): + """Tests ticket transmission between two connected links. + + This class must be mixed into a unittest.TestCase that implements the abstract + methods it provides. + """ + __metaclass__ = abc.ABCMeta + + # This is a unittest.TestCase mix-in. + # pylint: disable=invalid-name + + @abc.abstractmethod + def create_transmitting_links(self): + """Creates two connected links for use in this test. + + Returns: + Two links.Links, the first of which will be used on the invocation side + of RPCs and the second of which will be used on the service side of + RPCs. + """ + raise NotImplementedError() + + @abc.abstractmethod + def destroy_transmitting_links(self, invocation_side_link, service_side_link): + """Destroys the two connected links created for this test. + + + Args: + invocation_side_link: The link used on the invocation side of RPCs in + this test. + service_side_link: The link used on the service side of RPCs in this + test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def create_invocation_initial_metadata(self): + """Creates a value for use as invocation-side initial metadata. + + Returns: + A metadata value appropriate for use as invocation-side initial metadata + or None if invocation-side initial metadata transmission is not + supported by the links under test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def create_invocation_terminal_metadata(self): + """Creates a value for use as invocation-side terminal metadata. + + Returns: + A metadata value appropriate for use as invocation-side terminal + metadata or None if invocation-side terminal metadata transmission is + not supported by the links under test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def create_service_initial_metadata(self): + """Creates a value for use as service-side initial metadata. + + Returns: + A metadata value appropriate for use as service-side initial metadata or + None if service-side initial metadata transmission is not supported by + the links under test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def create_service_terminal_metadata(self): + """Creates a value for use as service-side terminal metadata. + + Returns: + A metadata value appropriate for use as service-side terminal metadata or + None if service-side terminal metadata transmission is not supported by + the links under test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def create_invocation_completion(self): + """Creates values for use as invocation-side code and message. + + Returns: + An invocation-side code value and an invocation-side message value. + Either or both may be None if invocation-side code and/or + invocation-side message transmission is not supported by the links + under test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def create_service_completion(self): + """Creates values for use as service-side code and message. + + Returns: + A service-side code value and a service-side message value. Either or + both may be None if service-side code and/or service-side message + transmission is not supported by the links under test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def assertMetadataTransmitted(self, original_metadata, transmitted_metadata): + """Asserts that transmitted_metadata contains original_metadata. + + Args: + original_metadata: A metadata object used in this test. + transmitted_metadata: A metadata object obtained after transmission + through the system under test. + + Raises: + AssertionError: if the transmitted_metadata object does not contain + original_metadata. + """ + raise NotImplementedError() + + def group_and_method(self): + """Returns the group and method used in this test case. + + Returns: + A pair of the group and method used in this test case. + """ + return _TRANSMISSION_GROUP, _TRANSMISSION_METHOD + + def serialize_request(self, request): + """Serializes a request value used in this test case. + + Args: + request: A request value created by this test case. + + Returns: + A bytestring that is the serialization of the given request. + """ + return request + + def deserialize_request(self, serialized_request): + """Deserializes a request value used in this test case. + + Args: + serialized_request: A bytestring that is the serialization of some request + used in this test case. + + Returns: + The request value encoded by the given bytestring. + """ + return serialized_request + + def serialize_response(self, response): + """Serializes a response value used in this test case. + + Args: + response: A response value created by this test case. + + Returns: + A bytestring that is the serialization of the given response. + """ + return response + + def deserialize_response(self, serialized_response): + """Deserializes a response value used in this test case. + + Args: + serialized_response: A bytestring that is the serialization of some + response used in this test case. + + Returns: + The response value encoded by the given bytestring. + """ + return serialized_response + + def _assert_is_valid_metadata_payload_sequence( + self, ticket_sequence, payloads, initial_metadata, terminal_metadata): + initial_metadata_seen = False + seen_payloads = [] + terminal_metadata_seen = False + + for ticket in ticket_sequence: + if ticket.initial_metadata is not None: + self.assertFalse(initial_metadata_seen) + self.assertFalse(seen_payloads) + self.assertFalse(terminal_metadata_seen) + self.assertMetadataTransmitted(initial_metadata, ticket.initial_metadata) + initial_metadata_seen = True + + if ticket.payload is not None: + self.assertFalse(terminal_metadata_seen) + seen_payloads.append(ticket.payload) + + if ticket.terminal_metadata is not None: + self.assertFalse(terminal_metadata_seen) + self.assertMetadataTransmitted(terminal_metadata, ticket.terminal_metadata) + terminal_metadata_seen = True + self.assertSequenceEqual(payloads, seen_payloads) + + def _assert_is_valid_invocation_sequence( + self, ticket_sequence, group, method, payloads, initial_metadata, + terminal_metadata, termination): + self.assertLess(0, len(ticket_sequence)) + self.assertEqual(group, ticket_sequence[0].group) + self.assertEqual(method, ticket_sequence[0].method) + self._assert_is_valid_metadata_payload_sequence( + ticket_sequence, payloads, initial_metadata, terminal_metadata) + self.assertIs(termination, ticket_sequence[-1].termination) + + def _assert_is_valid_service_sequence( + self, ticket_sequence, payloads, initial_metadata, terminal_metadata, + code, message, termination): + self.assertLess(0, len(ticket_sequence)) + self._assert_is_valid_metadata_payload_sequence( + ticket_sequence, payloads, initial_metadata, terminal_metadata) + self.assertEqual(code, ticket_sequence[-1].code) + self.assertEqual(message, ticket_sequence[-1].message) + self.assertIs(termination, ticket_sequence[-1].termination) + + def setUp(self): + self._invocation_link, self._service_link = self.create_transmitting_links() + self._invocation_mate = test_utilities.RecordingLink() + self._service_mate = test_utilities.RecordingLink() + self._invocation_link.join_link(self._invocation_mate) + self._service_link.join_link(self._service_mate) + + def tearDown(self): + self.destroy_transmitting_links(self._invocation_link, self._service_link) + + def testSimplestRoundTrip(self): + """Tests transmission of one ticket in each direction.""" + invocation_operation_id = object() + invocation_payload = b'\x07' * 1023 + timeout = test_constants.LONG_TIMEOUT + invocation_initial_metadata = self.create_invocation_initial_metadata() + invocation_terminal_metadata = self.create_invocation_terminal_metadata() + invocation_code, invocation_message = self.create_invocation_completion() + service_payload = b'\x08' * 1025 + service_initial_metadata = self.create_service_initial_metadata() + service_terminal_metadata = self.create_service_terminal_metadata() + service_code, service_message = self.create_service_completion() + + original_invocation_ticket = links.Ticket( + invocation_operation_id, 0, _TRANSMISSION_GROUP, _TRANSMISSION_METHOD, + links.Ticket.Subscription.FULL, timeout, 0, invocation_initial_metadata, + invocation_payload, invocation_terminal_metadata, invocation_code, + invocation_message, links.Ticket.Termination.COMPLETION, None) + self._invocation_link.accept_ticket(original_invocation_ticket) + + self._service_mate.block_until_tickets_satisfy( + at_least_n_payloads_received_predicate(1)) + service_operation_id = self._service_mate.tickets()[0].operation_id + + self._service_mate.block_until_tickets_satisfy(terminated) + self._assert_is_valid_invocation_sequence( + self._service_mate.tickets(), _TRANSMISSION_GROUP, _TRANSMISSION_METHOD, + (invocation_payload,), invocation_initial_metadata, + invocation_terminal_metadata, links.Ticket.Termination.COMPLETION) + + original_service_ticket = links.Ticket( + service_operation_id, 0, None, None, links.Ticket.Subscription.FULL, + timeout, 0, service_initial_metadata, service_payload, + service_terminal_metadata, service_code, service_message, + links.Ticket.Termination.COMPLETION, None) + self._service_link.accept_ticket(original_service_ticket) + self._invocation_mate.block_until_tickets_satisfy(terminated) + self._assert_is_valid_service_sequence( + self._invocation_mate.tickets(), (service_payload,), + service_initial_metadata, service_terminal_metadata, service_code, + service_message, links.Ticket.Termination.COMPLETION) diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/links/test_utilities.py b/src/python/grpcio_test/grpc_test/framework/interfaces/links/test_utilities.py new file mode 100644 index 00000000..39c7f2fc --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/interfaces/links/test_utilities.py @@ -0,0 +1,167 @@ +# Copyright 2015, 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. + +"""State and behavior appropriate for use in tests.""" + +import logging +import threading +import time + +from grpc.framework.interfaces.links import links +from grpc.framework.interfaces.links import utilities + +# A more-or-less arbitrary limit on the length of raw data values to be logged. +_UNCOMFORTABLY_LONG = 48 + + +def _safe_for_log_ticket(ticket): + """Creates a safe-for-printing-to-the-log ticket for a given ticket. + + Args: + ticket: Any links.Ticket. + + Returns: + A links.Ticket that is as much as can be equal to the given ticket but + possibly features values like the string "" in + place of the actual values of the given ticket. + """ + if isinstance(ticket.payload, (basestring,)): + payload_length = len(ticket.payload) + else: + payload_length = -1 + if payload_length < _UNCOMFORTABLY_LONG: + return ticket + else: + return links.Ticket( + ticket.operation_id, ticket.sequence_number, + ticket.group, ticket.method, ticket.subscription, ticket.timeout, + ticket.allowance, ticket.initial_metadata, + ''.format(payload_length), + ticket.terminal_metadata, ticket.code, ticket.message, + ticket.termination, None) + + +class RecordingLink(links.Link): + """A Link that records every ticket passed to it.""" + + def __init__(self): + self._condition = threading.Condition() + self._tickets = [] + + def accept_ticket(self, ticket): + with self._condition: + self._tickets.append(ticket) + self._condition.notify_all() + + def join_link(self, link): + pass + + def block_until_tickets_satisfy(self, predicate): + """Blocks until the received tickets satisfy the given predicate. + + Args: + predicate: A callable that takes a sequence of tickets and returns a + boolean value. + """ + with self._condition: + while not predicate(self._tickets): + self._condition.wait() + + def tickets(self): + """Returns a copy of the list of all tickets received by this Link.""" + with self._condition: + return tuple(self._tickets) + + +class _Pipe(object): + """A conduit that logs all tickets passed through it.""" + + def __init__(self, name): + self._lock = threading.Lock() + self._name = name + self._left_mate = utilities.NULL_LINK + self._right_mate = utilities.NULL_LINK + + def accept_left_to_right_ticket(self, ticket): + with self._lock: + logging.warning( + '%s: moving left to right through %s: %s', time.time(), self._name, + _safe_for_log_ticket(ticket)) + try: + self._right_mate.accept_ticket(ticket) + except Exception as e: # pylint: disable=broad-except + logging.exception(e) + + def accept_right_to_left_ticket(self, ticket): + with self._lock: + logging.warning( + '%s: moving right to left through %s: %s', time.time(), self._name, + _safe_for_log_ticket(ticket)) + try: + self._left_mate.accept_ticket(ticket) + except Exception as e: # pylint: disable=broad-except + logging.exception(e) + + def join_left_mate(self, left_mate): + with self._lock: + self._left_mate = utilities.NULL_LINK if left_mate is None else left_mate + + def join_right_mate(self, right_mate): + with self._lock: + self._right_mate = ( + utilities.NULL_LINK if right_mate is None else right_mate) + + +class _Facade(links.Link): + + def __init__(self, accept, join): + self._accept = accept + self._join = join + + def accept_ticket(self, ticket): + self._accept(ticket) + + def join_link(self, link): + self._join(link) + + +def logging_links(name): + """Creates a conduit that logs all tickets passed through it. + + Args: + name: A name to use for the conduit to identify itself in logging output. + + Returns: + Two links.Links, the first of which is the "left" side of the conduit + and the second of which is the "right" side of the conduit. + """ + pipe = _Pipe(name) + left_facade = _Facade(pipe.accept_left_to_right_ticket, pipe.join_left_mate) + right_facade = _Facade(pipe.accept_right_to_left_ticket, pipe.join_right_mate) + return left_facade, right_facade diff --git a/src/python/grpcio_test/grpc_test/resources.py b/src/python/grpcio_test/grpc_test/resources.py new file mode 100644 index 00000000..2c304531 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/resources.py @@ -0,0 +1,56 @@ +# Copyright 2015, 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. + +"""Constants and functions for data used in interoperability testing.""" + +import os + +import pkg_resources + +_ROOT_CERTIFICATES_RESOURCE_PATH = 'credentials/ca.pem' +_PRIVATE_KEY_RESOURCE_PATH = 'credentials/server1.key' +_CERTIFICATE_CHAIN_RESOURCE_PATH = 'credentials/server1.pem' + + +def test_root_certificates(): + return pkg_resources.resource_string( + __name__, _ROOT_CERTIFICATES_RESOURCE_PATH) + + +def prod_root_certificates(): + return open(os.environ['SSL_CERT_FILE'], mode='rb').read() + + +def private_key(): + return pkg_resources.resource_string(__name__, _PRIVATE_KEY_RESOURCE_PATH) + + +def certificate_chain(): + return pkg_resources.resource_string( + __name__, _CERTIFICATE_CHAIN_RESOURCE_PATH) diff --git a/src/python/grpcio_test/grpc_test/test_common.py b/src/python/grpcio_test/grpc_test/test_common.py new file mode 100644 index 00000000..44284be8 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/test_common.py @@ -0,0 +1,76 @@ +# Copyright 2015, 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. + +"""Common code used throughout tests of gRPC.""" + +import collections + +INVOCATION_INITIAL_METADATA = ((b'0', b'abc'), (b'1', b'def'), (b'2', b'ghi'),) +SERVICE_INITIAL_METADATA = ((b'3', b'jkl'), (b'4', b'mno'), (b'5', b'pqr'),) +SERVICE_TERMINAL_METADATA = ((b'6', b'stu'), (b'7', b'vwx'), (b'8', b'yza'),) +DETAILS = b'test details' + + +def metadata_transmitted(original_metadata, transmitted_metadata): + """Judges whether or not metadata was acceptably transmitted. + + gRPC is allowed to insert key-value pairs into the metadata values given by + applications and to reorder key-value pairs with different keys but it is not + allowed to alter existing key-value pairs or to reorder key-value pairs with + the same key. + + Args: + original_metadata: A metadata value used in a test of gRPC. + transmitted_metadata: A metadata value corresponding to original_metadata + after having been transmitted via gRPC. + + Returns: + A boolean indicating whether transmitted_metadata accurately reflects + original_metadata after having been transmitted via gRPC. + """ + original = collections.defaultdict(list) + for key, value in original_metadata: + original[key].append(value) + transmitted = collections.defaultdict(list) + for key, value in transmitted_metadata: + transmitted[key].append(value) + + for key, values in original.iteritems(): + transmitted_values = transmitted[key] + transmitted_iterator = iter(transmitted_values) + try: + for value in values: + while True: + transmitted_value = next(transmitted_iterator) + if value == transmitted_value: + break + except StopIteration: + return False + else: + return True diff --git a/src/python/grpcio_test/requirements.txt b/src/python/grpcio_test/requirements.txt new file mode 100644 index 00000000..fea80ca0 --- /dev/null +++ b/src/python/grpcio_test/requirements.txt @@ -0,0 +1,6 @@ +grpcio>=0.11.0b0 +oauth2client>=1.4.7 +protobuf>=3.0.0a3 +pytest>=2.6 +pytest-cov>=2.0 +pytest-xdist>=1.11 diff --git a/src/python/grpcio_test/setup.cfg b/src/python/grpcio_test/setup.cfg new file mode 100644 index 00000000..b32d3f59 --- /dev/null +++ b/src/python/grpcio_test/setup.cfg @@ -0,0 +1,3 @@ +[pytest] +norecursedirs = _cython +python_files = *_test.py diff --git a/src/python/grpcio_test/setup.py b/src/python/grpcio_test/setup.py new file mode 100644 index 00000000..fe36bc92 --- /dev/null +++ b/src/python/grpcio_test/setup.py @@ -0,0 +1,92 @@ +# Copyright 2015, 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. + +"""A setup module for the GRPC Python interop testing package.""" + +import os +import os.path + +import setuptools + +# Ensure we're in the proper directory whether or not we're being used by pip. +os.chdir(os.path.dirname(os.path.abspath(__file__))) + +# Break import-style to ensure we can actually find our commands module. +import commands + +_PACKAGES = setuptools.find_packages('.', exclude=['*._cython', '*._cython.*']) + +_PACKAGE_DIRECTORIES = { + '': '.', +} + +_PACKAGE_DATA = { + 'grpc_interop': [ + 'credentials/ca.pem', + 'credentials/server1.key', + 'credentials/server1.pem', + ], + 'grpc_protoc_plugin': [ + 'test.proto', + ], + 'grpc_test': [ + 'credentials/ca.pem', + 'credentials/server1.key', + 'credentials/server1.pem', + ], +} + +_SETUP_REQUIRES = ( + 'pytest>=2.6', + 'pytest-cov>=2.0', + 'pytest-xdist>=1.11', + 'pytest-timeout>=0.5', +) + +_INSTALL_REQUIRES = ( + 'oauth2client>=1.4.7', + 'grpcio>=0.11.0b0', + # TODO(issue 3321): Unpin protobuf dependency. + 'protobuf==3.0.0a3', +) + +_COMMAND_CLASS = { + 'test': commands.RunTests +} + +setuptools.setup( + name='grpcio_test', + version='0.11.0b0', + packages=_PACKAGES, + package_dir=_PACKAGE_DIRECTORIES, + package_data=_PACKAGE_DATA, + install_requires=_INSTALL_REQUIRES + _SETUP_REQUIRES, + setup_requires=_SETUP_REQUIRES, + cmdclass=_COMMAND_CLASS, +) diff --git a/src/ruby/.gitignore b/src/ruby/.gitignore new file mode 100755 index 00000000..62fcb4fa --- /dev/null +++ b/src/ruby/.gitignore @@ -0,0 +1,15 @@ +/.bundle/ +/.yardoc +/Gemfile.lock +/_yardoc/ +/coverage/ +/doc/ +/pkg/ +/spec/reports/ +/tmp/ +*.bundle +*.so +*.o +*.a +mkmf.log +vendor diff --git a/src/ruby/.rspec b/src/ruby/.rspec new file mode 100755 index 00000000..efeee2c1 --- /dev/null +++ b/src/ruby/.rspec @@ -0,0 +1,6 @@ +-I. +-Ipb +--backtrace +--require spec_helper +--format documentation +--color diff --git a/src/ruby/.rubocop.yml b/src/ruby/.rubocop.yml new file mode 100644 index 00000000..312bdca3 --- /dev/null +++ b/src/ruby/.rubocop.yml @@ -0,0 +1,11 @@ +# This is the configuration used to check the rubocop source code. + +inherit_from: .rubocop_todo.yml + +AllCops: + Exclude: + - 'bin/apis/**/*' + - 'bin/math.rb' + - 'bin/math_services.rb' + - 'pb/grpc/health/v1alpha/*' + - 'pb/test/**/*' diff --git a/src/ruby/.rubocop_todo.yml b/src/ruby/.rubocop_todo.yml new file mode 100644 index 00000000..05db4045 --- /dev/null +++ b/src/ruby/.rubocop_todo.yml @@ -0,0 +1,44 @@ +# This configuration was generated by `rubocop --auto-gen-config` +# on 2015-05-22 13:23:34 -0700 using RuboCop version 0.30.1. +# The point is for the user to remove these configuration records +# one by one as the offenses are removed from the code base. +# Note that changes in the inspected code, or installation of new +# versions of RuboCop, may require this file to be generated again. + +# Offense count: 30 +Metrics/AbcSize: + Max: 38 + +# Offense count: 3 +# Configuration parameters: CountComments. +Metrics/ClassLength: + Max: 200 + +# Offense count: 35 +# Configuration parameters: CountComments. +Metrics/MethodLength: + Max: 36 + +# Offense count: 7 +# Configuration parameters: CountKeywordArgs. +Metrics/ParameterLists: + Max: 8 + +# Offense count: 9 +# Configuration parameters: AllowedVariables. +Style/GlobalVars: + Enabled: false + +# Offense count: 1 +# Configuration parameters: EnforcedStyle, MinBodyLength, SupportedStyles. +Style/Next: + Enabled: false + +# Offense count: 2 +# Configuration parameters: Methods. +Style/SingleLineBlockParams: + Enabled: false + +# Offense count: 1 +Style/StructInheritance: + Enabled: false diff --git a/src/ruby/CHANGELOG.md b/src/ruby/CHANGELOG.md new file mode 100644 index 00000000..8ec6e3cf --- /dev/null +++ b/src/ruby/CHANGELOG.md @@ -0,0 +1,11 @@ +## 0.6.1 (2015-04-14) + +### Changes + +* Begins this ChangeLog ([@tbetbetbe][]) +* Updates to version 0.4 of googleauth. ([@tbetbetbe][]) +* Switch the extension to use the call API. ([@tbetbetbe][]) +* Refactor the C extension to avoid identifiers used by ruby ([@yugui][]) + +[@tbetbetbe]: https://github.com/tbetbetbe +[@yugui]: https://github.com/yugui diff --git a/src/ruby/Gemfile b/src/ruby/Gemfile new file mode 100755 index 00000000..597a7d4f --- /dev/null +++ b/src/ruby/Gemfile @@ -0,0 +1,4 @@ +source 'https://rubygems.org' + +# Specify your gem's dependencies in grpc.gemspec +gemspec diff --git a/src/ruby/README.md b/src/ruby/README.md new file mode 100644 index 00000000..8c56ceb1 --- /dev/null +++ b/src/ruby/README.md @@ -0,0 +1,102 @@ +gRPC Ruby +========= + +A Ruby implementation of gRPC. + +Status +------ + +Alpha : Ready for early adopters + +PREREQUISITES +------------- + +- Ruby 2.x. The gRPC API uses keyword args. +- [homebrew][] on Mac OS X. These simplify the installation of the gRPC C core. + +INSTALLATION +--------------- + +**Linux (Debian):** + +Add [Debian jessie-backports][] to your `sources.list` file. Example: + +```sh +echo "deb http://http.debian.net/debian jessie-backports main" | \ +sudo tee -a /etc/apt/sources.list +``` + +Install the gRPC Debian package + +```sh +sudo apt-get update +sudo apt-get install libgrpc-dev +``` + +Install the gRPC Ruby package + +```sh +gem install grpc +``` + +**Mac OS X** + +Install [homebrew][]. Run the following command to install gRPC Ruby. +```sh +$ curl -fsSL https://goo.gl/getgrpc | bash -s ruby +``` +This will download and run the [gRPC install script][], then install the latest version of gRPC Ruby gem. It also installs Protocol Buffers compiler (_protoc_) and the gRPC _protoc_ plugin for ruby. + +BUILD FROM SOURCE +--------------------- +- Clone this repository + +- Install Ruby 2.x. Consider doing this with [RVM](http://rvm.io), it's a nice way of controlling + the exact ruby version that's used. +```sh +$ command curl -sSL https://rvm.io/mpapis.asc | gpg --import - +$ \curl -sSL https://get.rvm.io | bash -s stable --ruby=ruby-2 +$ +$ # follow the instructions to ensure that your're using the latest stable version of Ruby +$ # and that the rvm command is installed +``` +- Make sure your run `source $HOME/.rvm/scripts/rvm` as instructed to complete the set up of RVM + +- Install [bundler](http://bundler.io/) +``` +$ gem install bundler +``` + +- Finally, build and install the gRPC gem locally. +```sh +$ # from this directory +$ bundle install # creates the ruby bundle, including building the grpc extension +$ rake # runs the unit tests, see rake -T for other options +``` + +DOCUMENTATION +------------- +- rubydoc for the gRPC gem is available online at [rubydoc][]. +- the gRPC Ruby reference documentation is available online at [grpc.io][] + +CONTENTS +-------- +Directory structure is the layout for [ruby extensions][] +- ext: the gRPC ruby extension +- lib: the entrypoint gRPC ruby library to be used in a 'require' statement +- spec: Rspec unittests +- bin: example gRPC clients and servers, e.g, + + ```ruby + stub = Math::Math::Stub.new('my.test.math.server.com:8080') + req = Math::DivArgs.new(dividend: 7, divisor: 3) + GRPC.logger.info("div(7/3): req=#{req.inspect}") + resp = stub.div(req) + GRPC.logger.info("Answer: #{resp.inspect}") + ``` +[homebrew]:http://brew.sh +[gRPC install script]:https://raw.githubusercontent.com/grpc/homebrew-grpc/master/scripts/install +[ruby extensions]:http://guides.rubygems.org/gems-with-extensions/ +[rubydoc]: http://www.rubydoc.info/gems/grpc +[grpc.io]: http://www.grpc.io/docs/installation/ruby.html +[Debian jessie-backports]:http://backports.debian.org/Instructions/ diff --git a/src/ruby/Rakefile b/src/ruby/Rakefile new file mode 100755 index 00000000..cc7832b1 --- /dev/null +++ b/src/ruby/Rakefile @@ -0,0 +1,58 @@ +# -*- ruby -*- +require 'rake/extensiontask' +require 'rspec/core/rake_task' +require 'rubocop/rake_task' +require 'bundler/gem_tasks' + +# Add rubocop style checking tasks +RuboCop::RakeTask.new + +# Add the extension compiler task +Rake::ExtensionTask.new 'grpc' do |ext| + ext.lib_dir = File.join('lib', 'grpc') +end + +# Define the test suites +SPEC_SUITES = [ + { id: :wrapper, title: 'wrapper layer', files: %w(spec/*.rb) }, + { id: :idiomatic, title: 'idiomatic layer', dir: %w(spec/generic), + tags: ['~bidi', '~server'] }, + { id: :bidi, title: 'bidi tests', dir: %w(spec/generic), + tag: 'bidi' }, + { id: :server, title: 'rpc server thread tests', dir: %w(spec/generic), + tag: 'server' }, + { id: :pb, title: 'protobuf service tests', dir: %w(spec/pb) } +] +namespace :suite do + SPEC_SUITES.each do |suite| + desc "Run all specs in the #{suite[:title]} spec suite" + RSpec::Core::RakeTask.new(suite[:id]) do |t| + ENV['COVERAGE_NAME'] = suite[:id].to_s + spec_files = [] + suite[:files].each { |f| spec_files += Dir[f] } if suite[:files] + + if suite[:dir] + suite[:dir].each { |f| spec_files += Dir["#{f}/**/*_spec.rb"] } + end + helper = 'spec/spec_helper.rb' + spec_files << helper unless spec_files.include?(helper) + + t.pattern = spec_files + t.rspec_opts = "--tag #{suite[:tag]}" if suite[:tag] + if suite[:tags] + t.rspec_opts = suite[:tags].map { |x| "--tag #{x}" }.join(' ') + end + end + end +end + +# Define dependencies between the suites. +task 'suite:wrapper' => [:compile, :rubocop] +task 'suite:idiomatic' => 'suite:wrapper' +task 'suite:bidi' => 'suite:wrapper' +task 'suite:server' => 'suite:wrapper' +task 'suite:pb' => 'suite:server' + +desc 'Compiles the gRPC extension then runs all the tests' +task all: ['suite:idiomatic', 'suite:bidi', 'suite:pb', 'suite:server'] +task default: :all diff --git a/src/ruby/bin/apis/google/protobuf/empty.rb b/src/ruby/bin/apis/google/protobuf/empty.rb new file mode 100644 index 00000000..2f6bbc95 --- /dev/null +++ b/src/ruby/bin/apis/google/protobuf/empty.rb @@ -0,0 +1,44 @@ +# Copyright 2015, 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. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: google/protobuf/empty.proto + +require 'google/protobuf' + +Google::Protobuf::DescriptorPool.generated_pool.build do + add_message "google.protobuf.Empty" do + end +end + +module Google + module Protobuf + Empty = Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.Empty").msgclass + end +end diff --git a/src/ruby/bin/apis/pubsub_demo.rb b/src/ruby/bin/apis/pubsub_demo.rb new file mode 100755 index 00000000..a039d036 --- /dev/null +++ b/src/ruby/bin/apis/pubsub_demo.rb @@ -0,0 +1,264 @@ +#!/usr/bin/env ruby + +# Copyright 2015, 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. + +# pubsub_demo demos accesses the Google PubSub API via its gRPC interface +# +# $ GOOGLE_APPLICATION_CREDENTIALS= \ +# SSL_CERT_FILE= \ +# path/to/pubsub_demo.rb \ +# [--action= ] +# +# There are options related to the chosen action, see #parse_args below. +# - the possible actions are given by the method names of NamedAction class +# - the default action is list_some_topics + +this_dir = File.expand_path(File.dirname(__FILE__)) +lib_dir = File.join(File.dirname(File.dirname(this_dir)), 'lib') +$LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir) +$LOAD_PATH.unshift(this_dir) unless $LOAD_PATH.include?(this_dir) + +require 'optparse' + +require 'grpc' +require 'googleauth' +require 'google/protobuf' + +require 'google/protobuf/empty' +require 'tech/pubsub/proto/pubsub' +require 'tech/pubsub/proto/pubsub_services' + +# loads the certificates used to access the test server securely. +def load_prod_cert + fail 'could not find a production cert' if ENV['SSL_CERT_FILE'].nil? + p "loading prod certs from #{ENV['SSL_CERT_FILE']}" + File.open(ENV['SSL_CERT_FILE']) do |f| + return f.read + end +end + +# creates a SSL Credentials from the production certificates. +def ssl_creds + GRPC::Core::Credentials.new(load_prod_cert) +end + +# Builds the metadata authentication update proc. +def auth_proc(opts) + auth_creds = Google::Auth.get_application_default + return auth_creds.updater_proc +end + +# Creates a stub for accessing the publisher service. +def publisher_stub(opts) + address = "#{opts.host}:#{opts.port}" + stub_clz = Tech::Pubsub::PublisherService::Stub # shorter + GRPC.logger.info("... access PublisherService at #{address}") + stub_clz.new(address, + creds: ssl_creds, update_metadata: auth_proc(opts), + GRPC::Core::Channel::SSL_TARGET => opts.host) +end + +# Creates a stub for accessing the subscriber service. +def subscriber_stub(opts) + address = "#{opts.host}:#{opts.port}" + stub_clz = Tech::Pubsub::SubscriberService::Stub # shorter + GRPC.logger.info("... access SubscriberService at #{address}") + stub_clz.new(address, + creds: ssl_creds, update_metadata: auth_proc(opts), + GRPC::Core::Channel::SSL_TARGET => opts.host) +end + +# defines methods corresponding to each interop test case. +class NamedActions + include Tech::Pubsub + + # Initializes NamedActions + # + # @param pub [Stub] a stub for accessing the publisher service + # @param sub [Stub] a stub for accessing the publisher service + # @param args [Args] provides access to the command line + def initialize(pub, sub, args) + @pub = pub + @sub = sub + @args = args + end + + # Removes the test topic if it exists + def remove_topic + name = test_topic_name + p "... removing Topic #{name}" + @pub.delete_topic(DeleteTopicRequest.new(topic: name)) + p "removed Topic: #{name} OK" + rescue GRPC::BadStatus => e + p "Could not delete a topics: rpc failed with '#{e}'" + end + + # Creates a test topic + def create_topic + name = test_topic_name + p "... creating Topic #{name}" + resp = @pub.create_topic(Topic.new(name: name)) + p "created Topic: #{resp.name} OK" + rescue GRPC::BadStatus => e + p "Could not create a topics: rpc failed with '#{e}'" + end + + # Lists topics in the project + def list_some_topics + p 'Listing topics' + p '-------------_' + list_project_topics.topic.each { |t| p t.name } + rescue GRPC::BadStatus => e + p "Could not list topics: rpc failed with '#{e}'" + end + + # Checks if a topics exists in a project + def check_exists + name = test_topic_name + p "... checking for topic #{name}" + exists = topic_exists?(name) + p "#{name} is a topic" if exists + p "#{name} is not a topic" unless exists + rescue GRPC::BadStatus => e + p "Could not check for a topics: rpc failed with '#{e}'" + end + + # Publishes some messages + def random_pub_sub + topic_name, sub_name = test_topic_name, test_sub_name + create_topic_if_needed(topic_name) + @sub.create_subscription(Subscription.new(name: sub_name, + topic: topic_name)) + msg_count = rand(10..30) + msg_count.times do |x| + msg = PubsubMessage.new(data: "message #{x}") + @pub.publish(PublishRequest.new(topic: topic_name, message: msg)) + end + p "Sent #{msg_count} messages to #{topic_name}, checking for them now." + batch = @sub.pull_batch(PullBatchRequest.new(subscription: sub_name, + max_events: msg_count)) + ack_ids = batch.pull_responses.map { |x| x.ack_id } + p "Got #{ack_ids.size} messages; acknowledging them.." + @sub.acknowledge(AcknowledgeRequest.new(subscription: sub_name, + ack_id: ack_ids)) + p "Test messages were acknowledged OK, deleting the subscription" + del_req = DeleteSubscriptionRequest.new(subscription: sub_name) + @sub.delete_subscription(del_req) + rescue GRPC::BadStatus => e + p "Could not do random pub sub: rpc failed with '#{e}'" + end + + private + + # test_topic_name is the topic name to use in this test. + def test_topic_name + unless @args.topic_name.nil? + return "/topics/#{@args.project_id}/#{@args.topic_name}" + end + now_text = Time.now.utc.strftime('%Y%m%d%H%M%S%L') + "/topics/#{@args.project_id}/#{ENV['USER']}-#{now_text}" + end + + # test_sub_name is the subscription name to use in this test. + def test_sub_name + unless @args.sub_name.nil? + return "/subscriptions/#{@args.project_id}/#{@args.sub_name}" + end + now_text = Time.now.utc.strftime('%Y%m%d%H%M%S%L') + "/subscriptions/#{@args.project_id}/#{ENV['USER']}-#{now_text}" + end + + # determines if the topic name exists + def topic_exists?(name) + topics = list_project_topics.topic.map { |t| t.name } + topics.include?(name) + end + + def create_topic_if_needed(name) + return if topic_exists?(name) + @pub.create_topic(Topic.new(name: name)) + end + + def list_project_topics + q = "cloud.googleapis.com/project in (/projects/#{@args.project_id})" + @pub.list_topics(ListTopicsRequest.new(query: q)) + end +end + +# Args is used to hold the command line info. +Args = Struct.new(:host, :port, :action, :project_id, :topic_name, + :sub_name) + +# validates the the command line options, returning them as an Arg. +def parse_args + args = Args.new('pubsub-staging.googleapis.com', + 443, 'list_some_topics', 'stoked-keyword-656') + OptionParser.new do |opts| + opts.on('--server_host SERVER_HOST', 'server hostname') do |v| + args.host = v + end + opts.on('--server_port SERVER_PORT', 'server port') do |v| + args.port = v + end + + # instance_methods(false) gives only the methods defined in that class. + scenes = NamedActions.instance_methods(false).map { |t| t.to_s } + scene_list = scenes.join(',') + opts.on("--action CODE", scenes, {}, 'pick a demo action', + " (#{scene_list})") do |v| + args.action = v + end + + # Set the remaining values. + %w(project_id topic_name sub_name).each do |o| + opts.on("--#{o} VALUE", "#{o}") do |v| + args[o] = v + end + end + end.parse! + _check_args(args) +end + +def _check_args(args) + %w(host port action).each do |a| + if args[a].nil? + raise OptionParser::MissingArgument.new("please specify --#{a}") + end + end + args +end + +def main + args = parse_args + pub, sub = publisher_stub(args), subscriber_stub(args) + NamedActions.new(pub, sub, args).method(args.action).call +end + +main diff --git a/src/ruby/bin/apis/tech/pubsub/proto/pubsub.rb b/src/ruby/bin/apis/tech/pubsub/proto/pubsub.rb new file mode 100644 index 00000000..d61431f1 --- /dev/null +++ b/src/ruby/bin/apis/tech/pubsub/proto/pubsub.rb @@ -0,0 +1,174 @@ +# Copyright 2015, 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. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: tech/pubsub/proto/pubsub.proto + +require 'google/protobuf' + +require 'google/protobuf/empty' +Google::Protobuf::DescriptorPool.generated_pool.build do + add_message "tech.pubsub.Topic" do + optional :name, :string, 1 + end + add_message "tech.pubsub.PubsubMessage" do + optional :data, :string, 1 + optional :message_id, :string, 3 + end + add_message "tech.pubsub.GetTopicRequest" do + optional :topic, :string, 1 + end + add_message "tech.pubsub.PublishRequest" do + optional :topic, :string, 1 + optional :message, :message, 2, "tech.pubsub.PubsubMessage" + end + add_message "tech.pubsub.PublishBatchRequest" do + optional :topic, :string, 1 + repeated :messages, :message, 2, "tech.pubsub.PubsubMessage" + end + add_message "tech.pubsub.PublishBatchResponse" do + repeated :message_ids, :string, 1 + end + add_message "tech.pubsub.ListTopicsRequest" do + optional :query, :string, 1 + optional :max_results, :int32, 2 + optional :page_token, :string, 3 + end + add_message "tech.pubsub.ListTopicsResponse" do + repeated :topic, :message, 1, "tech.pubsub.Topic" + optional :next_page_token, :string, 2 + end + add_message "tech.pubsub.DeleteTopicRequest" do + optional :topic, :string, 1 + end + add_message "tech.pubsub.Subscription" do + optional :name, :string, 1 + optional :topic, :string, 2 + optional :query, :string, 3 + optional :truncation_policy, :message, 4, "tech.pubsub.Subscription.TruncationPolicy" + optional :push_config, :message, 5, "tech.pubsub.PushConfig" + optional :ack_deadline_seconds, :int32, 6 + optional :garbage_collect_seconds, :int64, 7 + end + add_message "tech.pubsub.Subscription.TruncationPolicy" do + optional :max_bytes, :int64, 1 + optional :max_age_seconds, :int64, 2 + end + add_message "tech.pubsub.PushConfig" do + optional :push_endpoint, :string, 1 + end + add_message "tech.pubsub.PubsubEvent" do + optional :subscription, :string, 1 + optional :message, :message, 2, "tech.pubsub.PubsubMessage" + optional :truncated, :bool, 3 + optional :deleted, :bool, 4 + end + add_message "tech.pubsub.GetSubscriptionRequest" do + optional :subscription, :string, 1 + end + add_message "tech.pubsub.ListSubscriptionsRequest" do + optional :query, :string, 1 + optional :max_results, :int32, 3 + optional :page_token, :string, 4 + end + add_message "tech.pubsub.ListSubscriptionsResponse" do + repeated :subscription, :message, 1, "tech.pubsub.Subscription" + optional :next_page_token, :string, 2 + end + add_message "tech.pubsub.TruncateSubscriptionRequest" do + optional :subscription, :string, 1 + end + add_message "tech.pubsub.DeleteSubscriptionRequest" do + optional :subscription, :string, 1 + end + add_message "tech.pubsub.ModifyPushConfigRequest" do + optional :subscription, :string, 1 + optional :push_config, :message, 2, "tech.pubsub.PushConfig" + end + add_message "tech.pubsub.PullRequest" do + optional :subscription, :string, 1 + optional :return_immediately, :bool, 2 + end + add_message "tech.pubsub.PullResponse" do + optional :ack_id, :string, 1 + optional :pubsub_event, :message, 2, "tech.pubsub.PubsubEvent" + end + add_message "tech.pubsub.PullBatchRequest" do + optional :subscription, :string, 1 + optional :return_immediately, :bool, 2 + optional :max_events, :int32, 3 + end + add_message "tech.pubsub.PullBatchResponse" do + repeated :pull_responses, :message, 2, "tech.pubsub.PullResponse" + end + add_message "tech.pubsub.ModifyAckDeadlineRequest" do + optional :subscription, :string, 1 + optional :ack_id, :string, 2 + optional :ack_deadline_seconds, :int32, 3 + end + add_message "tech.pubsub.AcknowledgeRequest" do + optional :subscription, :string, 1 + repeated :ack_id, :string, 2 + end + add_message "tech.pubsub.NackRequest" do + optional :subscription, :string, 1 + repeated :ack_id, :string, 2 + end +end + +module Tech + module Pubsub + Topic = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.Topic").msgclass + PubsubMessage = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.PubsubMessage").msgclass + GetTopicRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.GetTopicRequest").msgclass + PublishRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.PublishRequest").msgclass + PublishBatchRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.PublishBatchRequest").msgclass + PublishBatchResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.PublishBatchResponse").msgclass + ListTopicsRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.ListTopicsRequest").msgclass + ListTopicsResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.ListTopicsResponse").msgclass + DeleteTopicRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.DeleteTopicRequest").msgclass + Subscription = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.Subscription").msgclass + Subscription::TruncationPolicy = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.Subscription.TruncationPolicy").msgclass + PushConfig = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.PushConfig").msgclass + PubsubEvent = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.PubsubEvent").msgclass + GetSubscriptionRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.GetSubscriptionRequest").msgclass + ListSubscriptionsRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.ListSubscriptionsRequest").msgclass + ListSubscriptionsResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.ListSubscriptionsResponse").msgclass + TruncateSubscriptionRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.TruncateSubscriptionRequest").msgclass + DeleteSubscriptionRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.DeleteSubscriptionRequest").msgclass + ModifyPushConfigRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.ModifyPushConfigRequest").msgclass + PullRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.PullRequest").msgclass + PullResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.PullResponse").msgclass + PullBatchRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.PullBatchRequest").msgclass + PullBatchResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.PullBatchResponse").msgclass + ModifyAckDeadlineRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.ModifyAckDeadlineRequest").msgclass + AcknowledgeRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.AcknowledgeRequest").msgclass + NackRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.NackRequest").msgclass + end +end diff --git a/src/ruby/bin/apis/tech/pubsub/proto/pubsub_services.rb b/src/ruby/bin/apis/tech/pubsub/proto/pubsub_services.rb new file mode 100644 index 00000000..43c52656 --- /dev/null +++ b/src/ruby/bin/apis/tech/pubsub/proto/pubsub_services.rb @@ -0,0 +1,103 @@ +# Copyright 2015, 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. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# Source: tech/pubsub/proto/pubsub.proto for package 'tech.pubsub' + +require 'grpc' +require 'google/protobuf/empty' +require 'tech/pubsub/proto/pubsub' + +module Tech + module Pubsub + module PublisherService + + # TODO: add proto service documentation here + class Service + + include GRPC::GenericService + + self.marshal_class_method = :encode + self.unmarshal_class_method = :decode + self.service_name = 'tech.pubsub.PublisherService' + + rpc :CreateTopic, Topic, Topic + rpc :Publish, PublishRequest, Google::Protobuf::Empty + rpc :PublishBatch, PublishBatchRequest, PublishBatchResponse + rpc :GetTopic, GetTopicRequest, Topic + rpc :ListTopics, ListTopicsRequest, ListTopicsResponse + rpc :DeleteTopic, DeleteTopicRequest, Google::Protobuf::Empty + end + + Stub = Service.rpc_stub_class + end + module SubscriberService + + # TODO: add proto service documentation here + class Service + + include GRPC::GenericService + + self.marshal_class_method = :encode + self.unmarshal_class_method = :decode + self.service_name = 'tech.pubsub.SubscriberService' + + rpc :CreateSubscription, Subscription, Subscription + rpc :GetSubscription, GetSubscriptionRequest, Subscription + rpc :ListSubscriptions, ListSubscriptionsRequest, ListSubscriptionsResponse + rpc :DeleteSubscription, DeleteSubscriptionRequest, Google::Protobuf::Empty + rpc :TruncateSubscription, TruncateSubscriptionRequest, Google::Protobuf::Empty + rpc :ModifyPushConfig, ModifyPushConfigRequest, Google::Protobuf::Empty + rpc :Pull, PullRequest, PullResponse + rpc :PullBatch, PullBatchRequest, PullBatchResponse + rpc :ModifyAckDeadline, ModifyAckDeadlineRequest, Google::Protobuf::Empty + rpc :Acknowledge, AcknowledgeRequest, Google::Protobuf::Empty + rpc :Nack, NackRequest, Google::Protobuf::Empty + end + + Stub = Service.rpc_stub_class + end + module PushEndpointService + + # TODO: add proto service documentation here + class Service + + include GRPC::GenericService + + self.marshal_class_method = :encode + self.unmarshal_class_method = :decode + self.service_name = 'tech.pubsub.PushEndpointService' + + rpc :HandlePubsubEvent, PubsubEvent, Google::Protobuf::Empty + end + + Stub = Service.rpc_stub_class + end + end +end diff --git a/src/ruby/bin/grpc_ruby_interop_client b/src/ruby/bin/grpc_ruby_interop_client new file mode 100755 index 00000000..e79fd33a --- /dev/null +++ b/src/ruby/bin/grpc_ruby_interop_client @@ -0,0 +1,33 @@ +#!/usr/bin/env ruby + +# Copyright 2015, 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. + +# Provides a gem binary entry point for the interop client. +require 'test/client' diff --git a/src/ruby/bin/grpc_ruby_interop_server b/src/ruby/bin/grpc_ruby_interop_server new file mode 100755 index 00000000..656a5f7c --- /dev/null +++ b/src/ruby/bin/grpc_ruby_interop_server @@ -0,0 +1,33 @@ +#!/usr/bin/env ruby + +# Copyright 2015, 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. + +# Provides a gem binary entry point for the interop server +require 'test/server' diff --git a/src/ruby/bin/interop/interop_client.rb b/src/ruby/bin/interop/interop_client.rb new file mode 100755 index 00000000..239083f3 --- /dev/null +++ b/src/ruby/bin/interop/interop_client.rb @@ -0,0 +1,51 @@ +#!/usr/bin/env ruby + +# Copyright 2015, 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. + +# ####################################################################### +# DEPRECATED: The behaviour in this file has been moved to pb/test/client.rb +# +# This file remains to support existing tools and scripts that use it. +# ###################################################################### +# +# interop_client is a testing tool that accesses a gRPC interop testing +# server and runs a test on it. +# +# Helps validate interoperation b/w different gRPC implementations. +# +# Usage: $ path/to/interop_client.rb --server_host= \ +# --server_port= \ +# --test_case= + +this_dir = File.expand_path(File.dirname(__FILE__)) +pb_dir = File.join(File.dirname(File.dirname(this_dir)), 'pb') +$LOAD_PATH.unshift(pb_dir) unless $LOAD_PATH.include?(pb_dir) + +require 'test/client' diff --git a/src/ruby/bin/interop/interop_server.rb b/src/ruby/bin/interop/interop_server.rb new file mode 100755 index 00000000..c6b0d00e --- /dev/null +++ b/src/ruby/bin/interop/interop_server.rb @@ -0,0 +1,50 @@ +#!/usr/bin/env ruby + +# Copyright 2015, 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. + +# ####################################################################### +# DEPRECATED: The behaviour in this file has been moved to pb/test/server.rb +# +# This file remains to support existing tools and scripts that use it. +# ###################################################################### +# +# interop_server is a Testing app that runs a gRPC interop testing server. +# +# It helps validate interoperation b/w gRPC in different environments +# +# Helps validate interoperation b/w different gRPC implementations. +# +# Usage: $ path/to/interop_server.rb --port + +this_dir = File.expand_path(File.dirname(__FILE__)) +pb_dir = File.join(File.dirname(File.dirname(this_dir)), 'pb') +$LOAD_PATH.unshift(pb_dir) unless $LOAD_PATH.include?(pb_dir) + +require 'test/server' diff --git a/src/ruby/bin/math.proto b/src/ruby/bin/math.proto new file mode 100755 index 00000000..311e148c --- /dev/null +++ b/src/ruby/bin/math.proto @@ -0,0 +1,80 @@ + +// Copyright 2015, 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. + +syntax = "proto3"; + +package math; + +message DivArgs { + int64 dividend = 1; + int64 divisor = 2; +} + +message DivReply { + int64 quotient = 1; + int64 remainder = 2; +} + +message FibArgs { + int64 limit = 1; +} + +message Num { + int64 num = 1; +} + +message FibReply { + int64 count = 1; +} + +service Math { + // Div divides args.dividend by args.divisor and returns the quotient and + // remainder. + rpc Div (DivArgs) returns (DivReply) { + } + + // DivMany accepts an arbitrary number of division args from the client stream + // and sends back the results in the reply stream. The stream continues until + // the client closes its end; the server does the same after sending all the + // replies. The stream ends immediately if either end aborts. + rpc DivMany (stream DivArgs) returns (stream DivReply) { + } + + // Fib generates numbers in the Fibonacci sequence. If args.limit > 0, Fib + // generates up to limit numbers; otherwise it continues until the call is + // canceled. Unlike Fib above, Fib has no final FibReply. + rpc Fib (FibArgs) returns (stream Num) { + } + + // Sum sums a stream of numbers, returning the final result once the stream + // is closed. + rpc Sum (stream Num) returns (Num) { + } +} diff --git a/src/ruby/bin/math.rb b/src/ruby/bin/math.rb new file mode 100755 index 00000000..323993ed --- /dev/null +++ b/src/ruby/bin/math.rb @@ -0,0 +1,61 @@ +# Copyright 2015, 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. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: math.proto + +require 'google/protobuf' + +Google::Protobuf::DescriptorPool.generated_pool.build do + add_message "math.DivArgs" do + optional :dividend, :int64, 1 + optional :divisor, :int64, 2 + end + add_message "math.DivReply" do + optional :quotient, :int64, 1 + optional :remainder, :int64, 2 + end + add_message "math.FibArgs" do + optional :limit, :int64, 1 + end + add_message "math.Num" do + optional :num, :int64, 1 + end + add_message "math.FibReply" do + optional :count, :int64, 1 + end +end + +module Math + DivArgs = Google::Protobuf::DescriptorPool.generated_pool.lookup("math.DivArgs").msgclass + DivReply = Google::Protobuf::DescriptorPool.generated_pool.lookup("math.DivReply").msgclass + FibArgs = Google::Protobuf::DescriptorPool.generated_pool.lookup("math.FibArgs").msgclass + Num = Google::Protobuf::DescriptorPool.generated_pool.lookup("math.Num").msgclass + FibReply = Google::Protobuf::DescriptorPool.generated_pool.lookup("math.FibReply").msgclass +end diff --git a/src/ruby/bin/math_client.rb b/src/ruby/bin/math_client.rb new file mode 100755 index 00000000..0ebd26f7 --- /dev/null +++ b/src/ruby/bin/math_client.rb @@ -0,0 +1,147 @@ +#!/usr/bin/env ruby + +# Copyright 2015, 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. + +# Sample app that accesses a Calc service running on a Ruby gRPC server and +# helps validate RpcServer as a gRPC server using proto2 serialization. +# +# Usage: $ path/to/math_client.rb + +this_dir = File.expand_path(File.dirname(__FILE__)) +lib_dir = File.join(File.dirname(this_dir), 'lib') +$LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir) +$LOAD_PATH.unshift(this_dir) unless $LOAD_PATH.include?(this_dir) + +require 'grpc' +require 'math_services' +require 'optparse' + +include GRPC::Core::TimeConsts + +def do_div(stub) + GRPC.logger.info('request_response') + GRPC.logger.info('----------------') + req = Math::DivArgs.new(dividend: 7, divisor: 3) + GRPC.logger.info("div(7/3): req=#{req.inspect}") + resp = stub.div(req, timeout: INFINITE_FUTURE) + GRPC.logger.info("Answer: #{resp.inspect}") + GRPC.logger.info('----------------') +end + +def do_sum(stub) + # to make client streaming requests, pass an enumerable of the inputs + GRPC.logger.info('client_streamer') + GRPC.logger.info('---------------') + reqs = [1, 2, 3, 4, 5].map { |x| Math::Num.new(num: x) } + GRPC.logger.info("sum(1, 2, 3, 4, 5): reqs=#{reqs.inspect}") + resp = stub.sum(reqs) # reqs.is_a?(Enumerable) + GRPC.logger.info("Answer: #{resp.inspect}") + GRPC.logger.info('---------------') +end + +def do_fib(stub) + GRPC.logger.info('server_streamer') + GRPC.logger.info('----------------') + req = Math::FibArgs.new(limit: 11) + GRPC.logger.info("fib(11): req=#{req.inspect}") + resp = stub.fib(req, timeout: INFINITE_FUTURE) + resp.each do |r| + GRPC.logger.info("Answer: #{r.inspect}") + end + GRPC.logger.info('----------------') +end + +def do_div_many(stub) + GRPC.logger.info('bidi_streamer') + GRPC.logger.info('-------------') + reqs = [] + reqs << Math::DivArgs.new(dividend: 7, divisor: 3) + reqs << Math::DivArgs.new(dividend: 5, divisor: 2) + reqs << Math::DivArgs.new(dividend: 7, divisor: 2) + GRPC.logger.info("div(7/3), div(5/2), div(7/2): reqs=#{reqs.inspect}") + resp = stub.div_many(reqs, timeout: INFINITE_FUTURE) + resp.each do |r| + GRPC.logger.info("Answer: #{r.inspect}") + end + GRPC.logger.info('----------------') +end + +def load_test_certs + this_dir = File.expand_path(File.dirname(__FILE__)) + data_dir = File.join(File.dirname(this_dir), 'spec/testdata') + files = ['ca.pem', 'server1.key', 'server1.pem'] + files.map { |f| File.open(File.join(data_dir, f)).read } +end + +def test_creds + certs = load_test_certs + GRPC::Core::Credentials.new(certs[0]) +end + +def main + options = { + 'host' => 'localhost:7071', + 'secure' => false + } + OptionParser.new do |opts| + opts.banner = 'Usage: [--host :] [--secure|-s]' + opts.on('--host HOST', ':') do |v| + options['host'] = v + end + opts.on('-s', '--secure', 'access using test creds') do |v| + options['secure'] = v + end + end.parse! + + # The Math::Math:: module occurs because the service has the same name as its + # package. That practice should be avoided by defining real services. + + p options + if options['secure'] + stub_opts = { + :creds => test_creds, + GRPC::Core::Channel::SSL_TARGET => 'foo.test.google.fr' + } + p stub_opts + p options['host'] + stub = Math::Math::Stub.new(options['host'], **stub_opts) + GRPC.logger.info("... connecting securely on #{options['host']}") + else + stub = Math::Math::Stub.new(options['host']) + GRPC.logger.info("... connecting insecurely on #{options['host']}") + end + + do_div(stub) + do_sum(stub) + do_fib(stub) + do_div_many(stub) +end + +main diff --git a/src/ruby/bin/math_server.rb b/src/ruby/bin/math_server.rb new file mode 100755 index 00000000..562f1973 --- /dev/null +++ b/src/ruby/bin/math_server.rb @@ -0,0 +1,206 @@ +#!/usr/bin/env ruby + +# Copyright 2015, 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. + +# Sample gRPC Ruby server that implements the Math::Calc service and helps +# validate GRPC::RpcServer as GRPC implementation using proto2 serialization. +# +# Usage: $ path/to/math_server.rb + +this_dir = File.expand_path(File.dirname(__FILE__)) +lib_dir = File.join(File.dirname(this_dir), 'lib') +$LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir) +$LOAD_PATH.unshift(this_dir) unless $LOAD_PATH.include?(this_dir) + +require 'forwardable' +require 'grpc' +require 'logger' +require 'math_services' +require 'optparse' + +# RubyLogger defines a logger for gRPC based on the standard ruby logger. +module RubyLogger + def logger + LOGGER + end + + LOGGER = Logger.new(STDOUT) +end + +# GRPC is the general RPC module +module GRPC + # Inject the noop #logger if no module-level logger method has been injected. + extend RubyLogger +end + +# Holds state for a fibonacci series +class Fibber + def initialize(limit) + fail "bad limit: got #{limit}, want limit > 0" if limit < 1 + @limit = limit + end + + def generator + return enum_for(:generator) unless block_given? + idx, current, previous = 0, 1, 1 + until idx == @limit + if idx.zero? || idx == 1 + yield Math::Num.new(num: 1) + idx += 1 + next + end + tmp = current + current = previous + current + previous = tmp + yield Math::Num.new(num: current) + idx += 1 + end + end +end + +# A EnumeratorQueue wraps a Queue to yield the items added to it. +class EnumeratorQueue + extend Forwardable + def_delegators :@q, :push + + def initialize(sentinel) + @q = Queue.new + @sentinel = sentinel + end + + def each_item + return enum_for(:each_item) unless block_given? + loop do + r = @q.pop + break if r.equal?(@sentinel) + fail r if r.is_a? Exception + yield r + end + end +end + +# The Math::Math:: module occurs because the service has the same name as its +# package. That practice should be avoided by defining real services. +class Calculator < Math::Math::Service + def div(div_args, _call) + if div_args.divisor.zero? + # To send non-OK status handlers raise a StatusError with the code and + # and detail they want sent as a Status. + fail GRPC::StatusError.new(GRPC::Status::INVALID_ARGUMENT, + 'divisor cannot be 0') + end + + Math::DivReply.new(quotient: div_args.dividend / div_args.divisor, + remainder: div_args.dividend % div_args.divisor) + end + + def sum(call) + # the requests are accesible as the Enumerator call#each_request + nums = call.each_remote_read.collect(&:num) + sum = nums.inject { |s, x| s + x } + Math::Num.new(num: sum) + end + + def fib(fib_args, _call) + if fib_args.limit < 1 + fail StatusError.new(Status::INVALID_ARGUMENT, 'limit must be >= 0') + end + + # return an Enumerator of Nums + Fibber.new(fib_args.limit).generator + # just return the generator, GRPC::GenericServer sends each actual response + end + + def div_many(requests) + # requests is an lazy Enumerator of the requests sent by the client. + q = EnumeratorQueue.new(self) + t = Thread.new do + begin + requests.each do |req| + GRPC.logger.info("read #{req.inspect}") + resp = Math::DivReply.new(quotient: req.dividend / req.divisor, + remainder: req.dividend % req.divisor) + q.push(resp) + Thread.pass # let the internal Bidi threads run + end + GRPC.logger.info('finished reads') + q.push(self) + rescue StandardError => e + q.push(e) # share the exception with the enumerator + raise e + end + end + t.priority = -2 # hint that the div_many thread should not be favoured + q.each_item + end +end + +def load_test_certs + this_dir = File.expand_path(File.dirname(__FILE__)) + data_dir = File.join(File.dirname(this_dir), 'spec/testdata') + files = ['ca.pem', 'server1.key', 'server1.pem'] + files.map { |f| File.open(File.join(data_dir, f)).read } +end + +def test_server_creds + certs = load_test_certs + GRPC::Core::ServerCredentials.new( + nil, [{ private_key: certs[1], cert_chain: certs[2] }], false) +end + +def main + options = { + 'host' => 'localhost:7071', + 'secure' => false + } + OptionParser.new do |opts| + opts.banner = 'Usage: [--host :] [--secure|-s]' + opts.on('--host HOST', ':') do |v| + options['host'] = v + end + opts.on('-s', '--secure', 'access using test creds') do |v| + options['secure'] = v + end + end.parse! + + s = GRPC::RpcServer.new + if options['secure'] + s.add_http2_port(options['host'], test_server_creds) + GRPC.logger.info("... running securely on #{options['host']}") + else + s.add_http2_port(options['host']) + GRPC.logger.info("... running insecurely on #{options['host']}") + end + + s.handle(Calculator) + s.run_till_terminated +end + +main diff --git a/src/ruby/bin/math_services.rb b/src/ruby/bin/math_services.rb new file mode 100755 index 00000000..cf58a539 --- /dev/null +++ b/src/ruby/bin/math_services.rb @@ -0,0 +1,56 @@ +# Copyright 2015, 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. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# Source: math.proto for package 'math' + +require 'grpc' +require 'math' + +module Math + module Math + + # TODO: add proto service documentation here + class Service + + include GRPC::GenericService + + self.marshal_class_method = :encode + self.unmarshal_class_method = :decode + self.service_name = 'math.Math' + + rpc :Div, DivArgs, DivReply + rpc :DivMany, stream(DivArgs), stream(DivReply) + rpc :Fib, FibArgs, stream(Num) + rpc :Sum, stream(Num), Num + end + + Stub = Service.rpc_stub_class + end +end diff --git a/src/ruby/bin/noproto_client.rb b/src/ruby/bin/noproto_client.rb new file mode 100755 index 00000000..390a9c59 --- /dev/null +++ b/src/ruby/bin/noproto_client.rb @@ -0,0 +1,108 @@ +#!/usr/bin/env ruby + +# Copyright 2015, 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. + +# Sample app that helps validate RpcServer without protobuf serialization. +# +# Usage: $ ruby -S path/to/noproto_client.rb + +this_dir = File.expand_path(File.dirname(__FILE__)) +lib_dir = File.join(File.dirname(this_dir), 'lib') +$LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir) + +require 'grpc' +require 'optparse' + +# a simple non-protobuf message class. +class NoProtoMsg + def self.marshal(_o) + '' + end + + def self.unmarshal(_o) + NoProtoMsg.new + end +end + +# service the uses the non-protobuf message class. +class NoProtoService + include GRPC::GenericService + rpc :AnRPC, NoProtoMsg, NoProtoMsg +end + +NoProtoStub = NoProtoService.rpc_stub_class + +def load_test_certs + this_dir = File.expand_path(File.dirname(__FILE__)) + data_dir = File.join(File.dirname(this_dir), 'spec/testdata') + files = ['ca.pem', 'server1.key', 'server1.pem'] + files.map { |f| File.open(File.join(data_dir, f)).read } +end + +def test_creds + certs = load_test_certs + GRPC::Core::Credentials.new(certs[0]) +end + +def main + options = { + 'host' => 'localhost:7071', + 'secure' => false + } + OptionParser.new do |opts| + opts.banner = 'Usage: [--host :] [--secure|-s]' + opts.on('--host HOST', ':') do |v| + options['host'] = v + end + opts.on('-s', '--secure', 'access using test creds') do |v| + options['secure'] = v + end + end.parse! + + if options['secure'] + stub_opts = { + :creds => test_creds, + GRPC::Core::Channel::SSL_TARGET => 'foo.test.google.fr' + } + p stub_opts + p options['host'] + stub = NoProtoStub.new(options['host'], **stub_opts) + GRPC.logger.info("... connecting securely on #{options['host']}") + else + stub = NoProtoStub.new(options['host']) + GRPC.logger.info("... connecting insecurely on #{options['host']}") + end + + GRPC.logger.info('sending a NoProto rpc') + resp = stub.an_rpc(NoProtoMsg.new) + GRPC.logger.info("got a response: #{resp}") +end + +main diff --git a/src/ruby/bin/noproto_server.rb b/src/ruby/bin/noproto_server.rb new file mode 100755 index 00000000..72a57620 --- /dev/null +++ b/src/ruby/bin/noproto_server.rb @@ -0,0 +1,112 @@ +#!/usr/bin/env ruby + +# Copyright 2015, 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. + +# Sample app that helps validate RpcServer without protobuf serialization. +# +# Usage: $ path/to/noproto_server.rb + +this_dir = File.expand_path(File.dirname(__FILE__)) +lib_dir = File.join(File.dirname(this_dir), 'lib') +$LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir) + +require 'grpc' +require 'optparse' + +# a simple non-protobuf message class. +class NoProtoMsg + def self.marshal(_o) + '' + end + + def self.unmarshal(_o) + NoProtoMsg.new + end +end + +# service the uses the non-protobuf message class. +class NoProtoService + include GRPC::GenericService + rpc :AnRPC, NoProtoMsg, NoProtoMsg +end + +# an implementation of the non-protobuf service. +class NoProto < NoProtoService + def initialize(_default_var = 'ignored') + end + + def an_rpc(req, _call) + GRPC.logger.info('echo service received a request') + req + end +end + +def load_test_certs + this_dir = File.expand_path(File.dirname(__FILE__)) + data_dir = File.join(File.dirname(this_dir), 'spec/testdata') + files = ['ca.pem', 'server1.key', 'server1.pem'] + files.map { |f| File.open(File.join(data_dir, f)).read } +end + +def test_server_creds + certs = load_test_certs + GRPC::Core::ServerCredentials.new( + nil, [{ private_key: certs[1], cert_chain: certs[2] }], false) +end + +def main + options = { + 'host' => 'localhost:9090', + 'secure' => false + } + OptionParser.new do |opts| + opts.banner = 'Usage: [--host :] [--secure|-s]' + opts.on('--host HOST', ':') do |v| + options['host'] = v + end + opts.on('-s', '--secure', 'access using test creds') do |v| + options['secure'] = v + end + end.parse! + + s = GRPC::RpcServer.new + if options['secure'] + s.add_http2_port(options['host'], test_server_creds) + GRPC.logger.info("... running securely on #{options['host']}") + else + s.add_http2_port(options['host']) + GRPC.logger.info("... running insecurely on #{options['host']}") + end + + s.handle(NoProto) + s.run_till_terminated +end + +main diff --git a/src/ruby/ext/grpc/extconf.rb b/src/ruby/ext/grpc/extconf.rb new file mode 100644 index 00000000..7972272e --- /dev/null +++ b/src/ruby/ext/grpc/extconf.rb @@ -0,0 +1,108 @@ +# Copyright 2015, 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. + +require 'mkmf' + +LIBDIR = RbConfig::CONFIG['libdir'] +INCLUDEDIR = RbConfig::CONFIG['includedir'] + +HEADER_DIRS = [ + # Search /opt/local (Mac source install) + '/opt/local/include', + + # Search /usr/local (Source install) + '/usr/local/include', + + # Check the ruby install locations + INCLUDEDIR +] + +LIB_DIRS = [ + # Search /opt/local (Mac source install) + '/opt/local/lib', + + # Search /usr/local (Source install) + '/usr/local/lib', + + # Check the ruby install locations + LIBDIR +] + +def check_grpc_root + grpc_root = ENV['GRPC_ROOT'] + if grpc_root.nil? + r = File.expand_path(File.join(File.dirname(__FILE__), '../../../..')) + grpc_root = r if File.exist?(File.join(r, 'include/grpc/grpc.h')) + end + grpc_root +end + +grpc_pkg_config = system('pkg-config --exists grpc') + +if grpc_pkg_config + $CFLAGS << ' ' + `pkg-config --static --cflags grpc`.strip + ' ' + $LDFLAGS << ' ' + `pkg-config --static --libs grpc`.strip + ' ' +else + dir_config('grpc', HEADER_DIRS, LIB_DIRS) + fail 'libdl not found' unless have_library('dl', 'dlopen') + fail 'zlib not found' unless have_library('z', 'inflate') + begin + fail 'Fail' unless have_library('gpr', 'gpr_now') + fail 'Fail' unless have_library('grpc', 'grpc_channel_destroy') + rescue + # Check to see if GRPC_ROOT is defined or available + grpc_root = check_grpc_root + + # Stop if there is still no grpc_root + exit 1 if grpc_root.nil? + + grpc_config = ENV['GRPC_CONFIG'] || 'opt' + if ENV.key?('GRPC_LIB_DIR') + grpc_lib_dir = File.join(grpc_root, ENV['GRPC_LIB_DIR']) + else + grpc_lib_dir = File.join(File.join(grpc_root, 'libs'), grpc_config) + end + unless File.exist?(File.join(grpc_lib_dir, 'libgrpc.a')) + print "Building internal gRPC\n" + system("make -C #{grpc_root} static_c CONFIG=#{grpc_config}") + end + $CFLAGS << ' -I' + File.join(grpc_root, 'include') + $LDFLAGS << ' -L' + grpc_lib_dir + raise 'gpr not found' unless have_library('gpr', 'gpr_now') + raise 'grpc not found' unless have_library('grpc', 'grpc_channel_destroy') + end +end + +$CFLAGS << ' -std=c99 ' +$CFLAGS << ' -Wall ' +$CFLAGS << ' -Wextra ' +$CFLAGS << ' -pedantic ' +$CFLAGS << ' -Werror ' + +create_makefile('grpc/grpc') diff --git a/src/ruby/ext/grpc/rb_byte_buffer.c b/src/ruby/ext/grpc/rb_byte_buffer.c new file mode 100644 index 00000000..0aa34c84 --- /dev/null +++ b/src/ruby/ext/grpc/rb_byte_buffer.c @@ -0,0 +1,68 @@ +/* + * + * Copyright 2015, 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 "rb_byte_buffer.h" + +#include + +#include +#include +#include +#include "rb_grpc.h" + +grpc_byte_buffer* grpc_rb_s_to_byte_buffer(char *string, size_t length) { + gpr_slice slice = gpr_slice_from_copied_buffer(string, length); + grpc_byte_buffer *buffer = grpc_raw_byte_buffer_create(&slice, 1); + gpr_slice_unref(slice); + return buffer; +} + +VALUE grpc_rb_byte_buffer_to_s(grpc_byte_buffer *buffer) { + size_t length = 0; + char *string = NULL; + size_t offset = 0; + grpc_byte_buffer_reader reader; + gpr_slice next; + if (buffer == NULL) { + return Qnil; + + } + length = grpc_byte_buffer_length(buffer); + string = xmalloc(length + 1); + grpc_byte_buffer_reader_init(&reader, buffer); + while (grpc_byte_buffer_reader_next(&reader, &next) != 0) { + memcpy(string + offset, GPR_SLICE_START_PTR(next), GPR_SLICE_LENGTH(next)); + offset += GPR_SLICE_LENGTH(next); + } + return rb_str_new(string, length); +} diff --git a/src/ruby/ext/grpc/rb_byte_buffer.h b/src/ruby/ext/grpc/rb_byte_buffer.h new file mode 100644 index 00000000..c7ddd764 --- /dev/null +++ b/src/ruby/ext/grpc/rb_byte_buffer.h @@ -0,0 +1,47 @@ +/* + * + * Copyright 2015, 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_RB_BYTE_BUFFER_H_ +#define GRPC_RB_BYTE_BUFFER_H_ + +#include + +#include + +/* Converts a char* with a length to a grpc_byte_buffer */ +grpc_byte_buffer *grpc_rb_s_to_byte_buffer(char *string, size_t length); + +/* Converts a grpc_byte_buffer to a ruby string */ +VALUE grpc_rb_byte_buffer_to_s(grpc_byte_buffer *buffer); + +#endif /* GRPC_RB_BYTE_BUFFER_H_ */ diff --git a/src/ruby/ext/grpc/rb_call.c b/src/ruby/ext/grpc/rb_call.c new file mode 100644 index 00000000..6b5beb6f --- /dev/null +++ b/src/ruby/ext/grpc/rb_call.c @@ -0,0 +1,843 @@ +/* + * + * Copyright 2015, 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 "rb_call.h" + +#include + +#include +#include + +#include "rb_byte_buffer.h" +#include "rb_completion_queue.h" +#include "rb_grpc.h" + +/* grpc_rb_cCall is the Call class whose instances proxy grpc_call. */ +static VALUE grpc_rb_cCall; + +/* grpc_rb_eCallError is the ruby class of the exception thrown during call + operations; */ +VALUE grpc_rb_eCallError = Qnil; + +/* grpc_rb_eOutOfTime is the ruby class of the exception thrown to indicate + a timeout. */ +static VALUE grpc_rb_eOutOfTime = Qnil; + +/* grpc_rb_sBatchResult is struct class used to hold the results of a batch + * call. */ +static VALUE grpc_rb_sBatchResult; + +/* grpc_rb_cMdAry is the MetadataArray class whose instances proxy + * grpc_metadata_array. */ +static VALUE grpc_rb_cMdAry; + +/* id_cq is the name of the hidden ivar that preserves a reference to a + * completion queue */ +static ID id_cq; + +/* id_flags is the name of the hidden ivar that preserves the value of + * the flags used to create metadata from a Hash */ +static ID id_flags; + +/* id_input_md is the name of the hidden ivar that preserves the hash used to + * create metadata, so that references to the strings it contains last as long + * as the call the metadata is added to. */ +static ID id_input_md; + +/* id_metadata is name of the attribute used to access the metadata hash + * received by the call and subsequently saved on it. */ +static ID id_metadata; + +/* id_status is name of the attribute used to access the status object + * received by the call and subsequently saved on it. */ +static ID id_status; + +/* id_write_flag is name of the attribute used to access the write_flag + * saved on the call. */ +static ID id_write_flag; + +/* sym_* are the symbol for attributes of grpc_rb_sBatchResult. */ +static VALUE sym_send_message; +static VALUE sym_send_metadata; +static VALUE sym_send_close; +static VALUE sym_send_status; +static VALUE sym_message; +static VALUE sym_status; +static VALUE sym_cancelled; + +/* hash_all_calls is a hash of Call address -> reference count that is used to + * track the creation and destruction of rb_call instances. + */ +static VALUE hash_all_calls; + +/* Destroys a Call. */ +static void grpc_rb_call_destroy(void *p) { + grpc_call *call = NULL; + VALUE ref_count = Qnil; + if (p == NULL) { + return; + }; + call = (grpc_call *)p; + + ref_count = rb_hash_aref(hash_all_calls, OFFT2NUM((VALUE)call)); + if (ref_count == Qnil) { + return; /* No longer in the hash, so already deleted */ + } else if (NUM2UINT(ref_count) == 1) { + rb_hash_delete(hash_all_calls, OFFT2NUM((VALUE)call)); + grpc_call_destroy(call); + } else { + rb_hash_aset(hash_all_calls, OFFT2NUM((VALUE)call), + UINT2NUM(NUM2UINT(ref_count) - 1)); + } +} + +static size_t md_ary_datasize(const void *p) { + const grpc_metadata_array *const ary = (grpc_metadata_array *)p; + size_t i, datasize = sizeof(grpc_metadata_array); + for (i = 0; i < ary->count; ++i) { + const grpc_metadata *const md = &ary->metadata[i]; + datasize += strlen(md->key); + datasize += md->value_length; + } + datasize += ary->capacity * sizeof(grpc_metadata); + return datasize; +} + +static const rb_data_type_t grpc_rb_md_ary_data_type = { + "grpc_metadata_array", + {GRPC_RB_GC_NOT_MARKED, GRPC_RB_GC_DONT_FREE, md_ary_datasize, + {NULL, NULL}}, + NULL, + NULL, + 0}; + +/* Describes grpc_call struct for RTypedData */ +static const rb_data_type_t grpc_call_data_type = { + "grpc_call", + {GRPC_RB_GC_NOT_MARKED, grpc_rb_call_destroy, GRPC_RB_MEMSIZE_UNAVAILABLE, + {NULL, NULL}}, + NULL, + NULL, + /* it is unsafe to specify RUBY_TYPED_FREE_IMMEDIATELY because + * grpc_rb_call_destroy + * touches a hash object. + * TODO(yugui) Directly use st_table and call the free function earlier? + */ + 0}; + +/* Error code details is a hash containing text strings describing errors */ +VALUE rb_error_code_details; + +/* Obtains the error detail string for given error code */ +const char *grpc_call_error_detail_of(grpc_call_error err) { + VALUE detail_ref = rb_hash_aref(rb_error_code_details, UINT2NUM(err)); + const char *detail = "unknown error code!"; + if (detail_ref != Qnil) { + detail = StringValueCStr(detail_ref); + } + return detail; +} + +/* Called by clients to cancel an RPC on the server. + Can be called multiple times, from any thread. */ +static VALUE grpc_rb_call_cancel(VALUE self) { + grpc_call *call = NULL; + grpc_call_error err; + TypedData_Get_Struct(self, grpc_call, &grpc_call_data_type, call); + err = grpc_call_cancel(call, NULL); + if (err != GRPC_CALL_OK) { + rb_raise(grpc_rb_eCallError, "cancel failed: %s (code=%d)", + grpc_call_error_detail_of(err), err); + } + + return Qnil; +} + +/* Called to obtain the peer that this call is connected to. */ +static VALUE grpc_rb_call_get_peer(VALUE self) { + VALUE res = Qnil; + grpc_call *call = NULL; + char *peer = NULL; + TypedData_Get_Struct(self, grpc_call, &grpc_call_data_type, call); + peer = grpc_call_get_peer(call); + res = rb_str_new2(peer); + gpr_free(peer); + + return res; +} + +/* + call-seq: + status = call.status + + Gets the status object saved the call. */ +static VALUE grpc_rb_call_get_status(VALUE self) { + return rb_ivar_get(self, id_status); +} + +/* + call-seq: + call.status = status + + Saves a status object on the call. */ +static VALUE grpc_rb_call_set_status(VALUE self, VALUE status) { + if (!NIL_P(status) && rb_obj_class(status) != grpc_rb_sStatus) { + rb_raise(rb_eTypeError, "bad status: got:<%s> want: ", + rb_obj_classname(status)); + return Qnil; + } + + return rb_ivar_set(self, id_status, status); +} + +/* + call-seq: + metadata = call.metadata + + Gets the metadata object saved the call. */ +static VALUE grpc_rb_call_get_metadata(VALUE self) { + return rb_ivar_get(self, id_metadata); +} + +/* + call-seq: + call.metadata = metadata + + Saves the metadata hash on the call. */ +static VALUE grpc_rb_call_set_metadata(VALUE self, VALUE metadata) { + if (!NIL_P(metadata) && TYPE(metadata) != T_HASH) { + rb_raise(rb_eTypeError, "bad metadata: got:<%s> want: ", + rb_obj_classname(metadata)); + return Qnil; + } + + return rb_ivar_set(self, id_metadata, metadata); +} + +/* + call-seq: + write_flag = call.write_flag + + Gets the write_flag value saved the call. */ +static VALUE grpc_rb_call_get_write_flag(VALUE self) { + return rb_ivar_get(self, id_write_flag); +} + +/* + call-seq: + call.write_flag = write_flag + + Saves the write_flag on the call. */ +static VALUE grpc_rb_call_set_write_flag(VALUE self, VALUE write_flag) { + if (!NIL_P(write_flag) && TYPE(write_flag) != T_FIXNUM) { + rb_raise(rb_eTypeError, "bad write_flag: got:<%s> want: ", + rb_obj_classname(write_flag)); + return Qnil; + } + + return rb_ivar_set(self, id_write_flag, write_flag); +} + +/* grpc_rb_md_ary_fill_hash_cb is the hash iteration callback used + to fill grpc_metadata_array. + + it's capacity should have been computed via a prior call to + grpc_rb_md_ary_fill_hash_cb +*/ +static int grpc_rb_md_ary_fill_hash_cb(VALUE key, VALUE val, VALUE md_ary_obj) { + grpc_metadata_array *md_ary = NULL; + long array_length; + long i; + + /* Construct a metadata object from key and value and add it */ + TypedData_Get_Struct(md_ary_obj, grpc_metadata_array, + &grpc_rb_md_ary_data_type, md_ary); + + if (TYPE(val) == T_ARRAY) { + /* If the value is an array, add capacity for each value in the array */ + array_length = RARRAY_LEN(val); + for (i = 0; i < array_length; i++) { + if (TYPE(key) == T_SYMBOL) { + md_ary->metadata[md_ary->count].key = (char *)rb_id2name(SYM2ID(key)); + } else { /* StringValueCStr does all other type exclusions for us */ + md_ary->metadata[md_ary->count].key = StringValueCStr(key); + } + md_ary->metadata[md_ary->count].value = RSTRING_PTR(rb_ary_entry(val, i)); + md_ary->metadata[md_ary->count].value_length = + RSTRING_LEN(rb_ary_entry(val, i)); + md_ary->count += 1; + } + } else { + if (TYPE(key) == T_SYMBOL) { + md_ary->metadata[md_ary->count].key = (char *)rb_id2name(SYM2ID(key)); + } else { /* StringValueCStr does all other type exclusions for us */ + md_ary->metadata[md_ary->count].key = StringValueCStr(key); + } + md_ary->metadata[md_ary->count].value = RSTRING_PTR(val); + md_ary->metadata[md_ary->count].value_length = RSTRING_LEN(val); + md_ary->count += 1; + } + + return ST_CONTINUE; +} + +/* grpc_rb_md_ary_capacity_hash_cb is the hash iteration callback used + to pre-compute the capacity a grpc_metadata_array. +*/ +static int grpc_rb_md_ary_capacity_hash_cb(VALUE key, VALUE val, + VALUE md_ary_obj) { + grpc_metadata_array *md_ary = NULL; + + (void)key; + + /* Construct a metadata object from key and value and add it */ + TypedData_Get_Struct(md_ary_obj, grpc_metadata_array, + &grpc_rb_md_ary_data_type, md_ary); + + if (TYPE(val) == T_ARRAY) { + /* If the value is an array, add capacity for each value in the array */ + md_ary->capacity += RARRAY_LEN(val); + } else { + md_ary->capacity += 1; + } + return ST_CONTINUE; +} + +/* grpc_rb_md_ary_convert converts a ruby metadata hash into + a grpc_metadata_array. +*/ +static void grpc_rb_md_ary_convert(VALUE md_ary_hash, + grpc_metadata_array *md_ary) { + VALUE md_ary_obj = Qnil; + if (md_ary_hash == Qnil) { + return; /* Do nothing if the expected has value is nil */ + } + if (TYPE(md_ary_hash) != T_HASH) { + rb_raise(rb_eTypeError, "md_ary_convert: got <%s>, want ", + rb_obj_classname(md_ary_hash)); + return; + } + + /* Initialize the array, compute it's capacity, then fill it. */ + grpc_metadata_array_init(md_ary); + md_ary_obj = + TypedData_Wrap_Struct(grpc_rb_cMdAry, &grpc_rb_md_ary_data_type, md_ary); + rb_hash_foreach(md_ary_hash, grpc_rb_md_ary_capacity_hash_cb, md_ary_obj); + md_ary->metadata = gpr_malloc(md_ary->capacity * sizeof(grpc_metadata)); + rb_hash_foreach(md_ary_hash, grpc_rb_md_ary_fill_hash_cb, md_ary_obj); +} + +/* Converts a metadata array to a hash. */ +VALUE grpc_rb_md_ary_to_h(grpc_metadata_array *md_ary) { + VALUE key = Qnil; + VALUE new_ary = Qnil; + VALUE value = Qnil; + VALUE result = rb_hash_new(); + size_t i; + + for (i = 0; i < md_ary->count; i++) { + key = rb_str_new2(md_ary->metadata[i].key); + value = rb_hash_aref(result, key); + if (value == Qnil) { + value = rb_str_new(md_ary->metadata[i].value, + md_ary->metadata[i].value_length); + rb_hash_aset(result, key, value); + } else if (TYPE(value) == T_ARRAY) { + /* Add the string to the returned array */ + rb_ary_push(value, rb_str_new(md_ary->metadata[i].value, + md_ary->metadata[i].value_length)); + } else { + /* Add the current value with this key and the new one to an array */ + new_ary = rb_ary_new(); + rb_ary_push(new_ary, value); + rb_ary_push(new_ary, rb_str_new(md_ary->metadata[i].value, + md_ary->metadata[i].value_length)); + rb_hash_aset(result, key, new_ary); + } + } + return result; +} + +/* grpc_rb_call_check_op_keys_hash_cb is a hash iteration func that checks + each key of an ops hash is valid. +*/ +static int grpc_rb_call_check_op_keys_hash_cb(VALUE key, VALUE val, + VALUE ops_ary) { + (void)val; + /* Update the capacity; the value is an array, add capacity for each value in + * the array */ + if (TYPE(key) != T_FIXNUM) { + rb_raise(rb_eTypeError, "invalid operation : got <%s>, want ", + rb_obj_classname(key)); + return ST_STOP; + } + switch (NUM2INT(key)) { + case GRPC_OP_SEND_INITIAL_METADATA: + case GRPC_OP_SEND_MESSAGE: + case GRPC_OP_SEND_CLOSE_FROM_CLIENT: + case GRPC_OP_SEND_STATUS_FROM_SERVER: + case GRPC_OP_RECV_INITIAL_METADATA: + case GRPC_OP_RECV_MESSAGE: + case GRPC_OP_RECV_STATUS_ON_CLIENT: + case GRPC_OP_RECV_CLOSE_ON_SERVER: + rb_ary_push(ops_ary, key); + return ST_CONTINUE; + default: + rb_raise(rb_eTypeError, "invalid operation : bad value %d", NUM2INT(key)); + }; + return ST_STOP; +} + +/* grpc_rb_op_update_status_from_server adds the values in a ruby status + struct to the 'send_status_from_server' portion of an op. +*/ +static void grpc_rb_op_update_status_from_server(grpc_op *op, + grpc_metadata_array *md_ary, + VALUE status) { + VALUE code = rb_struct_aref(status, sym_code); + VALUE details = rb_struct_aref(status, sym_details); + VALUE metadata_hash = rb_struct_aref(status, sym_metadata); + + /* TODO: add check to ensure status is the correct struct type */ + if (TYPE(code) != T_FIXNUM) { + rb_raise(rb_eTypeError, "invalid code : got <%s>, want ", + rb_obj_classname(code)); + return; + } + if (TYPE(details) != T_STRING) { + rb_raise(rb_eTypeError, "invalid details : got <%s>, want ", + rb_obj_classname(code)); + return; + } + op->data.send_status_from_server.status = NUM2INT(code); + op->data.send_status_from_server.status_details = StringValueCStr(details); + grpc_rb_md_ary_convert(metadata_hash, md_ary); + op->data.send_status_from_server.trailing_metadata_count = md_ary->count; + op->data.send_status_from_server.trailing_metadata = md_ary->metadata; +} + +/* run_batch_stack holds various values used by the + * grpc_rb_call_run_batch function */ +typedef struct run_batch_stack { + /* The batch ops */ + grpc_op ops[8]; /* 8 is the maximum number of operations */ + size_t op_num; /* tracks the last added operation */ + + /* Data being sent */ + grpc_metadata_array send_metadata; + grpc_metadata_array send_trailing_metadata; + + /* Data being received */ + grpc_byte_buffer *recv_message; + grpc_metadata_array recv_metadata; + grpc_metadata_array recv_trailing_metadata; + int recv_cancelled; + grpc_status_code recv_status; + char *recv_status_details; + size_t recv_status_details_capacity; + uint write_flag; +} run_batch_stack; + +/* grpc_run_batch_stack_init ensures the run_batch_stack is properly + * initialized */ +static void grpc_run_batch_stack_init(run_batch_stack *st, uint write_flag) { + MEMZERO(st, run_batch_stack, 1); + grpc_metadata_array_init(&st->send_metadata); + grpc_metadata_array_init(&st->send_trailing_metadata); + grpc_metadata_array_init(&st->recv_metadata); + grpc_metadata_array_init(&st->recv_trailing_metadata); + st->op_num = 0; + st->write_flag = write_flag; +} + +/* grpc_run_batch_stack_cleanup ensures the run_batch_stack is properly + * cleaned up */ +static void grpc_run_batch_stack_cleanup(run_batch_stack *st) { + grpc_metadata_array_destroy(&st->send_metadata); + grpc_metadata_array_destroy(&st->send_trailing_metadata); + grpc_metadata_array_destroy(&st->recv_metadata); + grpc_metadata_array_destroy(&st->recv_trailing_metadata); + if (st->recv_status_details != NULL) { + gpr_free(st->recv_status_details); + } +} + +/* grpc_run_batch_stack_fill_ops fills the run_batch_stack ops array from + * ops_hash */ +static void grpc_run_batch_stack_fill_ops(run_batch_stack *st, VALUE ops_hash) { + VALUE this_op = Qnil; + VALUE this_value = Qnil; + VALUE ops_ary = rb_ary_new(); + size_t i = 0; + + /* Create a ruby array with just the operation keys */ + rb_hash_foreach(ops_hash, grpc_rb_call_check_op_keys_hash_cb, ops_ary); + + /* Fill the ops array */ + for (i = 0; i < (size_t)RARRAY_LEN(ops_ary); i++) { + this_op = rb_ary_entry(ops_ary, i); + this_value = rb_hash_aref(ops_hash, this_op); + st->ops[st->op_num].flags = 0; + switch (NUM2INT(this_op)) { + case GRPC_OP_SEND_INITIAL_METADATA: + /* N.B. later there is no need to explicitly delete the metadata keys + * and values, they are references to data in ruby objects. */ + grpc_rb_md_ary_convert(this_value, &st->send_metadata); + st->ops[st->op_num].data.send_initial_metadata.count = + st->send_metadata.count; + st->ops[st->op_num].data.send_initial_metadata.metadata = + st->send_metadata.metadata; + break; + case GRPC_OP_SEND_MESSAGE: + st->ops[st->op_num].data.send_message = grpc_rb_s_to_byte_buffer( + RSTRING_PTR(this_value), RSTRING_LEN(this_value)); + st->ops[st->op_num].flags = st->write_flag; + break; + case GRPC_OP_SEND_CLOSE_FROM_CLIENT: + break; + case GRPC_OP_SEND_STATUS_FROM_SERVER: + /* N.B. later there is no need to explicitly delete the metadata keys + * and values, they are references to data in ruby objects. */ + grpc_rb_op_update_status_from_server( + &st->ops[st->op_num], &st->send_trailing_metadata, this_value); + break; + case GRPC_OP_RECV_INITIAL_METADATA: + st->ops[st->op_num].data.recv_initial_metadata = &st->recv_metadata; + break; + case GRPC_OP_RECV_MESSAGE: + st->ops[st->op_num].data.recv_message = &st->recv_message; + break; + case GRPC_OP_RECV_STATUS_ON_CLIENT: + st->ops[st->op_num].data.recv_status_on_client.trailing_metadata = + &st->recv_trailing_metadata; + st->ops[st->op_num].data.recv_status_on_client.status = + &st->recv_status; + st->ops[st->op_num].data.recv_status_on_client.status_details = + &st->recv_status_details; + st->ops[st->op_num].data.recv_status_on_client.status_details_capacity = + &st->recv_status_details_capacity; + break; + case GRPC_OP_RECV_CLOSE_ON_SERVER: + st->ops[st->op_num].data.recv_close_on_server.cancelled = + &st->recv_cancelled; + break; + default: + grpc_run_batch_stack_cleanup(st); + rb_raise(rb_eTypeError, "invalid operation : bad value %d", + NUM2INT(this_op)); + }; + st->ops[st->op_num].op = (grpc_op_type)NUM2INT(this_op); + st->ops[st->op_num].reserved = NULL; + st->op_num++; + } +} + +/* grpc_run_batch_stack_build_result fills constructs a ruby BatchResult struct + after the results have run */ +static VALUE grpc_run_batch_stack_build_result(run_batch_stack *st) { + size_t i = 0; + VALUE result = rb_struct_new(grpc_rb_sBatchResult, Qnil, Qnil, Qnil, Qnil, + Qnil, Qnil, Qnil, Qnil, NULL); + for (i = 0; i < st->op_num; i++) { + switch (st->ops[i].op) { + case GRPC_OP_SEND_INITIAL_METADATA: + rb_struct_aset(result, sym_send_metadata, Qtrue); + break; + case GRPC_OP_SEND_MESSAGE: + rb_struct_aset(result, sym_send_message, Qtrue); + grpc_byte_buffer_destroy(st->ops[i].data.send_message); + break; + case GRPC_OP_SEND_CLOSE_FROM_CLIENT: + rb_struct_aset(result, sym_send_close, Qtrue); + break; + case GRPC_OP_SEND_STATUS_FROM_SERVER: + rb_struct_aset(result, sym_send_status, Qtrue); + break; + case GRPC_OP_RECV_INITIAL_METADATA: + rb_struct_aset(result, sym_metadata, + grpc_rb_md_ary_to_h(&st->recv_metadata)); + case GRPC_OP_RECV_MESSAGE: + rb_struct_aset(result, sym_message, + grpc_rb_byte_buffer_to_s(st->recv_message)); + break; + case GRPC_OP_RECV_STATUS_ON_CLIENT: + rb_struct_aset( + result, sym_status, + rb_struct_new(grpc_rb_sStatus, UINT2NUM(st->recv_status), + (st->recv_status_details == NULL + ? Qnil + : rb_str_new2(st->recv_status_details)), + grpc_rb_md_ary_to_h(&st->recv_trailing_metadata), + NULL)); + break; + case GRPC_OP_RECV_CLOSE_ON_SERVER: + rb_struct_aset(result, sym_send_close, Qtrue); + break; + default: + break; + } + } + return result; +} + +/* call-seq: + cq = CompletionQueue.new + ops = { + GRPC::Core::CallOps::SEND_INITIAL_METADATA => , + GRPC::Core::CallOps::SEND_MESSAGE => , + ... + } + tag = Object.new + timeout = 10 + call.start_batch(cqueue, tag, timeout, ops) + + Start a batch of operations defined in the array ops; when complete, post a + completion of type 'tag' to the completion queue bound to the call. + + Also waits for the batch to complete, until timeout is reached. + The order of ops specified in the batch has no significance. + Only one operation of each type can be active at once in any given + batch */ +static VALUE grpc_rb_call_run_batch(VALUE self, VALUE cqueue, VALUE tag, + VALUE timeout, VALUE ops_hash) { + run_batch_stack st; + grpc_call *call = NULL; + grpc_event ev; + grpc_call_error err; + VALUE result = Qnil; + VALUE rb_write_flag = rb_ivar_get(self, id_write_flag); + uint write_flag = 0; + TypedData_Get_Struct(self, grpc_call, &grpc_call_data_type, call); + + /* Validate the ops args, adding them to a ruby array */ + if (TYPE(ops_hash) != T_HASH) { + rb_raise(rb_eTypeError, "call#run_batch: ops hash should be a hash"); + return Qnil; + } + if (rb_write_flag != Qnil) { + write_flag = NUM2UINT(rb_write_flag); + } + grpc_run_batch_stack_init(&st, write_flag); + grpc_run_batch_stack_fill_ops(&st, ops_hash); + + /* call grpc_call_start_batch, then wait for it to complete using + * pluck_event */ + err = grpc_call_start_batch(call, st.ops, st.op_num, ROBJECT(tag), NULL); + if (err != GRPC_CALL_OK) { + grpc_run_batch_stack_cleanup(&st); + rb_raise(grpc_rb_eCallError, + "grpc_call_start_batch failed with %s (code=%d)", + grpc_call_error_detail_of(err), err); + return Qnil; + } + ev = grpc_rb_completion_queue_pluck_event(cqueue, tag, timeout); + if (ev.type == GRPC_QUEUE_TIMEOUT) { + grpc_run_batch_stack_cleanup(&st); + rb_raise(grpc_rb_eOutOfTime, "grpc_call_start_batch timed out"); + return Qnil; + } + + /* Build and return the BatchResult struct result, + if there is an error, it's reflected in the status */ + result = grpc_run_batch_stack_build_result(&st); + grpc_run_batch_stack_cleanup(&st); + return result; +} + +static void Init_grpc_write_flags() { + /* Constants representing the write flags in grpc.h */ + VALUE grpc_rb_mWriteFlags = + rb_define_module_under(grpc_rb_mGrpcCore, "WriteFlags"); + rb_define_const(grpc_rb_mWriteFlags, "BUFFER_HINT", + UINT2NUM(GRPC_WRITE_BUFFER_HINT)); + rb_define_const(grpc_rb_mWriteFlags, "NO_COMPRESS", + UINT2NUM(GRPC_WRITE_NO_COMPRESS)); +} + +static void Init_grpc_error_codes() { + /* Constants representing the error codes of grpc_call_error in grpc.h */ + VALUE grpc_rb_mRpcErrors = + rb_define_module_under(grpc_rb_mGrpcCore, "RpcErrors"); + rb_define_const(grpc_rb_mRpcErrors, "OK", UINT2NUM(GRPC_CALL_OK)); + rb_define_const(grpc_rb_mRpcErrors, "ERROR", UINT2NUM(GRPC_CALL_ERROR)); + rb_define_const(grpc_rb_mRpcErrors, "NOT_ON_SERVER", + UINT2NUM(GRPC_CALL_ERROR_NOT_ON_SERVER)); + rb_define_const(grpc_rb_mRpcErrors, "NOT_ON_CLIENT", + UINT2NUM(GRPC_CALL_ERROR_NOT_ON_CLIENT)); + rb_define_const(grpc_rb_mRpcErrors, "ALREADY_ACCEPTED", + UINT2NUM(GRPC_CALL_ERROR_ALREADY_ACCEPTED)); + rb_define_const(grpc_rb_mRpcErrors, "ALREADY_INVOKED", + UINT2NUM(GRPC_CALL_ERROR_ALREADY_INVOKED)); + rb_define_const(grpc_rb_mRpcErrors, "NOT_INVOKED", + UINT2NUM(GRPC_CALL_ERROR_NOT_INVOKED)); + rb_define_const(grpc_rb_mRpcErrors, "ALREADY_FINISHED", + UINT2NUM(GRPC_CALL_ERROR_ALREADY_FINISHED)); + rb_define_const(grpc_rb_mRpcErrors, "TOO_MANY_OPERATIONS", + UINT2NUM(GRPC_CALL_ERROR_TOO_MANY_OPERATIONS)); + rb_define_const(grpc_rb_mRpcErrors, "INVALID_FLAGS", + UINT2NUM(GRPC_CALL_ERROR_INVALID_FLAGS)); + + /* Add the detail strings to a Hash */ + rb_error_code_details = rb_hash_new(); + rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_OK), + rb_str_new2("ok")); + rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_ERROR), + rb_str_new2("unknown error")); + rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_ERROR_NOT_ON_SERVER), + rb_str_new2("not available on a server")); + rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_ERROR_NOT_ON_CLIENT), + rb_str_new2("not available on a client")); + rb_hash_aset(rb_error_code_details, + UINT2NUM(GRPC_CALL_ERROR_ALREADY_ACCEPTED), + rb_str_new2("call is already accepted")); + rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_ERROR_ALREADY_INVOKED), + rb_str_new2("call is already invoked")); + rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_ERROR_NOT_INVOKED), + rb_str_new2("call is not yet invoked")); + rb_hash_aset(rb_error_code_details, + UINT2NUM(GRPC_CALL_ERROR_ALREADY_FINISHED), + rb_str_new2("call is already finished")); + rb_hash_aset(rb_error_code_details, + UINT2NUM(GRPC_CALL_ERROR_TOO_MANY_OPERATIONS), + rb_str_new2("outstanding read or write present")); + rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_ERROR_INVALID_FLAGS), + rb_str_new2("a bad flag was given")); + rb_define_const(grpc_rb_mRpcErrors, "ErrorMessages", rb_error_code_details); + rb_obj_freeze(rb_error_code_details); +} + +static void Init_grpc_op_codes() { + /* Constants representing operation type codes in grpc.h */ + VALUE grpc_rb_mCallOps = rb_define_module_under(grpc_rb_mGrpcCore, "CallOps"); + rb_define_const(grpc_rb_mCallOps, "SEND_INITIAL_METADATA", + UINT2NUM(GRPC_OP_SEND_INITIAL_METADATA)); + rb_define_const(grpc_rb_mCallOps, "SEND_MESSAGE", + UINT2NUM(GRPC_OP_SEND_MESSAGE)); + rb_define_const(grpc_rb_mCallOps, "SEND_CLOSE_FROM_CLIENT", + UINT2NUM(GRPC_OP_SEND_CLOSE_FROM_CLIENT)); + rb_define_const(grpc_rb_mCallOps, "SEND_STATUS_FROM_SERVER", + UINT2NUM(GRPC_OP_SEND_STATUS_FROM_SERVER)); + rb_define_const(grpc_rb_mCallOps, "RECV_INITIAL_METADATA", + UINT2NUM(GRPC_OP_RECV_INITIAL_METADATA)); + rb_define_const(grpc_rb_mCallOps, "RECV_MESSAGE", + UINT2NUM(GRPC_OP_RECV_MESSAGE)); + rb_define_const(grpc_rb_mCallOps, "RECV_STATUS_ON_CLIENT", + UINT2NUM(GRPC_OP_RECV_STATUS_ON_CLIENT)); + rb_define_const(grpc_rb_mCallOps, "RECV_CLOSE_ON_SERVER", + UINT2NUM(GRPC_OP_RECV_CLOSE_ON_SERVER)); +} + +void Init_grpc_call() { + /* CallError inherits from Exception to signal that it is non-recoverable */ + grpc_rb_eCallError = + rb_define_class_under(grpc_rb_mGrpcCore, "CallError", rb_eException); + grpc_rb_eOutOfTime = + rb_define_class_under(grpc_rb_mGrpcCore, "OutOfTime", rb_eException); + grpc_rb_cCall = rb_define_class_under(grpc_rb_mGrpcCore, "Call", rb_cObject); + grpc_rb_cMdAry = + rb_define_class_under(grpc_rb_mGrpcCore, "MetadataArray", rb_cObject); + + /* Prevent allocation or inialization of the Call class */ + rb_define_alloc_func(grpc_rb_cCall, grpc_rb_cannot_alloc); + rb_define_method(grpc_rb_cCall, "initialize", grpc_rb_cannot_init, 0); + rb_define_method(grpc_rb_cCall, "initialize_copy", grpc_rb_cannot_init_copy, + 1); + + /* Add ruby analogues of the Call methods. */ + rb_define_method(grpc_rb_cCall, "run_batch", grpc_rb_call_run_batch, 4); + rb_define_method(grpc_rb_cCall, "cancel", grpc_rb_call_cancel, 0); + rb_define_method(grpc_rb_cCall, "peer", grpc_rb_call_get_peer, 0); + rb_define_method(grpc_rb_cCall, "status", grpc_rb_call_get_status, 0); + rb_define_method(grpc_rb_cCall, "status=", grpc_rb_call_set_status, 1); + rb_define_method(grpc_rb_cCall, "metadata", grpc_rb_call_get_metadata, 0); + rb_define_method(grpc_rb_cCall, "metadata=", grpc_rb_call_set_metadata, 1); + rb_define_method(grpc_rb_cCall, "write_flag", grpc_rb_call_get_write_flag, 0); + rb_define_method(grpc_rb_cCall, "write_flag=", grpc_rb_call_set_write_flag, + 1); + + /* Ids used to support call attributes */ + id_metadata = rb_intern("metadata"); + id_status = rb_intern("status"); + id_write_flag = rb_intern("write_flag"); + + /* Ids used by the c wrapping internals. */ + id_cq = rb_intern("__cq"); + id_flags = rb_intern("__flags"); + id_input_md = rb_intern("__input_md"); + + /* Ids used in constructing the batch result. */ + sym_send_message = ID2SYM(rb_intern("send_message")); + sym_send_metadata = ID2SYM(rb_intern("send_metadata")); + sym_send_close = ID2SYM(rb_intern("send_close")); + sym_send_status = ID2SYM(rb_intern("send_status")); + sym_message = ID2SYM(rb_intern("message")); + sym_status = ID2SYM(rb_intern("status")); + sym_cancelled = ID2SYM(rb_intern("cancelled")); + + /* The Struct used to return the run_batch result. */ + grpc_rb_sBatchResult = rb_struct_define( + "BatchResult", "send_message", "send_metadata", "send_close", + "send_status", "message", "metadata", "status", "cancelled", NULL); + + /* The hash for reference counting calls, to ensure they can't be destroyed + * more than once */ + hash_all_calls = rb_hash_new(); + rb_define_const(grpc_rb_cCall, "INTERNAL_ALL_CALLs", hash_all_calls); + + Init_grpc_error_codes(); + Init_grpc_op_codes(); + Init_grpc_write_flags(); +} + +/* Gets the call from the ruby object */ +grpc_call *grpc_rb_get_wrapped_call(VALUE v) { + grpc_call *c = NULL; + TypedData_Get_Struct(v, grpc_call, &grpc_call_data_type, c); + return c; +} + +/* Obtains the wrapped object for a given call */ +VALUE grpc_rb_wrap_call(grpc_call *c) { + VALUE obj = Qnil; + if (c == NULL) { + return Qnil; + } + obj = rb_hash_aref(hash_all_calls, OFFT2NUM((VALUE)c)); + if (obj == Qnil) { /* Not in the hash add it */ + rb_hash_aset(hash_all_calls, OFFT2NUM((VALUE)c), UINT2NUM(1)); + } else { + rb_hash_aset(hash_all_calls, OFFT2NUM((VALUE)c), + UINT2NUM(NUM2UINT(obj) + 1)); + } + return TypedData_Wrap_Struct(grpc_rb_cCall, &grpc_call_data_type, c); +} diff --git a/src/ruby/ext/grpc/rb_call.h b/src/ruby/ext/grpc/rb_call.h new file mode 100644 index 00000000..1d2fbc35 --- /dev/null +++ b/src/ruby/ext/grpc/rb_call.h @@ -0,0 +1,60 @@ +/* + * + * Copyright 2015, 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_RB_CALL_H_ +#define GRPC_RB_CALL_H_ + +#include + +#include + +/* Gets the wrapped call from a VALUE. */ +grpc_call* grpc_rb_get_wrapped_call(VALUE v); + +/* Gets the VALUE corresponding to given grpc_call. */ +VALUE grpc_rb_wrap_call(grpc_call* c); + +/* Provides the details of an call error */ +const char* grpc_call_error_detail_of(grpc_call_error err); + +/* Converts a metadata array to a hash. */ +VALUE grpc_rb_md_ary_to_h(grpc_metadata_array *md_ary); + +/* grpc_rb_eCallError is the ruby class of the exception thrown during call + operations. */ +extern VALUE grpc_rb_eCallError; + +/* Initializes the Call class. */ +void Init_grpc_call(); + +#endif /* GRPC_RB_CALL_H_ */ diff --git a/src/ruby/ext/grpc/rb_channel.c b/src/ruby/ext/grpc/rb_channel.c new file mode 100644 index 00000000..90afdc3f --- /dev/null +++ b/src/ruby/ext/grpc/rb_channel.c @@ -0,0 +1,418 @@ +/* + * + * Copyright 2015, 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 "rb_channel.h" + +#include + +#include +#include +#include +#include "rb_grpc.h" +#include "rb_call.h" +#include "rb_channel_args.h" +#include "rb_completion_queue.h" +#include "rb_credentials.h" +#include "rb_server.h" + +/* id_channel is the name of the hidden ivar that preserves a reference to the + * channel on a call, so that calls are not GCed before their channel. */ +static ID id_channel; + +/* id_target is the name of the hidden ivar that preserves a reference to the + * target string used to create the call, preserved so that it does not get + * GCed before the channel */ +static ID id_target; + +/* id_cqueue is the name of the hidden ivar that preserves a reference to the + * completion queue used to create the call, preserved so that it does not get + * GCed before the channel */ +static ID id_cqueue; + +/* grpc_rb_cChannel is the ruby class that proxies grpc_channel. */ +static VALUE grpc_rb_cChannel = Qnil; + +/* Used during the conversion of a hash to channel args during channel setup */ +static VALUE grpc_rb_cChannelArgs; + +/* grpc_rb_channel wraps a grpc_channel. It provides a peer ruby object, + * 'mark' to minimize copying when a channel is created from ruby. */ +typedef struct grpc_rb_channel { + /* Holder of ruby objects involved in constructing the channel */ + VALUE mark; + /* The actual channel */ + grpc_channel *wrapped; +} grpc_rb_channel; + +/* Destroys Channel instances. */ +static void grpc_rb_channel_free(void *p) { + grpc_rb_channel *ch = NULL; + if (p == NULL) { + return; + }; + ch = (grpc_rb_channel *)p; + + /* Deletes the wrapped object if the mark object is Qnil, which indicates + * that no other object is the actual owner. */ + if (ch->wrapped != NULL && ch->mark == Qnil) { + grpc_channel_destroy(ch->wrapped); + rb_warning("channel gc: destroyed the c channel"); + } else { + rb_warning("channel gc: did not destroy the c channel"); + } + + xfree(p); +} + +/* Protects the mark object from GC */ +static void grpc_rb_channel_mark(void *p) { + grpc_rb_channel *channel = NULL; + if (p == NULL) { + return; + } + channel = (grpc_rb_channel *)p; + if (channel->mark != Qnil) { + rb_gc_mark(channel->mark); + } +} + +static rb_data_type_t grpc_channel_data_type = { + "grpc_channel", + {grpc_rb_channel_mark, grpc_rb_channel_free, GRPC_RB_MEMSIZE_UNAVAILABLE, + {NULL, NULL}}, + NULL, NULL, + RUBY_TYPED_FREE_IMMEDIATELY +}; + +/* Allocates grpc_rb_channel instances. */ +static VALUE grpc_rb_channel_alloc(VALUE cls) { + grpc_rb_channel *wrapper = ALLOC(grpc_rb_channel); + wrapper->wrapped = NULL; + wrapper->mark = Qnil; + return TypedData_Wrap_Struct(cls, &grpc_channel_data_type, wrapper); +} + +/* + call-seq: + insecure_channel = Channel:new("myhost:8080", {'arg1': 'value1'}) + creds = ... + secure_channel = Channel:new("myhost:443", {'arg1': 'value1'}, creds) + + Creates channel instances. */ +static VALUE grpc_rb_channel_init(int argc, VALUE *argv, VALUE self) { + VALUE channel_args = Qnil; + VALUE credentials = Qnil; + VALUE target = Qnil; + grpc_rb_channel *wrapper = NULL; + grpc_credentials *creds = NULL; + grpc_channel *ch = NULL; + char *target_chars = NULL; + grpc_channel_args args; + MEMZERO(&args, grpc_channel_args, 1); + + /* "21" == 2 mandatory args, 1 (credentials) is optional */ + rb_scan_args(argc, argv, "21", &target, &channel_args, &credentials); + + TypedData_Get_Struct(self, grpc_rb_channel, &grpc_channel_data_type, wrapper); + target_chars = StringValueCStr(target); + grpc_rb_hash_convert_to_channel_args(channel_args, &args); + if (credentials == Qnil) { + ch = grpc_insecure_channel_create(target_chars, &args, NULL); + } else { + creds = grpc_rb_get_wrapped_credentials(credentials); + ch = grpc_secure_channel_create(creds, target_chars, &args, NULL); + } + if (args.args != NULL) { + xfree(args.args); /* Allocated by grpc_rb_hash_convert_to_channel_args */ + } + if (ch == NULL) { + rb_raise(rb_eRuntimeError, "could not create an rpc channel to target:%s", + target_chars); + return Qnil; + } + rb_ivar_set(self, id_target, target); + wrapper->wrapped = ch; + return self; +} + +/* + call-seq: + insecure_channel = Channel:new("myhost:8080", {'arg1': 'value1'}) + creds = ... + secure_channel = Channel:new("myhost:443", {'arg1': 'value1'}, creds) + + Creates channel instances. */ +static VALUE grpc_rb_channel_get_connectivity_state(int argc, VALUE *argv, + VALUE self) { + VALUE try_to_connect = Qfalse; + grpc_rb_channel *wrapper = NULL; + grpc_channel *ch = NULL; + + /* "01" == 0 mandatory args, 1 (try_to_connect) is optional */ + rb_scan_args(argc, argv, "01", try_to_connect); + + TypedData_Get_Struct(self, grpc_rb_channel, &grpc_channel_data_type, wrapper); + ch = wrapper->wrapped; + if (ch == NULL) { + rb_raise(rb_eRuntimeError, "closed!"); + return Qnil; + } + return NUM2LONG( + grpc_channel_check_connectivity_state(ch, (int)try_to_connect)); +} + +/* Watch for a change in connectivity state. + + Once the channel connectivity state is different from the last observed + state, tag will be enqueued on cq with success=1 + + If deadline expires BEFORE the state is changed, tag will be enqueued on + the completion queue with success=0 */ +static VALUE grpc_rb_channel_watch_connectivity_state(VALUE self, + VALUE last_state, + VALUE cqueue, + VALUE deadline, + VALUE tag) { + grpc_rb_channel *wrapper = NULL; + grpc_channel *ch = NULL; + grpc_completion_queue *cq = NULL; + + cq = grpc_rb_get_wrapped_completion_queue(cqueue); + TypedData_Get_Struct(self, grpc_rb_channel, &grpc_channel_data_type, wrapper); + ch = wrapper->wrapped; + if (ch == NULL) { + rb_raise(rb_eRuntimeError, "closed!"); + return Qnil; + } + grpc_channel_watch_connectivity_state( + ch, + NUM2LONG(last_state), + grpc_rb_time_timeval(deadline, /* absolute time */ 0), + cq, + ROBJECT(tag)); + + return Qnil; +} + +/* Clones Channel instances. + + Gives Channel a consistent implementation of Ruby's object copy/dup + protocol. */ +static VALUE grpc_rb_channel_init_copy(VALUE copy, VALUE orig) { + grpc_rb_channel *orig_ch = NULL; + grpc_rb_channel *copy_ch = NULL; + + if (copy == orig) { + return copy; + } + + /* Raise an error if orig is not a channel object or a subclass. */ + if (TYPE(orig) != T_DATA || + RDATA(orig)->dfree != (RUBY_DATA_FUNC)grpc_rb_channel_free) { + rb_raise(rb_eTypeError, "not a %s", rb_obj_classname(grpc_rb_cChannel)); + return Qnil; + } + + TypedData_Get_Struct(orig, grpc_rb_channel, &grpc_channel_data_type, orig_ch); + TypedData_Get_Struct(copy, grpc_rb_channel, &grpc_channel_data_type, copy_ch); + + /* use ruby's MEMCPY to make a byte-for-byte copy of the channel wrapper + * object. */ + MEMCPY(copy_ch, orig_ch, grpc_rb_channel, 1); + return copy; +} + +/* Create a call given a grpc_channel, in order to call method. The request + is not sent until grpc_call_invoke is called. */ +static VALUE grpc_rb_channel_create_call(VALUE self, VALUE cqueue, + VALUE parent, VALUE mask, + VALUE method, VALUE host, + VALUE deadline) { + VALUE res = Qnil; + grpc_rb_channel *wrapper = NULL; + grpc_call *call = NULL; + grpc_call *parent_call = NULL; + grpc_channel *ch = NULL; + grpc_completion_queue *cq = NULL; + int flags = GRPC_PROPAGATE_DEFAULTS; + char *method_chars = StringValueCStr(method); + char *host_chars = NULL; + if (host != Qnil) { + host_chars = StringValueCStr(host); + } + if (mask != Qnil) { + flags = NUM2UINT(mask); + } + if (parent != Qnil) { + parent_call = grpc_rb_get_wrapped_call(parent); + } + + cq = grpc_rb_get_wrapped_completion_queue(cqueue); + TypedData_Get_Struct(self, grpc_rb_channel, &grpc_channel_data_type, wrapper); + ch = wrapper->wrapped; + if (ch == NULL) { + rb_raise(rb_eRuntimeError, "closed!"); + return Qnil; + } + + call = grpc_channel_create_call(ch, parent_call, flags, cq, method_chars, + host_chars, grpc_rb_time_timeval( + deadline, + /* absolute time */ 0), NULL); + if (call == NULL) { + rb_raise(rb_eRuntimeError, "cannot create call with method %s", + method_chars); + return Qnil; + } + res = grpc_rb_wrap_call(call); + + /* Make this channel an instance attribute of the call so that it is not GCed + * before the call. */ + rb_ivar_set(res, id_channel, self); + + /* Make the completion queue an instance attribute of the call so that it is + * not GCed before the call. */ + rb_ivar_set(res, id_cqueue, cqueue); + return res; +} + + +/* Closes the channel, calling it's destroy method */ +static VALUE grpc_rb_channel_destroy(VALUE self) { + grpc_rb_channel *wrapper = NULL; + grpc_channel *ch = NULL; + + TypedData_Get_Struct(self, grpc_rb_channel, &grpc_channel_data_type, wrapper); + ch = wrapper->wrapped; + if (ch != NULL) { + grpc_channel_destroy(ch); + wrapper->wrapped = NULL; + wrapper->mark = Qnil; + } + + return Qnil; +} + + +/* Called to obtain the target that this channel accesses. */ +static VALUE grpc_rb_channel_get_target(VALUE self) { + grpc_rb_channel *wrapper = NULL; + VALUE res = Qnil; + char* target = NULL; + + TypedData_Get_Struct(self, grpc_rb_channel, &grpc_channel_data_type, wrapper); + target = grpc_channel_get_target(wrapper->wrapped); + res = rb_str_new2(target); + gpr_free(target); + + return res; +} + +static void Init_grpc_propagate_masks() { + /* Constants representing call propagation masks in grpc.h */ + VALUE grpc_rb_mPropagateMasks = rb_define_module_under( + grpc_rb_mGrpcCore, "PropagateMasks"); + rb_define_const(grpc_rb_mPropagateMasks, "DEADLINE", + UINT2NUM(GRPC_PROPAGATE_DEADLINE)); + rb_define_const(grpc_rb_mPropagateMasks, "CENSUS_STATS_CONTEXT", + UINT2NUM(GRPC_PROPAGATE_CENSUS_STATS_CONTEXT)); + rb_define_const(grpc_rb_mPropagateMasks, "CENSUS_TRACING_CONTEXT", + UINT2NUM(GRPC_PROPAGATE_CENSUS_TRACING_CONTEXT)); + rb_define_const(grpc_rb_mPropagateMasks, "CANCELLATION", + UINT2NUM(GRPC_PROPAGATE_CANCELLATION)); + rb_define_const(grpc_rb_mPropagateMasks, "DEFAULTS", + UINT2NUM(GRPC_PROPAGATE_DEFAULTS)); +} + +static void Init_grpc_connectivity_states() { + /* Constants representing call propagation masks in grpc.h */ + VALUE grpc_rb_mConnectivityStates = rb_define_module_under( + grpc_rb_mGrpcCore, "ConnectivityStates"); + rb_define_const(grpc_rb_mConnectivityStates, "IDLE", + LONG2NUM(GRPC_CHANNEL_IDLE)); + rb_define_const(grpc_rb_mConnectivityStates, "CONNECTING", + LONG2NUM(GRPC_CHANNEL_CONNECTING)); + rb_define_const(grpc_rb_mConnectivityStates, "READY", + LONG2NUM(GRPC_CHANNEL_READY)); + rb_define_const(grpc_rb_mConnectivityStates, "TRANSIENT_FAILURE", + LONG2NUM(GRPC_CHANNEL_TRANSIENT_FAILURE)); + rb_define_const(grpc_rb_mConnectivityStates, "FATAL_FAILURE", + LONG2NUM(GRPC_CHANNEL_FATAL_FAILURE)); +} + +void Init_grpc_channel() { + grpc_rb_cChannelArgs = rb_define_class("TmpChannelArgs", rb_cObject); + grpc_rb_cChannel = + rb_define_class_under(grpc_rb_mGrpcCore, "Channel", rb_cObject); + + /* Allocates an object managed by the ruby runtime */ + rb_define_alloc_func(grpc_rb_cChannel, grpc_rb_channel_alloc); + + /* Provides a ruby constructor and support for dup/clone. */ + rb_define_method(grpc_rb_cChannel, "initialize", grpc_rb_channel_init, -1); + rb_define_method(grpc_rb_cChannel, "initialize_copy", + grpc_rb_channel_init_copy, 1); + + /* Add ruby analogues of the Channel methods. */ + rb_define_method(grpc_rb_cChannel, "connectivity_state", + grpc_rb_channel_get_connectivity_state, + -1); + rb_define_method(grpc_rb_cChannel, "watch_connectivity_state", + grpc_rb_channel_watch_connectivity_state, 4); + rb_define_method(grpc_rb_cChannel, "create_call", + grpc_rb_channel_create_call, 6); + rb_define_method(grpc_rb_cChannel, "target", grpc_rb_channel_get_target, 0); + rb_define_method(grpc_rb_cChannel, "destroy", grpc_rb_channel_destroy, 0); + rb_define_alias(grpc_rb_cChannel, "close", "destroy"); + + id_channel = rb_intern("__channel"); + id_cqueue = rb_intern("__cqueue"); + id_target = rb_intern("__target"); + rb_define_const(grpc_rb_cChannel, "SSL_TARGET", + ID2SYM(rb_intern(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG))); + rb_define_const(grpc_rb_cChannel, "ENABLE_CENSUS", + ID2SYM(rb_intern(GRPC_ARG_ENABLE_CENSUS))); + rb_define_const(grpc_rb_cChannel, "MAX_CONCURRENT_STREAMS", + ID2SYM(rb_intern(GRPC_ARG_MAX_CONCURRENT_STREAMS))); + rb_define_const(grpc_rb_cChannel, "MAX_MESSAGE_LENGTH", + ID2SYM(rb_intern(GRPC_ARG_MAX_MESSAGE_LENGTH))); + Init_grpc_propagate_masks(); + Init_grpc_connectivity_states(); +} + +/* Gets the wrapped channel from the ruby wrapper */ +grpc_channel *grpc_rb_get_wrapped_channel(VALUE v) { + grpc_rb_channel *wrapper = NULL; + TypedData_Get_Struct(v, grpc_rb_channel, &grpc_channel_data_type, wrapper); + return wrapper->wrapped; +} diff --git a/src/ruby/ext/grpc/rb_channel.h b/src/ruby/ext/grpc/rb_channel.h new file mode 100644 index 00000000..77e1f6ac --- /dev/null +++ b/src/ruby/ext/grpc/rb_channel.h @@ -0,0 +1,47 @@ +/* + * + * Copyright 2015, 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_RB_CHANNEL_H_ +#define GRPC_RB_CHANNEL_H_ + +#include + +#include + +/* Initializes the Channel class. */ +void Init_grpc_channel(); + +/* Gets the wrapped channel from the ruby wrapper */ +grpc_channel* grpc_rb_get_wrapped_channel(VALUE v); + +#endif /* GRPC_RB_CHANNEL_H_ */ diff --git a/src/ruby/ext/grpc/rb_channel_args.c b/src/ruby/ext/grpc/rb_channel_args.c new file mode 100644 index 00000000..1ba30b69 --- /dev/null +++ b/src/ruby/ext/grpc/rb_channel_args.c @@ -0,0 +1,165 @@ +/* + * + * Copyright 2015, 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 "rb_channel_args.h" + +#include + +#include + +#include "rb_grpc.h" + +static rb_data_type_t grpc_rb_channel_args_data_type = { + "grpc_channel_args", + {GRPC_RB_GC_NOT_MARKED, GRPC_RB_GC_DONT_FREE, GRPC_RB_MEMSIZE_UNAVAILABLE, + {NULL, NULL}}, + NULL, NULL, + RUBY_TYPED_FREE_IMMEDIATELY +}; + +/* A callback the processes the hash key values in channel_args hash */ +static int grpc_rb_channel_create_in_process_add_args_hash_cb(VALUE key, + VALUE val, + VALUE args_obj) { + const char* the_key; + grpc_channel_args* args; + + switch (TYPE(key)) { + case T_STRING: + the_key = StringValuePtr(key); + break; + + case T_SYMBOL: + the_key = rb_id2name(SYM2ID(key)); + break; + + default: + rb_raise(rb_eTypeError, "bad chan arg: got <%s>, want ", + rb_obj_classname(key)); + return ST_STOP; + } + + TypedData_Get_Struct(args_obj, grpc_channel_args, + &grpc_rb_channel_args_data_type, args); + if (args->num_args <= 0) { + rb_raise(rb_eRuntimeError, "hash_cb bug: num_args is %lu for key:%s", + args->num_args, StringValueCStr(key)); + return ST_STOP; + } + + args->args[args->num_args - 1].key = (char*)the_key; + switch (TYPE(val)) { + case T_SYMBOL: + args->args[args->num_args - 1].type = GRPC_ARG_STRING; + args->args[args->num_args - 1].value.string = + (char*)rb_id2name(SYM2ID(val)); + --args->num_args; + return ST_CONTINUE; + + case T_STRING: + args->args[args->num_args - 1].type = GRPC_ARG_STRING; + args->args[args->num_args - 1].value.string = StringValueCStr(val); + --args->num_args; + return ST_CONTINUE; + + case T_FIXNUM: + args->args[args->num_args - 1].type = GRPC_ARG_INTEGER; + args->args[args->num_args - 1].value.integer = NUM2INT(val); + --args->num_args; + return ST_CONTINUE; + + default: + rb_raise(rb_eTypeError, "%s: bad value: got <%s>, want ", + StringValueCStr(key), rb_obj_classname(val)); + return ST_STOP; + } + rb_raise(rb_eRuntimeError, "impl bug: hash_cb reached to far while on key:%s", + StringValueCStr(key)); + return ST_STOP; +} + +/* channel_convert_params allows the call to + grpc_rb_hash_convert_to_channel_args to be made within an rb_protect + exception-handler. This allows any allocated memory to be freed before + propagating any exception that occurs */ +typedef struct channel_convert_params { + VALUE src_hash; + grpc_channel_args* dst; +} channel_convert_params; + +static VALUE grpc_rb_hash_convert_to_channel_args0(VALUE as_value) { + ID id_size = rb_intern("size"); + VALUE grpc_rb_cChannelArgs = rb_define_class("TmpChannelArgs", rb_cObject); + channel_convert_params* params = (channel_convert_params*)as_value; + size_t num_args = 0; + + if (!NIL_P(params->src_hash) && TYPE(params->src_hash) != T_HASH) { + rb_raise(rb_eTypeError, "bad channel args: got:<%s> want: a hash or nil", + rb_obj_classname(params->src_hash)); + return Qnil; + } + + if (TYPE(params->src_hash) == T_HASH) { + num_args = NUM2INT(rb_funcall(params->src_hash, id_size, 0)); + params->dst->num_args = num_args; + params->dst->args = ALLOC_N(grpc_arg, num_args); + MEMZERO(params->dst->args, grpc_arg, num_args); + rb_hash_foreach(params->src_hash, + grpc_rb_channel_create_in_process_add_args_hash_cb, + TypedData_Wrap_Struct(grpc_rb_cChannelArgs, + &grpc_rb_channel_args_data_type, + params->dst)); + /* reset num_args as grpc_rb_channel_create_in_process_add_args_hash_cb + * decrements it during has processing */ + params->dst->num_args = num_args; + } + return Qnil; +} + +void grpc_rb_hash_convert_to_channel_args(VALUE src_hash, + grpc_channel_args* dst) { + channel_convert_params params; + int status = 0; + + /* Make a protected call to grpc_rb_hash_convert_channel_args */ + params.src_hash = src_hash; + params.dst = dst; + rb_protect(grpc_rb_hash_convert_to_channel_args0, (VALUE) & params, &status); + if (status != 0) { + if (dst->args != NULL) { + /* Free any allocated memory before propagating the error */ + xfree(dst->args); + } + rb_jump_tag(status); + } +} diff --git a/src/ruby/ext/grpc/rb_channel_args.h b/src/ruby/ext/grpc/rb_channel_args.h new file mode 100644 index 00000000..591dd848 --- /dev/null +++ b/src/ruby/ext/grpc/rb_channel_args.h @@ -0,0 +1,53 @@ +/* + * + * Copyright 2015, 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_RB_CHANNEL_ARGS_H_ +#define GRPC_RB_CHANNEL_ARGS_H_ + +#include + +#include + +/* Converts a hash object containing channel args to a channel args instance. + * + * This func ALLOCs args->args. The caller is responsible for freeing it. If + * a ruby error is raised during processing of the hash values, the func takes + * care to deallocate any memory allocated so far, and propagate the error. + * + * @param src_hash A ruby hash + * @param dst the grpc_channel_args that the hash entries will be added to. + */ +void grpc_rb_hash_convert_to_channel_args(VALUE src_hash, + grpc_channel_args* dst); + +#endif /* GRPC_RB_CHANNEL_ARGS_H_ */ diff --git a/src/ruby/ext/grpc/rb_completion_queue.c b/src/ruby/ext/grpc/rb_completion_queue.c new file mode 100644 index 00000000..0bc9eb2a --- /dev/null +++ b/src/ruby/ext/grpc/rb_completion_queue.c @@ -0,0 +1,179 @@ +/* + * + * Copyright 2015, 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 "rb_completion_queue.h" + +#include +#include + +#include +#include +#include "rb_grpc.h" + +/* grpc_rb_cCompletionQueue is the ruby class that proxies + * grpc_completion_queue. */ +static VALUE grpc_rb_cCompletionQueue = Qnil; + +/* Used to allow grpc_completion_queue_next call to release the GIL */ +typedef struct next_call_stack { + grpc_completion_queue *cq; + grpc_event event; + gpr_timespec timeout; + void *tag; +} next_call_stack; + +/* Calls grpc_completion_queue_next without holding the ruby GIL */ +static void *grpc_rb_completion_queue_next_no_gil(void *param) { + next_call_stack *const next_call = (next_call_stack*)param; + next_call->event = + grpc_completion_queue_next(next_call->cq, next_call->timeout, NULL); + return NULL; +} + +/* Calls grpc_completion_queue_pluck without holding the ruby GIL */ +static void *grpc_rb_completion_queue_pluck_no_gil(void *param) { + next_call_stack *const next_call = (next_call_stack*)param; + next_call->event = grpc_completion_queue_pluck(next_call->cq, next_call->tag, + next_call->timeout, NULL); + return NULL; +} + +/* Shuts down and drains the completion queue if necessary. + * + * This is done when the ruby completion queue object is about to be GCed. + */ +static void grpc_rb_completion_queue_shutdown_drain(grpc_completion_queue *cq) { + next_call_stack next_call; + grpc_completion_type type; + int drained = 0; + MEMZERO(&next_call, next_call_stack, 1); + + grpc_completion_queue_shutdown(cq); + next_call.cq = cq; + next_call.event.type = GRPC_QUEUE_TIMEOUT; + /* TODO: the timeout should be a module level constant that defaults + * to gpr_inf_future(GPR_CLOCK_REALTIME). + * + * - at the moment this does not work, it stalls. Using a small timeout like + * this one works, and leads to fast test run times; a longer timeout was + * causing unnecessary delays in the test runs. + * + * - investigate further, this is probably another example of C-level cleanup + * not working consistently in all cases. + */ + next_call.timeout = gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), + gpr_time_from_micros(5e3, GPR_TIMESPAN)); + do { + rb_thread_call_without_gvl(grpc_rb_completion_queue_next_no_gil, + (void *)&next_call, NULL, NULL); + type = next_call.event.type; + if (type == GRPC_QUEUE_TIMEOUT) break; + if (type != GRPC_QUEUE_SHUTDOWN) { + ++drained; + rb_warning("completion queue shutdown: %d undrained events", drained); + } + } while (type != GRPC_QUEUE_SHUTDOWN); +} + +/* Helper function to free a completion queue. */ +static void grpc_rb_completion_queue_destroy(void *p) { + grpc_completion_queue *cq = NULL; + if (p == NULL) { + return; + } + cq = (grpc_completion_queue *)p; + grpc_rb_completion_queue_shutdown_drain(cq); + grpc_completion_queue_destroy(cq); +} + +static rb_data_type_t grpc_rb_completion_queue_data_type = { + "grpc_completion_queue", + {GRPC_RB_GC_NOT_MARKED, grpc_rb_completion_queue_destroy, + GRPC_RB_MEMSIZE_UNAVAILABLE, {NULL, NULL}}, + NULL, NULL, + /* cannot immediately free because grpc_rb_completion_queue_shutdown_drain + * calls rb_thread_call_without_gvl. */ + 0 +}; + +/* Allocates a completion queue. */ +static VALUE grpc_rb_completion_queue_alloc(VALUE cls) { + grpc_completion_queue *cq = grpc_completion_queue_create(NULL); + if (cq == NULL) { + rb_raise(rb_eArgError, "could not create a completion queue: not sure why"); + } + return TypedData_Wrap_Struct(cls, &grpc_rb_completion_queue_data_type, cq); +} + +/* Blocks until the next event for given tag is available, and returns the + * event. */ +grpc_event grpc_rb_completion_queue_pluck_event(VALUE self, VALUE tag, + VALUE timeout) { + next_call_stack next_call; + MEMZERO(&next_call, next_call_stack, 1); + TypedData_Get_Struct(self, grpc_completion_queue, + &grpc_rb_completion_queue_data_type, next_call.cq); + if (TYPE(timeout) == T_NIL) { + next_call.timeout = gpr_inf_future(GPR_CLOCK_REALTIME); + } else { + next_call.timeout = grpc_rb_time_timeval(timeout, /* absolute time*/ 0); + } + if (TYPE(tag) == T_NIL) { + next_call.tag = NULL; + } else { + next_call.tag = ROBJECT(tag); + } + next_call.event.type = GRPC_QUEUE_TIMEOUT; + rb_thread_call_without_gvl(grpc_rb_completion_queue_pluck_no_gil, + (void *)&next_call, NULL, NULL); + return next_call.event; +} + +void Init_grpc_completion_queue() { + grpc_rb_cCompletionQueue = + rb_define_class_under(grpc_rb_mGrpcCore, "CompletionQueue", rb_cObject); + + /* constructor: uses an alloc func without an initializer. Using a simple + alloc func works here as the grpc header does not specify any args for + this func, so no separate initialization step is necessary. */ + rb_define_alloc_func(grpc_rb_cCompletionQueue, + grpc_rb_completion_queue_alloc); +} + +/* Gets the wrapped completion queue from the ruby wrapper */ +grpc_completion_queue *grpc_rb_get_wrapped_completion_queue(VALUE v) { + grpc_completion_queue *cq = NULL; + TypedData_Get_Struct(v, grpc_completion_queue, + &grpc_rb_completion_queue_data_type, cq); + return cq; +} diff --git a/src/ruby/ext/grpc/rb_completion_queue.h b/src/ruby/ext/grpc/rb_completion_queue.h new file mode 100644 index 00000000..6cc4e965 --- /dev/null +++ b/src/ruby/ext/grpc/rb_completion_queue.h @@ -0,0 +1,55 @@ +/* + * + * Copyright 2015, 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_RB_COMPLETION_QUEUE_H_ +#define GRPC_RB_COMPLETION_QUEUE_H_ + +#include + +#include + +/* Gets the wrapped completion queue from the ruby wrapper */ +grpc_completion_queue *grpc_rb_get_wrapped_completion_queue(VALUE v); + +/** + * Makes the implementation of CompletionQueue#pluck available in other files + * + * This avoids having code that holds the GIL repeated at multiple sites. + */ +grpc_event grpc_rb_completion_queue_pluck_event(VALUE cqueue, VALUE tag, + VALUE timeout); + +/* Initializes the CompletionQueue class. */ +void Init_grpc_completion_queue(); + +#endif /* GRPC_RB_COMPLETION_QUEUE_H_ */ diff --git a/src/ruby/ext/grpc/rb_credentials.c b/src/ruby/ext/grpc/rb_credentials.c new file mode 100644 index 00000000..ae757f69 --- /dev/null +++ b/src/ruby/ext/grpc/rb_credentials.c @@ -0,0 +1,294 @@ +/* + * + * Copyright 2015, 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 "rb_credentials.h" + +#include + +#include +#include + +#include "rb_grpc.h" + +/* grpc_rb_cCredentials is the ruby class that proxies grpc_credentials. */ +static VALUE grpc_rb_cCredentials = Qnil; + +/* grpc_rb_credentials wraps a grpc_credentials. It provides a + * peer ruby object, 'mark' to minimize copying when a credential is + * created from ruby. */ +typedef struct grpc_rb_credentials { + /* Holder of ruby objects involved in constructing the credentials */ + VALUE mark; + + /* The actual credentials */ + grpc_credentials *wrapped; +} grpc_rb_credentials; + +/* Destroys the credentials instances. */ +static void grpc_rb_credentials_free(void *p) { + grpc_rb_credentials *wrapper = NULL; + if (p == NULL) { + return; + }; + wrapper = (grpc_rb_credentials *)p; + + /* Delete the wrapped object if the mark object is Qnil, which indicates that + * no other object is the actual owner. */ + if (wrapper->wrapped != NULL && wrapper->mark == Qnil) { + grpc_credentials_release(wrapper->wrapped); + wrapper->wrapped = NULL; + } + + xfree(p); +} + +/* Protects the mark object from GC */ +static void grpc_rb_credentials_mark(void *p) { + grpc_rb_credentials *wrapper = NULL; + if (p == NULL) { + return; + } + wrapper = (grpc_rb_credentials *)p; + + /* If it's not already cleaned up, mark the mark object */ + if (wrapper->mark != Qnil) { + rb_gc_mark(wrapper->mark); + } +} + +static rb_data_type_t grpc_rb_credentials_data_type = { + "grpc_credentials", + {grpc_rb_credentials_mark, grpc_rb_credentials_free, + GRPC_RB_MEMSIZE_UNAVAILABLE, {NULL, NULL}}, + NULL, + NULL, + RUBY_TYPED_FREE_IMMEDIATELY}; + +/* Allocates Credential instances. + Provides safe initial defaults for the instance fields. */ +static VALUE grpc_rb_credentials_alloc(VALUE cls) { + grpc_rb_credentials *wrapper = ALLOC(grpc_rb_credentials); + wrapper->wrapped = NULL; + wrapper->mark = Qnil; + return TypedData_Wrap_Struct(cls, &grpc_rb_credentials_data_type, wrapper); +} + +/* Clones Credentials instances. + Gives Credentials a consistent implementation of Ruby's object copy/dup + protocol. */ +static VALUE grpc_rb_credentials_init_copy(VALUE copy, VALUE orig) { + grpc_rb_credentials *orig_cred = NULL; + grpc_rb_credentials *copy_cred = NULL; + + if (copy == orig) { + return copy; + } + + /* Raise an error if orig is not a credentials object or a subclass. */ + if (TYPE(orig) != T_DATA || + RDATA(orig)->dfree != (RUBY_DATA_FUNC)grpc_rb_credentials_free) { + rb_raise(rb_eTypeError, "not a %s", rb_obj_classname(grpc_rb_cCredentials)); + } + + TypedData_Get_Struct(orig, grpc_rb_credentials, + &grpc_rb_credentials_data_type, orig_cred); + TypedData_Get_Struct(copy, grpc_rb_credentials, + &grpc_rb_credentials_data_type, copy_cred); + + /* use ruby's MEMCPY to make a byte-for-byte copy of the credentials + * wrapper object. */ + MEMCPY(copy_cred, orig_cred, grpc_rb_credentials, 1); + return copy; +} + +/* + call-seq: + creds = Credentials.default() + Creates the default credential instances. */ +static VALUE grpc_rb_default_credentials_create(VALUE cls) { + grpc_rb_credentials *wrapper = ALLOC(grpc_rb_credentials); + wrapper->wrapped = grpc_google_default_credentials_create(); + if (wrapper->wrapped == NULL) { + rb_raise(rb_eRuntimeError, + "could not create default credentials, not sure why"); + return Qnil; + } + + wrapper->mark = Qnil; + return TypedData_Wrap_Struct(cls, &grpc_rb_credentials_data_type, wrapper); +} + +/* + call-seq: + creds = Credentials.compute_engine() + Creates the default credential instances. */ +static VALUE grpc_rb_compute_engine_credentials_create(VALUE cls) { + grpc_rb_credentials *wrapper = ALLOC(grpc_rb_credentials); + wrapper->wrapped = grpc_google_compute_engine_credentials_create(NULL); + if (wrapper->wrapped == NULL) { + rb_raise(rb_eRuntimeError, + "could not create composite engine credentials, not sure why"); + return Qnil; + } + + wrapper->mark = Qnil; + return TypedData_Wrap_Struct(cls, &grpc_rb_credentials_data_type, wrapper); +} + +/* + call-seq: + creds1 = ... + creds2 = ... + creds3 = creds1.add(creds2) + Creates the default credential instances. */ +static VALUE grpc_rb_composite_credentials_create(VALUE self, VALUE other) { + grpc_rb_credentials *self_wrapper = NULL; + grpc_rb_credentials *other_wrapper = NULL; + grpc_rb_credentials *wrapper = NULL; + + TypedData_Get_Struct(self, grpc_rb_credentials, + &grpc_rb_credentials_data_type, self_wrapper); + TypedData_Get_Struct(other, grpc_rb_credentials, + &grpc_rb_credentials_data_type, other_wrapper); + wrapper = ALLOC(grpc_rb_credentials); + wrapper->wrapped = grpc_composite_credentials_create( + self_wrapper->wrapped, other_wrapper->wrapped, NULL); + if (wrapper->wrapped == NULL) { + rb_raise(rb_eRuntimeError, + "could not create composite credentials, not sure why"); + return Qnil; + } + + wrapper->mark = Qnil; + return TypedData_Wrap_Struct(grpc_rb_cCredentials, + &grpc_rb_credentials_data_type, wrapper); +} + +/* The attribute used on the mark object to hold the pem_root_certs. */ +static ID id_pem_root_certs; + +/* The attribute used on the mark object to hold the pem_private_key. */ +static ID id_pem_private_key; + +/* The attribute used on the mark object to hold the pem_private_key. */ +static ID id_pem_cert_chain; + +/* + call-seq: + creds1 = Credentials.new(pem_root_certs) + ... + creds2 = Credentials.new(pem_root_certs, pem_private_key, + pem_cert_chain) + pem_root_certs: (required) PEM encoding of the server root certificate + pem_private_key: (optional) PEM encoding of the client's private key + pem_cert_chain: (optional) PEM encoding of the client's cert chain + Initializes Credential instances. */ +static VALUE grpc_rb_credentials_init(int argc, VALUE *argv, VALUE self) { + VALUE pem_root_certs = Qnil; + VALUE pem_private_key = Qnil; + VALUE pem_cert_chain = Qnil; + grpc_rb_credentials *wrapper = NULL; + grpc_credentials *creds = NULL; + grpc_ssl_pem_key_cert_pair key_cert_pair; + MEMZERO(&key_cert_pair, grpc_ssl_pem_key_cert_pair, 1); + /* TODO: Remove mandatory arg when we support default roots. */ + /* "12" == 1 mandatory arg, 2 (credentials) is optional */ + rb_scan_args(argc, argv, "12", &pem_root_certs, &pem_private_key, + &pem_cert_chain); + + TypedData_Get_Struct(self, grpc_rb_credentials, + &grpc_rb_credentials_data_type, wrapper); + if (pem_root_certs == Qnil) { + rb_raise(rb_eRuntimeError, + "could not create a credential: nil pem_root_certs"); + return Qnil; + } + if (pem_private_key == Qnil && pem_cert_chain == Qnil) { + creds = + grpc_ssl_credentials_create(RSTRING_PTR(pem_root_certs), NULL, NULL); + } else { + key_cert_pair.private_key = RSTRING_PTR(pem_private_key); + key_cert_pair.cert_chain = RSTRING_PTR(pem_cert_chain); + creds = grpc_ssl_credentials_create(RSTRING_PTR(pem_root_certs), + &key_cert_pair, NULL); + } + if (creds == NULL) { + rb_raise(rb_eRuntimeError, "could not create a credentials, not sure why"); + return Qnil; + } + wrapper->wrapped = creds; + + /* Add the input objects as hidden fields to preserve them. */ + rb_ivar_set(self, id_pem_cert_chain, pem_cert_chain); + rb_ivar_set(self, id_pem_private_key, pem_private_key); + rb_ivar_set(self, id_pem_root_certs, pem_root_certs); + + return self; +} + +void Init_grpc_credentials() { + grpc_rb_cCredentials = + rb_define_class_under(grpc_rb_mGrpcCore, "Credentials", rb_cObject); + + /* Allocates an object managed by the ruby runtime */ + rb_define_alloc_func(grpc_rb_cCredentials, grpc_rb_credentials_alloc); + + /* Provides a ruby constructor and support for dup/clone. */ + rb_define_method(grpc_rb_cCredentials, "initialize", grpc_rb_credentials_init, + -1); + rb_define_method(grpc_rb_cCredentials, "initialize_copy", + grpc_rb_credentials_init_copy, 1); + + /* Provide static funcs that create new special instances. */ + rb_define_singleton_method(grpc_rb_cCredentials, "default", + grpc_rb_default_credentials_create, 0); + + rb_define_singleton_method(grpc_rb_cCredentials, "compute_engine", + grpc_rb_compute_engine_credentials_create, 0); + + /* Provide other methods. */ + rb_define_method(grpc_rb_cCredentials, "compose", + grpc_rb_composite_credentials_create, 1); + + id_pem_cert_chain = rb_intern("__pem_cert_chain"); + id_pem_private_key = rb_intern("__pem_private_key"); + id_pem_root_certs = rb_intern("__pem_root_certs"); +} + +/* Gets the wrapped grpc_credentials from the ruby wrapper */ +grpc_credentials *grpc_rb_get_wrapped_credentials(VALUE v) { + grpc_rb_credentials *wrapper = NULL; + TypedData_Get_Struct(v, grpc_rb_credentials, &grpc_rb_credentials_data_type, + wrapper); + return wrapper->wrapped; +} diff --git a/src/ruby/ext/grpc/rb_credentials.h b/src/ruby/ext/grpc/rb_credentials.h new file mode 100644 index 00000000..840f7d5f --- /dev/null +++ b/src/ruby/ext/grpc/rb_credentials.h @@ -0,0 +1,47 @@ +/* + * + * Copyright 2015, 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_RB_CREDENTIALS_H_ +#define GRPC_RB_CREDENTIALS_H_ + +#include + +#include + +/* Initializes the ruby Credentials class. */ +void Init_grpc_credentials(); + +/* Gets the wrapped credentials from the ruby wrapper */ +grpc_credentials* grpc_rb_get_wrapped_credentials(VALUE v); + +#endif /* GRPC_RB_CREDENTIALS_H_ */ diff --git a/src/ruby/ext/grpc/rb_grpc.c b/src/ruby/ext/grpc/rb_grpc.c new file mode 100644 index 00000000..327fd1a4 --- /dev/null +++ b/src/ruby/ext/grpc/rb_grpc.c @@ -0,0 +1,308 @@ +/* + * + * Copyright 2015, 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 "rb_grpc.h" + +#include +#include +#include +#include + +#include +#include +#include "rb_call.h" +#include "rb_channel.h" +#include "rb_completion_queue.h" +#include "rb_server.h" +#include "rb_credentials.h" +#include "rb_server_credentials.h" + +static VALUE grpc_rb_cTimeVal = Qnil; + +static rb_data_type_t grpc_rb_timespec_data_type = { + "gpr_timespec", + {GRPC_RB_GC_NOT_MARKED, GRPC_RB_GC_DONT_FREE, GRPC_RB_MEMSIZE_UNAVAILABLE, + {NULL, NULL}}, + NULL, + NULL, + RUBY_TYPED_FREE_IMMEDIATELY}; + +/* Alloc func that blocks allocation of a given object by raising an + * exception. */ +VALUE grpc_rb_cannot_alloc(VALUE cls) { + rb_raise(rb_eTypeError, + "allocation of %s only allowed from the gRPC native layer", + rb_class2name(cls)); + return Qnil; +} + +/* Init func that fails by raising an exception. */ +VALUE grpc_rb_cannot_init(VALUE self) { + rb_raise(rb_eTypeError, + "initialization of %s only allowed from the gRPC native layer", + rb_obj_classname(self)); + return Qnil; +} + +/* Init/Clone func that fails by raising an exception. */ +VALUE grpc_rb_cannot_init_copy(VALUE copy, VALUE self) { + (void)self; + rb_raise(rb_eTypeError, + "initialization of %s only allowed from the gRPC native layer", + rb_obj_classname(copy)); + return Qnil; +} + +/* id_tv_{,u}sec are accessor methods on Ruby Time instances. */ +static ID id_tv_sec; +static ID id_tv_nsec; + +/** + * grpc_rb_time_timeval creates a time_eval from a ruby time object. + * + * This func is copied from ruby source, MRI/source/time.c, which is published + * under the same license as the ruby.h, on which the entire extensions is + * based. + */ +gpr_timespec grpc_rb_time_timeval(VALUE time, int interval) { + gpr_timespec t; + gpr_timespec *time_const; + const char *tstr = interval ? "time interval" : "time"; + const char *want = " want |